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 }

Generated on Mon Sep 22 20:34:20 2008 for openttd by  doxygen 1.5.6