src/window.c
changeset 5475 2e6990a8c7c4
parent 5429 a664da42d64e
equal deleted inserted replaced
5474:ac55aefc54f3 5475:2e6990a8c7c4
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include <stdarg.h>
       
     5 #include "openttd.h"
       
     6 #include "debug.h"
       
     7 #include "functions.h"
       
     8 #include "map.h"
       
     9 #include "player.h"
       
    10 #include "window.h"
       
    11 #include "gfx.h"
       
    12 #include "viewport.h"
       
    13 #include "console.h"
       
    14 #include "variables.h"
       
    15 #include "table/sprites.h"
       
    16 #include "genworld.h"
       
    17 
       
    18 // delta between mouse cursor and upper left corner of dragged window
       
    19 static Point _drag_delta;
       
    20 
       
    21 static Window _windows[25];
       
    22 Window *_z_windows[lengthof(_windows)];
       
    23 Window **_last_z_window; ///< always points to the next free space in the z-array
       
    24 
       
    25 void CDECL SetWindowWidgetsDisabledState(Window *w, bool disab_stat, int widgets, ...)
       
    26 {
       
    27 	va_list wdg_list;
       
    28 
       
    29 	va_start(wdg_list, widgets);
       
    30 
       
    31 	while (widgets != WIDGET_LIST_END) {
       
    32 		SetWindowWidgetDisabledState(w, widgets, disab_stat);
       
    33 		widgets = va_arg(wdg_list, int);
       
    34 	}
       
    35 
       
    36 	va_end(wdg_list);
       
    37 }
       
    38 
       
    39 void CDECL SetWindowWidgetsHiddenState(Window *w, bool hidden_stat, int widgets, ...)
       
    40 {
       
    41 	va_list wdg_list;
       
    42 
       
    43 	va_start(wdg_list, widgets);
       
    44 
       
    45 	while (widgets != WIDGET_LIST_END) {
       
    46 		SetWindowWidgetHiddenState(w, widgets, hidden_stat);
       
    47 		widgets = va_arg(wdg_list, int);
       
    48 	}
       
    49 
       
    50 	va_end(wdg_list);
       
    51 }
       
    52 
       
    53 void CDECL SetWindowWidgetsLoweredState(Window *w, bool lowered_stat, int widgets, ...)
       
    54 {
       
    55 	va_list wdg_list;
       
    56 
       
    57 	va_start(wdg_list, widgets);
       
    58 
       
    59 	while (widgets != WIDGET_LIST_END) {
       
    60 		SetWindowWidgetLoweredState(w, widgets, lowered_stat);
       
    61 		widgets = va_arg(wdg_list, int);
       
    62 	}
       
    63 
       
    64 	va_end(wdg_list);
       
    65 }
       
    66 
       
    67 void RaiseWindowButtons(Window *w)
       
    68 {
       
    69 	uint i;
       
    70 
       
    71 	for (i = 0; i < w->widget_count; i++) {
       
    72 		if (IsWindowWidgetLowered(w, i)) {
       
    73 			RaiseWindowWidget(w, i);
       
    74 			InvalidateWidget(w, i);
       
    75 		}
       
    76 	}
       
    77 }
       
    78 
       
    79 void HandleButtonClick(Window *w, byte widget)
       
    80 {
       
    81 	LowerWindowWidget(w, widget);
       
    82 	w->flags4 |= 5 << WF_TIMEOUT_SHL;
       
    83 	InvalidateWidget(w, widget);
       
    84 }
       
    85 
       
    86 
       
    87 static void StartWindowDrag(Window *w);
       
    88 static void StartWindowSizing(Window *w);
       
    89 
       
    90 static void DispatchLeftClickEvent(Window *w, int x, int y)
       
    91 {
       
    92 	WindowEvent e;
       
    93 	const Widget *wi;
       
    94 
       
    95 	e.we.click.pt.x = x;
       
    96 	e.we.click.pt.y = y;
       
    97 	e.event = WE_CLICK;
       
    98 
       
    99 	if (w->desc_flags & WDF_DEF_WIDGET) {
       
   100 		e.we.click.widget = GetWidgetFromPos(w, x, y);
       
   101 		if (e.we.click.widget < 0) return; /* exit if clicked outside of widgets */
       
   102 
       
   103 		/* don't allow any interaction if the button has been disabled */
       
   104 		if (IsWindowWidgetDisabled(w, e.we.click.widget)) return;
       
   105 
       
   106 		wi = &w->widget[e.we.click.widget];
       
   107 
       
   108 		if (wi->type & WWB_MASK) {
       
   109 			/* special widget handling for buttons*/
       
   110 			switch (wi->type) {
       
   111 				case WWT_PANEL   | WWB_PUSHBUTTON: /* WWT_PUSHBTN */
       
   112 				case WWT_IMGBTN  | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */
       
   113 				case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */
       
   114 					HandleButtonClick(w, e.we.click.widget);
       
   115 					break;
       
   116 			}
       
   117 		} else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
       
   118 			ScrollbarClickHandler(w, wi, e.we.click.pt.x, e.we.click.pt.y);
       
   119 		}
       
   120 
       
   121 		if (w->desc_flags & WDF_STD_BTN) {
       
   122 			if (e.we.click.widget == 0) { /* 'X' */
       
   123 				DeleteWindow(w);
       
   124 				return;
       
   125 			}
       
   126 
       
   127 			if (e.we.click.widget == 1) { /* 'Title bar' */
       
   128 				StartWindowDrag(w);
       
   129 				return;
       
   130 			}
       
   131 		}
       
   132 
       
   133 		if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
       
   134 			StartWindowSizing(w);
       
   135 			InvalidateWidget(w, e.we.click.widget);
       
   136 			return;
       
   137 		}
       
   138 
       
   139 		if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) {
       
   140 			w->flags4 ^= WF_STICKY;
       
   141 			InvalidateWidget(w, e.we.click.widget);
       
   142 			return;
       
   143 		}
       
   144 	}
       
   145 
       
   146 	w->wndproc(w, &e);
       
   147 }
       
   148 
       
   149 static void DispatchRightClickEvent(Window *w, int x, int y)
       
   150 {
       
   151 	WindowEvent e;
       
   152 
       
   153 	/* default tooltips handler? */
       
   154 	if (w->desc_flags & WDF_STD_TOOLTIPS) {
       
   155 		e.we.click.widget = GetWidgetFromPos(w, x, y);
       
   156 		if (e.we.click.widget < 0)
       
   157 			return; /* exit if clicked outside of widgets */
       
   158 
       
   159 		if (w->widget[e.we.click.widget].tooltips != 0) {
       
   160 			GuiShowTooltips(w->widget[e.we.click.widget].tooltips);
       
   161 			return;
       
   162 		}
       
   163 	}
       
   164 
       
   165 	e.event = WE_RCLICK;
       
   166 	e.we.click.pt.x = x;
       
   167 	e.we.click.pt.y = y;
       
   168 	w->wndproc(w, &e);
       
   169 }
       
   170 
       
   171 /** Dispatch the mousewheel-action to the window which will scroll any
       
   172  * compatible scrollbars if the mouse is pointed over the bar or its contents
       
   173  * @param *w Window
       
   174  * @param widget the widget where the scrollwheel was used
       
   175  * @param wheel scroll up or down
       
   176  */
       
   177 static void DispatchMouseWheelEvent(Window *w, int widget, int wheel)
       
   178 {
       
   179 	const Widget *wi1, *wi2;
       
   180 	Scrollbar *sb;
       
   181 
       
   182 	if (widget < 0) return;
       
   183 
       
   184 	wi1 = &w->widget[widget];
       
   185 	wi2 = &w->widget[widget + 1];
       
   186 
       
   187 	/* The listbox can only scroll if scrolling was done on the scrollbar itself,
       
   188 	 * or on the listbox (and the next item is (must be) the scrollbar)
       
   189 	 * XXX - should be rewritten as a widget-dependent scroller but that's
       
   190 	 * not happening until someone rewrites the whole widget-code */
       
   191 	if ((sb = &w->vscroll,  wi1->type == WWT_SCROLLBAR)  || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR)  ||
       
   192 			(sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) {
       
   193 
       
   194 		if (sb->count > sb->cap) {
       
   195 			int pos = clamp(sb->pos + wheel, 0, sb->count - sb->cap);
       
   196 			if (pos != sb->pos) {
       
   197 				sb->pos = pos;
       
   198 				SetWindowDirty(w);
       
   199 			}
       
   200 		}
       
   201 	}
       
   202 }
       
   203 
       
   204 static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right, int bottom);
       
   205 
       
   206 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
       
   207 {
       
   208 	Window* const *wz;
       
   209 	DrawPixelInfo bk;
       
   210 	_cur_dpi = &bk;
       
   211 
       
   212 	FOR_ALL_WINDOWS(wz) {
       
   213 		const Window *w = *wz;
       
   214 		if (right > w->left &&
       
   215 				bottom > w->top &&
       
   216 				left < w->left + w->width &&
       
   217 				top < w->top + w->height) {
       
   218 			DrawOverlappedWindow(wz, left, top, right, bottom);
       
   219 		}
       
   220 	}
       
   221 }
       
   222 
       
   223 static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right, int bottom)
       
   224 {
       
   225 	Window* const *vz = wz;
       
   226 	int x;
       
   227 
       
   228 	while (++vz != _last_z_window) {
       
   229 		const Window *v = *vz;
       
   230 
       
   231 		if (right > v->left &&
       
   232 				bottom > v->top &&
       
   233 				left < v->left + v->width &&
       
   234 				top < v->top + v->height) {
       
   235 			if (left < (x=v->left)) {
       
   236 				DrawOverlappedWindow(wz, left, top, x, bottom);
       
   237 				DrawOverlappedWindow(wz, x, top, right, bottom);
       
   238 				return;
       
   239 			}
       
   240 
       
   241 			if (right > (x=v->left + v->width)) {
       
   242 				DrawOverlappedWindow(wz, left, top, x, bottom);
       
   243 				DrawOverlappedWindow(wz, x, top, right, bottom);
       
   244 				return;
       
   245 			}
       
   246 
       
   247 			if (top < (x=v->top)) {
       
   248 				DrawOverlappedWindow(wz, left, top, right, x);
       
   249 				DrawOverlappedWindow(wz, left, x, right, bottom);
       
   250 				return;
       
   251 			}
       
   252 
       
   253 			if (bottom > (x=v->top + v->height)) {
       
   254 				DrawOverlappedWindow(wz, left, top, right, x);
       
   255 				DrawOverlappedWindow(wz, left, x, right, bottom);
       
   256 				return;
       
   257 			}
       
   258 
       
   259 			return;
       
   260 		}
       
   261 	}
       
   262 
       
   263 	{
       
   264 		DrawPixelInfo *dp = _cur_dpi;
       
   265 		dp->width = right - left;
       
   266 		dp->height = bottom - top;
       
   267 		dp->left = left - (*wz)->left;
       
   268 		dp->top = top - (*wz)->top;
       
   269 		dp->pitch = _screen.pitch;
       
   270 		dp->dst_ptr = _screen.dst_ptr + top * _screen.pitch + left;
       
   271 		dp->zoom = 0;
       
   272 		CallWindowEventNP(*wz, WE_PAINT);
       
   273 	}
       
   274 }
       
   275 
       
   276 void CallWindowEventNP(Window *w, int event)
       
   277 {
       
   278 	WindowEvent e;
       
   279 
       
   280 	e.event = event;
       
   281 	w->wndproc(w, &e);
       
   282 }
       
   283 
       
   284 void SetWindowDirty(const Window *w)
       
   285 {
       
   286 	if (w == NULL) return;
       
   287 	SetDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height);
       
   288 }
       
   289 
       
   290 /** Find the Window whose parent pointer points to this window
       
   291  * @parent w Window to find child of
       
   292  * @return return a Window pointer that is the child of w, or NULL otherwise */
       
   293 static Window *FindChildWindow(const Window *w)
       
   294 {
       
   295 	Window* const *wz;
       
   296 
       
   297 	FOR_ALL_WINDOWS(wz) {
       
   298 		Window *v = *wz;
       
   299 		if (v->parent == w) return v;
       
   300 	}
       
   301 
       
   302 	return NULL;
       
   303 }
       
   304 
       
   305 /** Find the z-value of a window. A window must already be open
       
   306  * or the behaviour is undefined but function should never fail */
       
   307 Window **FindWindowZPosition(const Window *w)
       
   308 {
       
   309 	Window **wz;
       
   310 
       
   311 	for (wz = _z_windows; wz != _last_z_window; wz++) {
       
   312 		if (*wz == w) return wz;
       
   313 	}
       
   314 
       
   315 	DEBUG(misc, 3, "Window (class %d, number %d) is not open, probably removed by recursive calls",
       
   316 		w->window_class, w->window_number);
       
   317 	return NULL;
       
   318 }
       
   319 
       
   320 void DeleteWindow(Window *w)
       
   321 {
       
   322 	Window *v;
       
   323 	Window **wz;
       
   324 	if (w == NULL) return;
       
   325 
       
   326 	/* Delete any children a window might have in a head-recursive manner */
       
   327 	v = FindChildWindow(w);
       
   328 	if (v != NULL) DeleteWindow(v);
       
   329 
       
   330 	if (_thd.place_mode != VHM_NONE &&
       
   331 			_thd.window_class == w->window_class &&
       
   332 			_thd.window_number == w->window_number) {
       
   333 		ResetObjectToPlace();
       
   334 	}
       
   335 
       
   336 	CallWindowEventNP(w, WE_DESTROY);
       
   337 	if (w->viewport != NULL) DeleteWindowViewport(w);
       
   338 
       
   339 	SetWindowDirty(w);
       
   340 	free(w->widget);
       
   341 	w->widget = NULL;
       
   342 	w->widget_count = 0;
       
   343 	w->parent = NULL;
       
   344 
       
   345 	/* Find the window in the z-array, and effectively remove it
       
   346 	 * by moving all windows after it one to the left */
       
   347 	wz = FindWindowZPosition(w);
       
   348 	if (wz == NULL) return;
       
   349 	memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz);
       
   350 	_last_z_window--;
       
   351 }
       
   352 
       
   353 Window *FindWindowById(WindowClass cls, WindowNumber number)
       
   354 {
       
   355 	Window* const *wz;
       
   356 
       
   357 	FOR_ALL_WINDOWS(wz) {
       
   358 		Window *w = *wz;
       
   359 		if (w->window_class == cls && w->window_number == number) return w;
       
   360 	}
       
   361 
       
   362 	return NULL;
       
   363 }
       
   364 
       
   365 void DeleteWindowById(WindowClass cls, WindowNumber number)
       
   366 {
       
   367 	DeleteWindow(FindWindowById(cls, number));
       
   368 }
       
   369 
       
   370 void DeleteWindowByClass(WindowClass cls)
       
   371 {
       
   372 	Window* const *wz;
       
   373 
       
   374 restart_search:
       
   375 	/* When we find the window to delete, we need to restart the search
       
   376 	 * as deleting this window could cascade in deleting (many) others
       
   377 	 * anywhere in the z-array */
       
   378 	FOR_ALL_WINDOWS(wz) {
       
   379 		Window *w = *wz;
       
   380 		if (w->window_class == cls) {
       
   381 			DeleteWindow(w);
       
   382 			goto restart_search;
       
   383 		}
       
   384 	}
       
   385 }
       
   386 
       
   387 /** Delete all windows of a player. We identify windows of a player
       
   388  * by looking at the caption colour. If it is equal to the player ID
       
   389  * then we say the window belongs to the player and should be deleted
       
   390  * @param id PlayerID player identifier */
       
   391 void DeletePlayerWindows(PlayerID id)
       
   392 {
       
   393 	Window* const *wz;
       
   394 
       
   395 restart_search:
       
   396 	/* When we find the window to delete, we need to restart the search
       
   397 	 * as deleting this window could cascade in deleting (many) others
       
   398 	 * anywhere in the z-array */
       
   399 	FOR_ALL_WINDOWS(wz) {
       
   400 		Window *w = *wz;
       
   401 		if (w->caption_color == id) {
       
   402 			DeleteWindow(w);
       
   403 			goto restart_search;
       
   404 		}
       
   405 	}
       
   406 
       
   407 	/* Also delete the player specific windows, that don't have a player-colour */
       
   408 	DeleteWindowById(WC_BUY_COMPANY, id);
       
   409 }
       
   410 
       
   411 /** Change the owner of all the windows one player can take over from another
       
   412  * player in the case of a company merger. Do not change ownership of windows
       
   413  * that need to be deleted once takeover is complete
       
   414  * @param old_player PlayerID of original owner of the window
       
   415  * @param new_player PlayerID of the new owner of the window */
       
   416 void ChangeWindowOwner(PlayerID old_player, PlayerID new_player)
       
   417 {
       
   418 	Window* const *wz;
       
   419 
       
   420 	FOR_ALL_WINDOWS(wz) {
       
   421 		Window *w = *wz;
       
   422 
       
   423 		if (w->caption_color != old_player)      continue;
       
   424 		if (w->window_class == WC_PLAYER_COLOR)  continue;
       
   425 		if (w->window_class == WC_FINANCES)      continue;
       
   426 		if (w->window_class == WC_STATION_LIST)  continue;
       
   427 		if (w->window_class == WC_TRAINS_LIST)   continue;
       
   428 		if (w->window_class == WC_ROADVEH_LIST)  continue;
       
   429 		if (w->window_class == WC_SHIPS_LIST)    continue;
       
   430 		if (w->window_class == WC_AIRCRAFT_LIST) continue;
       
   431 		if (w->window_class == WC_BUY_COMPANY)   continue;
       
   432 		if (w->window_class == WC_COMPANY)       continue;
       
   433 
       
   434 		w->caption_color = new_player;
       
   435 	}
       
   436 }
       
   437 
       
   438 static void BringWindowToFront(const Window *w);
       
   439 
       
   440 /** Find a window and make it the top-window on the screen. The window
       
   441  * gets a white border for a brief period of time to visualize its
       
   442  * "activation"
       
   443  * @return a pointer to the window thus activated */
       
   444 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
       
   445 {
       
   446 	Window *w = FindWindowById(cls, number);
       
   447 
       
   448 	if (w != NULL) {
       
   449 		w->flags4 |= WF_WHITE_BORDER_MASK;
       
   450 		BringWindowToFront(w);
       
   451 		SetWindowDirty(w);
       
   452 	}
       
   453 
       
   454 	return w;
       
   455 }
       
   456 
       
   457 static inline bool IsVitalWindow(const Window *w)
       
   458 {
       
   459 	WindowClass wc = w->window_class;
       
   460 	return (wc == WC_MAIN_TOOLBAR || wc == WC_STATUS_BAR || wc == WC_NEWS_WINDOW || wc == WC_SEND_NETWORK_MSG);
       
   461 }
       
   462 
       
   463 /** On clicking on a window, make it the frontmost window of all. However
       
   464  * there are certain windows that always need to be on-top; these include
       
   465  * - Toolbar, Statusbar (always on)
       
   466  * - New window, Chatbar (only if open)
       
   467  * The window is marked dirty for a repaint if the window is actually moved
       
   468  * @param w window that is put into the foreground
       
   469  * @return pointer to the window, the same as the input pointer
       
   470  */
       
   471 static void BringWindowToFront(const Window *w)
       
   472 {
       
   473 	Window *tempz;
       
   474 	Window **wz = FindWindowZPosition(w);
       
   475 	Window **vz = _last_z_window;
       
   476 
       
   477 	/* Bring the window just below the vital windows */
       
   478 	do {
       
   479 		if (--vz < _z_windows) return;
       
   480 	} while (IsVitalWindow(*vz));
       
   481 
       
   482 	if (wz == vz) return; // window is already in the right position
       
   483 	assert(wz < vz);
       
   484 
       
   485 	tempz = *wz;
       
   486 	memmove(wz, wz + 1, (byte*)vz - (byte*)wz);
       
   487 	*vz = tempz;
       
   488 
       
   489 	SetWindowDirty(w);
       
   490 }
       
   491 
       
   492 /** We have run out of windows, so find a suitable candidate for replacement.
       
   493  * Keep all important windows intact. These are
       
   494  * - Main window (gamefield), Toolbar, Statusbar (always on)
       
   495  * - News window, Chatbar (when on)
       
   496  * - Any sticked windows since we wanted to keep these
       
   497  * @return w pointer to the window that is going to be deleted
       
   498  */
       
   499 static Window *FindDeletableWindow(void)
       
   500 {
       
   501 	Window* const *wz;
       
   502 
       
   503 	FOR_ALL_WINDOWS(wz) {
       
   504 		Window *w = *wz;
       
   505 		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w) && !(w->flags4 & WF_STICKY)) {
       
   506 			return w;
       
   507 		}
       
   508 	}
       
   509 	return NULL;
       
   510 }
       
   511 
       
   512 /** A window must be freed, and all are marked as important windows. Ease the
       
   513  * restriction a bit by allowing to delete sticky windows. Keep important/vital
       
   514  * windows intact (Main window, Toolbar, Statusbar, News Window, Chatbar)
       
   515  * Start finding an appropiate candidate from the lowest z-values (bottom)
       
   516  * @see FindDeletableWindow()
       
   517  * @return w Pointer to the window that is being deleted
       
   518  */
       
   519 static Window *ForceFindDeletableWindow(void)
       
   520 {
       
   521 	Window* const *wz;
       
   522 
       
   523 	for (wz = _z_windows;; wz++) {
       
   524 		Window *w = *wz;
       
   525 		assert(wz < _last_z_window);
       
   526 		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w;
       
   527 	}
       
   528 }
       
   529 
       
   530 bool IsWindowOfPrototype(const Window *w, const Widget *widget)
       
   531 {
       
   532 	return (w->original_widget == widget);
       
   533 }
       
   534 
       
   535 /* Copies 'widget' to 'w->widget' to allow for resizable windows */
       
   536 void AssignWidgetToWindow(Window *w, const Widget *widget)
       
   537 {
       
   538 	w->original_widget = widget;
       
   539 
       
   540 	if (widget != NULL) {
       
   541 		uint index = 1;
       
   542 		const Widget *wi;
       
   543 
       
   544 		for (wi = widget; wi->type != WWT_LAST; wi++) index++;
       
   545 
       
   546 		w->widget = realloc(w->widget, sizeof(*w->widget) * index);
       
   547 		memcpy(w->widget, widget, sizeof(*w->widget) * index);
       
   548 		w->widget_count = index - 1;
       
   549 	} else {
       
   550 		w->widget = NULL;
       
   551 		w->widget_count = 0;
       
   552 	}
       
   553 }
       
   554 
       
   555 static Window *FindFreeWindow(void)
       
   556 {
       
   557 	Window *w;
       
   558 
       
   559 	for (w = _windows; w < endof(_windows); w++) {
       
   560 		Window* const *wz;
       
   561 		bool window_in_use = false;
       
   562 
       
   563 		FOR_ALL_WINDOWS(wz) {
       
   564 			if (*wz == w) {
       
   565 				window_in_use = true;
       
   566 				break;
       
   567 			}
       
   568 		}
       
   569 
       
   570 		if (!window_in_use) return w;
       
   571 	}
       
   572 
       
   573 	assert(_last_z_window == endof(_z_windows));
       
   574 	return NULL;
       
   575 }
       
   576 
       
   577 /* Open a new window.
       
   578  * This function is called from AllocateWindow() or AllocateWindowDesc()
       
   579  * See descriptions for those functions for usage
       
   580  * See AllocateWindow() for description of arguments.
       
   581  * Only addition here is window_number, which is the window_number being assigned to the new window
       
   582  */
       
   583 static Window *LocalAllocateWindow(
       
   584 							int x, int y, int width, int height,
       
   585 							WindowProc *proc, WindowClass cls, const Widget *widget, int window_number)
       
   586 {
       
   587 	Window *w = FindFreeWindow();
       
   588 
       
   589 	/* We have run out of windows, close one and use that as the place for our new one */
       
   590 	if (w == NULL) {
       
   591 		w = FindDeletableWindow();
       
   592 		if (w == NULL) w = ForceFindDeletableWindow();
       
   593 		DeleteWindow(w);
       
   594 	}
       
   595 
       
   596 	// Set up window properties
       
   597 	memset(w, 0, sizeof(*w));
       
   598 	w->window_class = cls;
       
   599 	w->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border
       
   600 	w->caption_color = 0xFF;
       
   601 	w->left = x;
       
   602 	w->top = y;
       
   603 	w->width = width;
       
   604 	w->height = height;
       
   605 	w->wndproc = proc;
       
   606 	AssignWidgetToWindow(w, widget);
       
   607 	w->resize.width = width;
       
   608 	w->resize.height = height;
       
   609 	w->resize.step_width = 1;
       
   610 	w->resize.step_height = 1;
       
   611 	w->window_number = window_number;
       
   612 
       
   613 	{
       
   614 		Window **wz = _last_z_window;
       
   615 
       
   616 		/* Hacky way of specifying always-on-top windows. These windows are
       
   617 		 * always above other windows because they are moved below them.
       
   618 		 * status-bar is above news-window because it has been created earlier.
       
   619 		 * Also, as the chat-window is excluded from this, it will always be
       
   620 		 * the last window, thus always on top.
       
   621 		 * XXX - Yes, ugly, probably needs something like w->always_on_top flag
       
   622 		 * to implement correctly, but even then you need some kind of distinction
       
   623 		 * between on-top of chat/news and status windows, because these conflict */
       
   624 		if (wz != _z_windows && w->window_class != WC_SEND_NETWORK_MSG) {
       
   625 			if (FindWindowById(WC_MAIN_TOOLBAR, 0)     != NULL) wz--;
       
   626 			if (FindWindowById(WC_STATUS_BAR, 0)       != NULL) wz--;
       
   627 			if (FindWindowById(WC_NEWS_WINDOW, 0)      != NULL) wz--;
       
   628 			if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--;
       
   629 
       
   630 			assert(wz >= _z_windows);
       
   631 			if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz);
       
   632 		}
       
   633 
       
   634 		*wz = w;
       
   635 		_last_z_window++;
       
   636 	}
       
   637 
       
   638 	SetWindowDirty(w);
       
   639 	CallWindowEventNP(w, WE_CREATE);
       
   640 
       
   641 	return w;
       
   642 }
       
   643 
       
   644 /**
       
   645  * Open a new window. If there is no space for a new window, close an open
       
   646  * window. Try to avoid stickied windows, but if there is no else, close one of
       
   647  * those as well. Then make sure all created windows are below some always-on-top
       
   648  * ones. Finally set all variables and call the WE_CREATE event
       
   649  * @param x offset in pixels from the left of the screen
       
   650  * @param y offset in pixels from the top of the screen
       
   651  * @param width width in pixels of the window
       
   652  * @param height height in pixels of the window
       
   653  * @param *proc @see WindowProc function to call when any messages/updates happen to the window
       
   654  * @param cls @see WindowClass class of the window, used for identification and grouping
       
   655  * @param *widget @see Widget pointer to the window layout and various elements
       
   656  * @return @see Window pointer of the newly created window
       
   657  */
       
   658 Window *AllocateWindow(
       
   659 							int x, int y, int width, int height,
       
   660 							WindowProc *proc, WindowClass cls, const Widget *widget)
       
   661 {
       
   662 	return LocalAllocateWindow(x, y, width, height, proc, cls, widget, 0);
       
   663 }
       
   664 
       
   665 typedef struct SizeRect {
       
   666 	int left,top,width,height;
       
   667 } SizeRect;
       
   668 
       
   669 
       
   670 static SizeRect _awap_r;
       
   671 
       
   672 static bool IsGoodAutoPlace1(int left, int top)
       
   673 {
       
   674 	int right,bottom;
       
   675 	Window* const *wz;
       
   676 
       
   677 	_awap_r.left= left;
       
   678 	_awap_r.top = top;
       
   679 	right = _awap_r.width + left;
       
   680 	bottom = _awap_r.height + top;
       
   681 
       
   682 	if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height)
       
   683 		return false;
       
   684 
       
   685 	// Make sure it is not obscured by any window.
       
   686 	FOR_ALL_WINDOWS(wz) {
       
   687 		const Window *w = *wz;
       
   688 		if (w->window_class == WC_MAIN_WINDOW) continue;
       
   689 
       
   690 		if (right > w->left &&
       
   691 				w->left + w->width > left &&
       
   692 				bottom > w->top &&
       
   693 				w->top + w->height > top) {
       
   694 			return false;
       
   695 		}
       
   696 	}
       
   697 
       
   698 	return true;
       
   699 }
       
   700 
       
   701 static bool IsGoodAutoPlace2(int left, int top)
       
   702 {
       
   703 	int width,height;
       
   704 	Window* const *wz;
       
   705 
       
   706 	_awap_r.left= left;
       
   707 	_awap_r.top = top;
       
   708 	width = _awap_r.width;
       
   709 	height = _awap_r.height;
       
   710 
       
   711 	if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
       
   712 	if (top < 22 || top > _screen.height - (height>>2)) return false;
       
   713 
       
   714 	// Make sure it is not obscured by any window.
       
   715 	FOR_ALL_WINDOWS(wz) {
       
   716 		const Window *w = *wz;
       
   717 		if (w->window_class == WC_MAIN_WINDOW) continue;
       
   718 
       
   719 		if (left + width > w->left &&
       
   720 				w->left + w->width > left &&
       
   721 				top + height > w->top &&
       
   722 				w->top + w->height > top) {
       
   723 			return false;
       
   724 		}
       
   725 	}
       
   726 
       
   727 	return true;
       
   728 }
       
   729 
       
   730 static Point GetAutoPlacePosition(int width, int height)
       
   731 {
       
   732 	Window* const *wz;
       
   733 	Point pt;
       
   734 
       
   735 	_awap_r.width = width;
       
   736 	_awap_r.height = height;
       
   737 
       
   738 	if (IsGoodAutoPlace1(0, 24)) goto ok_pos;
       
   739 
       
   740 	FOR_ALL_WINDOWS(wz) {
       
   741 		const Window *w = *wz;
       
   742 		if (w->window_class == WC_MAIN_WINDOW) continue;
       
   743 
       
   744 		if (IsGoodAutoPlace1(w->left+w->width+2,w->top)) goto ok_pos;
       
   745 		if (IsGoodAutoPlace1(w->left-   width-2,w->top)) goto ok_pos;
       
   746 		if (IsGoodAutoPlace1(w->left,w->top+w->height+2)) goto ok_pos;
       
   747 		if (IsGoodAutoPlace1(w->left,w->top-   height-2)) goto ok_pos;
       
   748 		if (IsGoodAutoPlace1(w->left+w->width+2,w->top+w->height-height)) goto ok_pos;
       
   749 		if (IsGoodAutoPlace1(w->left-   width-2,w->top+w->height-height)) goto ok_pos;
       
   750 		if (IsGoodAutoPlace1(w->left+w->width-width,w->top+w->height+2)) goto ok_pos;
       
   751 		if (IsGoodAutoPlace1(w->left+w->width-width,w->top-   height-2)) goto ok_pos;
       
   752 	}
       
   753 
       
   754 	FOR_ALL_WINDOWS(wz) {
       
   755 		const Window *w = *wz;
       
   756 		if (w->window_class == WC_MAIN_WINDOW) continue;
       
   757 
       
   758 		if (IsGoodAutoPlace2(w->left+w->width+2,w->top)) goto ok_pos;
       
   759 		if (IsGoodAutoPlace2(w->left-   width-2,w->top)) goto ok_pos;
       
   760 		if (IsGoodAutoPlace2(w->left,w->top+w->height+2)) goto ok_pos;
       
   761 		if (IsGoodAutoPlace2(w->left,w->top-   height-2)) goto ok_pos;
       
   762 	}
       
   763 
       
   764 	{
       
   765 		int left=0,top=24;
       
   766 
       
   767 restart:;
       
   768 		FOR_ALL_WINDOWS(wz) {
       
   769 			const Window *w = *wz;
       
   770 
       
   771 			if (w->left == left && w->top == top) {
       
   772 				left += 5;
       
   773 				top += 5;
       
   774 				goto restart;
       
   775 			}
       
   776 		}
       
   777 
       
   778 		pt.x = left;
       
   779 		pt.y = top;
       
   780 		return pt;
       
   781 	}
       
   782 
       
   783 ok_pos:;
       
   784 	pt.x = _awap_r.left;
       
   785 	pt.y = _awap_r.top;
       
   786 	return pt;
       
   787 }
       
   788 
       
   789 static Window *LocalAllocateWindowDesc(const WindowDesc *desc, int window_number)
       
   790 {
       
   791 	Point pt;
       
   792 	Window *w;
       
   793 
       
   794 	/* By default position a child window at an offset of 10/10 of its parent.
       
   795 	 * However if it falls too extremely outside window positions, reposition
       
   796 	 * it to an automatic place */
       
   797 	if (desc->parent_cls != 0 /* WC_MAIN_WINDOW */ &&
       
   798 			(w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
       
   799 			w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
       
   800 
       
   801 		pt.x = w->left + 10;
       
   802 		if (pt.x > _screen.width + 10 - desc->width) {
       
   803 			pt.x = (_screen.width + 10 - desc->width) - 20;
       
   804 		}
       
   805 		pt.y = w->top + 10;
       
   806 	} else {
       
   807 		switch (desc->left) {
       
   808 			case WDP_ALIGN_TBR: { /* Align the right side with the top toolbar */
       
   809 				w = FindWindowById(WC_MAIN_TOOLBAR, 0);
       
   810 				pt.x = (w->left + w->width) - desc->width;
       
   811 			}	break;
       
   812 			case WDP_ALIGN_TBL: /* Align the left side with the top toolbar */
       
   813 				pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left;
       
   814 				break;
       
   815 			case WDP_AUTO: /* Find a good automatic position for the window */
       
   816 				pt = GetAutoPlacePosition(desc->width, desc->height);
       
   817 				goto allocate_window;
       
   818 			case WDP_CENTER: /* Centre the window horizontally */
       
   819 				pt.x = (_screen.width - desc->width) / 2;
       
   820 				break;
       
   821 			default:
       
   822 				pt.x = desc->left;
       
   823 				if (pt.x < 0) pt.x += _screen.width; // negative is from right of the screen
       
   824 		}
       
   825 
       
   826 		switch (desc->top) {
       
   827 			case WDP_CENTER: /* Centre the window vertically */
       
   828 				pt.y = (_screen.height - desc->height) / 2;
       
   829 				break;
       
   830 			/* WDP_AUTO sets the position at once and is controlled by desc->left.
       
   831 			 * Both left and top must be set to WDP_AUTO */
       
   832 			case WDP_AUTO:
       
   833 				NOT_REACHED();
       
   834 				assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO);
       
   835 				/* fallthrough */
       
   836 			default:
       
   837 				pt.y = desc->top;
       
   838 				if (pt.y < 0) pt.y += _screen.height; // negative is from bottom of the screen
       
   839 				break;
       
   840 		}
       
   841 	}
       
   842 
       
   843 allocate_window:
       
   844 	w = LocalAllocateWindow(pt.x, pt.y, desc->width, desc->height, desc->proc, desc->cls, desc->widgets, window_number);
       
   845 	w->desc_flags = desc->flags;
       
   846 	return w;
       
   847 }
       
   848 
       
   849 /**
       
   850  * Open a new window.
       
   851  * @param *desc The pointer to the WindowDesc to be created
       
   852  * @return @see Window pointer of the newly created window
       
   853  */
       
   854 Window *AllocateWindowDesc(const WindowDesc *desc)
       
   855 {
       
   856 	return LocalAllocateWindowDesc(desc, 0);
       
   857 }
       
   858 
       
   859 /**
       
   860  * Open a new window.
       
   861  * @param *desc The pointer to the WindowDesc to be created
       
   862  * @param window_number the window number of the new window
       
   863  * @return @see Window pointer of the newly created window
       
   864  */
       
   865 Window *AllocateWindowDescFront(const WindowDesc *desc, int window_number)
       
   866 {
       
   867 	Window *w;
       
   868 
       
   869 	if (BringWindowToFrontById(desc->cls, window_number)) return NULL;
       
   870 	w = LocalAllocateWindowDesc(desc, window_number);
       
   871 	return w;
       
   872 }
       
   873 
       
   874 /** Do a search for a window at specific coordinates. For this we start
       
   875  * at the topmost window, obviously and work our way down to the bottom
       
   876  * @return a pointer to the found window if any, NULL otherwise */
       
   877 Window *FindWindowFromPt(int x, int y)
       
   878 {
       
   879 	Window* const *wz;
       
   880 
       
   881 	for (wz = _last_z_window; wz != _z_windows;) {
       
   882 		Window *w = *--wz;
       
   883 		if (IS_INSIDE_1D(x, w->left, w->width) && IS_INSIDE_1D(y, w->top, w->height)) {
       
   884 			return w;
       
   885 		}
       
   886 	}
       
   887 
       
   888 	return NULL;
       
   889 }
       
   890 
       
   891 void InitWindowSystem(void)
       
   892 {
       
   893 	IConsoleClose();
       
   894 
       
   895 	memset(&_windows, 0, sizeof(_windows));
       
   896 	_last_z_window = _z_windows;
       
   897 	InitViewports();
       
   898 	_no_scroll = 0;
       
   899 }
       
   900 
       
   901 void UnInitWindowSystem(void)
       
   902 {
       
   903 	Window **wz;
       
   904 	/* Delete all malloced widgets, and reset z-array */
       
   905 	FOR_ALL_WINDOWS(wz) {
       
   906 		free((*wz)->widget);
       
   907 		(*wz)->widget = NULL;
       
   908 		(*wz)->widget_count = 0;
       
   909 		*wz = NULL;
       
   910 	}
       
   911 	_last_z_window = _z_windows;
       
   912 }
       
   913 
       
   914 void ResetWindowSystem(void)
       
   915 {
       
   916 	UnInitWindowSystem();
       
   917 	InitWindowSystem();
       
   918 	_thd.pos.x = 0;
       
   919 	_thd.pos.y = 0;
       
   920 	_thd.new_pos.x = 0;
       
   921 	_thd.new_pos.y = 0;
       
   922 }
       
   923 
       
   924 static void DecreaseWindowCounters(void)
       
   925 {
       
   926 	Window *w;
       
   927 	Window* const *wz;
       
   928 
       
   929 	for (wz = _last_z_window; wz != _z_windows;) {
       
   930 		w = *--wz;
       
   931 		// Unclick scrollbar buttons if they are pressed.
       
   932 		if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
       
   933 			w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
       
   934 			SetWindowDirty(w);
       
   935 		}
       
   936 		CallWindowEventNP(w, WE_MOUSELOOP);
       
   937 	}
       
   938 
       
   939 	for (wz = _last_z_window; wz != _z_windows;) {
       
   940 		w = *--wz;
       
   941 
       
   942 		if (w->flags4&WF_TIMEOUT_MASK && !(--w->flags4&WF_TIMEOUT_MASK)) {
       
   943 			CallWindowEventNP(w, WE_TIMEOUT);
       
   944 			if (w->desc_flags & WDF_UNCLICK_BUTTONS) RaiseWindowButtons(w);
       
   945 		}
       
   946 	}
       
   947 }
       
   948 
       
   949 Window *GetCallbackWnd(void)
       
   950 {
       
   951 	return FindWindowById(_thd.window_class, _thd.window_number);
       
   952 }
       
   953 
       
   954 static void HandlePlacePresize(void)
       
   955 {
       
   956 	Window *w;
       
   957 	WindowEvent e;
       
   958 
       
   959 	if (_special_mouse_mode != WSM_PRESIZE) return;
       
   960 
       
   961 	w = GetCallbackWnd();
       
   962 	if (w == NULL) return;
       
   963 
       
   964 	e.we.place.pt = GetTileBelowCursor();
       
   965 	if (e.we.place.pt.x == -1) {
       
   966 		_thd.selend.x = -1;
       
   967 		return;
       
   968 	}
       
   969 	e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
       
   970 	e.event = WE_PLACE_PRESIZE;
       
   971 	w->wndproc(w, &e);
       
   972 }
       
   973 
       
   974 static bool HandleDragDrop(void)
       
   975 {
       
   976 	Window *w;
       
   977 	WindowEvent e;
       
   978 
       
   979 	if (_special_mouse_mode != WSM_DRAGDROP) return true;
       
   980 
       
   981 	if (_left_button_down) return false;
       
   982 
       
   983 	w = GetCallbackWnd();
       
   984 
       
   985 	ResetObjectToPlace();
       
   986 
       
   987 	if (w != NULL) {
       
   988 		// send an event in client coordinates.
       
   989 		e.event = WE_DRAGDROP;
       
   990 		e.we.dragdrop.pt.x = _cursor.pos.x - w->left;
       
   991 		e.we.dragdrop.pt.y = _cursor.pos.y - w->top;
       
   992 		e.we.dragdrop.widget = GetWidgetFromPos(w, e.we.dragdrop.pt.x, e.we.dragdrop.pt.y);
       
   993 		w->wndproc(w, &e);
       
   994 	}
       
   995 	return false;
       
   996 }
       
   997 
       
   998 static bool HandlePopupMenu(void)
       
   999 {
       
  1000 	Window *w;
       
  1001 	WindowEvent e;
       
  1002 
       
  1003 	if (!_popup_menu_active) return true;
       
  1004 
       
  1005 	w = FindWindowById(WC_TOOLBAR_MENU, 0);
       
  1006 	if (w == NULL) {
       
  1007 		_popup_menu_active = false;
       
  1008 		return false;
       
  1009 	}
       
  1010 
       
  1011 	if (_left_button_down) {
       
  1012 		e.event = WE_POPUPMENU_OVER;
       
  1013 		e.we.popupmenu.pt = _cursor.pos;
       
  1014 	} else {
       
  1015 		_popup_menu_active = false;
       
  1016 		e.event = WE_POPUPMENU_SELECT;
       
  1017 		e.we.popupmenu.pt = _cursor.pos;
       
  1018 	}
       
  1019 
       
  1020 	w->wndproc(w, &e);
       
  1021 
       
  1022 	return false;
       
  1023 }
       
  1024 
       
  1025 static bool HandleMouseOver(void)
       
  1026 {
       
  1027 	Window *w;
       
  1028 	WindowEvent e;
       
  1029 	static Window *last_w = NULL;
       
  1030 
       
  1031 	w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
       
  1032 
       
  1033 	// We changed window, put a MOUSEOVER event to the last window
       
  1034 	if (last_w != NULL && last_w != w) {
       
  1035 		e.event = WE_MOUSEOVER;
       
  1036 		e.we.mouseover.pt.x = -1;
       
  1037 		e.we.mouseover.pt.y = -1;
       
  1038 		if (last_w->wndproc) last_w->wndproc(last_w, &e);
       
  1039 	}
       
  1040 	last_w = w;
       
  1041 
       
  1042 	if (w != NULL) {
       
  1043 		// send an event in client coordinates.
       
  1044 		e.event = WE_MOUSEOVER;
       
  1045 		e.we.mouseover.pt.x = _cursor.pos.x - w->left;
       
  1046 		e.we.mouseover.pt.y = _cursor.pos.y - w->top;
       
  1047 		if (w->widget != NULL) {
       
  1048 			e.we.mouseover.widget = GetWidgetFromPos(w, e.we.mouseover.pt.x, e.we.mouseover.pt.y);
       
  1049 		}
       
  1050 		w->wndproc(w, &e);
       
  1051 	}
       
  1052 
       
  1053 	// Mouseover never stops execution
       
  1054 	return true;
       
  1055 }
       
  1056 
       
  1057 /** Update all the widgets of a window based on their resize flags
       
  1058  * Both the areas of the old window and the new sized window are set dirty
       
  1059  * ensuring proper redrawal.
       
  1060  * @param w Window to resize
       
  1061  * @param x delta x-size of changed window (positive if larger, etc.(
       
  1062  * @param y delta y-size of changed window */
       
  1063 void ResizeWindow(Window *w, int x, int y)
       
  1064 {
       
  1065 	Widget *wi;
       
  1066 	bool resize_height = false;
       
  1067 	bool resize_width = false;
       
  1068 
       
  1069 	if (x == 0 && y == 0) return;
       
  1070 
       
  1071 	SetWindowDirty(w);
       
  1072 	for (wi = w->widget; wi->type != WWT_LAST; wi++) {
       
  1073 		/* Isolate the resizing flags */
       
  1074 		byte rsizeflag = GB(wi->display_flags, 0, 4);
       
  1075 
       
  1076 		if (rsizeflag == RESIZE_NONE) continue;
       
  1077 
       
  1078 		/* Resize the widget based on its resize-flag */
       
  1079 		if (rsizeflag & RESIZE_LEFT) {
       
  1080 			wi->left += x;
       
  1081 			resize_width = true;
       
  1082 		}
       
  1083 
       
  1084 		if (rsizeflag & RESIZE_RIGHT) {
       
  1085 			wi->right += x;
       
  1086 			resize_width = true;
       
  1087 		}
       
  1088 
       
  1089 		if (rsizeflag & RESIZE_TOP) {
       
  1090 			wi->top += y;
       
  1091 			resize_height = true;
       
  1092 		}
       
  1093 
       
  1094 		if (rsizeflag & RESIZE_BOTTOM) {
       
  1095 			wi->bottom += y;
       
  1096 			resize_height = true;
       
  1097 		}
       
  1098 	}
       
  1099 
       
  1100 	/* We resized at least 1 widget, so let's resize the window totally */
       
  1101 	if (resize_width)  w->width  += x;
       
  1102 	if (resize_height) w->height += y;
       
  1103 
       
  1104 	SetWindowDirty(w);
       
  1105 }
       
  1106 
       
  1107 static bool _dragging_window;
       
  1108 
       
  1109 static bool HandleWindowDragging(void)
       
  1110 {
       
  1111 	Window* const *wz;
       
  1112 	// Get out immediately if no window is being dragged at all.
       
  1113 	if (!_dragging_window) return true;
       
  1114 
       
  1115 	// Otherwise find the window...
       
  1116 	FOR_ALL_WINDOWS(wz) {
       
  1117 		Window *w = *wz;
       
  1118 
       
  1119 		if (w->flags4 & WF_DRAGGING) {
       
  1120 			const Widget *t = &w->widget[1]; // the title bar ... ugh
       
  1121 			const Window *v;
       
  1122 			int x;
       
  1123 			int y;
       
  1124 			int nx;
       
  1125 			int ny;
       
  1126 
       
  1127 			// Stop the dragging if the left mouse button was released
       
  1128 			if (!_left_button_down) {
       
  1129 				w->flags4 &= ~WF_DRAGGING;
       
  1130 				break;
       
  1131 			}
       
  1132 
       
  1133 			SetWindowDirty(w);
       
  1134 
       
  1135 			x = _cursor.pos.x + _drag_delta.x;
       
  1136 			y = _cursor.pos.y + _drag_delta.y;
       
  1137 			nx = x;
       
  1138 			ny = y;
       
  1139 
       
  1140 			if (_patches.window_snap_radius != 0) {
       
  1141 				Window* const *vz;
       
  1142 
       
  1143 				int hsnap = _patches.window_snap_radius;
       
  1144 				int vsnap = _patches.window_snap_radius;
       
  1145 				int delta;
       
  1146 
       
  1147 				FOR_ALL_WINDOWS(vz) {
       
  1148 					const Window *v = *vz;
       
  1149 
       
  1150 					if (v == w) continue; // Don't snap at yourself
       
  1151 
       
  1152 					if (y + w->height > v->top && y < v->top + v->height) {
       
  1153 						// Your left border <-> other right border
       
  1154 						delta = abs(v->left + v->width - x);
       
  1155 						if (delta <= hsnap) {
       
  1156 							nx = v->left + v->width;
       
  1157 							hsnap = delta;
       
  1158 						}
       
  1159 
       
  1160 						// Your right border <-> other left border
       
  1161 						delta = abs(v->left - x - w->width);
       
  1162 						if (delta <= hsnap) {
       
  1163 							nx = v->left - w->width;
       
  1164 							hsnap = delta;
       
  1165 						}
       
  1166 					}
       
  1167 
       
  1168 					if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
       
  1169 						// Your left border <-> other left border
       
  1170 						delta = abs(v->left - x);
       
  1171 						if (delta <= hsnap) {
       
  1172 							nx = v->left;
       
  1173 							hsnap = delta;
       
  1174 						}
       
  1175 
       
  1176 						// Your right border <-> other right border
       
  1177 						delta = abs(v->left + v->width - x - w->width);
       
  1178 						if (delta <= hsnap) {
       
  1179 							nx = v->left + v->width - w->width;
       
  1180 							hsnap = delta;
       
  1181 						}
       
  1182 					}
       
  1183 
       
  1184 					if (x + w->width > v->left && x < v->left + v->width) {
       
  1185 						// Your top border <-> other bottom border
       
  1186 						delta = abs(v->top + v->height - y);
       
  1187 						if (delta <= vsnap) {
       
  1188 							ny = v->top + v->height;
       
  1189 							vsnap = delta;
       
  1190 						}
       
  1191 
       
  1192 						// Your bottom border <-> other top border
       
  1193 						delta = abs(v->top - y - w->height);
       
  1194 						if (delta <= vsnap) {
       
  1195 							ny = v->top - w->height;
       
  1196 							vsnap = delta;
       
  1197 						}
       
  1198 					}
       
  1199 
       
  1200 					if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
       
  1201 						// Your top border <-> other top border
       
  1202 						delta = abs(v->top - y);
       
  1203 						if (delta <= vsnap) {
       
  1204 							ny = v->top;
       
  1205 							vsnap = delta;
       
  1206 						}
       
  1207 
       
  1208 						// Your bottom border <-> other bottom border
       
  1209 						delta = abs(v->top + v->height - y - w->height);
       
  1210 						if (delta <= vsnap) {
       
  1211 							ny = v->top + v->height - w->height;
       
  1212 							vsnap = delta;
       
  1213 						}
       
  1214 					}
       
  1215 				}
       
  1216 			}
       
  1217 
       
  1218 			// Make sure the window doesn't leave the screen
       
  1219 			// 13 is the height of the title bar
       
  1220 			nx = clamp(nx, 13 - t->right, _screen.width - 13 - t->left);
       
  1221 			ny = clamp(ny, 0, _screen.height - 13);
       
  1222 
       
  1223 			// Make sure the title bar isn't hidden by behind the main tool bar
       
  1224 			v = FindWindowById(WC_MAIN_TOOLBAR, 0);
       
  1225 			if (v != NULL) {
       
  1226 				int v_bottom = v->top + v->height;
       
  1227 				int v_right = v->left + v->width;
       
  1228 				if (ny + t->top >= v->top && ny + t->top < v_bottom) {
       
  1229 					if ((v->left < 13 && nx + t->left < v->left) ||
       
  1230 							(v_right > _screen.width - 13 && nx + t->right > v_right)) {
       
  1231 						ny = v_bottom;
       
  1232 					} else {
       
  1233 						if (nx + t->left > v->left - 13 &&
       
  1234 								nx + t->right < v_right + 13) {
       
  1235 							if (w->top >= v_bottom) {
       
  1236 								ny = v_bottom;
       
  1237 							} else if (w->left < nx) {
       
  1238 								nx = v->left - 13 - t->left;
       
  1239 							} else {
       
  1240 								nx = v_right + 13 - t->right;
       
  1241 							}
       
  1242 						}
       
  1243 					}
       
  1244 				}
       
  1245 			}
       
  1246 
       
  1247 			if (w->viewport != NULL) {
       
  1248 				w->viewport->left += nx - w->left;
       
  1249 				w->viewport->top  += ny - w->top;
       
  1250 			}
       
  1251 			w->left = nx;
       
  1252 			w->top  = ny;
       
  1253 
       
  1254 			SetWindowDirty(w);
       
  1255 			return false;
       
  1256 		} else if (w->flags4 & WF_SIZING) {
       
  1257 			WindowEvent e;
       
  1258 			int x, y;
       
  1259 
       
  1260 			/* Stop the sizing if the left mouse button was released */
       
  1261 			if (!_left_button_down) {
       
  1262 				w->flags4 &= ~WF_SIZING;
       
  1263 				SetWindowDirty(w);
       
  1264 				break;
       
  1265 			}
       
  1266 
       
  1267 			x = _cursor.pos.x - _drag_delta.x;
       
  1268 			y = _cursor.pos.y - _drag_delta.y;
       
  1269 
       
  1270 			/* X and Y has to go by step.. calculate it.
       
  1271 			 * The cast to int is necessary else x/y are implicitly casted to
       
  1272 			 * unsigned int, which won't work. */
       
  1273 			if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
       
  1274 
       
  1275 			if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
       
  1276 
       
  1277 			/* Check if we don't go below the minimum set size */
       
  1278 			if ((int)w->width + x < (int)w->resize.width)
       
  1279 				x = w->resize.width - w->width;
       
  1280 			if ((int)w->height + y < (int)w->resize.height)
       
  1281 				y = w->resize.height - w->height;
       
  1282 
       
  1283 			/* Window already on size */
       
  1284 			if (x == 0 && y == 0) return false;
       
  1285 
       
  1286 			/* Now find the new cursor pos.. this is NOT _cursor, because
       
  1287 			    we move in steps. */
       
  1288 			_drag_delta.x += x;
       
  1289 			_drag_delta.y += y;
       
  1290 
       
  1291 			/* ResizeWindow sets both pre- and after-size to dirty for redrawal */
       
  1292 			ResizeWindow(w, x, y);
       
  1293 
       
  1294 			e.event = WE_RESIZE;
       
  1295 			e.we.sizing.size.x = x + w->width;
       
  1296 			e.we.sizing.size.y = y + w->height;
       
  1297 			e.we.sizing.diff.x = x;
       
  1298 			e.we.sizing.diff.y = y;
       
  1299 			w->wndproc(w, &e);
       
  1300 			return false;
       
  1301 		}
       
  1302 	}
       
  1303 
       
  1304 	_dragging_window = false;
       
  1305 	return false;
       
  1306 }
       
  1307 
       
  1308 static void StartWindowDrag(Window *w)
       
  1309 {
       
  1310 	w->flags4 |= WF_DRAGGING;
       
  1311 	_dragging_window = true;
       
  1312 
       
  1313 	_drag_delta.x = w->left - _cursor.pos.x;
       
  1314 	_drag_delta.y = w->top  - _cursor.pos.y;
       
  1315 
       
  1316 	BringWindowToFront(w);
       
  1317 	DeleteWindowById(WC_DROPDOWN_MENU, 0);
       
  1318 }
       
  1319 
       
  1320 static void StartWindowSizing(Window *w)
       
  1321 {
       
  1322 	w->flags4 |= WF_SIZING;
       
  1323 	_dragging_window = true;
       
  1324 
       
  1325 	_drag_delta.x = _cursor.pos.x;
       
  1326 	_drag_delta.y = _cursor.pos.y;
       
  1327 
       
  1328 	BringWindowToFront(w);
       
  1329 	DeleteWindowById(WC_DROPDOWN_MENU, 0);
       
  1330 }
       
  1331 
       
  1332 
       
  1333 static bool HandleScrollbarScrolling(void)
       
  1334 {
       
  1335 	Window* const *wz;
       
  1336 	int i;
       
  1337 	int pos;
       
  1338 	Scrollbar *sb;
       
  1339 
       
  1340 	// Get out quickly if no item is being scrolled
       
  1341 	if (!_scrolling_scrollbar) return true;
       
  1342 
       
  1343 	// Find the scrolling window
       
  1344 	FOR_ALL_WINDOWS(wz) {
       
  1345 		Window *w = *wz;
       
  1346 
       
  1347 		if (w->flags4 & WF_SCROLL_MIDDLE) {
       
  1348 			// Abort if no button is clicked any more.
       
  1349 			if (!_left_button_down) {
       
  1350 				w->flags4 &= ~WF_SCROLL_MIDDLE;
       
  1351 				SetWindowDirty(w);
       
  1352 				break;
       
  1353 			}
       
  1354 
       
  1355 			if (w->flags4 & WF_HSCROLL) {
       
  1356 				sb = &w->hscroll;
       
  1357 				i = _cursor.pos.x - _cursorpos_drag_start.x;
       
  1358 			} else if (w->flags4 & WF_SCROLL2){
       
  1359 				sb = &w->vscroll2;
       
  1360 				i = _cursor.pos.y - _cursorpos_drag_start.y;
       
  1361 			} else {
       
  1362 				sb = &w->vscroll;
       
  1363 				i = _cursor.pos.y - _cursorpos_drag_start.y;
       
  1364 			}
       
  1365 
       
  1366 			// Find the item we want to move to and make sure it's inside bounds.
       
  1367 			pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap));
       
  1368 			if (pos != sb->pos) {
       
  1369 				sb->pos = pos;
       
  1370 				SetWindowDirty(w);
       
  1371 			}
       
  1372 			return false;
       
  1373 		}
       
  1374 	}
       
  1375 
       
  1376 	_scrolling_scrollbar = false;
       
  1377 	return false;
       
  1378 }
       
  1379 
       
  1380 static bool HandleViewportScroll(void)
       
  1381 {
       
  1382 	WindowEvent e;
       
  1383 	Window *w;
       
  1384 
       
  1385 	if (!_scrolling_viewport) return true;
       
  1386 
       
  1387 	w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
       
  1388 
       
  1389 	if (!_right_button_down || w == NULL) {
       
  1390 		_cursor.fix_at = false;
       
  1391 		_scrolling_viewport = false;
       
  1392 		return true;
       
  1393 	}
       
  1394 
       
  1395 	if (_patches.reverse_scroll) {
       
  1396 		e.we.scroll.delta.x = -_cursor.delta.x;
       
  1397 		e.we.scroll.delta.y = -_cursor.delta.y;
       
  1398 	} else {
       
  1399 		e.we.scroll.delta.x = _cursor.delta.x;
       
  1400 		e.we.scroll.delta.y = _cursor.delta.y;
       
  1401 	}
       
  1402 
       
  1403 	/* Create a scroll-event and send it to the window */
       
  1404 	e.event = WE_SCROLL;
       
  1405 	w->wndproc(w, &e);
       
  1406 
       
  1407 	_cursor.delta.x = 0;
       
  1408 	_cursor.delta.y = 0;
       
  1409 	return false;
       
  1410 }
       
  1411 
       
  1412 /** Check if a window can be made top-most window, and if so do
       
  1413  * it. If a window does not obscure any other windows, it will not
       
  1414  * be brought to the foreground. Also if the only obscuring windows
       
  1415  * are so-called system-windows, the window will not be moved.
       
  1416  * The function will return false when a child window of this window is a
       
  1417  * modal-popup; function returns a false and child window gets a white border
       
  1418  * @param w Window to bring on-top
       
  1419  * @return false if the window has an active modal child, true otherwise */
       
  1420 static bool MaybeBringWindowToFront(const Window *w)
       
  1421 {
       
  1422 	bool bring_to_front = false;
       
  1423 	Window* const *wz;
       
  1424 	Window* const *uz;
       
  1425 
       
  1426 	if (w->window_class == WC_MAIN_WINDOW ||
       
  1427 			IsVitalWindow(w) ||
       
  1428 			w->window_class == WC_TOOLTIPS ||
       
  1429 			w->window_class == WC_DROPDOWN_MENU) {
       
  1430 		return true;
       
  1431 	}
       
  1432 
       
  1433 	wz = FindWindowZPosition(w);
       
  1434 	for (uz = wz; ++uz != _last_z_window;) {
       
  1435 		Window *u = *uz;
       
  1436 
       
  1437 		/* A modal child will prevent the activation of the parent window */
       
  1438 		if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
       
  1439 			u->flags4 |= WF_WHITE_BORDER_MASK;
       
  1440 			SetWindowDirty(u);
       
  1441 			return false;
       
  1442 		}
       
  1443 
       
  1444 		if (u->window_class == WC_MAIN_WINDOW ||
       
  1445 				IsVitalWindow(u) ||
       
  1446 				u->window_class == WC_TOOLTIPS ||
       
  1447 				u->window_class == WC_DROPDOWN_MENU) {
       
  1448 			continue;
       
  1449 		}
       
  1450 
       
  1451 		/* Window sizes don't interfere, leave z-order alone */
       
  1452 		if (w->left + w->width <= u->left ||
       
  1453 				u->left + u->width <= w->left ||
       
  1454 				w->top  + w->height <= u->top ||
       
  1455 				u->top + u->height <= w->top) {
       
  1456 			continue;
       
  1457 		}
       
  1458 
       
  1459 		bring_to_front = true;
       
  1460 	}
       
  1461 
       
  1462 	if (bring_to_front) BringWindowToFront(w);
       
  1463 	return true;
       
  1464 }
       
  1465 
       
  1466 /** Send a message from one window to another. The receiving window is found by
       
  1467  * @param w @see Window pointer pointing to the other window
       
  1468  * @param msg Specifies the message to be sent
       
  1469  * @param wparam Specifies additional message-specific information
       
  1470  * @param lparam Specifies additional message-specific information
       
  1471  */
       
  1472 static void SendWindowMessageW(Window *w, uint msg, uint wparam, uint lparam)
       
  1473 {
       
  1474 	WindowEvent e;
       
  1475 
       
  1476 	e.event             = WE_MESSAGE;
       
  1477 	e.we.message.msg    = msg;
       
  1478 	e.we.message.wparam = wparam;
       
  1479 	e.we.message.lparam = lparam;
       
  1480 
       
  1481 	w->wndproc(w, &e);
       
  1482 }
       
  1483 
       
  1484 /** Send a message from one window to another. The receiving window is found by
       
  1485  * @param wnd_class @see WindowClass class AND
       
  1486  * @param wnd_num @see WindowNumber number, mostly 0
       
  1487  * @param msg Specifies the message to be sent
       
  1488  * @param wparam Specifies additional message-specific information
       
  1489  * @param lparam Specifies additional message-specific information
       
  1490  */
       
  1491 void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, uint msg, uint wparam, uint lparam)
       
  1492 {
       
  1493 	Window *w = FindWindowById(wnd_class, wnd_num);
       
  1494 	if (w != NULL) SendWindowMessageW(w, msg, wparam, lparam);
       
  1495 }
       
  1496 
       
  1497 /** Send a message from one window to another. The message will be sent
       
  1498  * to ALL windows of the windowclass specified in the first parameter
       
  1499  * @param wnd_class @see WindowClass class
       
  1500  * @param msg Specifies the message to be sent
       
  1501  * @param wparam Specifies additional message-specific information
       
  1502  * @param lparam Specifies additional message-specific information
       
  1503  */
       
  1504 void SendWindowMessageClass(WindowClass wnd_class, uint msg, uint wparam, uint lparam)
       
  1505 {
       
  1506 	Window* const *wz;
       
  1507 
       
  1508 	FOR_ALL_WINDOWS(wz) {
       
  1509 		if ((*wz)->window_class == wnd_class) SendWindowMessageW(*wz, msg, wparam, lparam);
       
  1510 	}
       
  1511 }
       
  1512 
       
  1513 /** Handle keyboard input.
       
  1514  * @param key Lower 8 bits contain the ASCII character, the higher
       
  1515  * 16 bits the keycode */
       
  1516 void HandleKeypress(uint32 key)
       
  1517 {
       
  1518 	Window* const *wz;
       
  1519 	WindowEvent e;
       
  1520 	/* Stores if a window with a textfield for typing is open
       
  1521 	 * If this is the case, keypress events are only passed to windows with text fields and
       
  1522 	 * to thein this main toolbar. */
       
  1523 	bool query_open = false;
       
  1524 
       
  1525 	/*
       
  1526 	* During the generation of the world, there might be
       
  1527 	* another thread that is currently building for example
       
  1528 	* a road. To not interfere with those tasks, we should
       
  1529 	* NOT change the _current_player here.
       
  1530 	*
       
  1531 	* This is not necessary either, as the only events that
       
  1532 	* can be handled are the 'close application' events
       
  1533 	*/
       
  1534 	if (!IsGeneratingWorld()) _current_player = _local_player;
       
  1535 
       
  1536 	// Setup event
       
  1537 	e.event = WE_KEYPRESS;
       
  1538 	e.we.keypress.key     = GB(key,  0, 16);
       
  1539 	e.we.keypress.keycode = GB(key, 16, 16);
       
  1540 	e.we.keypress.cont = true;
       
  1541 
       
  1542 	// check if we have a query string window open before allowing hotkeys
       
  1543 	if (FindWindowById(WC_QUERY_STRING,       0) != NULL ||
       
  1544 			FindWindowById(WC_SEND_NETWORK_MSG,   0) != NULL ||
       
  1545 			FindWindowById(WC_GENERATE_LANDSCAPE, 0) != NULL ||
       
  1546 			FindWindowById(WC_CONSOLE,            0) != NULL ||
       
  1547 			FindWindowById(WC_SAVELOAD,           0) != NULL) {
       
  1548 		query_open = true;
       
  1549 	}
       
  1550 
       
  1551 	// Call the event, start with the uppermost window.
       
  1552 	for (wz = _last_z_window; wz != _z_windows;) {
       
  1553 		Window *w = *--wz;
       
  1554 
       
  1555 		// if a query window is open, only call the event for certain window types
       
  1556 		if (query_open &&
       
  1557 				w->window_class != WC_QUERY_STRING &&
       
  1558 				w->window_class != WC_SEND_NETWORK_MSG &&
       
  1559 				w->window_class != WC_GENERATE_LANDSCAPE &&
       
  1560 				w->window_class != WC_CONSOLE &&
       
  1561 				w->window_class != WC_SAVELOAD) {
       
  1562 			continue;
       
  1563 		}
       
  1564 		w->wndproc(w, &e);
       
  1565 		if (!e.we.keypress.cont) break;
       
  1566 	}
       
  1567 
       
  1568 	if (e.we.keypress.cont) {
       
  1569 		Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
       
  1570 		// When there is no toolbar w is null, check for that
       
  1571 		if (w != NULL) w->wndproc(w, &e);
       
  1572 	}
       
  1573 }
       
  1574 
       
  1575 extern void UpdateTileSelection(void);
       
  1576 extern bool VpHandlePlaceSizingDrag(void);
       
  1577 
       
  1578 static int _input_events_this_tick = 0;
       
  1579 
       
  1580 static void HandleAutoscroll(void)
       
  1581 {
       
  1582 	Window *w;
       
  1583 	ViewPort *vp;
       
  1584 	int x = _cursor.pos.x;
       
  1585 	int y = _cursor.pos.y;
       
  1586 
       
  1587 	if (_input_events_this_tick != 0) {
       
  1588 		/* HandleAutoscroll is called only once per GameLoop() - so we can clear the counter here */
       
  1589 		_input_events_this_tick = 0;
       
  1590 		/* there were some inputs this tick, don't scroll ??? */
       
  1591 		return;
       
  1592 	}
       
  1593 
       
  1594 	if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
       
  1595 		w = FindWindowFromPt(x, y);
       
  1596 		if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
       
  1597 		vp = IsPtInWindowViewport(w, x, y);
       
  1598 		if (vp != NULL) {
       
  1599 			x -= vp->left;
       
  1600 			y -= vp->top;
       
  1601 			//here allows scrolling in both x and y axis
       
  1602 #define scrollspeed 3
       
  1603 			if (x - 15 < 0) {
       
  1604 				WP(w, vp_d).scrollpos_x += (x - 15) * scrollspeed << vp->zoom;
       
  1605 			} else if (15 - (vp->width - x) > 0) {
       
  1606 				WP(w, vp_d).scrollpos_x += (15 - (vp->width - x)) * scrollspeed << vp->zoom;
       
  1607 			}
       
  1608 			if (y - 15 < 0) {
       
  1609 				WP(w, vp_d).scrollpos_y += (y - 15) * scrollspeed << vp->zoom;
       
  1610 			} else if (15 - (vp->height - y) > 0) {
       
  1611 				WP(w,vp_d).scrollpos_y += (15 - (vp->height - y)) * scrollspeed << vp->zoom;
       
  1612 			}
       
  1613 #undef scrollspeed
       
  1614 		}
       
  1615 	}
       
  1616 }
       
  1617 
       
  1618 void MouseLoop(int click, int mousewheel)
       
  1619 {
       
  1620 	int x,y;
       
  1621 	Window *w;
       
  1622 	ViewPort *vp;
       
  1623 
       
  1624 	DecreaseWindowCounters();
       
  1625 	HandlePlacePresize();
       
  1626 	UpdateTileSelection();
       
  1627 	if (!VpHandlePlaceSizingDrag())  return;
       
  1628 	if (!HandleDragDrop())           return;
       
  1629 	if (!HandlePopupMenu())          return;
       
  1630 	if (!HandleWindowDragging())     return;
       
  1631 	if (!HandleScrollbarScrolling()) return;
       
  1632 	if (!HandleViewportScroll())     return;
       
  1633 	if (!HandleMouseOver())          return;
       
  1634 
       
  1635 	x = _cursor.pos.x;
       
  1636 	y = _cursor.pos.y;
       
  1637 
       
  1638 	if (click == 0 && mousewheel == 0) return;
       
  1639 
       
  1640 	w = FindWindowFromPt(x, y);
       
  1641 	if (w == NULL) return;
       
  1642 	if (!MaybeBringWindowToFront(w)) return;
       
  1643 	vp = IsPtInWindowViewport(w, x, y);
       
  1644 
       
  1645 	/* Don't allow any action in a viewport if either in menu of in generating world */
       
  1646 	if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
       
  1647 
       
  1648 	if (mousewheel != 0) {
       
  1649 		WindowEvent e;
       
  1650 
       
  1651 		/* Send WE_MOUSEWHEEL event to window */
       
  1652 		e.event = WE_MOUSEWHEEL;
       
  1653 		e.we.wheel.wheel = mousewheel;
       
  1654 		w->wndproc(w, &e);
       
  1655 
       
  1656 		/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
       
  1657 		if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel);
       
  1658 	}
       
  1659 
       
  1660 	if (vp != NULL) {
       
  1661 		switch (click) {
       
  1662 			case 1:
       
  1663 				DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
       
  1664 				if (_thd.place_mode != 0 &&
       
  1665 						// query button and place sign button work in pause mode
       
  1666 						_cursor.sprite != SPR_CURSOR_QUERY &&
       
  1667 						_cursor.sprite != SPR_CURSOR_SIGN &&
       
  1668 						_pause != 0 &&
       
  1669 						!_cheats.build_in_pause.value) {
       
  1670 					return;
       
  1671 				}
       
  1672 
       
  1673 				if (_thd.place_mode == 0) {
       
  1674 					HandleViewportClicked(vp, x, y);
       
  1675 				} else {
       
  1676 					PlaceObject();
       
  1677 				}
       
  1678 				break;
       
  1679 
       
  1680 			case 2:
       
  1681 				if (!(w->flags4 & WF_DISABLE_VP_SCROLL)) {
       
  1682 					_scrolling_viewport = true;
       
  1683 					_cursor.fix_at = true;
       
  1684 				}
       
  1685 				break;
       
  1686 		}
       
  1687 	} else {
       
  1688 		switch (click) {
       
  1689 			case 1: DispatchLeftClickEvent(w, x - w->left, y - w->top);  break;
       
  1690 			case 2: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
       
  1691 		}
       
  1692 	}
       
  1693 }
       
  1694 
       
  1695 void HandleMouseEvents(void)
       
  1696 {
       
  1697 	int click;
       
  1698 	int mousewheel;
       
  1699 
       
  1700 	/*
       
  1701 	 * During the generation of the world, there might be
       
  1702 	 * another thread that is currently building for example
       
  1703 	 * a road. To not interfere with those tasks, we should
       
  1704 	 * NOT change the _current_player here.
       
  1705 	 *
       
  1706 	 * This is not necessary either, as the only events that
       
  1707 	 * can be handled are the 'close application' events
       
  1708 	 */
       
  1709 	if (!IsGeneratingWorld()) _current_player = _local_player;
       
  1710 
       
  1711 	// Mouse event?
       
  1712 	click = 0;
       
  1713 	if (_left_button_down && !_left_button_clicked) {
       
  1714 		_left_button_clicked = true;
       
  1715 		click = 1;
       
  1716 		_input_events_this_tick++;
       
  1717 	} else if (_right_button_clicked) {
       
  1718 		_right_button_clicked = false;
       
  1719 		click = 2;
       
  1720 		_input_events_this_tick++;
       
  1721 	}
       
  1722 
       
  1723 	mousewheel = 0;
       
  1724 	if (_cursor.wheel) {
       
  1725 		mousewheel = _cursor.wheel;
       
  1726 		_cursor.wheel = 0;
       
  1727 		_input_events_this_tick++;
       
  1728 	}
       
  1729 
       
  1730 	MouseLoop(click, mousewheel);
       
  1731 }
       
  1732 
       
  1733 void InputLoop(void)
       
  1734 {
       
  1735 	HandleMouseEvents();
       
  1736 	HandleAutoscroll();
       
  1737 }
       
  1738 
       
  1739 void UpdateWindows(void)
       
  1740 {
       
  1741 	Window* const *wz;
       
  1742 	static int we4_timer = 0;
       
  1743 	int t = we4_timer + 1;
       
  1744 
       
  1745 	if (t >= 100) {
       
  1746 		for (wz = _last_z_window; wz != _z_windows;) {
       
  1747 			CallWindowEventNP(*--wz, WE_4);
       
  1748 		}
       
  1749 		t = 0;
       
  1750 	}
       
  1751 	we4_timer = t;
       
  1752 
       
  1753 	for (wz = _last_z_window; wz != _z_windows;) {
       
  1754 		Window *w = *--wz;
       
  1755 		if (w->flags4 & WF_WHITE_BORDER_MASK) {
       
  1756 			w->flags4 -= WF_WHITE_BORDER_ONE;
       
  1757 
       
  1758 			if (!(w->flags4 & WF_WHITE_BORDER_MASK)) SetWindowDirty(w);
       
  1759 		}
       
  1760 	}
       
  1761 
       
  1762 	DrawDirtyBlocks();
       
  1763 
       
  1764 	FOR_ALL_WINDOWS(wz) {
       
  1765 		if ((*wz)->viewport != NULL) UpdateViewportPosition(*wz);
       
  1766 	}
       
  1767 	DrawTextMessage();
       
  1768 	// Redraw mouse cursor in case it was hidden
       
  1769 	DrawMouseCursor();
       
  1770 }
       
  1771 
       
  1772 
       
  1773 int GetMenuItemIndex(const Window *w, int x, int y)
       
  1774 {
       
  1775 	if ((x -= w->left) >= 0 && x < w->width && (y -= w->top + 1) >= 0) {
       
  1776 		y /= 10;
       
  1777 
       
  1778 		if (y < WP(w, const menu_d).item_count &&
       
  1779 				!HASBIT(WP(w, const menu_d).disabled_items, y)) {
       
  1780 			return y;
       
  1781 		}
       
  1782 	}
       
  1783 	return -1;
       
  1784 }
       
  1785 
       
  1786 void InvalidateWindow(WindowClass cls, WindowNumber number)
       
  1787 {
       
  1788 	Window* const *wz;
       
  1789 
       
  1790 	FOR_ALL_WINDOWS(wz) {
       
  1791 		const Window *w = *wz;
       
  1792 		if (w->window_class == cls && w->window_number == number) SetWindowDirty(w);
       
  1793 	}
       
  1794 }
       
  1795 
       
  1796 void InvalidateWidget(const Window *w, byte widget_index)
       
  1797 {
       
  1798 	const Widget *wi = &w->widget[widget_index];
       
  1799 
       
  1800 	/* Don't redraw the window if the widget is invisible or of no-type */
       
  1801 	if (wi->type == WWT_EMPTY || IsWindowWidgetHidden(w, widget_index)) return;
       
  1802 
       
  1803 	SetDirtyBlocks(w->left + wi->left, w->top + wi->top, w->left + wi->right + 1, w->top + wi->bottom + 1);
       
  1804 }
       
  1805 
       
  1806 void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index)
       
  1807 {
       
  1808 	Window* const *wz;
       
  1809 
       
  1810 	FOR_ALL_WINDOWS(wz) {
       
  1811 		const Window *w = *wz;
       
  1812 		if (w->window_class == cls && w->window_number == number) {
       
  1813 			InvalidateWidget(w, widget_index);
       
  1814 		}
       
  1815 	}
       
  1816 }
       
  1817 
       
  1818 void InvalidateWindowClasses(WindowClass cls)
       
  1819 {
       
  1820 	Window* const *wz;
       
  1821 
       
  1822 	FOR_ALL_WINDOWS(wz) {
       
  1823 		if ((*wz)->window_class == cls) SetWindowDirty(*wz);
       
  1824 	}
       
  1825 }
       
  1826 
       
  1827 void InvalidateThisWindowData(Window *w)
       
  1828 {
       
  1829 	CallWindowEventNP(w, WE_INVALIDATE_DATA);
       
  1830 	SetWindowDirty(w);
       
  1831 }
       
  1832 
       
  1833 void InvalidateWindowData(WindowClass cls, WindowNumber number)
       
  1834 {
       
  1835 	Window* const *wz;
       
  1836 
       
  1837 	FOR_ALL_WINDOWS(wz) {
       
  1838 		Window *w = *wz;
       
  1839 		if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w);
       
  1840 	}
       
  1841 }
       
  1842 
       
  1843 void InvalidateWindowClassesData(WindowClass cls)
       
  1844 {
       
  1845 	Window* const *wz;
       
  1846 
       
  1847 	FOR_ALL_WINDOWS(wz) {
       
  1848 		if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz);
       
  1849 	}
       
  1850 }
       
  1851 
       
  1852 void CallWindowTickEvent(void)
       
  1853 {
       
  1854 	Window* const *wz;
       
  1855 
       
  1856 	for (wz = _last_z_window; wz != _z_windows;) {
       
  1857 		CallWindowEventNP(*--wz, WE_TICK);
       
  1858 	}
       
  1859 }
       
  1860 
       
  1861 void DeleteNonVitalWindows(void)
       
  1862 {
       
  1863 	Window* const *wz;
       
  1864 
       
  1865 restart_search:
       
  1866 	/* When we find the window to delete, we need to restart the search
       
  1867 	 * as deleting this window could cascade in deleting (many) others
       
  1868 	 * anywhere in the z-array */
       
  1869 	FOR_ALL_WINDOWS(wz) {
       
  1870 		Window *w = *wz;
       
  1871 		if (w->window_class != WC_MAIN_WINDOW &&
       
  1872 				w->window_class != WC_SELECT_GAME &&
       
  1873 				w->window_class != WC_MAIN_TOOLBAR &&
       
  1874 				w->window_class != WC_STATUS_BAR &&
       
  1875 				w->window_class != WC_TOOLBAR_MENU &&
       
  1876 				w->window_class != WC_TOOLTIPS &&
       
  1877 				(w->flags4 & WF_STICKY) == 0) { // do not delete windows which are 'pinned'
       
  1878 
       
  1879 			DeleteWindow(w);
       
  1880 			goto restart_search;
       
  1881 		}
       
  1882 	}
       
  1883 }
       
  1884 
       
  1885 /* It is possible that a stickied window gets to a position where the
       
  1886  * 'close' button is outside the gaming area. You cannot close it then; except
       
  1887  * with this function. It closes all windows calling the standard function,
       
  1888  * then, does a little hacked loop of closing all stickied windows. Note
       
  1889  * that standard windows (status bar, etc.) are not stickied, so these aren't affected */
       
  1890 void DeleteAllNonVitalWindows(void)
       
  1891 {
       
  1892 	Window* const *wz;
       
  1893 
       
  1894 	/* Delete every window except for stickied ones, then sticky ones as well */
       
  1895 	DeleteNonVitalWindows();
       
  1896 
       
  1897 restart_search:
       
  1898 	/* When we find the window to delete, we need to restart the search
       
  1899 	 * as deleting this window could cascade in deleting (many) others
       
  1900 	 * anywhere in the z-array */
       
  1901 	FOR_ALL_WINDOWS(wz) {
       
  1902 		if ((*wz)->flags4 & WF_STICKY) {
       
  1903 			DeleteWindow(*wz);
       
  1904 			goto restart_search;
       
  1905 		}
       
  1906 	}
       
  1907 }
       
  1908 
       
  1909 /* Delete all always on-top windows to get an empty screen */
       
  1910 void HideVitalWindows(void)
       
  1911 {
       
  1912 	DeleteWindowById(WC_MAIN_TOOLBAR, 0);
       
  1913 	DeleteWindowById(WC_STATUS_BAR, 0);
       
  1914 }
       
  1915 
       
  1916 int PositionMainToolbar(Window *w)
       
  1917 {
       
  1918 	DEBUG(misc, 5, "Repositioning Main Toolbar...");
       
  1919 
       
  1920 	if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
       
  1921 		w = FindWindowById(WC_MAIN_TOOLBAR, 0);
       
  1922 	}
       
  1923 
       
  1924 	switch (_patches.toolbar_pos) {
       
  1925 		case 1:  w->left = (_screen.width - w->width) / 2; break;
       
  1926 		case 2:  w->left = _screen.width - w->width; break;
       
  1927 		default: w->left = 0;
       
  1928 	}
       
  1929 	SetDirtyBlocks(0, 0, _screen.width, w->height); // invalidate the whole top part
       
  1930 	return w->left;
       
  1931 }
       
  1932 
       
  1933 void RelocateAllWindows(int neww, int newh)
       
  1934 {
       
  1935 	Window* const *wz;
       
  1936 
       
  1937 	FOR_ALL_WINDOWS(wz) {
       
  1938 		Window *w = *wz;
       
  1939 		int left, top;
       
  1940 
       
  1941 		if (w->window_class == WC_MAIN_WINDOW) {
       
  1942 			ViewPort *vp = w->viewport;
       
  1943 			vp->width = w->width = neww;
       
  1944 			vp->height = w->height = newh;
       
  1945 			vp->virtual_width = neww << vp->zoom;
       
  1946 			vp->virtual_height = newh << vp->zoom;
       
  1947 			continue; // don't modify top,left
       
  1948 		}
       
  1949 
       
  1950 		/* XXX - this probably needs something more sane. For example specying
       
  1951 		 * in a 'backup'-desc that the window should always be centred. */
       
  1952 		switch (w->window_class) {
       
  1953 			case WC_MAIN_TOOLBAR:
       
  1954 				top = w->top;
       
  1955 				left = PositionMainToolbar(w); // changes toolbar orientation
       
  1956 				break;
       
  1957 
       
  1958 			case WC_SELECT_GAME:
       
  1959 			case WC_GAME_OPTIONS:
       
  1960 			case WC_NETWORK_WINDOW:
       
  1961 				top = (newh - w->height) >> 1;
       
  1962 				left = (neww - w->width) >> 1;
       
  1963 				break;
       
  1964 
       
  1965 			case WC_NEWS_WINDOW:
       
  1966 				top = newh - w->height;
       
  1967 				left = (neww - w->width) >> 1;
       
  1968 				break;
       
  1969 
       
  1970 			case WC_STATUS_BAR:
       
  1971 				top = newh - w->height;
       
  1972 				left = (neww - w->width) >> 1;
       
  1973 				break;
       
  1974 
       
  1975 			case WC_SEND_NETWORK_MSG:
       
  1976 				top = (newh - 26); // 26 = height of status bar + height of chat bar
       
  1977 				left = (neww - w->width) >> 1;
       
  1978 				break;
       
  1979 
       
  1980 			case WC_CONSOLE:
       
  1981 				IConsoleResize(w);
       
  1982 				continue;
       
  1983 
       
  1984 			default:
       
  1985 				left = w->left;
       
  1986 				if (left + (w->width >> 1) >= neww) left = neww - w->width;
       
  1987 				top = w->top;
       
  1988 				if (top + (w->height >> 1) >= newh) top = newh - w->height;
       
  1989 				break;
       
  1990 		}
       
  1991 
       
  1992 		if (w->viewport != NULL) {
       
  1993 			w->viewport->left += left - w->left;
       
  1994 			w->viewport->top += top - w->top;
       
  1995 		}
       
  1996 
       
  1997 		w->left = left;
       
  1998 		w->top = top;
       
  1999 	}
       
  2000 }