src/window.cpp
branchNewGRF_ports
changeset 10724 68a692eacf22
parent 10274 b3c58f3df92b
child 10731 67db0d431d5e
equal deleted inserted replaced
10349:ff900a23e102 10724:68a692eacf22
     1 /* $Id$ */
     1 /* $Id$ */
     2 
     2 
     3 /** @file window.cpp windowing system, widgets and events */
     3 /** @file window.cpp Windowing system, widgets and events */
     4 
     4 
     5 #include "stdafx.h"
     5 #include "stdafx.h"
     6 #include <stdarg.h>
     6 #include <stdarg.h>
     7 #include "openttd.h"
     7 #include "openttd.h"
     8 #include "debug.h"
     8 #include "debug.h"
    18 #include "core/alloc_func.hpp"
    18 #include "core/alloc_func.hpp"
    19 #include "map_func.h"
    19 #include "map_func.h"
    20 #include "vehicle_base.h"
    20 #include "vehicle_base.h"
    21 #include "settings_type.h"
    21 #include "settings_type.h"
    22 #include "cheat_func.h"
    22 #include "cheat_func.h"
       
    23 #include "window_func.h"
       
    24 #include "tilehighlight_func.h"
    23 
    25 
    24 #include "table/sprites.h"
    26 #include "table/sprites.h"
    25 
    27 
    26 static Point _drag_delta; ///< delta between mouse cursor and upper left corner of dragged window
    28 static Point _drag_delta; ///< delta between mouse cursor and upper left corner of dragged window
    27 static Window *_mouseover_last_w = NULL; ///< Window of the last MOUSEOVER event
    29 static Window *_mouseover_last_w = NULL; ///< Window of the last MOUSEOVER event
    40 int _scrollbar_size;
    42 int _scrollbar_size;
    41 byte _scroller_click_timeout;
    43 byte _scroller_click_timeout;
    42 
    44 
    43 bool _scrolling_scrollbar;
    45 bool _scrolling_scrollbar;
    44 bool _scrolling_viewport;
    46 bool _scrolling_viewport;
    45 bool _popup_menu_active;
       
    46 
    47 
    47 byte _special_mouse_mode;
    48 byte _special_mouse_mode;
       
    49 
       
    50 
       
    51 /**
       
    52  * Call the window event handler for handling event \a e.
       
    53  * This is a temporary helper functions that will be removed
       
    54  * once all windows that still rely on WindowEvent and
       
    55  * WindowEventCodes have been rewritten to use the 'OnXXX'
       
    56  * event handlers.
       
    57  * @param e Window event to handle
       
    58  */
       
    59 void Window::HandleWindowEvent(WindowEvent *e)
       
    60 {
       
    61 	if (wndproc != NULL) wndproc(this, e);
       
    62 }
       
    63 
       
    64 void Window::OnPaint()
       
    65 {
       
    66 	WindowEvent e;
       
    67 	e.event = WE_PAINT;
       
    68 	this->HandleWindowEvent(&e);
       
    69 }
       
    70 
       
    71 bool Window::OnKeyPress(uint16 key, uint16 keycode)
       
    72 {
       
    73 	WindowEvent e;
       
    74 	e.event = WE_KEYPRESS;
       
    75 	e.we.keypress.key     = key;
       
    76 	e.we.keypress.keycode = keycode;
       
    77 	e.we.keypress.cont    = true;
       
    78 	this->HandleWindowEvent(&e);
       
    79 
       
    80 	return e.we.keypress.cont;
       
    81 }
       
    82 
       
    83 bool Window::OnCTRLStateChange()
       
    84 {
       
    85 	WindowEvent e;
       
    86 	e.event = WE_CTRL_CHANGED;
       
    87 	e.we.ctrl.cont = true;
       
    88 	this->HandleWindowEvent(&e);
       
    89 
       
    90 	return e.we.ctrl.cont;
       
    91 }
       
    92 
       
    93 void Window::OnClick(Point pt, int widget)
       
    94 {
       
    95 	WindowEvent e;
       
    96 	e.event = WE_CLICK;
       
    97 	e.we.click.pt     = pt;
       
    98 	e.we.click.widget = widget;
       
    99 	this->HandleWindowEvent(&e);
       
   100 }
       
   101 
       
   102 void Window::OnMouseLoop()
       
   103 {
       
   104 	WindowEvent e;
       
   105 	e.event = WE_MOUSELOOP;
       
   106 	this->HandleWindowEvent(&e);
       
   107 }
       
   108 
       
   109 void Window::OnTick()
       
   110 {
       
   111 	WindowEvent e;
       
   112 	e.event = WE_TICK;
       
   113 	this->HandleWindowEvent(&e);
       
   114 }
       
   115 
       
   116 void Window::OnHundredthTick()
       
   117 {
       
   118 	WindowEvent e;
       
   119 	e.event = WE_100_TICKS;
       
   120 	this->HandleWindowEvent(&e);
       
   121 }
       
   122 
       
   123 void Window::OnTimeout()
       
   124 {
       
   125 	WindowEvent e;
       
   126 	e.event = WE_TIMEOUT;
       
   127 	this->HandleWindowEvent(&e);
       
   128 }
       
   129 
       
   130 void Window::OnResize(Point new_size, Point delta)
       
   131 {
       
   132 	WindowEvent e;
       
   133 	e.event = WE_RESIZE;
       
   134 	e.we.sizing.size = new_size;
       
   135 	e.we.sizing.diff = delta;
       
   136 	this->HandleWindowEvent(&e);
       
   137 }
       
   138 
       
   139 void Window::OnDropdownSelect(int widget, int index)
       
   140 {
       
   141 	WindowEvent e;
       
   142 	e.event = WE_DROPDOWN_SELECT;
       
   143 	e.we.dropdown.button = widget;
       
   144 	e.we.dropdown.index  = index;
       
   145 	this->HandleWindowEvent(&e);
       
   146 }
       
   147 
       
   148 void Window::OnQueryTextFinished(char *str)
       
   149 {
       
   150 	WindowEvent e;
       
   151 	e.event = WE_ON_EDIT_TEXT;
       
   152 	e.we.edittext.str = str;
       
   153 	this->HandleWindowEvent(&e);
       
   154 }
       
   155 
       
   156 void Window::OnInvalidateData(int data)
       
   157 {
       
   158 	WindowEvent e;
       
   159 	e.event = WE_INVALIDATE_DATA;
       
   160 	e.we.invalidate.data = data;
       
   161 	this->HandleWindowEvent(&e);
       
   162 }
       
   163 
       
   164 void Window::OnPlaceObject(Point pt, TileIndex tile)
       
   165 {
       
   166 	WindowEvent e;
       
   167 	e.event = WE_PLACE_OBJ;
       
   168 	e.we.place.pt   = pt;
       
   169 	e.we.place.tile = tile;
       
   170 	this->HandleWindowEvent(&e);
       
   171 }
       
   172 
       
   173 void Window::OnPlaceObjectAbort()
       
   174 {
       
   175 	WindowEvent e;
       
   176 	e.event = WE_ABORT_PLACE_OBJ;
       
   177 	this->HandleWindowEvent(&e);
       
   178 }
       
   179 
       
   180 
       
   181 void Window::OnPlaceDrag(ViewportPlaceMethod select_method, byte select_proc, Point pt)
       
   182 {
       
   183 	WindowEvent e;
       
   184 	e.event = WE_PLACE_DRAG;
       
   185 	e.we.place.select_method = select_method;
       
   186 	e.we.place.select_proc   = select_proc;
       
   187 	e.we.place.pt            = pt;
       
   188 	this->HandleWindowEvent(&e);
       
   189 }
       
   190 
       
   191 void Window::OnPlaceMouseUp(ViewportPlaceMethod select_method, byte select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
       
   192 {
       
   193 	WindowEvent e;
       
   194 	e.event = WE_PLACE_MOUSEUP;
       
   195 	e.we.place.select_method = select_method;
       
   196 	e.we.place.select_proc   = select_proc;
       
   197 	e.we.place.pt            = pt;
       
   198 	e.we.place.tile          = end_tile;
       
   199 	e.we.place.starttile     = start_tile;
       
   200 	this->HandleWindowEvent(&e);
       
   201 }
       
   202 
       
   203 void Window::OnPlacePresize(Point pt, TileIndex tile)
       
   204 {
       
   205 	WindowEvent e;
       
   206 	e.event = WE_PLACE_PRESIZE;
       
   207 	e.we.place.pt   = pt;
       
   208 	e.we.place.tile = tile;
       
   209 	this->HandleWindowEvent(&e);
       
   210 }
       
   211 
    48 
   212 
    49 
   213 
    50 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
   214 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
    51 {
   215 {
    52 	va_list wdg_list;
   216 	va_list wdg_list;
    89 	va_end(wdg_list);
   253 	va_end(wdg_list);
    90 }
   254 }
    91 
   255 
    92 void Window::RaiseButtons()
   256 void Window::RaiseButtons()
    93 {
   257 {
    94 	uint i;
   258 	for (uint i = 0; i < this->widget_count; i++) {
    95 
       
    96 	for (i = 0; i < this->widget_count; i++) {
       
    97 		if (this->IsWidgetLowered(i)) {
   259 		if (this->IsWidgetLowered(i)) {
    98 			this->RaiseWidget(i);
   260 			this->RaiseWidget(i);
    99 			this->InvalidateWidget(i);
   261 			this->InvalidateWidget(i);
   100 		}
   262 		}
   101 	}
   263 	}
   128  * @param y Y coordinate of the click
   290  * @param y Y coordinate of the click
   129  * @param double_click Was it a double click?
   291  * @param double_click Was it a double click?
   130  */
   292  */
   131 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click)
   293 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click)
   132 {
   294 {
   133 	WindowEvent e;
   295 	int widget = 0;
   134 	const Widget *wi;
       
   135 
       
   136 	e.we.click.pt.x = x;
       
   137 	e.we.click.pt.y = y;
       
   138 	e.event = double_click ? WE_DOUBLE_CLICK : WE_CLICK;
       
   139 
       
   140 	if (w->desc_flags & WDF_DEF_WIDGET) {
   296 	if (w->desc_flags & WDF_DEF_WIDGET) {
   141 		e.we.click.widget = GetWidgetFromPos(w, x, y);
   297 		widget = GetWidgetFromPos(w, x, y);
   142 		if (e.we.click.widget < 0) return; // exit if clicked outside of widgets
   298 		if (widget < 0) return; // exit if clicked outside of widgets
   143 
   299 
   144 		/* don't allow any interaction if the button has been disabled */
   300 		/* don't allow any interaction if the button has been disabled */
   145 		if (w->IsWidgetDisabled(e.we.click.widget)) return;
   301 		if (w->IsWidgetDisabled(widget)) return;
   146 
   302 
   147 		wi = &w->widget[e.we.click.widget];
   303 		const Widget *wi = &w->widget[widget];
   148 
   304 
   149 		if (wi->type & WWB_MASK) {
   305 		if (wi->type & WWB_MASK) {
   150 			/* special widget handling for buttons*/
   306 			/* special widget handling for buttons*/
   151 			switch (wi->type) {
   307 			switch (wi->type) {
   152 				case WWT_PANEL   | WWB_PUSHBUTTON: /* WWT_PUSHBTN */
   308 				case WWT_PANEL   | WWB_PUSHBUTTON: /* WWT_PUSHBTN */
   153 				case WWT_IMGBTN  | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */
   309 				case WWT_IMGBTN  | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */
   154 				case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */
   310 				case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */
   155 					w->HandleButtonClick(e.we.click.widget);
   311 					w->HandleButtonClick(widget);
   156 					break;
   312 					break;
   157 			}
   313 			}
   158 		} else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
   314 		} else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
   159 			ScrollbarClickHandler(w, wi, e.we.click.pt.x, e.we.click.pt.y);
   315 			ScrollbarClickHandler(w, wi, x, y);
   160 		}
   316 		}
   161 
   317 
   162 		if (w->desc_flags & WDF_STD_BTN) {
   318 		if (w->desc_flags & WDF_STD_BTN) {
   163 			if (e.we.click.widget == 0) { /* 'X' */
   319 			if (widget == 0) { /* 'X' */
   164 				DeleteWindow(w);
   320 				delete w;
   165 				return;
   321 				return;
   166 			}
   322 			}
   167 
   323 
   168 			if (e.we.click.widget == 1) { /* 'Title bar' */
   324 			if (widget == 1) { /* 'Title bar' */
   169 				StartWindowDrag(w);
   325 				StartWindowDrag(w);
   170 				return;
   326 				return;
   171 			}
   327 			}
   172 		}
   328 		}
   173 
   329 
   174 		if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
   330 		if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
   175 			StartWindowSizing(w);
   331 			StartWindowSizing(w);
   176 			w->InvalidateWidget(e.we.click.widget);
   332 			w->InvalidateWidget(widget);
   177 			return;
   333 			return;
   178 		}
   334 		}
   179 
   335 
   180 		if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) {
   336 		if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) {
   181 			w->flags4 ^= WF_STICKY;
   337 			w->flags4 ^= WF_STICKY;
   182 			w->InvalidateWidget(e.we.click.widget);
   338 			w->InvalidateWidget(widget);
   183 			return;
   339 			return;
   184 		}
   340 		}
   185 	}
   341 	}
   186 
   342 
   187 	w->wndproc(w, &e);
   343 	Point pt = { x, y };
       
   344 
       
   345 	if (double_click) {
       
   346 		w->OnDoubleClick(pt, widget);
       
   347 	} else {
       
   348 		w->OnClick(pt, widget);
       
   349 	}
   188 }
   350 }
   189 
   351 
   190 /**
   352 /**
   191  * Dispatch right mouse-button click in window.
   353  * Dispatch right mouse-button click in window.
   192  * @param w Window to dispatch event in
   354  * @param w Window to dispatch event in
   193  * @param x X coordinate of the click
   355  * @param x X coordinate of the click
   194  * @param y Y coordinate of the click
   356  * @param y Y coordinate of the click
   195  */
   357  */
   196 static void DispatchRightClickEvent(Window *w, int x, int y)
   358 static void DispatchRightClickEvent(Window *w, int x, int y)
   197 {
   359 {
   198 	WindowEvent e;
   360 	int widget = 0;
   199 
   361 
   200 	/* default tooltips handler? */
   362 	/* default tooltips handler? */
   201 	if (w->desc_flags & WDF_STD_TOOLTIPS) {
   363 	if (w->desc_flags & WDF_STD_TOOLTIPS) {
   202 		e.we.click.widget = GetWidgetFromPos(w, x, y);
   364 		widget = GetWidgetFromPos(w, x, y);
   203 		if (e.we.click.widget < 0)
   365 		if (widget < 0) return; // exit if clicked outside of widgets
   204 			return; // exit if clicked outside of widgets
   366 
   205 
   367 		if (w->widget[widget].tooltips != 0) {
   206 		if (w->widget[e.we.click.widget].tooltips != 0) {
   368 			GuiShowTooltips(w->widget[widget].tooltips);
   207 			GuiShowTooltips(w->widget[e.we.click.widget].tooltips);
       
   208 			return;
   369 			return;
   209 		}
   370 		}
   210 	}
   371 	}
   211 
   372 
   212 	e.event = WE_RCLICK;
   373 	Point pt = { x, y };
   213 	e.we.click.pt.x = x;
   374 	w->OnRightClick(pt, widget);
   214 	e.we.click.pt.y = y;
       
   215 	w->wndproc(w, &e);
       
   216 }
   375 }
   217 
   376 
   218 /**
   377 /**
   219  * Dispatch the mousewheel-action to the window.
   378  * Dispatch the mousewheel-action to the window.
   220  * The window will scroll any compatible scrollbars if the mouse is pointed over the bar or its contents
   379  * The window will scroll any compatible scrollbars if the mouse is pointed over the bar or its contents
   222  * @param widget the widget where the scrollwheel was used
   381  * @param widget the widget where the scrollwheel was used
   223  * @param wheel scroll up or down
   382  * @param wheel scroll up or down
   224  */
   383  */
   225 static void DispatchMouseWheelEvent(Window *w, int widget, int wheel)
   384 static void DispatchMouseWheelEvent(Window *w, int widget, int wheel)
   226 {
   385 {
   227 	const Widget *wi1, *wi2;
       
   228 	Scrollbar *sb;
       
   229 
       
   230 	if (widget < 0) return;
   386 	if (widget < 0) return;
   231 
   387 
   232 	wi1 = &w->widget[widget];
   388 	const Widget *wi1 = &w->widget[widget];
   233 	wi2 = &w->widget[widget + 1];
   389 	const Widget *wi2 = &w->widget[widget + 1];
   234 
   390 
   235 	/* The listbox can only scroll if scrolling was done on the scrollbar itself,
   391 	/* The listbox can only scroll if scrolling was done on the scrollbar itself,
   236 	 * or on the listbox (and the next item is (must be) the scrollbar)
   392 	 * or on the listbox (and the next item is (must be) the scrollbar)
   237 	 * XXX - should be rewritten as a widget-dependent scroller but that's
   393 	 * XXX - should be rewritten as a widget-dependent scroller but that's
   238 	 * not happening until someone rewrites the whole widget-code */
   394 	 * not happening until someone rewrites the whole widget-code */
       
   395 	Scrollbar *sb;
   239 	if ((sb = &w->vscroll,  wi1->type == WWT_SCROLLBAR)  || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR)  ||
   396 	if ((sb = &w->vscroll,  wi1->type == WWT_SCROLLBAR)  || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR)  ||
   240 			(sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) {
   397 			(sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) {
   241 
   398 
   242 		if (sb->count > sb->cap) {
   399 		if (sb->count > sb->cap) {
   243 			int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap);
   400 			int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap);
   244 			if (pos != sb->pos) {
   401 			if (pos != sb->pos) {
   245 				sb->pos = pos;
   402 				sb->pos = pos;
   246 				SetWindowDirty(w);
   403 				w->SetDirty();
   247 			}
   404 			}
   248 		}
   405 		}
   249 	}
   406 	}
   250 }
   407 }
   251 
   408 
   310 	dp->left = left - (*wz)->left;
   467 	dp->left = left - (*wz)->left;
   311 	dp->top = top - (*wz)->top;
   468 	dp->top = top - (*wz)->top;
   312 	dp->pitch = _screen.pitch;
   469 	dp->pitch = _screen.pitch;
   313 	dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
   470 	dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
   314 	dp->zoom = ZOOM_LVL_NORMAL;
   471 	dp->zoom = ZOOM_LVL_NORMAL;
   315 	CallWindowEventNP(*wz, WE_PAINT);
   472 	(*wz)->OnPaint();
   316 }
   473 }
   317 
   474 
   318 /**
   475 /**
   319  * From a rectangle that needs redrawing, find the windows that intersect with the rectangle.
   476  * From a rectangle that needs redrawing, find the windows that intersect with the rectangle.
   320  * These windows should be re-painted.
   477  * These windows should be re-painted.
   340 		}
   497 		}
   341 	}
   498 	}
   342 }
   499 }
   343 
   500 
   344 /**
   501 /**
   345  * Dispatch an event to a possibly non-existing window.
       
   346  * If the window pointer w is \c NULL, the event is not dispatched
       
   347  * @param w Window to dispatch the event to, may be \c NULL
       
   348  * @param event Event to dispatch
       
   349  */
       
   350 void CallWindowEventNP(Window *w, int event)
       
   351 {
       
   352 	WindowEvent e;
       
   353 
       
   354 	e.event = event;
       
   355 	w->wndproc(w, &e);
       
   356 }
       
   357 
       
   358 /**
       
   359  * Mark entire window as dirty (in need of re-paint)
   502  * Mark entire window as dirty (in need of re-paint)
   360  * @param w Window to redraw
   503  * @param w Window to redraw
   361  * @ingroup dirty
   504  * @ingroup dirty
   362  */
   505  */
       
   506 void Window::SetDirty() const
       
   507 {
       
   508 	SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
       
   509 }
       
   510 
       
   511 /**
       
   512  * Mark entire window as dirty (in need of re-paint)
       
   513  * @param w Window to redraw
       
   514  * @ingroup dirty
       
   515  */
   363 void SetWindowDirty(const Window *w)
   516 void SetWindowDirty(const Window *w)
   364 {
   517 {
   365 	if (w == NULL) return;
   518 	if (w != NULL) w->SetDirty();
   366 	SetDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height);
       
   367 }
   519 }
   368 
   520 
   369 /** Find the Window whose parent pointer points to this window
   521 /** Find the Window whose parent pointer points to this window
   370  * @param w parent Window to find child of
   522  * @param w parent Window to find child of
   371  * @return a Window pointer that is the child of w, or NULL otherwise */
   523  * @return a Window pointer that is the child of w, or NULL otherwise */
   399 	return NULL;
   551 	return NULL;
   400 }
   552 }
   401 
   553 
   402 /**
   554 /**
   403  * Remove window and all its child windows from the window stack.
   555  * Remove window and all its child windows from the window stack.
   404  * @param w Window to delete
   556  */
   405  */
   557 Window::~Window()
   406 void DeleteWindow(Window *w)
   558 {
   407 {
       
   408 	if (w == NULL) return;
       
   409 
       
   410 	/* Delete any children a window might have in a head-recursive manner */
       
   411 	Window *v = FindChildWindow(w);
       
   412 	if (v != NULL) DeleteWindow(v);
       
   413 
       
   414 	if (_thd.place_mode != VHM_NONE &&
   559 	if (_thd.place_mode != VHM_NONE &&
   415 			_thd.window_class == w->window_class &&
   560 			_thd.window_class == this->window_class &&
   416 			_thd.window_number == w->window_number) {
   561 			_thd.window_number == this->window_number) {
   417 		ResetObjectToPlace();
   562 		ResetObjectToPlace();
   418 	}
   563 	}
   419 
   564 
   420 	CallWindowEventNP(w, WE_DESTROY);
       
   421 	if (w->viewport != NULL) DeleteWindowViewport(w);
       
   422 
       
   423 	SetWindowDirty(w);
       
   424 	free(w->widget);
       
   425 	w->widget = NULL;
       
   426 	w->widget_count = 0;
       
   427 	w->parent = NULL;
       
   428 
       
   429 	/* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
   565 	/* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */
   430 	if (_mouseover_last_w == w) _mouseover_last_w = NULL;
   566 	if (_mouseover_last_w == this) _mouseover_last_w = NULL;
   431 
   567 
   432 	/* Find the window in the z-array, and effectively remove it
   568 	/* Find the window in the z-array, and effectively remove it
   433 	 * by moving all windows after it one to the left */
   569 	 * by moving all windows after it one to the left. This must be
   434 	Window **wz = FindWindowZPosition(w);
   570 	 * done before removing the child so we cannot cause recursion
       
   571 	 * between the deletion of the parent and the child. */
       
   572 	Window **wz = FindWindowZPosition(this);
   435 	if (wz == NULL) return;
   573 	if (wz == NULL) return;
   436 	memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz);
   574 	memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz);
   437 	_last_z_window--;
   575 	_last_z_window--;
   438 
   576 
   439 	delete w;
   577 	/* Delete all children a window might have in a head-recursive manner */
       
   578 	Window *child = FindChildWindow(this);
       
   579 	while (child != NULL) {
       
   580 		delete child;
       
   581 		child = FindChildWindow(this);
       
   582 	}
       
   583 
       
   584 	WindowEvent e;
       
   585 	e.event = WE_DESTROY;
       
   586 	this->HandleWindowEvent(&e);
       
   587 	if (this->viewport != NULL) DeleteWindowViewport(this);
       
   588 
       
   589 	this->SetDirty();
       
   590 	free(this->widget);
   440 }
   591 }
   441 
   592 
   442 /**
   593 /**
   443  * Find a window by its class and window number
   594  * Find a window by its class and window number
   444  * @param cls Window class
   595  * @param cls Window class
   462  * @param cls Window class
   613  * @param cls Window class
   463  * @param number Number of the window within the window class
   614  * @param number Number of the window within the window class
   464  */
   615  */
   465 void DeleteWindowById(WindowClass cls, WindowNumber number)
   616 void DeleteWindowById(WindowClass cls, WindowNumber number)
   466 {
   617 {
   467 	DeleteWindow(FindWindowById(cls, number));
   618 	delete FindWindowById(cls, number);
   468 }
   619 }
   469 
   620 
   470 /**
   621 /**
   471  * Delete all windows of a given class
   622  * Delete all windows of a given class
   472  * @param cls Window class of windows to delete
   623  * @param cls Window class of windows to delete
   480 	 * as deleting this window could cascade in deleting (many) others
   631 	 * as deleting this window could cascade in deleting (many) others
   481 	 * anywhere in the z-array */
   632 	 * anywhere in the z-array */
   482 	FOR_ALL_WINDOWS(wz) {
   633 	FOR_ALL_WINDOWS(wz) {
   483 		Window *w = *wz;
   634 		Window *w = *wz;
   484 		if (w->window_class == cls) {
   635 		if (w->window_class == cls) {
   485 			DeleteWindow(w);
   636 			delete w;
   486 			goto restart_search;
   637 			goto restart_search;
   487 		}
   638 		}
   488 	}
   639 	}
   489 }
   640 }
   490 
   641 
   501 	 * as deleting this window could cascade in deleting (many) others
   652 	 * as deleting this window could cascade in deleting (many) others
   502 	 * anywhere in the z-array */
   653 	 * anywhere in the z-array */
   503 	FOR_ALL_WINDOWS(wz) {
   654 	FOR_ALL_WINDOWS(wz) {
   504 		Window *w = *wz;
   655 		Window *w = *wz;
   505 		if (w->caption_color == id) {
   656 		if (w->caption_color == id) {
   506 			DeleteWindow(w);
   657 			delete w;
   507 			goto restart_search;
   658 			goto restart_search;
   508 		}
   659 		}
   509 	}
   660 	}
   510 
   661 
   511 	/* Also delete the player specific windows, that don't have a player-colour */
   662 	/* Also delete the player specific windows, that don't have a player-colour */
   522 	Window* const *wz;
   673 	Window* const *wz;
   523 
   674 
   524 	FOR_ALL_WINDOWS(wz) {
   675 	FOR_ALL_WINDOWS(wz) {
   525 		Window *w = *wz;
   676 		Window *w = *wz;
   526 
   677 
   527 		if (w->caption_color != old_player)      continue;
   678 		if (w->caption_color != old_player) continue;
   528 		if (w->window_class == WC_PLAYER_COLOR)  continue;
   679 
   529 		if (w->window_class == WC_FINANCES)      continue;
   680 		switch (w->window_class) {
   530 		if (w->window_class == WC_STATION_LIST)  continue;
   681 			case WC_PLAYER_COLOR:
   531 		if (w->window_class == WC_TRAINS_LIST)   continue;
   682 			case WC_FINANCES:
   532 		if (w->window_class == WC_ROADVEH_LIST)  continue;
   683 			case WC_STATION_LIST:
   533 		if (w->window_class == WC_SHIPS_LIST)    continue;
   684 			case WC_TRAINS_LIST:
   534 		if (w->window_class == WC_AIRCRAFT_LIST) continue;
   685 			case WC_ROADVEH_LIST:
   535 		if (w->window_class == WC_BUY_COMPANY)   continue;
   686 			case WC_SHIPS_LIST:
   536 		if (w->window_class == WC_COMPANY)       continue;
   687 			case WC_AIRCRAFT_LIST:
   537 
   688 			case WC_BUY_COMPANY:
   538 		w->caption_color = new_player;
   689 			case WC_COMPANY:
       
   690 				continue;
       
   691 
       
   692 			default:
       
   693 				w->caption_color = new_player;
       
   694 				break;
       
   695 		}
   539 	}
   696 	}
   540 }
   697 }
   541 
   698 
   542 static void BringWindowToFront(const Window *w);
   699 static void BringWindowToFront(const Window *w);
   543 
   700 
   551 	Window *w = FindWindowById(cls, number);
   708 	Window *w = FindWindowById(cls, number);
   552 
   709 
   553 	if (w != NULL) {
   710 	if (w != NULL) {
   554 		w->flags4 |= WF_WHITE_BORDER_MASK;
   711 		w->flags4 |= WF_WHITE_BORDER_MASK;
   555 		BringWindowToFront(w);
   712 		BringWindowToFront(w);
   556 		SetWindowDirty(w);
   713 		w->SetDirty();
   557 	}
   714 	}
   558 
   715 
   559 	return w;
   716 	return w;
   560 }
   717 }
   561 
   718 
   562 static inline bool IsVitalWindow(const Window *w)
   719 static inline bool IsVitalWindow(const Window *w)
   563 {
   720 {
   564 	WindowClass wc = w->window_class;
   721 	switch (w->window_class) {
   565 	return (wc == WC_MAIN_TOOLBAR || wc == WC_STATUS_BAR || wc == WC_NEWS_WINDOW || wc == WC_SEND_NETWORK_MSG);
   722 		case WC_MAIN_TOOLBAR:
       
   723 		case WC_STATUS_BAR:
       
   724 		case WC_NEWS_WINDOW:
       
   725 		case WC_SEND_NETWORK_MSG:
       
   726 			return true;
       
   727 
       
   728 		default:
       
   729 			return false;
       
   730 	}
   566 }
   731 }
   567 
   732 
   568 /** On clicking on a window, make it the frontmost window of all. However
   733 /** On clicking on a window, make it the frontmost window of all. However
   569  * there are certain windows that always need to be on-top; these include
   734  * there are certain windows that always need to be on-top; these include
   570  * - Toolbar, Statusbar (always on)
   735  * - Toolbar, Statusbar (always on)
   573  * @param w window that is put into the foreground
   738  * @param w window that is put into the foreground
   574  * @return pointer to the window, the same as the input pointer
   739  * @return pointer to the window, the same as the input pointer
   575  */
   740  */
   576 static void BringWindowToFront(const Window *w)
   741 static void BringWindowToFront(const Window *w)
   577 {
   742 {
   578 	Window *tempz;
       
   579 	Window **wz = FindWindowZPosition(w);
   743 	Window **wz = FindWindowZPosition(w);
   580 	Window **vz = _last_z_window;
   744 	Window **vz = _last_z_window;
   581 
   745 
   582 	/* Bring the window just below the vital windows */
   746 	/* Bring the window just below the vital windows */
   583 	do {
   747 	do {
   585 	} while (IsVitalWindow(*vz));
   749 	} while (IsVitalWindow(*vz));
   586 
   750 
   587 	if (wz == vz) return; // window is already in the right position
   751 	if (wz == vz) return; // window is already in the right position
   588 	assert(wz < vz);
   752 	assert(wz < vz);
   589 
   753 
   590 	tempz = *wz;
   754 	Window *tempz = *wz;
   591 	memmove(wz, wz + 1, (byte*)vz - (byte*)wz);
   755 	memmove(wz, wz + 1, (byte*)vz - (byte*)wz);
   592 	*vz = tempz;
   756 	*vz = tempz;
   593 
   757 
   594 	SetWindowDirty(w);
   758 	w->SetDirty();
   595 }
   759 }
   596 
   760 
   597 /** We have run out of windows, so find a suitable candidate for replacement.
   761 /** We have run out of windows, so find a suitable candidate for replacement.
   598  * Keep all important windows intact. These are
   762  * Keep all important windows intact. These are
   599  * - Main window (gamefield), Toolbar, Statusbar (always on)
   763  * - Main window (gamefield), Toolbar, Statusbar (always on)
   630 		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w;
   794 		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w;
   631 	}
   795 	}
   632 	NOT_REACHED();
   796 	NOT_REACHED();
   633 }
   797 }
   634 
   798 
   635 bool IsWindowOfPrototype(const Window *w, const Widget *widget)
   799 /**
   636 {
   800  * Assign widgets to a new window by initialising its widget pointers, and by
   637 	return (w->original_widget == widget);
   801  * copying the widget array \a widget to \c w->widget to allow for resizable
   638 }
   802  * windows.
   639 
       
   640 /** Copies 'widget' to 'w->widget' to allow for resizable windows
       
   641  * @param w Window on which to attach the widget array
   803  * @param w Window on which to attach the widget array
   642  * @param widget pointer of widget array to fill the window with */
   804  * @param widget pointer of widget array to fill the window with
   643 void AssignWidgetToWindow(Window *w, const Widget *widget)
   805  *
   644 {
   806  * @post \c w->widget points to allocated memory and contains the copied widget array except for the terminating widget,
   645 	w->original_widget = widget;
   807  *       \c w->widget_count contains number of widgets in the allocated memory.
   646 
   808  */
       
   809 static void AssignWidgetToWindow(Window *w, const Widget *widget)
       
   810 {
   647 	if (widget != NULL) {
   811 	if (widget != NULL) {
   648 		uint index = 1;
   812 		uint index = 1;
   649 		const Widget *wi;
   813 
   650 
   814 		for (const Widget *wi = widget; wi->type != WWT_LAST; wi++) index++;
   651 		for (wi = widget; wi->type != WWT_LAST; wi++) index++;
   815 
   652 
   816 		w->widget = MallocT<Widget>(index);
   653 		w->widget = ReallocT(w->widget, index);
       
   654 		memcpy(w->widget, widget, sizeof(*w->widget) * index);
   817 		memcpy(w->widget, widget, sizeof(*w->widget) * index);
   655 		w->widget_count = index - 1;
   818 		w->widget_count = index - 1;
   656 	} else {
   819 	} else {
   657 		w->widget = NULL;
   820 		w->widget = NULL;
   658 		w->widget_count = 0;
   821 		w->widget_count = 0;
   659 	}
   822 	}
   660 }
   823 }
   661 
   824 
   662 /** Open a new window.
   825 /**
   663  * This function is called from AllocateWindow() or AllocateWindowDesc()
   826  * Initializes a new Window.
       
   827  * This function is called the constructors.
   664  * See descriptions for those functions for usage
   828  * See descriptions for those functions for usage
   665  * See AllocateWindow() for description of arguments.
       
   666  * Only addition here is window_number, which is the window_number being assigned to the new window
   829  * Only addition here is window_number, which is the window_number being assigned to the new window
   667  * @param x offset in pixels from the left of the screen
   830  * @param x offset in pixels from the left of the screen
   668  * @param y offset in pixels from the top of the screen
   831  * @param y offset in pixels from the top of the screen
   669  * @param min_width minimum width in pixels of the window
   832  * @param min_width minimum width in pixels of the window
   670  * @param min_height minimum height in pixels of the window
   833  * @param min_height minimum height in pixels of the window
   671  * @param def_width default width in pixels of the window
       
   672  * @param def_height default height in pixels of the window
       
   673  * @param *proc see WindowProc function to call when any messages/updates happen to the window
   834  * @param *proc see WindowProc function to call when any messages/updates happen to the window
   674  * @param cls see WindowClass class of the window, used for identification and grouping
   835  * @param cls see WindowClass class of the window, used for identification and grouping
   675  * @param *widget see Widget pointer to the window layout and various elements
   836  * @param *widget see Widget pointer to the window layout and various elements
   676  * @param window_number number being assigned to the new window
   837  * @param window_number number being assigned to the new window
   677  * @param data the data to be given during the WE_CREATE message
   838  * @return Window pointer of the newly created window
   678  * @return Window pointer of the newly created window */
   839  */
   679 static Window *LocalAllocateWindow(int x, int y, int min_width, int min_height, int def_width, int def_height,
   840 void Window::Initialize(int x, int y, int min_width, int min_height,
   680 				WindowProc *proc, WindowClass cls, const Widget *widget, int window_number, void *data)
   841 				WindowProc *proc, WindowClass cls, const Widget *widget, int window_number)
   681 {
   842 {
   682 	Window *w;
       
   683 
       
   684 	/* We have run out of windows, close one and use that as the place for our new one */
   843 	/* We have run out of windows, close one and use that as the place for our new one */
   685 	if (_last_z_window == endof(_z_windows)) {
   844 	if (_last_z_window == endof(_z_windows)) {
   686 		w = FindDeletableWindow();
   845 		Window *w = FindDeletableWindow();
   687 		if (w == NULL) w = ForceFindDeletableWindow();
   846 		if (w == NULL) w = ForceFindDeletableWindow();
   688 		DeleteWindow(w);
   847 		delete w;
   689 	}
   848 	}
   690 
       
   691 	w = new Window;
       
   692 
   849 
   693 	/* Set up window properties */
   850 	/* Set up window properties */
   694 	w->window_class = cls;
   851 	this->window_class = cls;
   695 	w->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border
   852 	this->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border
   696 	w->caption_color = 0xFF;
   853 	this->caption_color = 0xFF;
   697 	w->left = x;
   854 	this->left = x;
   698 	w->top = y;
   855 	this->top = y;
   699 	w->width = min_width;
   856 	this->width = min_width;
   700 	w->height = min_height;
   857 	this->height = min_height;
   701 	w->wndproc = proc;
   858 	this->wndproc = proc;
   702 	AssignWidgetToWindow(w, widget);
   859 	AssignWidgetToWindow(this, widget);
   703 	w->resize.width = min_width;
   860 	this->resize.width = min_width;
   704 	w->resize.height = min_height;
   861 	this->resize.height = min_height;
   705 	w->resize.step_width = 1;
   862 	this->resize.step_width = 1;
   706 	w->resize.step_height = 1;
   863 	this->resize.step_height = 1;
   707 	w->window_number = window_number;
   864 	this->window_number = window_number;
   708 
   865 
   709 	{
   866 	/* Hacky way of specifying always-on-top windows. These windows are
   710 		Window **wz = _last_z_window;
   867 		* always above other windows because they are moved below them.
   711 
   868 		* status-bar is above news-window because it has been created earlier.
   712 		/* Hacky way of specifying always-on-top windows. These windows are
   869 		* Also, as the chat-window is excluded from this, it will always be
   713 		 * always above other windows because they are moved below them.
   870 		* the last window, thus always on top.
   714 		 * status-bar is above news-window because it has been created earlier.
   871 		* XXX - Yes, ugly, probably needs something like w->always_on_top flag
   715 		 * Also, as the chat-window is excluded from this, it will always be
   872 		* to implement correctly, but even then you need some kind of distinction
   716 		 * the last window, thus always on top.
   873 		* between on-top of chat/news and status windows, because these conflict */
   717 		 * XXX - Yes, ugly, probably needs something like w->always_on_top flag
   874 	Window **wz = _last_z_window;
   718 		 * to implement correctly, but even then you need some kind of distinction
   875 	if (wz != _z_windows && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) {
   719 		 * between on-top of chat/news and status windows, because these conflict */
   876 		if (FindWindowById(WC_MAIN_TOOLBAR, 0)     != NULL) wz--;
   720 		if (wz != _z_windows && w->window_class != WC_SEND_NETWORK_MSG && w->window_class != WC_HIGHSCORE && w->window_class != WC_ENDSCREEN) {
   877 		if (FindWindowById(WC_STATUS_BAR, 0)       != NULL) wz--;
   721 			if (FindWindowById(WC_MAIN_TOOLBAR, 0)     != NULL) wz--;
   878 		if (FindWindowById(WC_NEWS_WINDOW, 0)      != NULL) wz--;
   722 			if (FindWindowById(WC_STATUS_BAR, 0)       != NULL) wz--;
   879 		if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--;
   723 			if (FindWindowById(WC_NEWS_WINDOW, 0)      != NULL) wz--;
   880 
   724 			if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--;
   881 		assert(wz >= _z_windows);
   725 
   882 		if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz);
   726 			assert(wz >= _z_windows);
   883 	}
   727 			if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz);
   884 
   728 		}
   885 	*wz = this;
   729 
   886 	_last_z_window++;
   730 		*wz = w;
       
   731 		_last_z_window++;
       
   732 	}
       
   733 
   887 
   734 	WindowEvent e;
   888 	WindowEvent e;
   735 	e.event = WE_CREATE;
   889 	e.event = WE_CREATE;
   736 	e.we.create.data = data;
   890 	this->HandleWindowEvent(&e);
   737 	w->wndproc(w, &e);
   891 }
   738 
   892 
       
   893 /**
       
   894  * Find a nice spot for this window and resize it towards the default size.
       
   895  * @param def_width default width in pixels of the window
       
   896  * @param def_height default height in pixels of the window
       
   897  */
       
   898 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
       
   899 {
   739 	/* Try to make windows smaller when our window is too small.
   900 	/* Try to make windows smaller when our window is too small.
   740 	 * w->(width|height) is normally the same as min_(width|height),
   901 	 * w->(width|height) is normally the same as min_(width|height),
   741 	 * but this way the GUIs can be made a little more dynamic;
   902 	 * but this way the GUIs can be made a little more dynamic;
   742 	 * one can use the same spec for multiple windows and those
   903 	 * one can use the same spec for multiple windows and those
   743 	 * can then determine the real minimum size of the window. */
   904 	 * can then determine the real minimum size of the window. */
   744 	if (w->width != def_width || w->height != def_height) {
   905 	if (this->width != def_width || this->height != def_height) {
   745 		/* Think about the overlapping toolbars when determining the minimum window size */
   906 		/* Think about the overlapping toolbars when determining the minimum window size */
   746 		int free_height = _screen.height;
   907 		int free_height = _screen.height;
   747 		const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
   908 		const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
   748 		if (wt != NULL) free_height -= wt->height;
   909 		if (wt != NULL) free_height -= wt->height;
   749 		wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
   910 		wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
   750 		if (wt != NULL) free_height -= wt->height;
   911 		if (wt != NULL) free_height -= wt->height;
   751 
   912 
   752 		int enlarge_x = max(min(def_width  - w->width,  _screen.width - w->width),  0);
   913 		int enlarge_x = max(min(def_width  - this->width,  _screen.width - this->width),  0);
   753 		int enlarge_y = max(min(def_height - w->height, free_height   - w->height), 0);
   914 		int enlarge_y = max(min(def_height - this->height, free_height   - this->height), 0);
   754 
   915 
   755 		/* X and Y has to go by step.. calculate it.
   916 		/* X and Y has to go by step.. calculate it.
   756 		 * The cast to int is necessary else x/y are implicitly casted to
   917 		 * The cast to int is necessary else x/y are implicitly casted to
   757 		 * unsigned int, which won't work. */
   918 		 * unsigned int, which won't work. */
   758 		if (w->resize.step_width  > 1) enlarge_x -= enlarge_x % (int)w->resize.step_width;
   919 		if (this->resize.step_width  > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
   759 		if (w->resize.step_height > 1) enlarge_y -= enlarge_y % (int)w->resize.step_height;
   920 		if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
   760 
   921 
   761 		ResizeWindow(w, enlarge_x, enlarge_y);
   922 		ResizeWindow(this, enlarge_x, enlarge_y);
   762 
   923 
   763 		WindowEvent e;
   924 		Point size;
   764 		e.event = WE_RESIZE;
   925 		Point diff;
   765 		e.we.sizing.size.x = w->width;
   926 		size.x = this->width;
   766 		e.we.sizing.size.y = w->height;
   927 		size.y = this->height;
   767 		e.we.sizing.diff.x = enlarge_x;
   928 		diff.x = enlarge_x;
   768 		e.we.sizing.diff.y = enlarge_y;
   929 		diff.y = enlarge_y;
   769 		w->wndproc(w, &e);
   930 		this->OnResize(size, diff);
   770 	}
   931 	}
   771 
   932 
   772 	int nx = w->left;
   933 	int nx = this->left;
   773 	int ny = w->top;
   934 	int ny = this->top;
   774 
   935 
   775 	if (nx + w->width > _screen.width) nx -= (nx + w->width - _screen.width);
   936 	if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
   776 
   937 
   777 	const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
   938 	const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
   778 	ny = max(ny, (wt == NULL || w == wt || y == 0) ? 0 : wt->height);
   939 	ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
   779 	nx = max(nx, 0);
   940 	nx = max(nx, 0);
   780 
   941 
   781 	if (w->viewport != NULL) {
   942 	if (this->viewport != NULL) {
   782 		w->viewport->left += nx - w->left;
   943 		this->viewport->left += nx - this->left;
   783 		w->viewport->top  += ny - w->top;
   944 		this->viewport->top  += ny - this->top;
   784 	}
   945 	}
   785 	w->left = nx;
   946 	this->left = nx;
   786 	w->top = ny;
   947 	this->top = ny;
   787 
   948 
   788 	SetWindowDirty(w);
   949 	this->SetDirty();
   789 
   950 }
   790 	return w;
   951 
       
   952 void Window::FindWindowPlacementAndResize(const WindowDesc *desc)
       
   953 {
       
   954 	this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
   791 }
   955 }
   792 
   956 
   793 /**
   957 /**
   794  * Open a new window. If there is no space for a new window, close an open
   958  * Open a new window. If there is no space for a new window, close an open
   795  * window. Try to avoid stickied windows, but if there is no else, close one of
   959  * window. Try to avoid stickied windows, but if there is no else, close one of
   802  * @param *proc see WindowProc function to call when any messages/updates happen to the window
   966  * @param *proc see WindowProc function to call when any messages/updates happen to the window
   803  * @param cls see WindowClass class of the window, used for identification and grouping
   967  * @param cls see WindowClass class of the window, used for identification and grouping
   804  * @param *widget see Widget pointer to the window layout and various elements
   968  * @param *widget see Widget pointer to the window layout and various elements
   805  * @return Window pointer of the newly created window
   969  * @return Window pointer of the newly created window
   806  */
   970  */
   807 Window *AllocateWindow(int x, int y, int width, int height,
   971 Window::Window(int x, int y, int width, int height, WindowProc *proc, WindowClass cls, const Widget *widget)
   808 			WindowProc *proc, WindowClass cls, const Widget *widget, void *data)
   972 {
   809 {
   973 	this->Initialize(x, y, width, height, proc, cls, widget, 0);
   810 	return LocalAllocateWindow(x, y, width, height, width, height, proc, cls, widget, 0, data);
   974 
       
   975 	if (proc != NULL) this->FindWindowPlacementAndResize(width, height);
   811 }
   976 }
   812 
   977 
   813 
   978 
   814 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
   979 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
   815 {
   980 {
   816 	Window* const *wz;
   981 	Window* const *wz;
   817 
   982 
   818 	int right  = width + left;
   983 	int right  = width + left;
   819 	int bottom = height + top;
   984 	int bottom = height + top;
   820 
   985 
   821 	if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height)
   986 	if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height) return false;
   822 		return false;
       
   823 
   987 
   824 	/* Make sure it is not obscured by any window. */
   988 	/* Make sure it is not obscured by any window. */
   825 	FOR_ALL_WINDOWS(wz) {
   989 	FOR_ALL_WINDOWS(wz) {
   826 		const Window *w = *wz;
   990 		const Window *w = *wz;
   827 		if (w->window_class == WC_MAIN_WINDOW) continue;
   991 		if (w->window_class == WC_MAIN_WINDOW) continue;
   914 		return pt;
  1078 		return pt;
   915 	}
  1079 	}
   916 }
  1080 }
   917 
  1081 
   918 /**
  1082 /**
   919  * Set the x and y coordinates of a new window.
  1083  * Compute the position of the top-left corner of a new window that is opened.
       
  1084  *
       
  1085  * By default position a child window at an offset of 10/10 of its parent.
       
  1086  * With the exception of WC_BUILD_TOOLBAR (build railway/roads/ship docks/airports)
       
  1087  * and WC_SCEN_LAND_GEN (landscaping). Whose child window has an offset of 0/36 of
       
  1088  * its parent. So it's exactly under the parent toolbar and no buttons will be covered.
       
  1089  * However if it falls too extremely outside window positions, reposition
       
  1090  * it to an automatic place.
   920  *
  1091  *
   921  * @param *desc         The pointer to the WindowDesc to be created
  1092  * @param *desc         The pointer to the WindowDesc to be created
   922  * @param window_number the window number of the new window
  1093  * @param window_number the window number of the new window
   923  * @param data          arbitrary data that is send with the WE_CREATE message
       
   924  *
  1094  *
   925  * @return see Window pointer of the newly created window
  1095  * @return Coordinate of the top-left corner of the new window
   926  */
  1096  */
   927 static Window *LocalAllocateWindowDesc(const WindowDesc *desc, int window_number, void *data)
  1097 static Point LocalGetWindowPlacement(const WindowDesc *desc, int window_number)
   928 {
  1098 {
   929 	Point pt;
  1099 	Point pt;
   930 	Window *w;
  1100 	Window *w;
   931 
  1101 
   932 	/* By default position a child window at an offset of 10/10 of its parent.
       
   933 	 * With the exception of WC_BUILD_TOOLBAR (build railway/roads/ship docks/airports)
       
   934 	 * and WC_SCEN_LAND_GEN (landscaping). Whose child window has an offset of 0/36 of
       
   935 	 * its parent. So it's exactly under the parent toolbar and no buttons will be covered.
       
   936 	 * However if it falls too extremely outside window positions, reposition
       
   937 	 * it to an automatic place */
       
   938 	if (desc->parent_cls != 0 /* WC_MAIN_WINDOW */ &&
  1102 	if (desc->parent_cls != 0 /* WC_MAIN_WINDOW */ &&
   939 			(w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
  1103 			(w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
   940 			w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
  1104 			w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
   941 
  1105 
   942 		pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
  1106 		pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
   944 			pt.x = (_screen.width + 10 - desc->default_width) - 20;
  1108 			pt.x = (_screen.width + 10 - desc->default_width) - 20;
   945 		}
  1109 		}
   946 		pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 36 : 10);
  1110 		pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 36 : 10);
   947 	} else {
  1111 	} else {
   948 		switch (desc->left) {
  1112 		switch (desc->left) {
   949 			case WDP_ALIGN_TBR: { /* Align the right side with the top toolbar */
  1113 			case WDP_ALIGN_TBR: // Align the right side with the top toolbar
   950 				w = FindWindowById(WC_MAIN_TOOLBAR, 0);
  1114 				w = FindWindowById(WC_MAIN_TOOLBAR, 0);
   951 				pt.x = (w->left + w->width) - desc->default_width;
  1115 				pt.x = (w->left + w->width) - desc->default_width;
   952 			} break;
  1116 				break;
   953 			case WDP_ALIGN_TBL: /* Align the left side with the top toolbar */
  1117 
       
  1118 			case WDP_ALIGN_TBL: // Align the left side with the top toolbar
   954 				pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left;
  1119 				pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left;
   955 				break;
  1120 				break;
   956 			case WDP_AUTO: /* Find a good automatic position for the window */
  1121 
   957 				pt = GetAutoPlacePosition(desc->default_width, desc->default_height);
  1122 			case WDP_AUTO: // Find a good automatic position for the window
   958 				goto allocate_window;
  1123 				return GetAutoPlacePosition(desc->default_width, desc->default_height);
   959 			case WDP_CENTER: /* Centre the window horizontally */
  1124 
       
  1125 			case WDP_CENTER: // Centre the window horizontally
   960 				pt.x = (_screen.width - desc->default_width) / 2;
  1126 				pt.x = (_screen.width - desc->default_width) / 2;
   961 				break;
  1127 				break;
       
  1128 
   962 			default:
  1129 			default:
   963 				pt.x = desc->left;
  1130 				pt.x = desc->left;
   964 				if (pt.x < 0) pt.x += _screen.width; // negative is from right of the screen
  1131 				if (pt.x < 0) pt.x += _screen.width; // negative is from right of the screen
   965 		}
  1132 		}
   966 
  1133 
   967 		switch (desc->top) {
  1134 		switch (desc->top) {
   968 			case WDP_CENTER: /* Centre the window vertically */
  1135 			case WDP_CENTER: // Centre the window vertically
   969 				pt.y = (_screen.height - desc->default_height) / 2;
  1136 				pt.y = (_screen.height - desc->default_height) / 2;
   970 				break;
  1137 				break;
       
  1138 
   971 			/* WDP_AUTO sets the position at once and is controlled by desc->left.
  1139 			/* WDP_AUTO sets the position at once and is controlled by desc->left.
   972 			 * Both left and top must be set to WDP_AUTO */
  1140 			 * Both left and top must be set to WDP_AUTO */
   973 			case WDP_AUTO:
  1141 			case WDP_AUTO:
   974 				NOT_REACHED();
  1142 				NOT_REACHED();
   975 				assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO);
  1143 				assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO);
   976 				/* fallthrough */
  1144 				/* fallthrough */
       
  1145 
   977 			default:
  1146 			default:
   978 				pt.y = desc->top;
  1147 				pt.y = desc->top;
   979 				if (pt.y < 0) pt.y += _screen.height; // negative is from bottom of the screen
  1148 				if (pt.y < 0) pt.y += _screen.height; // negative is from bottom of the screen
   980 				break;
  1149 				break;
   981 		}
  1150 		}
   982 	}
  1151 	}
   983 
  1152 
   984 allocate_window:
  1153 	return pt;
   985 	w = LocalAllocateWindow(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->default_width, desc->default_height, desc->proc, desc->cls, desc->widgets, window_number, data);
  1154 }
   986 	w->desc_flags = desc->flags;
  1155 
   987 	return w;
  1156 /**
   988 }
  1157  * Set the positions of a new window from a WindowDesc and open it.
   989 
  1158  *
   990 /**
  1159  * @param *desc         The pointer to the WindowDesc to be created
   991  * Open a new window.
  1160  * @param window_number the window number of the new window
   992  * @param *desc The pointer to the WindowDesc to be created
  1161  *
   993  * @param data arbitrary data that is send with the WE_CREATE message
       
   994  * @return Window pointer of the newly created window
  1162  * @return Window pointer of the newly created window
   995  */
  1163  */
   996 Window *AllocateWindowDesc(const WindowDesc *desc, void *data)
  1164 Window::Window(const WindowDesc *desc, WindowNumber window_number)
   997 {
  1165 {
   998 	return LocalAllocateWindowDesc(desc, 0, data);
  1166 	Point pt = LocalGetWindowPlacement(desc, window_number);
   999 }
  1167 	this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->proc, desc->cls, desc->widgets, window_number);
  1000 
  1168 	this->desc_flags = desc->flags;
  1001 /**
  1169 
  1002  * Open a new window.
  1170 	if (desc->proc != NULL) this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
  1003  * @param *desc The pointer to the WindowDesc to be created
       
  1004  * @param window_number the window number of the new window
       
  1005  * @param data arbitrary data that is send with the WE_CREATE message
       
  1006  * @return see Window pointer of the newly created window
       
  1007  */
       
  1008 Window *AllocateWindowDescFront(const WindowDesc *desc, int window_number, void *data)
       
  1009 {
       
  1010 	Window *w;
       
  1011 
       
  1012 	if (BringWindowToFrontById(desc->cls, window_number)) return NULL;
       
  1013 	w = LocalAllocateWindowDesc(desc, window_number, data);
       
  1014 	return w;
       
  1015 }
  1171 }
  1016 
  1172 
  1017 /** Do a search for a window at specific coordinates. For this we start
  1173 /** Do a search for a window at specific coordinates. For this we start
  1018  * at the topmost window, obviously and work our way down to the bottom
  1174  * at the topmost window, obviously and work our way down to the bottom
  1019  * @param x position x to query
  1175  * @param x position x to query
  1046 /**
  1202 /**
  1047  * Close down the windowing system
  1203  * Close down the windowing system
  1048  */
  1204  */
  1049 void UnInitWindowSystem()
  1205 void UnInitWindowSystem()
  1050 {
  1206 {
  1051 	Window **wz;
  1207 	while (_last_z_window != _z_windows) delete _z_windows[0];
  1052 
       
  1053 restart_search:
       
  1054 	/* Delete all windows, reset z-array.
       
  1055 	 * When we find the window to delete, we need to restart the search
       
  1056 	 * as deleting this window could cascade in deleting (many) others
       
  1057 	 * anywhere in the z-array. We call DeleteWindow() so that it can properly
       
  1058 	 * release own alloc'd memory, which otherwise could result in memleaks */
       
  1059 	FOR_ALL_WINDOWS(wz) {
       
  1060 		DeleteWindow(*wz);
       
  1061 		goto restart_search;
       
  1062 	}
       
  1063 
       
  1064 	assert(_last_z_window == _z_windows);
       
  1065 }
  1208 }
  1066 
  1209 
  1067 /**
  1210 /**
  1068  * Reset the windowing system, by means of shutting it down followed by re-initialization
  1211  * Reset the windowing system, by means of shutting it down followed by re-initialization
  1069  */
  1212  */
  1077 	_thd.new_pos.y = 0;
  1220 	_thd.new_pos.y = 0;
  1078 }
  1221 }
  1079 
  1222 
  1080 static void DecreaseWindowCounters()
  1223 static void DecreaseWindowCounters()
  1081 {
  1224 {
  1082 	Window *w;
       
  1083 	Window* const *wz;
  1225 	Window* const *wz;
  1084 
  1226 
  1085 	for (wz = _last_z_window; wz != _z_windows;) {
  1227 	for (wz = _last_z_window; wz != _z_windows;) {
  1086 		w = *--wz;
  1228 		Window *w = *--wz;
  1087 		/* Unclick scrollbar buttons if they are pressed. */
  1229 		/* Unclick scrollbar buttons if they are pressed. */
  1088 		if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
  1230 		if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
  1089 			w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
  1231 			w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
  1090 			SetWindowDirty(w);
  1232 			w->SetDirty();
  1091 		}
  1233 		}
  1092 		CallWindowEventNP(w, WE_MOUSELOOP);
  1234 		w->OnMouseLoop();
  1093 	}
  1235 	}
  1094 
  1236 
  1095 	for (wz = _last_z_window; wz != _z_windows;) {
  1237 	for (wz = _last_z_window; wz != _z_windows;) {
  1096 		w = *--wz;
  1238 		Window *w = *--wz;
  1097 
  1239 
  1098 		if (w->flags4&WF_TIMEOUT_MASK && !(--w->flags4&WF_TIMEOUT_MASK)) {
  1240 		if (w->flags4 & WF_TIMEOUT_MASK && !(--w->flags4 & WF_TIMEOUT_MASK)) {
  1099 			CallWindowEventNP(w, WE_TIMEOUT);
  1241 			w->OnTimeout();
  1100 			if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons();
  1242 			if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons();
  1101 		}
  1243 		}
  1102 	}
  1244 	}
  1103 }
  1245 }
  1104 
  1246 
  1107 	return FindWindowById(_thd.window_class, _thd.window_number);
  1249 	return FindWindowById(_thd.window_class, _thd.window_number);
  1108 }
  1250 }
  1109 
  1251 
  1110 static void HandlePlacePresize()
  1252 static void HandlePlacePresize()
  1111 {
  1253 {
  1112 	Window *w;
       
  1113 	WindowEvent e;
       
  1114 
       
  1115 	if (_special_mouse_mode != WSM_PRESIZE) return;
  1254 	if (_special_mouse_mode != WSM_PRESIZE) return;
  1116 
  1255 
  1117 	w = GetCallbackWnd();
  1256 	Window *w = GetCallbackWnd();
  1118 	if (w == NULL) return;
  1257 	if (w == NULL) return;
  1119 
  1258 
  1120 	e.we.place.pt = GetTileBelowCursor();
  1259 	Point pt = GetTileBelowCursor();
  1121 	if (e.we.place.pt.x == -1) {
  1260 	if (pt.x == -1) {
  1122 		_thd.selend.x = -1;
  1261 		_thd.selend.x = -1;
  1123 		return;
  1262 		return;
  1124 	}
  1263 	}
  1125 	e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
  1264 
  1126 	e.event = WE_PLACE_PRESIZE;
  1265 	w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
  1127 	w->wndproc(w, &e);
       
  1128 }
  1266 }
  1129 
  1267 
  1130 static bool HandleDragDrop()
  1268 static bool HandleDragDrop()
  1131 {
  1269 {
  1132 	Window *w;
       
  1133 	WindowEvent e;
       
  1134 
       
  1135 	if (_special_mouse_mode != WSM_DRAGDROP) return true;
  1270 	if (_special_mouse_mode != WSM_DRAGDROP) return true;
  1136 
       
  1137 	if (_left_button_down) return false;
  1271 	if (_left_button_down) return false;
  1138 
  1272 
  1139 	w = GetCallbackWnd();
  1273 	Window *w = GetCallbackWnd();
  1140 
  1274 
  1141 	if (w != NULL) {
  1275 	if (w != NULL) {
  1142 		/* send an event in client coordinates. */
  1276 		/* send an event in client coordinates. */
  1143 		e.event = WE_DRAGDROP;
  1277 		Point pt;
  1144 		e.we.dragdrop.pt.x = _cursor.pos.x - w->left;
  1278 		pt.x = _cursor.pos.x - w->left;
  1145 		e.we.dragdrop.pt.y = _cursor.pos.y - w->top;
  1279 		pt.y = _cursor.pos.y - w->top;
  1146 		e.we.dragdrop.widget = GetWidgetFromPos(w, e.we.dragdrop.pt.x, e.we.dragdrop.pt.y);
  1280 		w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
  1147 		w->wndproc(w, &e);
       
  1148 	}
  1281 	}
  1149 
  1282 
  1150 	ResetObjectToPlace();
  1283 	ResetObjectToPlace();
  1151 
  1284 
  1152 	return false;
  1285 	return false;
  1153 }
  1286 }
  1154 
  1287 
  1155 static bool HandlePopupMenu()
       
  1156 {
       
  1157 	Window *w;
       
  1158 	WindowEvent e;
       
  1159 
       
  1160 	if (!_popup_menu_active) return true;
       
  1161 
       
  1162 	w = FindWindowById(WC_TOOLBAR_MENU, 0);
       
  1163 	if (w == NULL) {
       
  1164 		_popup_menu_active = false;
       
  1165 		return false;
       
  1166 	}
       
  1167 
       
  1168 	if (_left_button_down) {
       
  1169 		e.event = WE_POPUPMENU_OVER;
       
  1170 		e.we.popupmenu.pt = _cursor.pos;
       
  1171 	} else {
       
  1172 		_popup_menu_active = false;
       
  1173 		e.event = WE_POPUPMENU_SELECT;
       
  1174 		e.we.popupmenu.pt = _cursor.pos;
       
  1175 	}
       
  1176 
       
  1177 	w->wndproc(w, &e);
       
  1178 
       
  1179 	return false;
       
  1180 }
       
  1181 
       
  1182 static bool HandleMouseOver()
  1288 static bool HandleMouseOver()
  1183 {
  1289 {
  1184 	WindowEvent e;
       
  1185 
       
  1186 	Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
  1290 	Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
  1187 
  1291 
  1188 	/* We changed window, put a MOUSEOVER event to the last window */
  1292 	/* We changed window, put a MOUSEOVER event to the last window */
  1189 	if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
  1293 	if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
  1190 		/* Reset mouse-over coordinates of previous window */
  1294 		/* Reset mouse-over coordinates of previous window */
  1191 		e.event = WE_MOUSEOVER;
  1295 		Point pt = { -1, -1 };
  1192 		e.we.mouseover.pt.x = -1;
  1296 		_mouseover_last_w->OnMouseOver(pt, 0);
  1193 		e.we.mouseover.pt.y = -1;
       
  1194 		if (_mouseover_last_w->wndproc != NULL) _mouseover_last_w->wndproc(_mouseover_last_w, &e);
       
  1195 	}
  1297 	}
  1196 
  1298 
  1197 	/* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
  1299 	/* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
  1198 	_mouseover_last_w = w;
  1300 	_mouseover_last_w = w;
  1199 
  1301 
  1200 	if (w != NULL) {
  1302 	if (w != NULL) {
  1201 		/* send an event in client coordinates. */
  1303 		/* send an event in client coordinates. */
  1202 		e.event = WE_MOUSEOVER;
  1304 		Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
  1203 		e.we.mouseover.pt.x = _cursor.pos.x - w->left;
  1305 		int widget = 0;
  1204 		e.we.mouseover.pt.y = _cursor.pos.y - w->top;
       
  1205 		if (w->widget != NULL) {
  1306 		if (w->widget != NULL) {
  1206 			e.we.mouseover.widget = GetWidgetFromPos(w, e.we.mouseover.pt.x, e.we.mouseover.pt.y);
  1307 			widget = GetWidgetFromPos(w, pt.x, pt.y);
  1207 		}
  1308 		}
  1208 		w->wndproc(w, &e);
  1309 		w->OnMouseOver(pt, widget);
  1209 	}
  1310 	}
  1210 
  1311 
  1211 	/* Mouseover never stops execution */
  1312 	/* Mouseover never stops execution */
  1212 	return true;
  1313 	return true;
  1213 }
  1314 }
  1221  * @param x delta x-size of changed window (positive if larger, etc.)
  1322  * @param x delta x-size of changed window (positive if larger, etc.)
  1222  * @param y delta y-size of changed window
  1323  * @param y delta y-size of changed window
  1223  */
  1324  */
  1224 void ResizeWindow(Window *w, int x, int y)
  1325 void ResizeWindow(Window *w, int x, int y)
  1225 {
  1326 {
  1226 	Widget *wi;
       
  1227 	bool resize_height = false;
  1327 	bool resize_height = false;
  1228 	bool resize_width = false;
  1328 	bool resize_width = false;
  1229 
  1329 
  1230 	if (x == 0 && y == 0) return;
  1330 	if (x == 0 && y == 0) return;
  1231 
  1331 
  1232 	SetWindowDirty(w);
  1332 	w->SetDirty();
  1233 	for (wi = w->widget; wi->type != WWT_LAST; wi++) {
  1333 	for (Widget *wi = w->widget; wi->type != WWT_LAST; wi++) {
  1234 		/* Isolate the resizing flags */
  1334 		/* Isolate the resizing flags */
  1235 		byte rsizeflag = GB(wi->display_flags, 0, 4);
  1335 		byte rsizeflag = GB(wi->display_flags, 0, 4);
  1236 
  1336 
  1237 		if (rsizeflag == RESIZE_NONE) continue;
  1337 		if (rsizeflag == RESIZE_NONE) continue;
  1238 
  1338 
  1260 
  1360 
  1261 	/* We resized at least 1 widget, so let's resize the window totally */
  1361 	/* We resized at least 1 widget, so let's resize the window totally */
  1262 	if (resize_width)  w->width  += x;
  1362 	if (resize_width)  w->width  += x;
  1263 	if (resize_height) w->height += y;
  1363 	if (resize_height) w->height += y;
  1264 
  1364 
  1265 	SetWindowDirty(w);
  1365 	w->SetDirty();
  1266 }
  1366 }
  1267 
  1367 
  1268 static bool _dragging_window;
  1368 static bool _dragging_window;
  1269 
  1369 
  1270 static bool HandleWindowDragging()
  1370 static bool HandleWindowDragging()
  1277 	FOR_ALL_WINDOWS(wz) {
  1377 	FOR_ALL_WINDOWS(wz) {
  1278 		Window *w = *wz;
  1378 		Window *w = *wz;
  1279 
  1379 
  1280 		if (w->flags4 & WF_DRAGGING) {
  1380 		if (w->flags4 & WF_DRAGGING) {
  1281 			const Widget *t = &w->widget[1]; // the title bar ... ugh
  1381 			const Widget *t = &w->widget[1]; // the title bar ... ugh
  1282 			const Window *v;
       
  1283 			int x;
       
  1284 			int y;
       
  1285 			int nx;
       
  1286 			int ny;
       
  1287 
  1382 
  1288 			/* Stop the dragging if the left mouse button was released */
  1383 			/* Stop the dragging if the left mouse button was released */
  1289 			if (!_left_button_down) {
  1384 			if (!_left_button_down) {
  1290 				w->flags4 &= ~WF_DRAGGING;
  1385 				w->flags4 &= ~WF_DRAGGING;
  1291 				break;
  1386 				break;
  1292 			}
  1387 			}
  1293 
  1388 
  1294 			SetWindowDirty(w);
  1389 			w->SetDirty();
  1295 
  1390 
  1296 			x = _cursor.pos.x + _drag_delta.x;
  1391 			int x = _cursor.pos.x + _drag_delta.x;
  1297 			y = _cursor.pos.y + _drag_delta.y;
  1392 			int y = _cursor.pos.y + _drag_delta.y;
  1298 			nx = x;
  1393 			int nx = x;
  1299 			ny = y;
  1394 			int ny = y;
  1300 
  1395 
  1301 			if (_patches.window_snap_radius != 0) {
  1396 			if (_patches.window_snap_radius != 0) {
  1302 				Window* const *vz;
  1397 				Window* const *vz;
  1303 
  1398 
  1304 				int hsnap = _patches.window_snap_radius;
  1399 				int hsnap = _patches.window_snap_radius;
  1380 			 * 13 is the height of the title bar */
  1475 			 * 13 is the height of the title bar */
  1381 			nx = Clamp(nx, 13 - t->right, _screen.width - 13 - t->left);
  1476 			nx = Clamp(nx, 13 - t->right, _screen.width - 13 - t->left);
  1382 			ny = Clamp(ny, 0, _screen.height - 13);
  1477 			ny = Clamp(ny, 0, _screen.height - 13);
  1383 
  1478 
  1384 			/* Make sure the title bar isn't hidden by behind the main tool bar */
  1479 			/* Make sure the title bar isn't hidden by behind the main tool bar */
  1385 			v = FindWindowById(WC_MAIN_TOOLBAR, 0);
  1480 			Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0);
  1386 			if (v != NULL) {
  1481 			if (v != NULL) {
  1387 				int v_bottom = v->top + v->height;
  1482 				int v_bottom = v->top + v->height;
  1388 				int v_right = v->left + v->width;
  1483 				int v_right = v->left + v->width;
  1389 				if (ny + t->top >= v->top && ny + t->top < v_bottom) {
  1484 				if (ny + t->top >= v->top && ny + t->top < v_bottom) {
  1390 					if ((v->left < 13 && nx + t->left < v->left) ||
  1485 					if ((v->left < 13 && nx + t->left < v->left) ||
  1410 				w->viewport->top  += ny - w->top;
  1505 				w->viewport->top  += ny - w->top;
  1411 			}
  1506 			}
  1412 			w->left = nx;
  1507 			w->left = nx;
  1413 			w->top  = ny;
  1508 			w->top  = ny;
  1414 
  1509 
  1415 			SetWindowDirty(w);
  1510 			w->SetDirty();
  1416 			return false;
  1511 			return false;
  1417 		} else if (w->flags4 & WF_SIZING) {
  1512 		} else if (w->flags4 & WF_SIZING) {
  1418 			WindowEvent e;
       
  1419 			int x, y;
  1513 			int x, y;
  1420 
  1514 
  1421 			/* Stop the sizing if the left mouse button was released */
  1515 			/* Stop the sizing if the left mouse button was released */
  1422 			if (!_left_button_down) {
  1516 			if (!_left_button_down) {
  1423 				w->flags4 &= ~WF_SIZING;
  1517 				w->flags4 &= ~WF_SIZING;
  1424 				SetWindowDirty(w);
  1518 				w->SetDirty();
  1425 				break;
  1519 				break;
  1426 			}
  1520 			}
  1427 
  1521 
  1428 			x = _cursor.pos.x - _drag_delta.x;
  1522 			x = _cursor.pos.x - _drag_delta.x;
  1429 			y = _cursor.pos.y - _drag_delta.y;
  1523 			y = _cursor.pos.y - _drag_delta.y;
  1450 			_drag_delta.y += y;
  1544 			_drag_delta.y += y;
  1451 
  1545 
  1452 			/* ResizeWindow sets both pre- and after-size to dirty for redrawal */
  1546 			/* ResizeWindow sets both pre- and after-size to dirty for redrawal */
  1453 			ResizeWindow(w, x, y);
  1547 			ResizeWindow(w, x, y);
  1454 
  1548 
  1455 			e.event = WE_RESIZE;
  1549 			Point size;
  1456 			e.we.sizing.size.x = x + w->width;
  1550 			Point diff;
  1457 			e.we.sizing.size.y = y + w->height;
  1551 			size.x = x + w->width;
  1458 			e.we.sizing.diff.x = x;
  1552 			size.y = y + w->height;
  1459 			e.we.sizing.diff.y = y;
  1553 			diff.x = x;
  1460 			w->wndproc(w, &e);
  1554 			diff.y = y;
       
  1555 			w->OnResize(size, diff);
  1461 			return false;
  1556 			return false;
  1462 		}
  1557 		}
  1463 	}
  1558 	}
  1464 
  1559 
  1465 	_dragging_window = false;
  1560 	_dragging_window = false;
  1500 
  1595 
  1501 
  1596 
  1502 static bool HandleScrollbarScrolling()
  1597 static bool HandleScrollbarScrolling()
  1503 {
  1598 {
  1504 	Window* const *wz;
  1599 	Window* const *wz;
  1505 	int i;
       
  1506 	int pos;
       
  1507 	Scrollbar *sb;
       
  1508 
  1600 
  1509 	/* Get out quickly if no item is being scrolled */
  1601 	/* Get out quickly if no item is being scrolled */
  1510 	if (!_scrolling_scrollbar) return true;
  1602 	if (!_scrolling_scrollbar) return true;
  1511 
  1603 
  1512 	/* Find the scrolling window */
  1604 	/* Find the scrolling window */
  1515 
  1607 
  1516 		if (w->flags4 & WF_SCROLL_MIDDLE) {
  1608 		if (w->flags4 & WF_SCROLL_MIDDLE) {
  1517 			/* Abort if no button is clicked any more. */
  1609 			/* Abort if no button is clicked any more. */
  1518 			if (!_left_button_down) {
  1610 			if (!_left_button_down) {
  1519 				w->flags4 &= ~WF_SCROLL_MIDDLE;
  1611 				w->flags4 &= ~WF_SCROLL_MIDDLE;
  1520 				SetWindowDirty(w);
  1612 				w->SetDirty();
  1521 				break;
  1613 				break;
  1522 			}
  1614 			}
       
  1615 
       
  1616 			int i;
       
  1617 			Scrollbar *sb;
  1523 
  1618 
  1524 			if (w->flags4 & WF_HSCROLL) {
  1619 			if (w->flags4 & WF_HSCROLL) {
  1525 				sb = &w->hscroll;
  1620 				sb = &w->hscroll;
  1526 				i = _cursor.pos.x - _cursorpos_drag_start.x;
  1621 				i = _cursor.pos.x - _cursorpos_drag_start.x;
  1527 			} else if (w->flags4 & WF_SCROLL2){
  1622 			} else if (w->flags4 & WF_SCROLL2){
  1531 				sb = &w->vscroll;
  1626 				sb = &w->vscroll;
  1532 				i = _cursor.pos.y - _cursorpos_drag_start.y;
  1627 				i = _cursor.pos.y - _cursorpos_drag_start.y;
  1533 			}
  1628 			}
  1534 
  1629 
  1535 			/* Find the item we want to move to and make sure it's inside bounds. */
  1630 			/* Find the item we want to move to and make sure it's inside bounds. */
  1536 			pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap));
  1631 			int pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap));
  1537 			if (pos != sb->pos) {
  1632 			if (pos != sb->pos) {
  1538 				sb->pos = pos;
  1633 				sb->pos = pos;
  1539 				SetWindowDirty(w);
  1634 				w->SetDirty();
  1540 			}
  1635 			}
  1541 			return false;
  1636 			return false;
  1542 		}
  1637 		}
  1543 	}
  1638 	}
  1544 
  1639 
  1546 	return false;
  1641 	return false;
  1547 }
  1642 }
  1548 
  1643 
  1549 static bool HandleViewportScroll()
  1644 static bool HandleViewportScroll()
  1550 {
  1645 {
  1551 	WindowEvent e;
       
  1552 	Window *w;
       
  1553 
       
  1554 	bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
  1646 	bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
  1555 
  1647 
  1556 	if (!_scrolling_viewport) return true;
  1648 	if (!_scrolling_viewport) return true;
  1557 
  1649 
  1558 	w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
  1650 	Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
  1559 
  1651 
  1560 	if (!(_right_button_down || scrollwheel_scrolling) || w == NULL) {
  1652 	if (!(_right_button_down || scrollwheel_scrolling) || w == NULL) {
  1561 		_cursor.fix_at = false;
  1653 		_cursor.fix_at = false;
  1562 		_scrolling_viewport = false;
  1654 		_scrolling_viewport = false;
  1563 		return true;
  1655 		return true;
  1564 	}
  1656 	}
  1565 
  1657 
  1566 	if (WP(w, vp_d).follow_vehicle != INVALID_VEHICLE && w == FindWindowById(WC_MAIN_WINDOW, 0)) {
  1658 	if (w == FindWindowById(WC_MAIN_WINDOW, 0) && w->viewport->follow_vehicle != INVALID_VEHICLE) {
  1567 		/* If the main window is following a vehicle, then first let go of it! */
  1659 		/* If the main window is following a vehicle, then first let go of it! */
  1568 		const Vehicle *veh = GetVehicle(WP(w, vp_d).follow_vehicle);
  1660 		const Vehicle *veh = GetVehicle(w->viewport->follow_vehicle);
  1569 		ScrollMainWindowTo(veh->x_pos, veh->y_pos, true); /* This also resets follow_vehicle */
  1661 		ScrollMainWindowTo(veh->x_pos, veh->y_pos, true); /* This also resets follow_vehicle */
  1570 		return true;
  1662 		return true;
  1571 	}
  1663 	}
  1572 
  1664 
       
  1665 	Point delta;
  1573 	if (_patches.reverse_scroll) {
  1666 	if (_patches.reverse_scroll) {
  1574 		e.we.scroll.delta.x = -_cursor.delta.x;
  1667 		delta.x = -_cursor.delta.x;
  1575 		e.we.scroll.delta.y = -_cursor.delta.y;
  1668 		delta.y = -_cursor.delta.y;
  1576 	} else {
  1669 	} else {
  1577 		e.we.scroll.delta.x = _cursor.delta.x;
  1670 		delta.x = _cursor.delta.x;
  1578 		e.we.scroll.delta.y = _cursor.delta.y;
  1671 		delta.y = _cursor.delta.y;
  1579 	}
  1672 	}
  1580 
  1673 
  1581 	if (scrollwheel_scrolling) {
  1674 	if (scrollwheel_scrolling) {
  1582 		/* We are using scrollwheels for scrolling */
  1675 		/* We are using scrollwheels for scrolling */
  1583 		e.we.scroll.delta.x = _cursor.h_wheel;
  1676 		delta.x = _cursor.h_wheel;
  1584 		e.we.scroll.delta.y = _cursor.v_wheel;
  1677 		delta.y = _cursor.v_wheel;
  1585 		_cursor.v_wheel = 0;
  1678 		_cursor.v_wheel = 0;
  1586 		_cursor.h_wheel = 0;
  1679 		_cursor.h_wheel = 0;
  1587 	}
  1680 	}
  1588 
  1681 
  1589 	/* Create a scroll-event and send it to the window */
  1682 	/* Create a scroll-event and send it to the window */
  1590 	e.event = WE_SCROLL;
  1683 	w->OnScroll(delta);
  1591 	w->wndproc(w, &e);
       
  1592 
  1684 
  1593 	_cursor.delta.x = 0;
  1685 	_cursor.delta.x = 0;
  1594 	_cursor.delta.y = 0;
  1686 	_cursor.delta.y = 0;
  1595 	return false;
  1687 	return false;
  1596 }
  1688 }
  1604  * @param w Window to bring on-top
  1696  * @param w Window to bring on-top
  1605  * @return false if the window has an active modal child, true otherwise */
  1697  * @return false if the window has an active modal child, true otherwise */
  1606 static bool MaybeBringWindowToFront(const Window *w)
  1698 static bool MaybeBringWindowToFront(const Window *w)
  1607 {
  1699 {
  1608 	bool bring_to_front = false;
  1700 	bool bring_to_front = false;
  1609 	Window * const *wz;
       
  1610 
  1701 
  1611 	if (w->window_class == WC_MAIN_WINDOW ||
  1702 	if (w->window_class == WC_MAIN_WINDOW ||
  1612 			IsVitalWindow(w) ||
  1703 			IsVitalWindow(w) ||
  1613 			w->window_class == WC_TOOLTIPS ||
  1704 			w->window_class == WC_TOOLTIPS ||
  1614 			w->window_class == WC_DROPDOWN_MENU) {
  1705 			w->window_class == WC_DROPDOWN_MENU) {
  1615 		return true;
  1706 		return true;
  1616 	}
  1707 	}
  1617 
  1708 
  1618 	wz = FindWindowZPosition(w);
  1709 	Window * const *wz = FindWindowZPosition(w);
  1619 	for (Window * const *uz = wz; ++uz != _last_z_window;) {
  1710 	for (Window * const *uz = wz; ++uz != _last_z_window;) {
  1620 		Window *u = *uz;
  1711 		Window *u = *uz;
  1621 
  1712 
  1622 		/* A modal child will prevent the activation of the parent window */
  1713 		/* A modal child will prevent the activation of the parent window */
  1623 		if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
  1714 		if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
  1624 			u->flags4 |= WF_WHITE_BORDER_MASK;
  1715 			u->flags4 |= WF_WHITE_BORDER_MASK;
  1625 			SetWindowDirty(u);
  1716 			u->SetDirty();
  1626 			return false;
  1717 			return false;
  1627 		}
  1718 		}
  1628 
  1719 
  1629 		if (u->window_class == WC_MAIN_WINDOW ||
  1720 		if (u->window_class == WC_MAIN_WINDOW ||
  1630 				IsVitalWindow(u) ||
  1721 				IsVitalWindow(u) ||
  1646 
  1737 
  1647 	if (bring_to_front) BringWindowToFront(w);
  1738 	if (bring_to_front) BringWindowToFront(w);
  1648 	return true;
  1739 	return true;
  1649 }
  1740 }
  1650 
  1741 
  1651 /** Send a message from one window to another. The receiving window is found by
       
  1652  * @param w Window pointer pointing to the other window
       
  1653  * @param msg Specifies the message to be sent
       
  1654  * @param wparam Specifies additional message-specific information
       
  1655  * @param lparam Specifies additional message-specific information
       
  1656  */
       
  1657 static void SendWindowMessageW(Window *w, uint msg, uint wparam, uint lparam)
       
  1658 {
       
  1659 	WindowEvent e;
       
  1660 
       
  1661 	e.event             = WE_MESSAGE;
       
  1662 	e.we.message.msg    = msg;
       
  1663 	e.we.message.wparam = wparam;
       
  1664 	e.we.message.lparam = lparam;
       
  1665 
       
  1666 	w->wndproc(w, &e);
       
  1667 }
       
  1668 
       
  1669 /** Send a message from one window to another. The receiving window is found by
       
  1670  * @param wnd_class see WindowClass class AND
       
  1671  * @param wnd_num see WindowNumber number, mostly 0
       
  1672  * @param msg Specifies the message to be sent
       
  1673  * @param wparam Specifies additional message-specific information
       
  1674  * @param lparam Specifies additional message-specific information
       
  1675  */
       
  1676 void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, int msg, int wparam, int lparam)
       
  1677 {
       
  1678 	Window *w = FindWindowById(wnd_class, wnd_num);
       
  1679 	if (w != NULL) SendWindowMessageW(w, msg, wparam, lparam);
       
  1680 }
       
  1681 
       
  1682 /** Send a message from one window to another. The message will be sent
       
  1683  * to ALL windows of the windowclass specified in the first parameter
       
  1684  * @param wnd_class see WindowClass class
       
  1685  * @param msg Specifies the message to be sent
       
  1686  * @param wparam Specifies additional message-specific information
       
  1687  * @param lparam Specifies additional message-specific information
       
  1688  */
       
  1689 void SendWindowMessageClass(WindowClass wnd_class, int msg, int wparam, int lparam)
       
  1690 {
       
  1691 	Window* const *wz;
       
  1692 
       
  1693 	FOR_ALL_WINDOWS(wz) {
       
  1694 		if ((*wz)->window_class == wnd_class) SendWindowMessageW(*wz, msg, wparam, lparam);
       
  1695 	}
       
  1696 }
       
  1697 
       
  1698 /** Handle keyboard input.
  1742 /** Handle keyboard input.
  1699  * @param key Lower 8 bits contain the ASCII character, the higher 16 bits the keycode
  1743  * @param raw_key Lower 8 bits contain the ASCII character, the higher 16 bits the keycode
  1700  */
  1744  */
  1701 void HandleKeypress(uint32 key)
  1745 void HandleKeypress(uint32 raw_key)
  1702 {
  1746 {
  1703 	WindowEvent e;
       
  1704 	/* Stores if a window with a textfield for typing is open
  1747 	/* Stores if a window with a textfield for typing is open
  1705 	 * If this is the case, keypress events are only passed to windows with text fields and
  1748 	 * If this is the case, keypress events are only passed to windows with text fields and
  1706 	 * to thein this main toolbar. */
  1749 	 * to thein this main toolbar. */
  1707 	bool query_open = false;
  1750 	bool query_open = false;
  1708 
  1751 
  1716 	* can be handled are the 'close application' events
  1759 	* can be handled are the 'close application' events
  1717 	*/
  1760 	*/
  1718 	if (!IsGeneratingWorld()) _current_player = _local_player;
  1761 	if (!IsGeneratingWorld()) _current_player = _local_player;
  1719 
  1762 
  1720 	/* Setup event */
  1763 	/* Setup event */
  1721 	e.event = WE_KEYPRESS;
  1764 	uint16 key     = GB(raw_key,  0, 16);
  1722 	e.we.keypress.key     = GB(key,  0, 16);
  1765 	uint16 keycode = GB(raw_key, 16, 16);
  1723 	e.we.keypress.keycode = GB(key, 16, 16);
       
  1724 	e.we.keypress.cont = true;
       
  1725 
  1766 
  1726 	/*
  1767 	/*
  1727 	 * The Unicode standard defines an area called the private use area. Code points in this
  1768 	 * The Unicode standard defines an area called the private use area. Code points in this
  1728 	 * area are reserved for private use and thus not portable between systems. For instance,
  1769 	 * area are reserved for private use and thus not portable between systems. For instance,
  1729 	 * Apple defines code points for the arrow keys in this area, but these are only printable
  1770 	 * Apple defines code points for the arrow keys in this area, but these are only printable
  1730 	 * on a system running OS X. We don't want these keys to show up in text fields and such,
  1771 	 * on a system running OS X. We don't want these keys to show up in text fields and such,
  1731 	 * and thus we have to clear the unicode character when we encounter such a key.
  1772 	 * and thus we have to clear the unicode character when we encounter such a key.
  1732 	 */
  1773 	 */
  1733 	if (e.we.keypress.key >= 0xE000 && e.we.keypress.key <= 0xF8FF) e.we.keypress.key = 0;
  1774 	if (key >= 0xE000 && key <= 0xF8FF) key = 0;
  1734 
  1775 
  1735 	/*
  1776 	/*
  1736 	 * If both key and keycode is zero, we don't bother to process the event.
  1777 	 * If both key and keycode is zero, we don't bother to process the event.
  1737 	 */
  1778 	 */
  1738 	if (e.we.keypress.key == 0 && e.we.keypress.keycode == 0) return;
  1779 	if (key == 0 && keycode == 0) return;
  1739 
  1780 
  1740 	/* check if we have a query string window open before allowing hotkeys */
  1781 	/* check if we have a query string window open before allowing hotkeys */
  1741 	if (FindWindowById(WC_QUERY_STRING,            0) != NULL ||
  1782 	if (FindWindowById(WC_QUERY_STRING,            0) != NULL ||
  1742 			FindWindowById(WC_SEND_NETWORK_MSG,        0) != NULL ||
  1783 			FindWindowById(WC_SEND_NETWORK_MSG,        0) != NULL ||
  1743 			FindWindowById(WC_GENERATE_LANDSCAPE,      0) != NULL ||
  1784 			FindWindowById(WC_GENERATE_LANDSCAPE,      0) != NULL ||
  1759 				w->window_class != WC_CONSOLE &&
  1800 				w->window_class != WC_CONSOLE &&
  1760 				w->window_class != WC_SAVELOAD &&
  1801 				w->window_class != WC_SAVELOAD &&
  1761 				w->window_class != WC_COMPANY_PASSWORD_WINDOW) {
  1802 				w->window_class != WC_COMPANY_PASSWORD_WINDOW) {
  1762 			continue;
  1803 			continue;
  1763 		}
  1804 		}
  1764 		w->wndproc(w, &e);
  1805 		;
  1765 		if (!e.we.keypress.cont) break;
  1806 		if (!w->OnKeyPress(key, keycode)) return;
  1766 	}
  1807 	}
  1767 
  1808 
  1768 	if (e.we.keypress.cont) {
  1809 	Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
  1769 		Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
  1810 	/* When there is no toolbar w is null, check for that */
  1770 		/* When there is no toolbar w is null, check for that */
  1811 	if (w != NULL) w->OnKeyPress(key, keycode);
  1771 		if (w != NULL) w->wndproc(w, &e);
       
  1772 	}
       
  1773 }
  1812 }
  1774 
  1813 
  1775 /**
  1814 /**
  1776  * State of CONTROL key has changed
  1815  * State of CONTROL key has changed
  1777  */
  1816  */
  1778 void HandleCtrlChanged()
  1817 void HandleCtrlChanged()
  1779 {
  1818 {
  1780 	WindowEvent e;
       
  1781 
       
  1782 	e.event = WE_CTRL_CHANGED;
       
  1783 	e.we.ctrl.cont = true;
       
  1784 
       
  1785 	/* Call the event, start with the uppermost window. */
  1819 	/* Call the event, start with the uppermost window. */
  1786 	for (Window* const *wz = _last_z_window; wz != _z_windows;) {
  1820 	for (Window* const *wz = _last_z_window; wz != _z_windows;) {
  1787 		Window *w = *--wz;
  1821 		Window *w = *--wz;
  1788 		w->wndproc(w, &e);
  1822 		if (!w->OnCTRLStateChange()) break;
  1789 		if (!e.we.ctrl.cont) break;
       
  1790 	}
  1823 	}
  1791 }
  1824 }
  1792 
  1825 
  1793 /**
  1826 /**
  1794  * Local counter that is incremented each time an mouse input event is detected.
  1827  * Local counter that is incremented each time an mouse input event is detected.
  1802  * If needed and switched on, perform auto scrolling (automatically
  1835  * If needed and switched on, perform auto scrolling (automatically
  1803  * moving window contents when mouse is near edge of the window).
  1836  * moving window contents when mouse is near edge of the window).
  1804  */
  1837  */
  1805 static void HandleAutoscroll()
  1838 static void HandleAutoscroll()
  1806 {
  1839 {
  1807 	Window *w;
       
  1808 	ViewPort *vp;
       
  1809 	int x = _cursor.pos.x;
       
  1810 	int y = _cursor.pos.y;
       
  1811 
       
  1812 	if (_input_events_this_tick != 0) {
  1840 	if (_input_events_this_tick != 0) {
  1813 		/* HandleAutoscroll is called only once per GameLoop() - so we can clear the counter here */
  1841 		/* HandleAutoscroll is called only once per GameLoop() - so we can clear the counter here */
  1814 		_input_events_this_tick = 0;
  1842 		_input_events_this_tick = 0;
  1815 		/* there were some inputs this tick, don't scroll ??? */
  1843 		/* there were some inputs this tick, don't scroll ??? */
  1816 		return;
  1844 		return;
  1817 	}
  1845 	}
  1818 
  1846 
  1819 	if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
  1847 	if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
  1820 		w = FindWindowFromPt(x, y);
  1848 		int x = _cursor.pos.x;
       
  1849 		int y = _cursor.pos.y;
       
  1850 		Window *w = FindWindowFromPt(x, y);
  1821 		if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
  1851 		if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
  1822 		vp = IsPtInWindowViewport(w, x, y);
  1852 		ViewPort *vp = IsPtInWindowViewport(w, x, y);
  1823 		if (vp != NULL) {
  1853 		if (vp != NULL) {
  1824 			x -= vp->left;
  1854 			x -= vp->left;
  1825 			y -= vp->top;
  1855 			y -= vp->top;
       
  1856 
  1826 			/* here allows scrolling in both x and y axis */
  1857 			/* here allows scrolling in both x and y axis */
  1827 #define scrollspeed 3
  1858 #define scrollspeed 3
  1828 			if (x - 15 < 0) {
  1859 			if (x - 15 < 0) {
  1829 				WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
  1860 				w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
  1830 			} else if (15 - (vp->width - x) > 0) {
  1861 			} else if (15 - (vp->width - x) > 0) {
  1831 				WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
  1862 				w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
  1832 			}
  1863 			}
  1833 			if (y - 15 < 0) {
  1864 			if (y - 15 < 0) {
  1834 				WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
  1865 				w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
  1835 			} else if (15 - (vp->height - y) > 0) {
  1866 			} else if (15 - (vp->height - y) > 0) {
  1836 				WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
  1867 				w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
  1837 			}
  1868 			}
  1838 #undef scrollspeed
  1869 #undef scrollspeed
  1839 		}
  1870 		}
  1840 	}
  1871 	}
  1841 }
  1872 }
  1853 extern void UpdateTileSelection();
  1884 extern void UpdateTileSelection();
  1854 extern bool VpHandlePlaceSizingDrag();
  1885 extern bool VpHandlePlaceSizingDrag();
  1855 
  1886 
  1856 void MouseLoop(MouseClick click, int mousewheel)
  1887 void MouseLoop(MouseClick click, int mousewheel)
  1857 {
  1888 {
  1858 	int x,y;
       
  1859 	Window *w;
       
  1860 	ViewPort *vp;
       
  1861 	bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
       
  1862 
       
  1863 	DecreaseWindowCounters();
  1889 	DecreaseWindowCounters();
  1864 	HandlePlacePresize();
  1890 	HandlePlacePresize();
  1865 	UpdateTileSelection();
  1891 	UpdateTileSelection();
  1866 	if (!VpHandlePlaceSizingDrag())  return;
  1892 	if (!VpHandlePlaceSizingDrag())  return;
  1867 	if (!HandleDragDrop())           return;
  1893 	if (!HandleDragDrop())           return;
  1868 	if (!HandlePopupMenu())          return;
       
  1869 	if (!HandleWindowDragging())     return;
  1894 	if (!HandleWindowDragging())     return;
  1870 	if (!HandleScrollbarScrolling()) return;
  1895 	if (!HandleScrollbarScrolling()) return;
  1871 	if (!HandleViewportScroll())     return;
  1896 	if (!HandleViewportScroll())     return;
  1872 	if (!HandleMouseOver())          return;
  1897 	if (!HandleMouseOver())          return;
  1873 
  1898 
  1874 	x = _cursor.pos.x;
  1899 	bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
  1875 	y = _cursor.pos.y;
       
  1876 
       
  1877 	if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
  1900 	if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
  1878 
  1901 
  1879 	w = FindWindowFromPt(x, y);
  1902 	int x = _cursor.pos.x;
       
  1903 	int y = _cursor.pos.y;
       
  1904 	Window *w = FindWindowFromPt(x, y);
  1880 	if (w == NULL) return;
  1905 	if (w == NULL) return;
       
  1906 
  1881 	if (!MaybeBringWindowToFront(w)) return;
  1907 	if (!MaybeBringWindowToFront(w)) return;
  1882 	vp = IsPtInWindowViewport(w, x, y);
  1908 	ViewPort *vp = IsPtInWindowViewport(w, x, y);
  1883 
  1909 
  1884 	/* Don't allow any action in a viewport if either in menu of in generating world */
  1910 	/* Don't allow any action in a viewport if either in menu of in generating world */
  1885 	if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
  1911 	if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
  1886 
  1912 
  1887 	if (mousewheel != 0) {
  1913 	if (mousewheel != 0) {
  1888 		if (_patches.scrollwheel_scrolling == 0) {
  1914 		if (_patches.scrollwheel_scrolling == 0) {
  1889 			/* Scrollwheel is in zoom mode. Make the zoom event. */
  1915 			/* Send mousewheel event to window */
  1890 			WindowEvent e;
  1916 			w->OnMouseWheel(mousewheel);
  1891 
       
  1892 			/* Send WE_MOUSEWHEEL event to window */
       
  1893 			e.event = WE_MOUSEWHEEL;
       
  1894 			e.we.wheel.wheel = mousewheel;
       
  1895 			w->wndproc(w, &e);
       
  1896 		}
  1917 		}
  1897 
  1918 
  1898 		/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
  1919 		/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
  1899 		if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel);
  1920 		if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel);
  1900 	}
  1921 	}
  1931 			default:
  1952 			default:
  1932 				break;
  1953 				break;
  1933 		}
  1954 		}
  1934 	} else {
  1955 	} else {
  1935 		switch (click) {
  1956 		switch (click) {
  1936 			case MC_DOUBLE_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, true);
  1957 			case MC_DOUBLE_LEFT:
  1937 				/* fallthough, and also give a single-click for backwards compatible */
  1958 				DispatchLeftClickEvent(w, x - w->left, y - w->top, true);
  1938 			case MC_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, false); break;
  1959 				if (_mouseover_last_w == NULL) break; // The window got removed.
       
  1960 				/* fallthough, and also give a single-click for backwards compatibility */
       
  1961 			case MC_LEFT:
       
  1962 				DispatchLeftClickEvent(w, x - w->left, y - w->top, false);
       
  1963 				break;
       
  1964 
  1939 			default:
  1965 			default:
  1940 				if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
  1966 				if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
  1941 				/* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
  1967 				/* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
  1942 				* Simulate a right button click so we can get started. */
  1968 				* Simulate a right button click so we can get started. */
  1943 				/* fallthough */
  1969 				/* fallthough */
  1952 void HandleMouseEvents()
  1978 void HandleMouseEvents()
  1953 {
  1979 {
  1954 	static int double_click_time = 0;
  1980 	static int double_click_time = 0;
  1955 	static int double_click_x = 0;
  1981 	static int double_click_x = 0;
  1956 	static int double_click_y = 0;
  1982 	static int double_click_y = 0;
  1957 	MouseClick click;
       
  1958 	int mousewheel;
       
  1959 
  1983 
  1960 	/*
  1984 	/*
  1961 	 * During the generation of the world, there might be
  1985 	 * During the generation of the world, there might be
  1962 	 * another thread that is currently building for example
  1986 	 * another thread that is currently building for example
  1963 	 * a road. To not interfere with those tasks, we should
  1987 	 * a road. To not interfere with those tasks, we should
  1967 	 * can be handled are the 'close application' events
  1991 	 * can be handled are the 'close application' events
  1968 	 */
  1992 	 */
  1969 	if (!IsGeneratingWorld()) _current_player = _local_player;
  1993 	if (!IsGeneratingWorld()) _current_player = _local_player;
  1970 
  1994 
  1971 	/* Mouse event? */
  1995 	/* Mouse event? */
  1972 	click = MC_NONE;
  1996 	MouseClick click = MC_NONE;
  1973 	if (_left_button_down && !_left_button_clicked) {
  1997 	if (_left_button_down && !_left_button_clicked) {
  1974 		click = MC_LEFT;
  1998 		click = MC_LEFT;
  1975 		if (double_click_time != 0 && _realtime_tick - double_click_time   < TIME_BETWEEN_DOUBLE_CLICK &&
  1999 		if (double_click_time != 0 && _realtime_tick - double_click_time   < TIME_BETWEEN_DOUBLE_CLICK &&
  1976 			  double_click_x != 0    && abs(_cursor.pos.x - double_click_x) < MAX_OFFSET_DOUBLE_CLICK  &&
  2000 			  double_click_x != 0    && abs(_cursor.pos.x - double_click_x) < MAX_OFFSET_DOUBLE_CLICK  &&
  1977 			  double_click_y != 0    && abs(_cursor.pos.y - double_click_y) < MAX_OFFSET_DOUBLE_CLICK) {
  2001 			  double_click_y != 0    && abs(_cursor.pos.y - double_click_y) < MAX_OFFSET_DOUBLE_CLICK) {
  1986 		_right_button_clicked = false;
  2010 		_right_button_clicked = false;
  1987 		click = MC_RIGHT;
  2011 		click = MC_RIGHT;
  1988 		_input_events_this_tick++;
  2012 		_input_events_this_tick++;
  1989 	}
  2013 	}
  1990 
  2014 
  1991 	mousewheel = 0;
  2015 	int mousewheel = 0;
  1992 	if (_cursor.wheel) {
  2016 	if (_cursor.wheel) {
  1993 		mousewheel = _cursor.wheel;
  2017 		mousewheel = _cursor.wheel;
  1994 		_cursor.wheel = 0;
  2018 		_cursor.wheel = 0;
  1995 		_input_events_this_tick++;
  2019 		_input_events_this_tick++;
  1996 	}
  2020 	}
  2016 	static int we4_timer = 0;
  2040 	static int we4_timer = 0;
  2017 	int t = we4_timer + 1;
  2041 	int t = we4_timer + 1;
  2018 
  2042 
  2019 	if (t >= 100) {
  2043 	if (t >= 100) {
  2020 		for (wz = _last_z_window; wz != _z_windows;) {
  2044 		for (wz = _last_z_window; wz != _z_windows;) {
  2021 			CallWindowEventNP(*--wz, WE_4);
  2045 			(*--wz)->OnHundredthTick();
  2022 		}
  2046 		}
  2023 		t = 0;
  2047 		t = 0;
  2024 	}
  2048 	}
  2025 	we4_timer = t;
  2049 	we4_timer = t;
  2026 
  2050 
  2027 	for (wz = _last_z_window; wz != _z_windows;) {
  2051 	for (wz = _last_z_window; wz != _z_windows;) {
  2028 		Window *w = *--wz;
  2052 		Window *w = *--wz;
  2029 		if (w->flags4 & WF_WHITE_BORDER_MASK) {
  2053 		if (w->flags4 & WF_WHITE_BORDER_MASK) {
  2030 			w->flags4 -= WF_WHITE_BORDER_ONE;
  2054 			w->flags4 -= WF_WHITE_BORDER_ONE;
  2031 
  2055 
  2032 			if (!(w->flags4 & WF_WHITE_BORDER_MASK)) SetWindowDirty(w);
  2056 			if (!(w->flags4 & WF_WHITE_BORDER_MASK)) w->SetDirty();
  2033 		}
  2057 		}
  2034 	}
  2058 	}
  2035 
  2059 
  2036 	DrawDirtyBlocks();
  2060 	DrawDirtyBlocks();
  2037 
  2061 
  2041 	DrawChatMessage();
  2065 	DrawChatMessage();
  2042 	/* Redraw mouse cursor in case it was hidden */
  2066 	/* Redraw mouse cursor in case it was hidden */
  2043 	DrawMouseCursor();
  2067 	DrawMouseCursor();
  2044 }
  2068 }
  2045 
  2069 
  2046 
       
  2047 /**
       
  2048  * In a window with menu_d custom extension, retrieve the menu item number from a position
       
  2049  * @param w Window holding the menu items
       
  2050  * @param x X coordinate of the position
       
  2051  * @param y Y coordinate of the position
       
  2052  * @return Index number of the menu item, or \c -1 if no valid selection under position
       
  2053  */
       
  2054 int GetMenuItemIndex(const Window *w, int x, int y)
       
  2055 {
       
  2056 	if ((x -= w->left) >= 0 && x < w->width && (y -= w->top + 1) >= 0) {
       
  2057 		y /= 10;
       
  2058 
       
  2059 		if (y < WP(w, const menu_d).item_count &&
       
  2060 				!HasBit(WP(w, const menu_d).disabled_items, y)) {
       
  2061 			return y;
       
  2062 		}
       
  2063 	}
       
  2064 	return -1;
       
  2065 }
       
  2066 
       
  2067 /**
  2070 /**
  2068  * Mark window as dirty (in need of repainting)
  2071  * Mark window as dirty (in need of repainting)
  2069  * @param cls Window class
  2072  * @param cls Window class
  2070  * @param number Window number in that class
  2073  * @param number Window number in that class
  2071  */
  2074  */
  2073 {
  2076 {
  2074 	Window* const *wz;
  2077 	Window* const *wz;
  2075 
  2078 
  2076 	FOR_ALL_WINDOWS(wz) {
  2079 	FOR_ALL_WINDOWS(wz) {
  2077 		const Window *w = *wz;
  2080 		const Window *w = *wz;
  2078 		if (w->window_class == cls && w->window_number == number) SetWindowDirty(w);
  2081 		if (w->window_class == cls && w->window_number == number) w->SetDirty();
  2079 	}
  2082 	}
  2080 }
  2083 }
  2081 
  2084 
  2082 /**
  2085 /**
  2083  * Mark a particular widget in a particular window as dirty (in need of repainting)
  2086  * Mark a particular widget in a particular window as dirty (in need of repainting)
  2104 void InvalidateWindowClasses(WindowClass cls)
  2107 void InvalidateWindowClasses(WindowClass cls)
  2105 {
  2108 {
  2106 	Window* const *wz;
  2109 	Window* const *wz;
  2107 
  2110 
  2108 	FOR_ALL_WINDOWS(wz) {
  2111 	FOR_ALL_WINDOWS(wz) {
  2109 		if ((*wz)->window_class == cls) SetWindowDirty(*wz);
  2112 		if ((*wz)->window_class == cls) (*wz)->SetDirty();
  2110 	}
  2113 	}
  2111 }
  2114 }
  2112 
  2115 
  2113 /**
  2116 /**
  2114  * Mark window data as invalid (in need of re-computing)
  2117  * Mark window data as invalid (in need of re-computing)
  2115  * @param w Window with invalid data
  2118  * @param w Window with invalid data
  2116  */
  2119  */
  2117 void InvalidateThisWindowData(Window *w)
  2120 void InvalidateThisWindowData(Window *w, int data)
  2118 {
  2121 {
  2119 	CallWindowEventNP(w, WE_INVALIDATE_DATA);
  2122 	w->OnInvalidateData(data);
  2120 	SetWindowDirty(w);
  2123 	w->SetDirty();
  2121 }
  2124 }
  2122 
  2125 
  2123 /**
  2126 /**
  2124  * Mark window data of the window of a given class and specific window number as invalid (in need of re-computing)
  2127  * Mark window data of the window of a given class and specific window number as invalid (in need of re-computing)
  2125  * @param cls Window class
  2128  * @param cls Window class
  2126  * @param number Window number within the class
  2129  * @param number Window number within the class
  2127  */
  2130  */
  2128 void InvalidateWindowData(WindowClass cls, WindowNumber number)
  2131 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data)
  2129 {
  2132 {
  2130 	Window* const *wz;
  2133 	Window* const *wz;
  2131 
  2134 
  2132 	FOR_ALL_WINDOWS(wz) {
  2135 	FOR_ALL_WINDOWS(wz) {
  2133 		Window *w = *wz;
  2136 		Window *w = *wz;
  2134 		if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w);
  2137 		if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w, data);
  2135 	}
  2138 	}
  2136 }
  2139 }
  2137 
  2140 
  2138 /**
  2141 /**
  2139  * Mark window data of all windows of a given class as invalid (in need of re-computing)
  2142  * Mark window data of all windows of a given class as invalid (in need of re-computing)
  2140  * @param cls Window class
  2143  * @param cls Window class
  2141  */
  2144  */
  2142 void InvalidateWindowClassesData(WindowClass cls)
  2145 void InvalidateWindowClassesData(WindowClass cls, int data)
  2143 {
  2146 {
  2144 	Window* const *wz;
  2147 	Window* const *wz;
  2145 
  2148 
  2146 	FOR_ALL_WINDOWS(wz) {
  2149 	FOR_ALL_WINDOWS(wz) {
  2147 		if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz);
  2150 		if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz, data);
  2148 	}
  2151 	}
  2149 }
  2152 }
  2150 
  2153 
  2151 /**
  2154 /**
  2152  * Dispatch WE_TICK event over all windows
  2155  * Dispatch WE_TICK event over all windows
  2153  */
  2156  */
  2154 void CallWindowTickEvent()
  2157 void CallWindowTickEvent()
  2155 {
  2158 {
  2156 	for (Window * const *wz = _last_z_window; wz != _z_windows;) {
  2159 	for (Window * const *wz = _last_z_window; wz != _z_windows;) {
  2157 		CallWindowEventNP(*--wz, WE_TICK);
  2160 		(*--wz)->OnTick();
  2158 	}
  2161 	}
  2159 }
  2162 }
  2160 
  2163 
  2161 /**
  2164 /**
  2162  * Try to delete a non-vital window.
  2165  * Try to delete a non-vital window.
  2180 				w->window_class != WC_STATUS_BAR &&
  2183 				w->window_class != WC_STATUS_BAR &&
  2181 				w->window_class != WC_TOOLBAR_MENU &&
  2184 				w->window_class != WC_TOOLBAR_MENU &&
  2182 				w->window_class != WC_TOOLTIPS &&
  2185 				w->window_class != WC_TOOLTIPS &&
  2183 				(w->flags4 & WF_STICKY) == 0) { // do not delete windows which are 'pinned'
  2186 				(w->flags4 & WF_STICKY) == 0) { // do not delete windows which are 'pinned'
  2184 
  2187 
  2185 			DeleteWindow(w);
  2188 			delete w;
  2186 			goto restart_search;
  2189 			goto restart_search;
  2187 		}
  2190 		}
  2188 	}
  2191 	}
  2189 }
  2192 }
  2190 
  2193 
  2204 	/* When we find the window to delete, we need to restart the search
  2207 	/* When we find the window to delete, we need to restart the search
  2205 	 * as deleting this window could cascade in deleting (many) others
  2208 	 * as deleting this window could cascade in deleting (many) others
  2206 	 * anywhere in the z-array */
  2209 	 * anywhere in the z-array */
  2207 	FOR_ALL_WINDOWS(wz) {
  2210 	FOR_ALL_WINDOWS(wz) {
  2208 		if ((*wz)->flags4 & WF_STICKY) {
  2211 		if ((*wz)->flags4 & WF_STICKY) {
  2209 			DeleteWindow(*wz);
  2212 			delete *wz;
  2210 			goto restart_search;
  2213 			goto restart_search;
  2211 		}
  2214 		}
  2212 	}
  2215 	}
  2213 }
  2216 }
  2214 
  2217 
  2269 		switch (w->window_class) {
  2272 		switch (w->window_class) {
  2270 			case WC_MAIN_TOOLBAR:
  2273 			case WC_MAIN_TOOLBAR:
  2271 				if (neww - w->width != 0) {
  2274 				if (neww - w->width != 0) {
  2272 					ResizeWindow(w, min(neww, 640) - w->width, 0);
  2275 					ResizeWindow(w, min(neww, 640) - w->width, 0);
  2273 
  2276 
  2274 					WindowEvent e;
  2277 					Point size;
  2275 					e.event = WE_RESIZE;
  2278 					Point delta;
  2276 					e.we.sizing.size.x = w->width;
  2279 					size.x = w->width;
  2277 					e.we.sizing.size.y = w->height;
  2280 					size.y = w->height;
  2278 					e.we.sizing.diff.x = neww - w->width;
  2281 					delta.x = neww - w->width;
  2279 					e.we.sizing.diff.y = 0;
  2282 					delta.y = 0;
  2280 					w->wndproc(w, &e);
  2283 					w->OnResize(size, delta);
  2281 				}
  2284 				}
  2282 
  2285 
  2283 				top = w->top;
  2286 				top = w->top;
  2284 				left = PositionMainToolbar(w); // changes toolbar orientation
  2287 				left = PositionMainToolbar(w); // changes toolbar orientation
  2285 				break;
  2288 				break;
  2337 
  2340 
  2338 		w->left = left;
  2341 		w->left = left;
  2339 		w->top = top;
  2342 		w->top = top;
  2340 	}
  2343 	}
  2341 }
  2344 }
       
  2345 
       
  2346 /** Destructor of the base class PickerWindowBase
       
  2347  * Main utility is to stop the base Window destructor from triggering
       
  2348  * a free while the child will already be free, in this case by the ResetObjectToPlace().
       
  2349  */
       
  2350 PickerWindowBase::~PickerWindowBase()
       
  2351 {
       
  2352 	this->window_class = WC_INVALID; // stop the ancestor from freeing the already (to be) child
       
  2353 	ResetObjectToPlace();
       
  2354 }