# window.cpp

Go to the documentation of this file.

    00001 /* $Id: window.cpp 12869 2008-04-24 12:36:18Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include <stdarg.h>
    00007 #include "openttd.h"
    00008 #include "debug.h"
    00009 #include "player_func.h"
    00010 #include "gfx_func.h"
    00011 #include "console.h"
    00012 #include "viewport_func.h"
    00013 #include "variables.h"
    00014 #include "genworld.h"
    00015 #include "blitter/factory.hpp"
    00016 #include "window_gui.h"
    00017 #include "zoom_func.h"
    00018 #include "core/alloc_func.hpp"
    00019 #include "map_func.h"
    00020 #include "vehicle_base.h"
    00021 #include "settings_type.h"
    00022
    00023 #include "table/sprites.h"
    00024
    00025 /* delta between mouse cursor and upper left corner of dragged window */
    00026 static Point _drag_delta;
    00027
    00028 static Window _windows[MAX_NUMBER_OF_WINDOWS];
    00029 Window *_z_windows[lengthof(_windows)];
    00030 Window **_last_z_window;
    00031
    00032 Point _cursorpos_drag_start;
    00033
    00034 int _scrollbar_start_pos;
    00035 int _scrollbar_size;
    00036 byte _scroller_click_timeout;
    00037
    00038 bool _scrolling_scrollbar;
    00039 bool _scrolling_viewport;
    00040 bool _popup_menu_active;
    00041
    00042 byte _special_mouse_mode;
    00043
    00044
    00045 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
    00046 {
    00047   va_list wdg_list;
    00048
    00049   va_start(wdg_list, widgets);
    00050
    00051   while (widgets != WIDGET_LIST_END) {
    00052     SetWidgetDisabledState(widgets, disab_stat);
    00053     widgets = va_arg(wdg_list, int);
    00054   }
    00055
    00056   va_end(wdg_list);
    00057 }
    00058
    00059 void CDECL Window::SetWidgetsHiddenState(bool hidden_stat, int widgets, ...)
    00060 {
    00061   va_list wdg_list;
    00062
    00063   va_start(wdg_list, widgets);
    00064
    00065   while (widgets != WIDGET_LIST_END) {
    00066     SetWidgetHiddenState(widgets, hidden_stat);
    00067     widgets = va_arg(wdg_list, int);
    00068   }
    00069
    00070   va_end(wdg_list);
    00071 }
    00072
    00073 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
    00074 {
    00075   va_list wdg_list;
    00076
    00077   va_start(wdg_list, widgets);
    00078
    00079   while (widgets != WIDGET_LIST_END) {
    00080     SetWidgetLoweredState(widgets, lowered_stat);
    00081     widgets = va_arg(wdg_list, int);
    00082   }
    00083
    00084   va_end(wdg_list);
    00085 }
    00086
    00087 void Window::RaiseButtons()
    00088 {
    00089   uint i;
    00090
    00091   for (i = 0; i < this->widget_count; i++) {
    00092     if (this->IsWidgetLowered(i)) {
    00093       this->RaiseWidget(i);
    00094       this->InvalidateWidget(i);
    00095     }
    00096   }
    00097 }
    00098
    00099 void Window::InvalidateWidget(byte widget_index) const
    00100 {
    00101   const Widget *wi = &this->widget[widget_index];
    00102
    00103   /* Don't redraw the window if the widget is invisible or of no-type */
    00104   if (wi->type == WWT_EMPTY || IsWidgetHidden(widget_index)) return;
    00105
    00106   SetDirtyBlocks(this->left + wi->left, this->top + wi->top, this->left + wi->right + 1, this->top + wi->bottom + 1);
    00107 }
    00108
    00109 void Window::HandleButtonClick(byte widget)
    00110 {
    00111   this->LowerWidget(widget);
    00112   this->flags4 |= 5 << WF_TIMEOUT_SHL;
    00113   this->InvalidateWidget(widget);
    00114 }
    00115
    00116 static void StartWindowDrag(Window *w);
    00117 static void StartWindowSizing(Window *w);
    00118
    00119 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click)
    00120 {
    00121   WindowEvent e;
    00122   const Widget *wi;
    00123
    00124   e.we.click.pt.x = x;
    00125   e.we.click.pt.y = y;
    00126   e.event = double_click ? WE_DOUBLE_CLICK : WE_CLICK;
    00127
    00128   if (w->desc_flags & WDF_DEF_WIDGET) {
    00129     e.we.click.widget = GetWidgetFromPos(w, x, y);
    00130     if (e.we.click.widget < 0) return; // exit if clicked outside of widgets
    00131
    00132     /* don't allow any interaction if the button has been disabled */
    00133     if (w->IsWidgetDisabled(e.we.click.widget)) return;
    00134
    00135     wi = &w->widget[e.we.click.widget];
    00136
    00137     if (wi->type & WWB_MASK) {
    00138       /* special widget handling for buttons*/
    00139       switch (wi->type) {
    00140         case WWT_PANEL   | WWB_PUSHBUTTON: /* WWT_PUSHBTN */
    00141         case WWT_IMGBTN  | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */
    00142         case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */
    00143           w->HandleButtonClick(e.we.click.widget);
    00144           break;
    00145       }
    00146     } else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
    00147       ScrollbarClickHandler(w, wi, e.we.click.pt.x, e.we.click.pt.y);
    00148     }
    00149
    00150     if (w->desc_flags & WDF_STD_BTN) {
    00151       if (e.we.click.widget == 0) { /* 'X' */
    00152         DeleteWindow(w);
    00153         return;
    00154       }
    00155
    00156       if (e.we.click.widget == 1) { /* 'Title bar' */
    00157         StartWindowDrag(w);
    00158         return;
    00159       }
    00160     }
    00161
    00162     if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
    00163       StartWindowSizing(w);
    00164       w->InvalidateWidget(e.we.click.widget);
    00165       return;
    00166     }
    00167
    00168     if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) {
    00169       w->flags4 ^= WF_STICKY;
    00170       w->InvalidateWidget(e.we.click.widget);
    00171       return;
    00172     }
    00173   }
    00174
    00175   w->wndproc(w, &e);
    00176 }
    00177
    00178 static void DispatchRightClickEvent(Window *w, int x, int y)
    00179 {
    00180   WindowEvent e;
    00181
    00182   /* default tooltips handler? */
    00183   if (w->desc_flags & WDF_STD_TOOLTIPS) {
    00184     e.we.click.widget = GetWidgetFromPos(w, x, y);
    00185     if (e.we.click.widget < 0)
    00186       return; // exit if clicked outside of widgets
    00187
    00188     if (w->widget[e.we.click.widget].tooltips != 0) {
    00189       GuiShowTooltips(w->widget[e.we.click.widget].tooltips);
    00190       return;
    00191     }
    00192   }
    00193
    00194   e.event = WE_RCLICK;
    00195   e.we.click.pt.x = x;
    00196   e.we.click.pt.y = y;
    00197   w->wndproc(w, &e);
    00198 }
    00199
    00206 static void DispatchMouseWheelEvent(Window *w, int widget, int wheel)
    00207 {
    00208   const Widget *wi1, *wi2;
    00209   Scrollbar *sb;
    00210
    00211   if (widget < 0) return;
    00212
    00213   wi1 = &w->widget[widget];
    00214   wi2 = &w->widget[widget + 1];
    00215
    00216   /* The listbox can only scroll if scrolling was done on the scrollbar itself,
    00217    * or on the listbox (and the next item is (must be) the scrollbar)
    00218    * XXX - should be rewritten as a widget-dependent scroller but that's
    00219    * not happening until someone rewrites the whole widget-code */
    00220   if ((sb = &w->vscroll,  wi1->type == WWT_SCROLLBAR)  || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR)  ||
    00221       (sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) {
    00222
    00223     if (sb->count > sb->cap) {
    00224       int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap);
    00225       if (pos != sb->pos) {
    00226         sb->pos = pos;
    00227         SetWindowDirty(w);
    00228       }
    00229     }
    00230   }
    00231 }
    00232
    00233 static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right, int bottom);
    00234
    00235 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
    00236 {
    00237   Window* const *wz;
    00238   DrawPixelInfo bk;
    00239   _cur_dpi = &bk;
    00240
    00241   FOR_ALL_WINDOWS(wz) {
    00242     const Window *w = *wz;
    00243     if (right > w->left &&
    00244         bottom > w->top &&
    00245         left < w->left + w->width &&
    00246         top < w->top + w->height) {
    00247       DrawOverlappedWindow(wz, left, top, right, bottom);
    00248     }
    00249   }
    00250 }
    00251
    00252 static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right, int bottom)
    00253 {
    00254   Window* const *vz = wz;
    00255   int x;
    00256
    00257   while (++vz != _last_z_window) {
    00258     const Window *v = *vz;
    00259
    00260     if (right > v->left &&
    00261         bottom > v->top &&
    00262         left < v->left + v->width &&
    00263         top < v->top + v->height) {
    00264       if (left < (x = v->left)) {
    00265         DrawOverlappedWindow(wz, left, top, x, bottom);
    00266         DrawOverlappedWindow(wz, x, top, right, bottom);
    00267         return;
    00268       }
    00269
    00270       if (right > (x = v->left + v->width)) {
    00271         DrawOverlappedWindow(wz, left, top, x, bottom);
    00272         DrawOverlappedWindow(wz, x, top, right, bottom);
    00273         return;
    00274       }
    00275
    00276       if (top < (x = v->top)) {
    00277         DrawOverlappedWindow(wz, left, top, right, x);
    00278         DrawOverlappedWindow(wz, left, x, right, bottom);
    00279         return;
    00280       }
    00281
    00282       if (bottom > (x = v->top + v->height)) {
    00283         DrawOverlappedWindow(wz, left, top, right, x);
    00284         DrawOverlappedWindow(wz, left, x, right, bottom);
    00285         return;
    00286       }
    00287
    00288       return;
    00289     }
    00290   }
    00291
    00292   {
    00293     DrawPixelInfo *dp = _cur_dpi;
    00294     dp->width = right - left;
    00295     dp->height = bottom - top;
    00296     dp->left = left - (*wz)->left;
    00297     dp->top = top - (*wz)->top;
    00298     dp->pitch = _screen.pitch;
    00299     dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
    00300     dp->zoom = ZOOM_LVL_NORMAL;
    00301     CallWindowEventNP(*wz, WE_PAINT);
    00302   }
    00303 }
    00304
    00305 void CallWindowEventNP(Window *w, int event)
    00306 {
    00307   WindowEvent e;
    00308
    00309   e.event = event;
    00310   w->wndproc(w, &e);
    00311 }
    00312
    00313 void SetWindowDirty(const Window *w)
    00314 {
    00315   if (w == NULL) return;
    00316   SetDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height);
    00317 }
    00318
    00322 static Window *FindChildWindow(const Window *w)
    00323 {
    00324   Window* const *wz;
    00325
    00326   FOR_ALL_WINDOWS(wz) {
    00327     Window *v = *wz;
    00328     if (v->parent == w) return v;
    00329   }
    00330
    00331   return NULL;
    00332 }
    00333
    00338 Window **FindWindowZPosition(const Window *w)
    00339 {
    00340   Window **wz;
    00341
    00342   for (wz = _z_windows; wz != _last_z_window; wz++) {
    00343     if (*wz == w) return wz;
    00344   }
    00345
    00346   DEBUG(misc, 3, "Window (cls %d, number %d) is not open, probably removed by recursive calls",
    00347     w->window_class, w->window_number);
    00348   return NULL;
    00349 }
    00350
    00351 void DeleteWindow(Window *w)
    00352 {
    00353   Window *v;
    00354   Window **wz;
    00355   if (w == NULL) return;
    00356
    00357   /* Delete any children a window might have in a head-recursive manner */
    00358   v = FindChildWindow(w);
    00359   if (v != NULL) DeleteWindow(v);
    00360
    00361   if (_thd.place_mode != VHM_NONE &&
    00362       _thd.window_class == w->window_class &&
    00363       _thd.window_number == w->window_number) {
    00364     ResetObjectToPlace();
    00365   }
    00366
    00367   CallWindowEventNP(w, WE_DESTROY);
    00368   if (w->viewport != NULL) DeleteWindowViewport(w);
    00369
    00370   SetWindowDirty(w);
    00371   free(w->widget);
    00372   w->widget = NULL;
    00373   w->widget_count = 0;
    00374   w->parent = NULL;
    00375
    00376   /* Find the window in the z-array, and effectively remove it
    00377    * by moving all windows after it one to the left */
    00378   wz = FindWindowZPosition(w);
    00379   if (wz == NULL) return;
    00380   memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz);
    00381   _last_z_window--;
    00382 }
    00383
    00384 Window *FindWindowById(WindowClass cls, WindowNumber number)
    00385 {
    00386   Window* const *wz;
    00387
    00388   FOR_ALL_WINDOWS(wz) {
    00389     Window *w = *wz;
    00390     if (w->window_class == cls && w->window_number == number) return w;
    00391   }
    00392
    00393   return NULL;
    00394 }
    00395
    00396 void DeleteWindowById(WindowClass cls, WindowNumber number)
    00397 {
    00398   DeleteWindow(FindWindowById(cls, number));
    00399 }
    00400
    00401 void DeleteWindowByClass(WindowClass cls)
    00402 {
    00403   Window* const *wz;
    00404
    00405 restart_search:
    00406   /* When we find the window to delete, we need to restart the search
    00407    * as deleting this window could cascade in deleting (many) others
    00408    * anywhere in the z-array */
    00409   FOR_ALL_WINDOWS(wz) {
    00410     Window *w = *wz;
    00411     if (w->window_class == cls) {
    00412       DeleteWindow(w);
    00413       goto restart_search;
    00414     }
    00415   }
    00416 }
    00417
    00422 void DeletePlayerWindows(PlayerID id)
    00423 {
    00424   Window* const *wz;
    00425
    00426 restart_search:
    00427   /* When we find the window to delete, we need to restart the search
    00428    * as deleting this window could cascade in deleting (many) others
    00429    * anywhere in the z-array */
    00430   FOR_ALL_WINDOWS(wz) {
    00431     Window *w = *wz;
    00432     if (w->caption_color == id) {
    00433       DeleteWindow(w);
    00434       goto restart_search;
    00435     }
    00436   }
    00437
    00438   /* Also delete the player specific windows, that don't have a player-colour */
    00439   DeleteWindowById(WC_BUY_COMPANY, id);
    00440 }
    00441
    00447 void ChangeWindowOwner(PlayerID old_player, PlayerID new_player)
    00448 {
    00449   Window* const *wz;
    00450
    00451   FOR_ALL_WINDOWS(wz) {
    00452     Window *w = *wz;
    00453
    00454     if (w->caption_color != old_player)      continue;
    00455     if (w->window_class == WC_PLAYER_COLOR)  continue;
    00456     if (w->window_class == WC_FINANCES)      continue;
    00457     if (w->window_class == WC_STATION_LIST)  continue;
    00458     if (w->window_class == WC_TRAINS_LIST)   continue;
    00459     if (w->window_class == WC_ROADVEH_LIST)  continue;
    00460     if (w->window_class == WC_SHIPS_LIST)    continue;
    00461     if (w->window_class == WC_AIRCRAFT_LIST) continue;
    00462     if (w->window_class == WC_BUY_COMPANY)   continue;
    00463     if (w->window_class == WC_COMPANY)       continue;
    00464
    00465     w->caption_color = new_player;
    00466   }
    00467 }
    00468
    00469 static void BringWindowToFront(const Window *w);
    00470
    00476 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
    00477 {
    00478   Window *w = FindWindowById(cls, number);
    00479
    00480   if (w != NULL) {
    00481     w->flags4 |= WF_WHITE_BORDER_MASK;
    00482     BringWindowToFront(w);
    00483     SetWindowDirty(w);
    00484   }
    00485
    00486   return w;
    00487 }
    00488
    00489 static inline bool IsVitalWindow(const Window *w)
    00490 {
    00491   WindowClass wc = w->window_class;
    00492   return (wc == WC_MAIN_TOOLBAR || wc == WC_STATUS_BAR || wc == WC_NEWS_WINDOW || wc == WC_SEND_NETWORK_MSG);
    00493 }
    00494
    00503 static void BringWindowToFront(const Window *w)
    00504 {
    00505   Window *tempz;
    00506   Window **wz = FindWindowZPosition(w);
    00507   Window **vz = _last_z_window;
    00508
    00509   /* Bring the window just below the vital windows */
    00510   do {
    00511     if (--vz < _z_windows) return;
    00512   } while (IsVitalWindow(*vz));
    00513
    00514   if (wz == vz) return; // window is already in the right position
    00515   assert(wz < vz);
    00516
    00517   tempz = *wz;
    00518   memmove(wz, wz + 1, (byte*)vz - (byte*)wz);
    00519   *vz = tempz;
    00520
    00521   SetWindowDirty(w);
    00522 }
    00523
    00531 static Window *FindDeletableWindow()
    00532 {
    00533   Window* const *wz;
    00534
    00535   FOR_ALL_WINDOWS(wz) {
    00536     Window *w = *wz;
    00537     if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w) && !(w->flags4 & WF_STICKY)) {
    00538       return w;
    00539     }
    00540   }
    00541   return NULL;
    00542 }
    00543
    00551 static Window *ForceFindDeletableWindow()
    00552 {
    00553   Window* const *wz;
    00554
    00555   for (wz = _z_windows;; wz++) {
    00556     Window *w = *wz;
    00557     assert(wz < _last_z_window);
    00558     if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w;
    00559   }
    00560 }
    00561
    00562 bool IsWindowOfPrototype(const Window *w, const Widget *widget)
    00563 {
    00564   return (w->original_widget == widget);
    00565 }
    00566
    00570 void AssignWidgetToWindow(Window *w, const Widget *widget)
    00571 {
    00572   w->original_widget = widget;
    00573
    00574   if (widget != NULL) {
    00575     uint index = 1;
    00576     const Widget *wi;
    00577
    00578     for (wi = widget; wi->type != WWT_LAST; wi++) index++;
    00579
    00580     w->widget = ReallocT(w->widget, index);
    00581     memcpy(w->widget, widget, sizeof(*w->widget) * index);
    00582     w->widget_count = index - 1;
    00583   } else {
    00584     w->widget = NULL;
    00585     w->widget_count = 0;
    00586   }
    00587 }
    00588
    00589 static Window *FindFreeWindow()
    00590 {
    00591   Window *w;
    00592
    00593   for (w = _windows; w < endof(_windows); w++) {
    00594     Window* const *wz;
    00595     bool window_in_use = false;
    00596
    00597     FOR_ALL_WINDOWS(wz) {
    00598       if (*wz == w) {
    00599         window_in_use = true;
    00600         break;
    00601       }
    00602     }
    00603
    00604     if (!window_in_use) return w;
    00605   }
    00606
    00607   assert(_last_z_window == endof(_z_windows));
    00608   return NULL;
    00609 }
    00610
    00628 static Window *LocalAllocateWindow(
    00629               int x, int y, int min_width, int min_height, int def_width, int def_height,
    00630               WindowProc *proc, WindowClass cls, const Widget *widget, int window_number, void *data)
    00631 {
    00632   Window *w = FindFreeWindow();
    00633
    00634   /* We have run out of windows, close one and use that as the place for our new one */
    00635   if (w == NULL) {
    00636     w = FindDeletableWindow();
    00637     if (w == NULL) w = ForceFindDeletableWindow();
    00638     DeleteWindow(w);
    00639   }
    00640
    00641   /* Set up window properties */
    00642   memset(w, 0, sizeof(*w));
    00643   w->window_class = cls;
    00644   w->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border
    00645   w->caption_color = 0xFF;
    00646   w->left = x;
    00647   w->top = y;
    00648   w->width = min_width;
    00649   w->height = min_height;
    00650   w->wndproc = proc;
    00651   AssignWidgetToWindow(w, widget);
    00652   w->resize.width = min_width;
    00653   w->resize.height = min_height;
    00654   w->resize.step_width = 1;
    00655   w->resize.step_height = 1;
    00656   w->window_number = window_number;
    00657
    00658   {
    00659     Window **wz = _last_z_window;
    00660
    00661     /* Hacky way of specifying always-on-top windows. These windows are
    00662      * always above other windows because they are moved below them.
    00663      * status-bar is above news-window because it has been created earlier.
    00664      * Also, as the chat-window is excluded from this, it will always be
    00665      * the last window, thus always on top.
    00666      * XXX - Yes, ugly, probably needs something like w->always_on_top flag
    00667      * to implement correctly, but even then you need some kind of distinction
    00668      * between on-top of chat/news and status windows, because these conflict */
    00669     if (wz != _z_windows && w->window_class != WC_SEND_NETWORK_MSG && w->window_class != WC_HIGHSCORE && w->window_class != WC_ENDSCREEN) {
    00670       if (FindWindowById(WC_MAIN_TOOLBAR, 0)     != NULL) wz--;
    00671       if (FindWindowById(WC_STATUS_BAR, 0)       != NULL) wz--;
    00672       if (FindWindowById(WC_NEWS_WINDOW, 0)      != NULL) wz--;
    00673       if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--;
    00674
    00675       assert(wz >= _z_windows);
    00676       if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz);
    00677     }
    00678
    00679     *wz = w;
    00680     _last_z_window++;
    00681   }
    00682
    00683   WindowEvent e;
    00684   e.event = WE_CREATE;
    00685   e.we.create.data = data;
    00686   w->wndproc(w, &e);
    00687
    00688   /* Try to make windows smaller when our window is too small.
    00689    * w->(width|height) is normally the same as min_(width|height),
    00690    * but this way the GUIs can be made a little more dynamic;
    00691    * one can use the same spec for multiple windows and those
    00692    * can then determine the real minimum size of the window. */
    00693   if (w->width != def_width || w->height != def_height) {
    00694     /* Think about the overlapping toolbars when determining the minimum window size */
    00695     int free_height = _screen.height;
    00696     const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
    00697     if (wt != NULL) free_height -= wt->height;
    00698     wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
    00699     if (wt != NULL) free_height -= wt->height;
    00700
    00701     int enlarge_x = max(min(def_width  - w->width,  _screen.width - w->width),  0);
    00702     int enlarge_y = max(min(def_height - w->height, free_height   - w->height), 0);
    00703
    00704     /* X and Y has to go by step.. calculate it.
    00705      * The cast to int is necessary else x/y are implicitly casted to
    00706      * unsigned int, which won't work. */
    00707     if (w->resize.step_width  > 1) enlarge_x -= enlarge_x % (int)w->resize.step_width;
    00708     if (w->resize.step_height > 1) enlarge_y -= enlarge_y % (int)w->resize.step_height;
    00709
    00710     ResizeWindow(w, enlarge_x, enlarge_y);
    00711
    00712     WindowEvent e;
    00713     e.event = WE_RESIZE;
    00714     e.we.sizing.size.x = w->width;
    00715     e.we.sizing.size.y = w->height;
    00716     e.we.sizing.diff.x = enlarge_x;
    00717     e.we.sizing.diff.y = enlarge_y;
    00718     w->wndproc(w, &e);
    00719   }
    00720
    00721   int nx = w->left;
    00722   int ny = w->top;
    00723
    00724   if (nx + w->width > _screen.width) nx -= (nx + w->width - _screen.width);
    00725
    00726   const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
    00727   ny = max(ny, (wt == NULL || w == wt || y == 0) ? 0 : wt->height);
    00728   nx = max(nx, 0);
    00729
    00730   if (w->viewport != NULL) {
    00731     w->viewport->left += nx - w->left;
    00732     w->viewport->top  += ny - w->top;
    00733   }
    00734   w->left = nx;
    00735   w->top = ny;
    00736
    00737   SetWindowDirty(w);
    00738
    00739   return w;
    00740 }
    00741
    00755 Window *AllocateWindow(
    00756               int x, int y, int width, int height,
    00757               WindowProc *proc, WindowClass cls, const Widget *widget, void *data)
    00758 {
    00759   return LocalAllocateWindow(x, y, width, height, width, height, proc, cls, widget, 0, data);
    00760 }
    00761
    00762 struct SizeRect {
    00763   int left,top,width,height;
    00764 };
    00765
    00766
    00767 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
    00768 {
    00769   Window* const *wz;
    00770
    00771   int right  = width + left;
    00772   int bottom = height + top;
    00773
    00774   if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height)
    00775     return false;
    00776
    00777   /* Make sure it is not obscured by any window. */
    00778   FOR_ALL_WINDOWS(wz) {
    00779     const Window *w = *wz;
    00780     if (w->window_class == WC_MAIN_WINDOW) continue;
    00781
    00782     if (right > w->left &&
    00783         w->left + w->width > left &&
    00784         bottom > w->top &&
    00785         w->top + w->height > top) {
    00786       return false;
    00787     }
    00788   }
    00789
    00790   pos.x = left;
    00791   pos.y = top;
    00792   return true;
    00793 }
    00794
    00795 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
    00796 {
    00797   Window* const *wz;
    00798
    00799   if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
    00800   if (top < 22 || top > _screen.height - (height>>2)) return false;
    00801
    00802   /* Make sure it is not obscured by any window. */
    00803   FOR_ALL_WINDOWS(wz) {
    00804     const Window *w = *wz;
    00805     if (w->window_class == WC_MAIN_WINDOW) continue;
    00806
    00807     if (left + width > w->left &&
    00808         w->left + w->width > left &&
    00809         top + height > w->top &&
    00810         w->top + w->height > top) {
    00811       return false;
    00812     }
    00813   }
    00814
    00815   pos.x = left;
    00816   pos.y = top;
    00817   return true;
    00818 }
    00819
    00820 static Point GetAutoPlacePosition(int width, int height)
    00821 {
    00822   Window* const *wz;
    00823   Point pt;
    00824
    00825   if (IsGoodAutoPlace1(0, 24, width, height, pt)) return pt;
    00826
    00827   FOR_ALL_WINDOWS(wz) {
    00828     const Window *w = *wz;
    00829     if (w->window_class == WC_MAIN_WINDOW) continue;
    00830
    00831     if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
    00832     if (IsGoodAutoPlace1(w->left - width - 2,    w->top, width, height, pt)) return pt;
    00833     if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
    00834     if (IsGoodAutoPlace1(w->left, w->top - height - 2,    width, height, pt)) return pt;
    00835     if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
    00836     if (IsGoodAutoPlace1(w->left - width - 2,    w->top + w->height - height, width, height, pt)) return pt;
    00837     if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
    00838     if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2,    width, height, pt)) return pt;
    00839   }
    00840
    00841   FOR_ALL_WINDOWS(wz) {
    00842     const Window *w = *wz;
    00843     if (w->window_class == WC_MAIN_WINDOW) continue;
    00844
    00845     if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
    00846     if (IsGoodAutoPlace2(w->left - width - 2,    w->top, width, height, pt)) return pt;
    00847     if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
    00848     if (IsGoodAutoPlace2(w->left, w->top - height - 2,    width, height, pt)) return pt;
    00849   }
    00850
    00851   {
    00852     int left = 0, top = 24;
    00853
    00854 restart:
    00855     FOR_ALL_WINDOWS(wz) {
    00856       const Window *w = *wz;
    00857
    00858       if (w->left == left && w->top == top) {
    00859         left += 5;
    00860         top += 5;
    00861         goto restart;
    00862       }
    00863     }
    00864
    00865     pt.x = left;
    00866     pt.y = top;
    00867     return pt;
    00868   }
    00869 }
    00870
    00880 static Window *LocalAllocateWindowDesc(const WindowDesc *desc, int window_number, void *data)
    00881 {
    00882   Point pt;
    00883   Window *w;
    00884
    00885   /* By default position a child window at an offset of 10/10 of its parent.
    00886    * With the exception of WC_BUILD_TOOLBAR (build railway/roads/ship docks/airports)
    00887    * and WC_SCEN_LAND_GEN (landscaping). Whose child window has an offset of 0/36 of
    00888    * its parent. So it's exactly under the parent toolbar and no buttons will be covered.
    00889    * However if it falls too extremely outside window positions, reposition
    00890    * it to an automatic place */
    00891   if (desc->parent_cls != 0 /* WC_MAIN_WINDOW */ &&
    00892       (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
    00893       w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
    00894
    00895     pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
    00896     if (pt.x > _screen.width + 10 - desc->default_width) {
    00897       pt.x = (_screen.width + 10 - desc->default_width) - 20;
    00898     }
    00899     pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 36 : 10);
    00900   } else {
    00901     switch (desc->left) {
    00902       case WDP_ALIGN_TBR: { /* Align the right side with the top toolbar */
    00903         w = FindWindowById(WC_MAIN_TOOLBAR, 0);
    00904         pt.x = (w->left + w->width) - desc->default_width;
    00905       } break;
    00906       case WDP_ALIGN_TBL: /* Align the left side with the top toolbar */
    00907         pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left;
    00908         break;
    00909       case WDP_AUTO: /* Find a good automatic position for the window */
    00910         pt = GetAutoPlacePosition(desc->default_width, desc->default_height);
    00911         goto allocate_window;
    00912       case WDP_CENTER: /* Centre the window horizontally */
    00913         pt.x = (_screen.width - desc->default_width) / 2;
    00914         break;
    00915       default:
    00916         pt.x = desc->left;
    00917         if (pt.x < 0) pt.x += _screen.width; // negative is from right of the screen
    00918     }
    00919
    00920     switch (desc->top) {
    00921       case WDP_CENTER: /* Centre the window vertically */
    00922         pt.y = (_screen.height - desc->default_height) / 2;
    00923         break;
    00924       /* WDP_AUTO sets the position at once and is controlled by desc->left.
    00925        * Both left and top must be set to WDP_AUTO */
    00926       case WDP_AUTO:
    00927         NOT_REACHED();
    00928         assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO);
    00929         /* fallthrough */
    00930       default:
    00931         pt.y = desc->top;
    00932         if (pt.y < 0) pt.y += _screen.height; // negative is from bottom of the screen
    00933         break;
    00934     }
    00935   }
    00936
    00937 allocate_window:
    00938   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);
    00939   w->desc_flags = desc->flags;
    00940   return w;
    00941 }
    00942
    00949 Window *AllocateWindowDesc(const WindowDesc *desc, void *data)
    00950 {
    00951   return LocalAllocateWindowDesc(desc, 0, data);
    00952 }
    00953
    00961 Window *AllocateWindowDescFront(const WindowDesc *desc, int window_number, void *data)
    00962 {
    00963   Window *w;
    00964
    00965   if (BringWindowToFrontById(desc->cls, window_number)) return NULL;
    00966   w = LocalAllocateWindowDesc(desc, window_number, data);
    00967   return w;
    00968 }
    00969
    00975 Window *FindWindowFromPt(int x, int y)
    00976 {
    00977   Window* const *wz;
    00978
    00979   for (wz = _last_z_window; wz != _z_windows;) {
    00980     Window *w = *--wz;
    00981     if (IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
    00982       return w;
    00983     }
    00984   }
    00985
    00986   return NULL;
    00987 }
    00988
    00989 void InitWindowSystem()
    00990 {
    00991   IConsoleClose();
    00992
    00993   memset(&_windows, 0, sizeof(_windows));
    00994   _last_z_window = _z_windows;
    00995   InitViewports();
    00996   _no_scroll = 0;
    00997 }
    00998
    00999 void UnInitWindowSystem()
    01000 {
    01001   Window **wz;
    01002
    01003 restart_search:
    01004   /* Delete all windows, reset z-array.
    01005    *When we find the window to delete, we need to restart the search
    01006    * as deleting this window could cascade in deleting (many) others
    01007    * anywhere in the z-array. We call DeleteWindow() so that it can properly
    01008    * release own alloc'd memory, which otherwise could result in memleaks */
    01009   FOR_ALL_WINDOWS(wz) {
    01010     DeleteWindow(*wz);
    01011     goto restart_search;
    01012   }
    01013
    01014   assert(_last_z_window == _z_windows);
    01015 }
    01016
    01017 void ResetWindowSystem()
    01018 {
    01019   UnInitWindowSystem();
    01020   InitWindowSystem();
    01021   _thd.pos.x = 0;
    01022   _thd.pos.y = 0;
    01023   _thd.new_pos.x = 0;
    01024   _thd.new_pos.y = 0;
    01025 }
    01026
    01027 static void DecreaseWindowCounters()
    01028 {
    01029   Window *w;
    01030   Window* const *wz;
    01031
    01032   for (wz = _last_z_window; wz != _z_windows;) {
    01033     w = *--wz;
    01034     /* Unclick scrollbar buttons if they are pressed. */
    01035     if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
    01036       w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
    01037       SetWindowDirty(w);
    01038     }
    01039     CallWindowEventNP(w, WE_MOUSELOOP);
    01040   }
    01041
    01042   for (wz = _last_z_window; wz != _z_windows;) {
    01043     w = *--wz;
    01044
    01045     if (w->flags4&WF_TIMEOUT_MASK && !(--w->flags4&WF_TIMEOUT_MASK)) {
    01046       CallWindowEventNP(w, WE_TIMEOUT);
    01047       if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons();
    01048     }
    01049   }
    01050 }
    01051
    01052 Window *GetCallbackWnd()
    01053 {
    01054   return FindWindowById(_thd.window_class, _thd.window_number);
    01055 }
    01056
    01057 static void HandlePlacePresize()
    01058 {
    01059   Window *w;
    01060   WindowEvent e;
    01061
    01062   if (_special_mouse_mode != WSM_PRESIZE) return;
    01063
    01064   w = GetCallbackWnd();
    01065   if (w == NULL) return;
    01066
    01067   e.we.place.pt = GetTileBelowCursor();
    01068   if (e.we.place.pt.x == -1) {
    01069     _thd.selend.x = -1;
    01070     return;
    01071   }
    01072   e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
    01073   e.event = WE_PLACE_PRESIZE;
    01074   w->wndproc(w, &e);
    01075 }
    01076
    01077 static bool HandleDragDrop()
    01078 {
    01079   Window *w;
    01080   WindowEvent e;
    01081
    01082   if (_special_mouse_mode != WSM_DRAGDROP) return true;
    01083
    01084   if (_left_button_down) return false;
    01085
    01086   w = GetCallbackWnd();
    01087
    01088   if (w != NULL) {
    01089     /* send an event in client coordinates. */
    01090     e.event = WE_DRAGDROP;
    01091     e.we.dragdrop.pt.x = _cursor.pos.x - w->left;
    01092     e.we.dragdrop.pt.y = _cursor.pos.y - w->top;
    01093     e.we.dragdrop.widget = GetWidgetFromPos(w, e.we.dragdrop.pt.x, e.we.dragdrop.pt.y);
    01094     w->wndproc(w, &e);
    01095   }
    01096
    01097   ResetObjectToPlace();
    01098
    01099   return false;
    01100 }
    01101
    01102 static bool HandlePopupMenu()
    01103 {
    01104   Window *w;
    01105   WindowEvent e;
    01106
    01107   if (!_popup_menu_active) return true;
    01108
    01109   w = FindWindowById(WC_TOOLBAR_MENU, 0);
    01110   if (w == NULL) {
    01111     _popup_menu_active = false;
    01112     return false;
    01113   }
    01114
    01115   if (_left_button_down) {
    01116     e.event = WE_POPUPMENU_OVER;
    01117     e.we.popupmenu.pt = _cursor.pos;
    01118   } else {
    01119     _popup_menu_active = false;
    01120     e.event = WE_POPUPMENU_SELECT;
    01121     e.we.popupmenu.pt = _cursor.pos;
    01122   }
    01123
    01124   w->wndproc(w, &e);
    01125
    01126   return false;
    01127 }
    01128
    01129 static bool HandleMouseOver()
    01130 {
    01131   Window *w;
    01132   WindowEvent e;
    01133   static Window *last_w = NULL;
    01134
    01135   w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
    01136
    01137   /* We changed window, put a MOUSEOVER event to the last window */
    01138   if (last_w != NULL && last_w != w) {
    01139     e.event = WE_MOUSEOVER;
    01140     e.we.mouseover.pt.x = -1;
    01141     e.we.mouseover.pt.y = -1;
    01142     if (last_w->wndproc) last_w->wndproc(last_w, &e);
    01143   }
    01144   last_w = w;
    01145
    01146   if (w != NULL) {
    01147     /* send an event in client coordinates. */
    01148     e.event = WE_MOUSEOVER;
    01149     e.we.mouseover.pt.x = _cursor.pos.x - w->left;
    01150     e.we.mouseover.pt.y = _cursor.pos.y - w->top;
    01151     if (w->widget != NULL) {
    01152       e.we.mouseover.widget = GetWidgetFromPos(w, e.we.mouseover.pt.x, e.we.mouseover.pt.y);
    01153     }
    01154     w->wndproc(w, &e);
    01155   }
    01156
    01157   /* Mouseover never stops execution */
    01158   return true;
    01159 }
    01160
    01167 void ResizeWindow(Window *w, int x, int y)
    01168 {
    01169   Widget *wi;
    01170   bool resize_height = false;
    01171   bool resize_width = false;
    01172
    01173   if (x == 0 && y == 0) return;
    01174
    01175   SetWindowDirty(w);
    01176   for (wi = w->widget; wi->type != WWT_LAST; wi++) {
    01177     /* Isolate the resizing flags */
    01178     byte rsizeflag = GB(wi->display_flags, 0, 4);
    01179
    01180     if (rsizeflag == RESIZE_NONE) continue;
    01181
    01182     /* Resize the widget based on its resize-flag */
    01183     if (rsizeflag & RESIZE_LEFT) {
    01184       wi->left += x;
    01185       resize_width = true;
    01186     }
    01187
    01188     if (rsizeflag & RESIZE_RIGHT) {
    01189       wi->right += x;
    01190       resize_width = true;
    01191     }
    01192
    01193     if (rsizeflag & RESIZE_TOP) {
    01194       wi->top += y;
    01195       resize_height = true;
    01196     }
    01197
    01198     if (rsizeflag & RESIZE_BOTTOM) {
    01199       wi->bottom += y;
    01200       resize_height = true;
    01201     }
    01202   }
    01203
    01204   /* We resized at least 1 widget, so let's resize the window totally */
    01205   if (resize_width)  w->width  += x;
    01206   if (resize_height) w->height += y;
    01207
    01208   SetWindowDirty(w);
    01209 }
    01210
    01211 static bool _dragging_window;
    01212
    01213 static bool HandleWindowDragging()
    01214 {
    01215   Window* const *wz;
    01216   /* Get out immediately if no window is being dragged at all. */
    01217   if (!_dragging_window) return true;
    01218
    01219   /* Otherwise find the window... */
    01220   FOR_ALL_WINDOWS(wz) {
    01221     Window *w = *wz;
    01222
    01223     if (w->flags4 & WF_DRAGGING) {
    01224       const Widget *t = &w->widget[1]; // the title bar ... ugh
    01225       const Window *v;
    01226       int x;
    01227       int y;
    01228       int nx;
    01229       int ny;
    01230
    01231       /* Stop the dragging if the left mouse button was released */
    01232       if (!_left_button_down) {
    01233         w->flags4 &= ~WF_DRAGGING;
    01234         break;
    01235       }
    01236
    01237       SetWindowDirty(w);
    01238
    01239       x = _cursor.pos.x + _drag_delta.x;
    01240       y = _cursor.pos.y + _drag_delta.y;
    01241       nx = x;
    01242       ny = y;
    01243
    01244       if (_patches.window_snap_radius != 0) {
    01245         Window* const *vz;
    01246
    01247         int hsnap = _patches.window_snap_radius;
    01248         int vsnap = _patches.window_snap_radius;
    01249         int delta;
    01250
    01251         FOR_ALL_WINDOWS(vz) {
    01252           const Window *v = *vz;
    01253
    01254           if (v == w) continue; // Don't snap at yourself
    01255
    01256           if (y + w->height > v->top && y < v->top + v->height) {
    01257             /* Your left border <-> other right border */
    01258             delta = abs(v->left + v->width - x);
    01259             if (delta <= hsnap) {
    01260               nx = v->left + v->width;
    01261               hsnap = delta;
    01262             }
    01263
    01264             /* Your right border <-> other left border */
    01265             delta = abs(v->left - x - w->width);
    01266             if (delta <= hsnap) {
    01267               nx = v->left - w->width;
    01268               hsnap = delta;
    01269             }
    01270           }
    01271
    01272           if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
    01273             /* Your left border <-> other left border */
    01274             delta = abs(v->left - x);
    01275             if (delta <= hsnap) {
    01276               nx = v->left;
    01277               hsnap = delta;
    01278             }
    01279
    01280             /* Your right border <-> other right border */
    01281             delta = abs(v->left + v->width - x - w->width);
    01282             if (delta <= hsnap) {
    01283               nx = v->left + v->width - w->width;
    01284               hsnap = delta;
    01285             }
    01286           }
    01287
    01288           if (x + w->width > v->left && x < v->left + v->width) {
    01289             /* Your top border <-> other bottom border */
    01290             delta = abs(v->top + v->height - y);
    01291             if (delta <= vsnap) {
    01292               ny = v->top + v->height;
    01293               vsnap = delta;
    01294             }
    01295
    01296             /* Your bottom border <-> other top border */
    01297             delta = abs(v->top - y - w->height);
    01298             if (delta <= vsnap) {
    01299               ny = v->top - w->height;
    01300               vsnap = delta;
    01301             }
    01302           }
    01303
    01304           if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
    01305             /* Your top border <-> other top border */
    01306             delta = abs(v->top - y);
    01307             if (delta <= vsnap) {
    01308               ny = v->top;
    01309               vsnap = delta;
    01310             }
    01311
    01312             /* Your bottom border <-> other bottom border */
    01313             delta = abs(v->top + v->height - y - w->height);
    01314             if (delta <= vsnap) {
    01315               ny = v->top + v->height - w->height;
    01316               vsnap = delta;
    01317             }
    01318           }
    01319         }
    01320       }
    01321
    01322       /* Make sure the window doesn't leave the screen
    01323        * 13 is the height of the title bar */
    01324       nx = Clamp(nx, 13 - t->right, _screen.width - 13 - t->left);
    01325       ny = Clamp(ny, 0, _screen.height - 13);
    01326
    01327       /* Make sure the title bar isn't hidden by behind the main tool bar */
    01328       v = FindWindowById(WC_MAIN_TOOLBAR, 0);
    01329       if (v != NULL) {
    01330         int v_bottom = v->top + v->height;
    01331         int v_right = v->left + v->width;
    01332         if (ny + t->top >= v->top && ny + t->top < v_bottom) {
    01333           if ((v->left < 13 && nx + t->left < v->left) ||
    01334               (v_right > _screen.width - 13 && nx + t->right > v_right)) {
    01335             ny = v_bottom;
    01336           } else {
    01337             if (nx + t->left > v->left - 13 &&
    01338                 nx + t->right < v_right + 13) {
    01339               if (w->top >= v_bottom) {
    01340                 ny = v_bottom;
    01341               } else if (w->left < nx) {
    01342                 nx = v->left - 13 - t->left;
    01343               } else {
    01344                 nx = v_right + 13 - t->right;
    01345               }
    01346             }
    01347           }
    01348         }
    01349       }
    01350
    01351       if (w->viewport != NULL) {
    01352         w->viewport->left += nx - w->left;
    01353         w->viewport->top  += ny - w->top;
    01354       }
    01355       w->left = nx;
    01356       w->top  = ny;
    01357
    01358       SetWindowDirty(w);
    01359       return false;
    01360     } else if (w->flags4 & WF_SIZING) {
    01361       WindowEvent e;
    01362       int x, y;
    01363
    01364       /* Stop the sizing if the left mouse button was released */
    01365       if (!_left_button_down) {
    01366         w->flags4 &= ~WF_SIZING;
    01367         SetWindowDirty(w);
    01368         break;
    01369       }
    01370
    01371       x = _cursor.pos.x - _drag_delta.x;
    01372       y = _cursor.pos.y - _drag_delta.y;
    01373
    01374       /* X and Y has to go by step.. calculate it.
    01375        * The cast to int is necessary else x/y are implicitly casted to
    01376        * unsigned int, which won't work. */
    01377       if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
    01378
    01379       if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
    01380
    01381       /* Check if we don't go below the minimum set size */
    01382       if ((int)w->width + x < (int)w->resize.width)
    01383         x = w->resize.width - w->width;
    01384       if ((int)w->height + y < (int)w->resize.height)
    01385         y = w->resize.height - w->height;
    01386
    01387       /* Window already on size */
    01388       if (x == 0 && y == 0) return false;
    01389
    01390       /* Now find the new cursor pos.. this is NOT _cursor, because
    01391           we move in steps. */
    01392       _drag_delta.x += x;
    01393       _drag_delta.y += y;
    01394
    01395       /* ResizeWindow sets both pre- and after-size to dirty for redrawal */
    01396       ResizeWindow(w, x, y);
    01397
    01398       e.event = WE_RESIZE;
    01399       e.we.sizing.size.x = x + w->width;
    01400       e.we.sizing.size.y = y + w->height;
    01401       e.we.sizing.diff.x = x;
    01402       e.we.sizing.diff.y = y;
    01403       w->wndproc(w, &e);
    01404       return false;
    01405     }
    01406   }
    01407
    01408   _dragging_window = false;
    01409   return false;
    01410 }
    01411
    01412 static void StartWindowDrag(Window *w)
    01413 {
    01414   w->flags4 |= WF_DRAGGING;
    01415   _dragging_window = true;
    01416
    01417   _drag_delta.x = w->left - _cursor.pos.x;
    01418   _drag_delta.y = w->top  - _cursor.pos.y;
    01419
    01420   BringWindowToFront(w);
    01421   DeleteWindowById(WC_DROPDOWN_MENU, 0);
    01422 }
    01423
    01424 static void StartWindowSizing(Window *w)
    01425 {
    01426   w->flags4 |= WF_SIZING;
    01427   _dragging_window = true;
    01428
    01429   _drag_delta.x = _cursor.pos.x;
    01430   _drag_delta.y = _cursor.pos.y;
    01431
    01432   BringWindowToFront(w);
    01433   DeleteWindowById(WC_DROPDOWN_MENU, 0);
    01434 }
    01435
    01436
    01437 static bool HandleScrollbarScrolling()
    01438 {
    01439   Window* const *wz;
    01440   int i;
    01441   int pos;
    01442   Scrollbar *sb;
    01443
    01444   /* Get out quickly if no item is being scrolled */
    01445   if (!_scrolling_scrollbar) return true;
    01446
    01447   /* Find the scrolling window */
    01448   FOR_ALL_WINDOWS(wz) {
    01449     Window *w = *wz;
    01450
    01451     if (w->flags4 & WF_SCROLL_MIDDLE) {
    01452       /* Abort if no button is clicked any more. */
    01453       if (!_left_button_down) {
    01454         w->flags4 &= ~WF_SCROLL_MIDDLE;
    01455         SetWindowDirty(w);
    01456         break;
    01457       }
    01458
    01459       if (w->flags4 & WF_HSCROLL) {
    01460         sb = &w->hscroll;
    01461         i = _cursor.pos.x - _cursorpos_drag_start.x;
    01462       } else if (w->flags4 & WF_SCROLL2){
    01463         sb = &w->vscroll2;
    01464         i = _cursor.pos.y - _cursorpos_drag_start.y;
    01465       } else {
    01466         sb = &w->vscroll;
    01467         i = _cursor.pos.y - _cursorpos_drag_start.y;
    01468       }
    01469
    01470       /* Find the item we want to move to and make sure it's inside bounds. */
    01471       pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap));
    01472       if (pos != sb->pos) {
    01473         sb->pos = pos;
    01474         SetWindowDirty(w);
    01475       }
    01476       return false;
    01477     }
    01478   }
    01479
    01480   _scrolling_scrollbar = false;
    01481   return false;
    01482 }
    01483
    01484 static bool HandleViewportScroll()
    01485 {
    01486   WindowEvent e;
    01487   Window *w;
    01488
    01489   bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
    01490
    01491   if (!_scrolling_viewport) return true;
    01492
    01493   w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
    01494
    01495   if (!(_right_button_down || scrollwheel_scrolling) || w == NULL) {
    01496     _cursor.fix_at = false;
    01497     _scrolling_viewport = false;
    01498     return true;
    01499   }
    01500
    01501   if (WP(w, vp_d).follow_vehicle != INVALID_VEHICLE && w == FindWindowById(WC_MAIN_WINDOW, 0)) {
    01502     /* If the main window is following a vehicle, then first let go of it! */
    01503     const Vehicle *veh = GetVehicle(WP(w, vp_d).follow_vehicle);
    01504     ScrollMainWindowTo(veh->x_pos, veh->y_pos, true); /* This also resets follow_vehicle */
    01505     return true;
    01506   }
    01507
    01508   if (_patches.reverse_scroll) {
    01509     e.we.scroll.delta.x = -_cursor.delta.x;
    01510     e.we.scroll.delta.y = -_cursor.delta.y;
    01511   } else {
    01512     e.we.scroll.delta.x = _cursor.delta.x;
    01513     e.we.scroll.delta.y = _cursor.delta.y;
    01514   }
    01515
    01516   if (scrollwheel_scrolling) {
    01517     /* We are using scrollwheels for scrolling */
    01518     e.we.scroll.delta.x = _cursor.h_wheel;
    01519     e.we.scroll.delta.y = _cursor.v_wheel;
    01520     _cursor.v_wheel = 0;
    01521     _cursor.h_wheel = 0;
    01522   }
    01523
    01524   /* Create a scroll-event and send it to the window */
    01525   e.event = WE_SCROLL;
    01526   w->wndproc(w, &e);
    01527
    01528   _cursor.delta.x = 0;
    01529   _cursor.delta.y = 0;
    01530   return false;
    01531 }
    01532
    01541 static bool MaybeBringWindowToFront(const Window *w)
    01542 {
    01543   bool bring_to_front = false;
    01544   Window* const *wz;
    01545   Window* const *uz;
    01546
    01547   if (w->window_class == WC_MAIN_WINDOW ||
    01548       IsVitalWindow(w) ||
    01549       w->window_class == WC_TOOLTIPS ||
    01550       w->window_class == WC_DROPDOWN_MENU) {
    01551     return true;
    01552   }
    01553
    01554   wz = FindWindowZPosition(w);
    01555   for (uz = wz; ++uz != _last_z_window;) {
    01556     Window *u = *uz;
    01557
    01558     /* A modal child will prevent the activation of the parent window */
    01559     if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
    01560       u->flags4 |= WF_WHITE_BORDER_MASK;
    01561       SetWindowDirty(u);
    01562       return false;
    01563     }
    01564
    01565     if (u->window_class == WC_MAIN_WINDOW ||
    01566         IsVitalWindow(u) ||
    01567         u->window_class == WC_TOOLTIPS ||
    01568         u->window_class == WC_DROPDOWN_MENU) {
    01569       continue;
    01570     }
    01571
    01572     /* Window sizes don't interfere, leave z-order alone */
    01573     if (w->left + w->width <= u->left ||
    01574         u->left + u->width <= w->left ||
    01575         w->top  + w->height <= u->top ||
    01576         u->top + u->height <= w->top) {
    01577       continue;
    01578     }
    01579
    01580     bring_to_front = true;
    01581   }
    01582
    01583   if (bring_to_front) BringWindowToFront(w);
    01584   return true;
    01585 }
    01586
    01593 static void SendWindowMessageW(Window *w, uint msg, uint wparam, uint lparam)
    01594 {
    01595   WindowEvent e;
    01596
    01597   e.event             = WE_MESSAGE;
    01598   e.we.message.msg    = msg;
    01599   e.we.message.wparam = wparam;
    01600   e.we.message.lparam = lparam;
    01601
    01602   w->wndproc(w, &e);
    01603 }
    01604
    01612 void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, int msg, int wparam, int lparam)
    01613 {
    01614   Window *w = FindWindowById(wnd_class, wnd_num);
    01615   if (w != NULL) SendWindowMessageW(w, msg, wparam, lparam);
    01616 }
    01617
    01625 void SendWindowMessageClass(WindowClass wnd_class, int msg, int wparam, int lparam)
    01626 {
    01627   Window* const *wz;
    01628
    01629   FOR_ALL_WINDOWS(wz) {
    01630     if ((*wz)->window_class == wnd_class) SendWindowMessageW(*wz, msg, wparam, lparam);
    01631   }
    01632 }
    01633
    01637 void HandleKeypress(uint32 key)
    01638 {
    01639   Window* const *wz;
    01640   WindowEvent e;
    01641   /* Stores if a window with a textfield for typing is open
    01642    * If this is the case, keypress events are only passed to windows with text fields and
    01643    * to thein this main toolbar. */
    01644   bool query_open = false;
    01645
    01646   /*
    01647   * During the generation of the world, there might be
    01648   * another thread that is currently building for example
    01649   * a road. To not interfere with those tasks, we should
    01650   * NOT change the _current_player here.
    01651   *
    01652   * This is not necessary either, as the only events that
    01653   * can be handled are the 'close application' events
    01654   */
    01655   if (!IsGeneratingWorld()) _current_player = _local_player;
    01656
    01657   /* Setup event */
    01658   e.event = WE_KEYPRESS;
    01659   e.we.keypress.key     = GB(key,  0, 16);
    01660   e.we.keypress.keycode = GB(key, 16, 16);
    01661   e.we.keypress.cont = true;
    01662
    01663   /*
    01664    * The Unicode standard defines an area called the private use area. Code points in this
    01665    * area are reserved for private use and thus not portable between systems. For instance,
    01666    * Apple defines code points for the arrow keys in this area, but these are only printable
    01667    * on a system running OS X. We don't want these keys to show up in text fields and such,
    01668    * and thus we have to clear the unicode character when we encounter such a key.
    01669    */
    01670   if (e.we.keypress.key >= 0xE000 && e.we.keypress.key <= 0xF8FF) e.we.keypress.key = 0;
    01671
    01672   /*
    01673    * If both key and keycode is zero, we don't bother to process the event.
    01674    */
    01675   if (e.we.keypress.key == 0 && e.we.keypress.keycode == 0) return;
    01676
    01677   /* check if we have a query string window open before allowing hotkeys */
    01678   if (FindWindowById(WC_QUERY_STRING,            0) != NULL ||
    01679       FindWindowById(WC_SEND_NETWORK_MSG,        0) != NULL ||
    01680       FindWindowById(WC_GENERATE_LANDSCAPE,      0) != NULL ||
    01681       FindWindowById(WC_CONSOLE,                 0) != NULL ||
    01682       FindWindowById(WC_SAVELOAD,                0) != NULL ||
    01683       FindWindowById(WC_COMPANY_PASSWORD_WINDOW, 0) != NULL) {
    01684     query_open = true;
    01685   }
    01686
    01687   /* Call the event, start with the uppermost window. */
    01688   for (wz = _last_z_window; wz != _z_windows;) {
    01689     Window *w = *--wz;
    01690
    01691     /* if a query window is open, only call the event for certain window types */
    01692     if (query_open &&
    01693         w->window_class != WC_QUERY_STRING &&
    01694         w->window_class != WC_SEND_NETWORK_MSG &&
    01695         w->window_class != WC_GENERATE_LANDSCAPE &&
    01696         w->window_class != WC_CONSOLE &&
    01697         w->window_class != WC_SAVELOAD &&
    01698         w->window_class != WC_COMPANY_PASSWORD_WINDOW) {
    01699       continue;
    01700     }
    01701     w->wndproc(w, &e);
    01702     if (!e.we.keypress.cont) break;
    01703   }
    01704
    01705   if (e.we.keypress.cont) {
    01706     Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
    01707     /* When there is no toolbar w is null, check for that */
    01708     if (w != NULL) w->wndproc(w, &e);
    01709   }
    01710 }
    01711
    01712 void HandleCtrlChanged()
    01713 {
    01714   WindowEvent e;
    01715
    01716   e.event = WE_CTRL_CHANGED;
    01717   e.we.ctrl.cont = true;
    01718
    01719   /* Call the event, start with the uppermost window. */
    01720   for (Window* const *wz = _last_z_window; wz != _z_windows;) {
    01721     Window *w = *--wz;
    01722     w->wndproc(w, &e);
    01723     if (!e.we.ctrl.cont) break;
    01724   }
    01725 }
    01726
    01727 extern void UpdateTileSelection();
    01728 extern bool VpHandlePlaceSizingDrag();
    01729
    01730 static int _input_events_this_tick = 0;
    01731
    01732 static void HandleAutoscroll()
    01733 {
    01734   Window *w;
    01735   ViewPort *vp;
    01736   int x = _cursor.pos.x;
    01737   int y = _cursor.pos.y;
    01738
    01739   if (_input_events_this_tick != 0) {
    01740     /* HandleAutoscroll is called only once per GameLoop() - so we can clear the counter here */
    01741     _input_events_this_tick = 0;
    01742     /* there were some inputs this tick, don't scroll ??? */
    01743     return;
    01744   }
    01745
    01746   if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
    01747     w = FindWindowFromPt(x, y);
    01748     if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
    01749     vp = IsPtInWindowViewport(w, x, y);
    01750     if (vp != NULL) {
    01751       x -= vp->left;
    01752       y -= vp->top;
    01753       /* here allows scrolling in both x and y axis */
    01754 #define scrollspeed 3
    01755       if (x - 15 < 0) {
    01756         WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
    01757       } else if (15 - (vp->width - x) > 0) {
    01758         WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
    01759       }
    01760       if (y - 15 < 0) {
    01761         WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
    01762       } else if (15 - (vp->height - y) > 0) {
    01763         WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
    01764       }
    01765 #undef scrollspeed
    01766     }
    01767   }
    01768 }
    01769
    01770 enum MouseClick {
    01771   MC_NONE = 0,
    01772   MC_LEFT,
    01773   MC_RIGHT,
    01774   MC_DOUBLE_LEFT,
    01775
    01776   MAX_OFFSET_DOUBLE_CLICK = 5,
    01777   TIME_BETWEEN_DOUBLE_CLICK = 500,
    01778 };
    01779
    01780 void MouseLoop(MouseClick click, int mousewheel)
    01781 {
    01782   int x,y;
    01783   Window *w;
    01784   ViewPort *vp;
    01785   bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
    01786
    01787   DecreaseWindowCounters();
    01788   HandlePlacePresize();
    01789   UpdateTileSelection();
    01790   if (!VpHandlePlaceSizingDrag())  return;
    01791   if (!HandleDragDrop())           return;
    01792   if (!HandlePopupMenu())          return;
    01793   if (!HandleWindowDragging())     return;
    01794   if (!HandleScrollbarScrolling()) return;
    01795   if (!HandleViewportScroll())     return;
    01796   if (!HandleMouseOver())          return;
    01797
    01798   x = _cursor.pos.x;
    01799   y = _cursor.pos.y;
    01800
    01801   if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
    01802
    01803   w = FindWindowFromPt(x, y);
    01804   if (w == NULL) return;
    01805   if (!MaybeBringWindowToFront(w)) return;
    01806   vp = IsPtInWindowViewport(w, x, y);
    01807
    01808   /* Don't allow any action in a viewport if either in menu of in generating world */
    01809   if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
    01810
    01811   if (mousewheel != 0) {
    01812     if (_patches.scrollwheel_scrolling == 0) {
    01813       /* Scrollwheel is in zoom mode. Make the zoom event. */
    01814       WindowEvent e;
    01815
    01816       /* Send WE_MOUSEWHEEL event to window */
    01817       e.event = WE_MOUSEWHEEL;
    01818       e.we.wheel.wheel = mousewheel;
    01819       w->wndproc(w, &e);
    01820     }
    01821
    01822     /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
    01823     if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel);
    01824   }
    01825
    01826   if (vp != NULL) {
    01827     if (scrollwheel_scrolling) click = MC_RIGHT; // we are using the scrollwheel in a viewport, so we emulate right mouse button
    01828     switch (click) {
    01829       case MC_DOUBLE_LEFT:
    01830       case MC_LEFT:
    01831         DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
    01832         if (_thd.place_mode != VHM_NONE &&
    01833             /* query button and place sign button work in pause mode */
    01834             _cursor.sprite != SPR_CURSOR_QUERY &&
    01835             _cursor.sprite != SPR_CURSOR_SIGN &&
    01836             _pause_game != 0 &&
    01837             !_cheats.build_in_pause.value) {
    01838           return;
    01839         }
    01840
    01841         if (_thd.place_mode == VHM_NONE) {
    01842           HandleViewportClicked(vp, x, y);
    01843         } else {
    01844           PlaceObject();
    01845         }
    01846         break;
    01847
    01848       case MC_RIGHT:
    01849         if (!(w->flags4 & WF_DISABLE_VP_SCROLL)) {
    01850           _scrolling_viewport = true;
    01851           _cursor.fix_at = true;
    01852         }
    01853         break;
    01854
    01855       default:
    01856         break;
    01857     }
    01858   } else {
    01859     switch (click) {
    01860       case MC_DOUBLE_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, true);
    01861         /* fallthough, and also give a single-click for backwards compatible */
    01862       case MC_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, false); break;
    01863       default:
    01864         if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
    01865         /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
    01866         * Simulate a right button click so we can get started. */
    01867         /* fallthough */
    01868       case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
    01869     }
    01870   }
    01871 }
    01872
    01873 void HandleMouseEvents()
    01874 {
    01875   static int double_click_time = 0;
    01876   static int double_click_x = 0;
    01877   static int double_click_y = 0;
    01878   MouseClick click;
    01879   int mousewheel;
    01880
    01881   /*
    01882    * During the generation of the world, there might be
    01883    * another thread that is currently building for example
    01884    * a road. To not interfere with those tasks, we should
    01885    * NOT change the _current_player here.
    01886    *
    01887    * This is not necessary either, as the only events that
    01888    * can be handled are the 'close application' events
    01889    */
    01890   if (!IsGeneratingWorld()) _current_player = _local_player;
    01891
    01892   /* Mouse event? */
    01893   click = MC_NONE;
    01894   if (_left_button_down && !_left_button_clicked) {
    01895     click = MC_LEFT;
    01896     if (double_click_time != 0 && _realtime_tick - double_click_time   < TIME_BETWEEN_DOUBLE_CLICK &&
    01897         double_click_x != 0    && abs(_cursor.pos.x - double_click_x) < MAX_OFFSET_DOUBLE_CLICK  &&
    01898         double_click_y != 0    && abs(_cursor.pos.y - double_click_y) < MAX_OFFSET_DOUBLE_CLICK) {
    01899       click = MC_DOUBLE_LEFT;
    01900     }
    01901     double_click_time = _realtime_tick;
    01902     double_click_x = _cursor.pos.x;
    01903     double_click_y = _cursor.pos.y;
    01904     _left_button_clicked = true;
    01905     _input_events_this_tick++;
    01906   } else if (_right_button_clicked) {
    01907     _right_button_clicked = false;
    01908     click = MC_RIGHT;
    01909     _input_events_this_tick++;
    01910   }
    01911
    01912   mousewheel = 0;
    01913   if (_cursor.wheel) {
    01914     mousewheel = _cursor.wheel;
    01915     _cursor.wheel = 0;
    01916     _input_events_this_tick++;
    01917   }
    01918
    01919   MouseLoop(click, mousewheel);
    01920 }
    01921
    01922 void InputLoop()
    01923 {
    01924   HandleMouseEvents();
    01925   HandleAutoscroll();
    01926 }
    01927
    01928 void UpdateWindows()
    01929 {
    01930   Window* const *wz;
    01931   static int we4_timer = 0;
    01932   int t = we4_timer + 1;
    01933
    01934   if (t >= 100) {
    01935     for (wz = _last_z_window; wz != _z_windows;) {
    01936       CallWindowEventNP(*--wz, WE_4);
    01937     }
    01938     t = 0;
    01939   }
    01940   we4_timer = t;
    01941
    01942   for (wz = _last_z_window; wz != _z_windows;) {
    01943     Window *w = *--wz;
    01944     if (w->flags4 & WF_WHITE_BORDER_MASK) {
    01945       w->flags4 -= WF_WHITE_BORDER_ONE;
    01946
    01947       if (!(w->flags4 & WF_WHITE_BORDER_MASK)) SetWindowDirty(w);
    01948     }
    01949   }
    01950
    01951   DrawDirtyBlocks();
    01952
    01953   FOR_ALL_WINDOWS(wz) {
    01954     if ((*wz)->viewport != NULL) UpdateViewportPosition(*wz);
    01955   }
    01956   DrawChatMessage();
    01957   /* Redraw mouse cursor in case it was hidden */
    01958   DrawMouseCursor();
    01959 }
    01960
    01961
    01962 int GetMenuItemIndex(const Window *w, int x, int y)
    01963 {
    01964   if ((x -= w->left) >= 0 && x < w->width && (y -= w->top + 1) >= 0) {
    01965     y /= 10;
    01966
    01967     if (y < WP(w, const menu_d).item_count &&
    01968         !HasBit(WP(w, const menu_d).disabled_items, y)) {
    01969       return y;
    01970     }
    01971   }
    01972   return -1;
    01973 }
    01974
    01975 void InvalidateWindow(WindowClass cls, WindowNumber number)
    01976 {
    01977   Window* const *wz;
    01978
    01979   FOR_ALL_WINDOWS(wz) {
    01980     const Window *w = *wz;
    01981     if (w->window_class == cls && w->window_number == number) SetWindowDirty(w);
    01982   }
    01983 }
    01984
    01985 void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index)
    01986 {
    01987   Window* const *wz;
    01988
    01989   FOR_ALL_WINDOWS(wz) {
    01990     const Window *w = *wz;
    01991     if (w->window_class == cls && w->window_number == number) {
    01992       w->InvalidateWidget(widget_index);
    01993     }
    01994   }
    01995 }
    01996
    01997 void InvalidateWindowClasses(WindowClass cls)
    01998 {
    01999   Window* const *wz;
    02000
    02001   FOR_ALL_WINDOWS(wz) {
    02002     if ((*wz)->window_class == cls) SetWindowDirty(*wz);
    02003   }
    02004 }
    02005
    02006 void InvalidateThisWindowData(Window *w)
    02007 {
    02008   CallWindowEventNP(w, WE_INVALIDATE_DATA);
    02009   SetWindowDirty(w);
    02010 }
    02011
    02012 void InvalidateWindowData(WindowClass cls, WindowNumber number)
    02013 {
    02014   Window* const *wz;
    02015
    02016   FOR_ALL_WINDOWS(wz) {
    02017     Window *w = *wz;
    02018     if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w);
    02019   }
    02020 }
    02021
    02022 void InvalidateWindowClassesData(WindowClass cls)
    02023 {
    02024   Window* const *wz;
    02025
    02026   FOR_ALL_WINDOWS(wz) {
    02027     if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz);
    02028   }
    02029 }
    02030
    02031 void CallWindowTickEvent()
    02032 {
    02033   Window* const *wz;
    02034
    02035   for (wz = _last_z_window; wz != _z_windows;) {
    02036     CallWindowEventNP(*--wz, WE_TICK);
    02037   }
    02038 }
    02039
    02040 void DeleteNonVitalWindows()
    02041 {
    02042   Window* const *wz;
    02043
    02044 restart_search:
    02045   /* When we find the window to delete, we need to restart the search
    02046    * as deleting this window could cascade in deleting (many) others
    02047    * anywhere in the z-array */
    02048   FOR_ALL_WINDOWS(wz) {
    02049     Window *w = *wz;
    02050     if (w->window_class != WC_MAIN_WINDOW &&
    02051         w->window_class != WC_SELECT_GAME &&
    02052         w->window_class != WC_MAIN_TOOLBAR &&
    02053         w->window_class != WC_STATUS_BAR &&
    02054         w->window_class != WC_TOOLBAR_MENU &&
    02055         w->window_class != WC_TOOLTIPS &&
    02056         (w->flags4 & WF_STICKY) == 0) { // do not delete windows which are 'pinned'
    02057
    02058       DeleteWindow(w);
    02059       goto restart_search;
    02060     }
    02061   }
    02062 }
    02063
    02069 void DeleteAllNonVitalWindows()
    02070 {
    02071   Window* const *wz;
    02072
    02073   /* Delete every window except for stickied ones, then sticky ones as well */
    02074   DeleteNonVitalWindows();
    02075
    02076 restart_search:
    02077   /* When we find the window to delete, we need to restart the search
    02078    * as deleting this window could cascade in deleting (many) others
    02079    * anywhere in the z-array */
    02080   FOR_ALL_WINDOWS(wz) {
    02081     if ((*wz)->flags4 & WF_STICKY) {
    02082       DeleteWindow(*wz);
    02083       goto restart_search;
    02084     }
    02085   }
    02086 }
    02087
    02089 void HideVitalWindows()
    02090 {
    02091   DeleteWindowById(WC_TOOLBAR_MENU, 0);
    02092   DeleteWindowById(WC_MAIN_TOOLBAR, 0);
    02093   DeleteWindowById(WC_STATUS_BAR, 0);
    02094 }
    02095
    02096 int PositionMainToolbar(Window *w)
    02097 {
    02098   DEBUG(misc, 5, "Repositioning Main Toolbar...");
    02099
    02100   if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
    02101     w = FindWindowById(WC_MAIN_TOOLBAR, 0);
    02102   }
    02103
    02104   switch (_patches.toolbar_pos) {
    02105     case 1:  w->left = (_screen.width - w->width) / 2; break;
    02106     case 2:  w->left = _screen.width - w->width; break;
    02107     default: w->left = 0;
    02108   }
    02109   SetDirtyBlocks(0, 0, _screen.width, w->height); // invalidate the whole top part
    02110   return w->left;
    02111 }
    02112
    02113 void RelocateAllWindows(int neww, int newh)
    02114 {
    02115   Window* const *wz;
    02116
    02117   FOR_ALL_WINDOWS(wz) {
    02118     Window *w = *wz;
    02119     int left, top;
    02120
    02121     if (w->window_class == WC_MAIN_WINDOW) {
    02122       ViewPort *vp = w->viewport;
    02123       vp->width = w->width = neww;
    02124       vp->height = w->height = newh;
    02125       vp->virtual_width = ScaleByZoom(neww, vp->zoom);
    02126       vp->virtual_height = ScaleByZoom(newh, vp->zoom);
    02127       continue; // don't modify top,left
    02128     }
    02129
    02130     /* XXX - this probably needs something more sane. For example specying
    02131      * in a 'backup'-desc that the window should always be centred. */
    02132     switch (w->window_class) {
    02133       case WC_MAIN_TOOLBAR:
    02134         if (neww - w->width != 0) {
    02135           ResizeWindow(w, min(neww, 640) - w->width, 0);
    02136
    02137           WindowEvent e;
    02138           e.event = WE_RESIZE;
    02139           e.we.sizing.size.x = w->width;
    02140           e.we.sizing.size.y = w->height;
    02141           e.we.sizing.diff.x = neww - w->width;
    02142           e.we.sizing.diff.y = 0;
    02143           w->wndproc(w, &e);
    02144         }
    02145
    02146         top = w->top;
    02147         left = PositionMainToolbar(w); // changes toolbar orientation
    02148         break;
    02149
    02150       case WC_SELECT_GAME:
    02151       case WC_GAME_OPTIONS:
    02152       case WC_NETWORK_WINDOW:
    02153         top = (newh - w->height) >> 1;
    02154         left = (neww - w->width) >> 1;
    02155         break;
    02156
    02157       case WC_NEWS_WINDOW:
    02158         top = newh - w->height;
    02159         left = (neww - w->width) >> 1;
    02160         break;
    02161
    02162       case WC_STATUS_BAR:
    02163         ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
    02164         top = newh - w->height;
    02165         left = (neww - w->width) >> 1;
    02166         break;
    02167
    02168       case WC_SEND_NETWORK_MSG:
    02169         ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
    02170         top = (newh - 26); // 26 = height of status bar + height of chat bar
    02171         left = (neww - w->width) >> 1;
    02172         break;
    02173
    02174       case WC_CONSOLE:
    02175         IConsoleResize(w);
    02176         continue;
    02177
    02178       default: {
    02179         left = w->left;
    02180         if (left + (w->width >> 1) >= neww) left = neww - w->width;
    02181         if (left < 0) left = 0;
    02182
    02183         top = w->top;
    02184         if (top + (w->height >> 1) >= newh) top = newh - w->height;
    02185
    02186         const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
    02187         if (wt != NULL) {
    02188           if (top < wt->height && wt->left < (w->left + w->width) && (wt->left + wt->width) > w->left) top = wt->height;
    02189           if (top >= newh) top = newh - 1;
    02190         } else {
    02191           if (top < 0) top = 0;
    02192         }
    02193       } break;
    02194     }
    02195
    02196     if (w->viewport != NULL) {
    02197       w->viewport->left += left - w->left;
    02198       w->viewport->top += top - w->top;
    02199     }
    02200
    02201     w->left = left;
    02202     w->top = top;
    02203   }
    02204 }