00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include <stdarg.h>
00014 #include "company_func.h"
00015 #include "gfx_func.h"
00016 #include "console_func.h"
00017 #include "console_gui.h"
00018 #include "viewport_func.h"
00019 #include "genworld.h"
00020 #include "progress.h"
00021 #include "blitter/factory.hpp"
00022 #include "zoom_func.h"
00023 #include "vehicle_base.h"
00024 #include "window_func.h"
00025 #include "tilehighlight_func.h"
00026 #include "network/network.h"
00027 #include "querystring_gui.h"
00028 #include "widgets/dropdown_func.h"
00029 #include "strings_func.h"
00030 #include "settings_type.h"
00031 #include "newgrf_debug.h"
00032 #include "hotkeys.h"
00033 #include "toolbar_gui.h"
00034 #include "statusbar_gui.h"
00035
00036
00037 static Point _drag_delta;
00038 static Window *_mouseover_last_w = NULL;
00039 static Window *_last_scroll_window = NULL;
00040
00042 Window *_z_front_window = NULL;
00044 Window *_z_back_window = NULL;
00045
00046
00047
00048
00049
00050
00051 Window *_focused_window;
00052
00053 Point _cursorpos_drag_start;
00054
00055 int _scrollbar_start_pos;
00056 int _scrollbar_size;
00057 byte _scroller_click_timeout = 0;
00058
00059 bool _scrolling_viewport;
00060 bool _mouse_hovering;
00061
00062 SpecialMouseMode _special_mouse_mode;
00063
00065 WindowDesc::WindowDesc(WindowPosition def_pos, int16 def_width, int16 def_height,
00066 WindowClass window_class, WindowClass parent_class, uint32 flags,
00067 const NWidgetPart *nwid_parts, int16 nwid_length) :
00068 default_pos(def_pos),
00069 default_width(def_width),
00070 default_height(def_height),
00071 cls(window_class),
00072 parent_cls(parent_class),
00073 flags(flags),
00074 nwid_parts(nwid_parts),
00075 nwid_length(nwid_length)
00076 {
00077 }
00078
00079 WindowDesc::~WindowDesc()
00080 {
00081 }
00082
00092 int Window::GetRowFromWidget(int clickpos, int widget, int padding, int line_height) const
00093 {
00094 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
00095 if (line_height < 0) line_height = wid->resize_y;
00096 if (clickpos < (int)wid->pos_y + padding) return INT_MAX;
00097 return (clickpos - (int)wid->pos_y - padding) / line_height;
00098 }
00099
00105 const Scrollbar *Window::GetScrollbar(uint widnum) const
00106 {
00107 return this->GetWidget<NWidgetScrollbar>(widnum);
00108 }
00109
00115 Scrollbar *Window::GetScrollbar(uint widnum)
00116 {
00117 return this->GetWidget<NWidgetScrollbar>(widnum);
00118 }
00119
00120
00125 void SetFocusedWindow(Window *w)
00126 {
00127 if (_focused_window == w) return;
00128
00129
00130 if (_focused_window != NULL) {
00131 if (_focused_window->nested_focus != NULL) _focused_window->nested_focus->SetDirty(_focused_window);
00132 }
00133
00134
00135 Window *old_focused = _focused_window;
00136 _focused_window = w;
00137
00138
00139 if (old_focused != NULL) old_focused->OnFocusLost();
00140 if (_focused_window != NULL) _focused_window->OnFocus();
00141 }
00142
00148 static bool EditBoxInGlobalFocus()
00149 {
00150 if (_focused_window == NULL) return false;
00151
00152
00153 if (_focused_window->window_class == WC_CONSOLE) return true;
00154
00155 return _focused_window->nested_focus != NULL && _focused_window->nested_focus->type == WWT_EDITBOX;
00156 }
00157
00161 void Window::UnfocusFocusedWidget()
00162 {
00163 if (this->nested_focus != NULL) {
00164
00165 this->nested_focus->SetDirty(this);
00166 this->nested_focus = NULL;
00167 }
00168 }
00169
00175 bool Window::SetFocusedWidget(byte widget_index)
00176 {
00177
00178 if (widget_index >= this->nested_array_size) return false;
00179
00180 assert(this->nested_array[widget_index] != NULL);
00181 if (this->nested_focus != NULL) {
00182 if (this->GetWidget<NWidgetCore>(widget_index) == this->nested_focus) return false;
00183
00184
00185 this->nested_focus->SetDirty(this);
00186 }
00187 this->nested_focus = this->GetWidget<NWidgetCore>(widget_index);
00188 return true;
00189 }
00190
00198 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
00199 {
00200 va_list wdg_list;
00201
00202 va_start(wdg_list, widgets);
00203
00204 while (widgets != WIDGET_LIST_END) {
00205 SetWidgetDisabledState(widgets, disab_stat);
00206 widgets = va_arg(wdg_list, int);
00207 }
00208
00209 va_end(wdg_list);
00210 }
00211
00217 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
00218 {
00219 va_list wdg_list;
00220
00221 va_start(wdg_list, widgets);
00222
00223 while (widgets != WIDGET_LIST_END) {
00224 SetWidgetLoweredState(widgets, lowered_stat);
00225 widgets = va_arg(wdg_list, int);
00226 }
00227
00228 va_end(wdg_list);
00229 }
00230
00235 void Window::RaiseButtons(bool autoraise)
00236 {
00237 for (uint i = 0; i < this->nested_array_size; i++) {
00238 if (this->nested_array[i] != NULL && (this->nested_array[i]->type & ~WWB_PUSHBUTTON) < WWT_LAST &&
00239 (!autoraise || (this->nested_array[i]->type & WWB_PUSHBUTTON)) && this->IsWidgetLowered(i)) {
00240 this->RaiseWidget(i);
00241 this->SetWidgetDirty(i);
00242 }
00243 }
00244 }
00245
00250 void Window::SetWidgetDirty(byte widget_index) const
00251 {
00252
00253 if (this->nested_array == NULL) return;
00254
00255 this->nested_array[widget_index]->SetDirty(this);
00256 }
00257
00263 void Window::HandleButtonClick(byte widget)
00264 {
00265 this->LowerWidget(widget);
00266 this->flags4 |= WF_TIMEOUT_BEGIN;
00267 this->SetWidgetDirty(widget);
00268 }
00269
00270 static void StartWindowDrag(Window *w);
00271 static void StartWindowSizing(Window *w, bool to_left);
00272
00280 static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
00281 {
00282 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
00283 WidgetType widget_type = (nw != NULL) ? nw->type : WWT_EMPTY;
00284
00285 bool focused_widget_changed = false;
00286
00287 if (_focused_window != w &&
00288 (w->desc_flags & WDF_NO_FOCUS) == 0 &&
00289 widget_type != WWT_CLOSEBOX) {
00290 focused_widget_changed = true;
00291 if (_focused_window != NULL) {
00292 _focused_window->OnFocusLost();
00293
00294
00295 if (w->window_class != WC_OSK) DeleteWindowById(WC_OSK, 0);
00296 }
00297 SetFocusedWindow(w);
00298 w->OnFocus();
00299 }
00300
00301 if (nw == NULL) return;
00302
00303
00304 if (nw->IsDisabled()) return;
00305
00306 int widget_index = nw->index;
00307
00308
00309
00310 if (widget_type != WWT_CAPTION) {
00311
00312 if (w->nested_focus != NULL && w->nested_focus->type == WWT_EDITBOX && w->nested_focus != nw && w->window_class != WC_OSK) {
00313 DeleteWindowById(WC_OSK, 0);
00314 }
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325 focused_widget_changed |= w->SetFocusedWidget(widget_index);
00326 }
00327
00328
00329
00330 if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
00331
00332 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
00333
00334 switch (widget_type) {
00335 case NWID_VSCROLLBAR:
00336 case NWID_HSCROLLBAR:
00337 ScrollbarClickHandler(w, nw, x, y);
00338 break;
00339
00340 case WWT_EDITBOX:
00341 if (!focused_widget_changed) {
00342
00343 QueryStringBaseWindow *qs = dynamic_cast<QueryStringBaseWindow *>(w);
00344 if (qs != NULL) {
00345 qs->OnOpenOSKWindow(widget_index);
00346 }
00347 }
00348 break;
00349
00350 case WWT_CLOSEBOX:
00351 delete w;
00352 return;
00353
00354 case WWT_CAPTION:
00355 StartWindowDrag(w);
00356 return;
00357
00358 case WWT_RESIZEBOX:
00359
00360
00361 StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
00362 nw->SetDirty(w);
00363 return;
00364
00365 case WWT_DEBUGBOX:
00366 w->ShowNewGRFInspectWindow();
00367 break;
00368
00369 case WWT_SHADEBOX:
00370 nw->SetDirty(w);
00371 w->SetShaded(!w->IsShaded());
00372 return;
00373
00374 case WWT_STICKYBOX:
00375 w->flags4 ^= WF_STICKY;
00376 nw->SetDirty(w);
00377 return;
00378
00379 default:
00380 break;
00381 }
00382
00383
00384 if (widget_index < 0) return;
00385
00386 Point pt = { x, y };
00387 w->OnClick(pt, widget_index, click_count);
00388 }
00389
00396 static void DispatchRightClickEvent(Window *w, int x, int y)
00397 {
00398 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00399 if (wid == NULL) return;
00400
00401
00402 if (wid->index >= 0) {
00403 Point pt = { x, y };
00404 if (w->OnRightClick(pt, wid->index)) return;
00405 }
00406
00407 if (_settings_client.gui.hover_delay == 0 && wid->tool_tip != 0) GuiShowTooltips(w, wid->tool_tip, 0, NULL, TCC_RIGHT_CLICK);
00408 }
00409
00416 static void DispatchHoverEvent(Window *w, int x, int y)
00417 {
00418 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00419
00420
00421 if (wid == NULL) return;
00422
00423
00424 if (wid->tool_tip != 0) {
00425 GuiShowTooltips(w, wid->tool_tip);
00426 return;
00427 }
00428
00429
00430 if (wid->index < 0) return;
00431
00432 Point pt = { x, y };
00433 w->OnHover(pt, wid->index);
00434 }
00435
00443 static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
00444 {
00445 if (nwid == NULL) return;
00446
00447
00448 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
00449 w->SetShaded(wheel < 0);
00450 return;
00451 }
00452
00453
00454 if (nwid->type == NWID_VSCROLLBAR) {
00455 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
00456 if (sb->GetCount() > sb->GetCapacity()) {
00457 sb->UpdatePosition(wheel);
00458 w->SetDirty();
00459 }
00460 return;
00461 }
00462
00463
00464 Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : NULL);
00465 if (sb != NULL && sb->GetCount() > sb->GetCapacity()) {
00466 sb->UpdatePosition(wheel);
00467 w->SetDirty();
00468 }
00469 }
00470
00476 static bool MayBeShown(const Window *w)
00477 {
00478
00479 if (!HasModalProgress()) return true;
00480
00481 switch (w->window_class) {
00482 case WC_MAIN_WINDOW:
00483 case WC_MODAL_PROGRESS:
00484 case WC_QUERY_STRING:
00485 return true;
00486
00487 default:
00488 return false;
00489 }
00490 }
00491
00504 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00505 {
00506 const Window *v;
00507 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00508 if (MayBeShown(v) &&
00509 right > v->left &&
00510 bottom > v->top &&
00511 left < v->left + v->width &&
00512 top < v->top + v->height) {
00513
00514 int x;
00515
00516 if (left < (x = v->left)) {
00517 DrawOverlappedWindow(w, left, top, x, bottom);
00518 DrawOverlappedWindow(w, x, top, right, bottom);
00519 return;
00520 }
00521
00522 if (right > (x = v->left + v->width)) {
00523 DrawOverlappedWindow(w, left, top, x, bottom);
00524 DrawOverlappedWindow(w, x, top, right, bottom);
00525 return;
00526 }
00527
00528 if (top < (x = v->top)) {
00529 DrawOverlappedWindow(w, left, top, right, x);
00530 DrawOverlappedWindow(w, left, x, right, bottom);
00531 return;
00532 }
00533
00534 if (bottom > (x = v->top + v->height)) {
00535 DrawOverlappedWindow(w, left, top, right, x);
00536 DrawOverlappedWindow(w, left, x, right, bottom);
00537 return;
00538 }
00539
00540 return;
00541 }
00542 }
00543
00544
00545 DrawPixelInfo *dp = _cur_dpi;
00546 dp->width = right - left;
00547 dp->height = bottom - top;
00548 dp->left = left - w->left;
00549 dp->top = top - w->top;
00550 dp->pitch = _screen.pitch;
00551 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00552 dp->zoom = ZOOM_LVL_NORMAL;
00553 w->OnPaint();
00554 }
00555
00564 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00565 {
00566 Window *w;
00567 DrawPixelInfo bk;
00568 _cur_dpi = &bk;
00569
00570 FOR_ALL_WINDOWS_FROM_BACK(w) {
00571 if (MayBeShown(w) &&
00572 right > w->left &&
00573 bottom > w->top &&
00574 left < w->left + w->width &&
00575 top < w->top + w->height) {
00576
00577 DrawOverlappedWindow(w, left, top, right, bottom);
00578 }
00579 }
00580 }
00581
00586 void Window::SetDirty() const
00587 {
00588 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00589 }
00590
00597 void Window::ReInit(int rx, int ry)
00598 {
00599 this->SetDirty();
00600
00601
00602 int window_width = this->width;
00603 int window_height = this->height;
00604
00605 this->OnInit();
00606
00607 this->nested_root->SetupSmallestSize(this, false);
00608 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
00609 this->width = this->nested_root->smallest_x;
00610 this->height = this->nested_root->smallest_y;
00611 this->resize.step_width = this->nested_root->resize_x;
00612 this->resize.step_height = this->nested_root->resize_y;
00613
00614
00615 window_width = max(window_width + rx, this->width);
00616 window_height = max(window_height + ry, this->height);
00617 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
00618 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
00619
00620
00621 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
00622 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
00623
00624 ResizeWindow(this, dx, dy);
00625
00626 }
00627
00633 void Window::SetShaded(bool make_shaded)
00634 {
00635 if (this->shade_select == NULL) return;
00636
00637 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
00638 if (this->shade_select->shown_plane != desired) {
00639 if (make_shaded) {
00640 this->unshaded_size.width = this->width;
00641 this->unshaded_size.height = this->height;
00642 this->shade_select->SetDisplayedPlane(desired);
00643 this->ReInit(0, -this->height);
00644 } else {
00645 this->shade_select->SetDisplayedPlane(desired);
00646 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
00647 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
00648 this->ReInit(dx, dy);
00649 }
00650 }
00651 }
00652
00659 static Window *FindChildWindow(const Window *w, WindowClass wc)
00660 {
00661 Window *v;
00662 FOR_ALL_WINDOWS_FROM_BACK(v) {
00663 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
00664 }
00665
00666 return NULL;
00667 }
00668
00673 void Window::DeleteChildWindows(WindowClass wc) const
00674 {
00675 Window *child = FindChildWindow(this, wc);
00676 while (child != NULL) {
00677 delete child;
00678 child = FindChildWindow(this, wc);
00679 }
00680 }
00681
00685 Window::~Window()
00686 {
00687 if (_thd.window_class == this->window_class &&
00688 _thd.window_number == this->window_number) {
00689 ResetObjectToPlace();
00690 }
00691
00692
00693 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00694
00695
00696 if (_last_scroll_window == this) _last_scroll_window = NULL;
00697
00698
00699 if (_focused_window == this) _focused_window = NULL;
00700
00701 this->DeleteChildWindows();
00702
00703 if (this->viewport != NULL) DeleteWindowViewport(this);
00704
00705 this->SetDirty();
00706
00707 free(this->nested_array);
00708 delete this->nested_root;
00709
00710 this->window_class = WC_INVALID;
00711 }
00712
00719 Window *FindWindowById(WindowClass cls, WindowNumber number)
00720 {
00721 Window *w;
00722 FOR_ALL_WINDOWS_FROM_BACK(w) {
00723 if (w->window_class == cls && w->window_number == number) return w;
00724 }
00725
00726 return NULL;
00727 }
00728
00735 Window *FindWindowByClass(WindowClass cls)
00736 {
00737 Window *w;
00738 FOR_ALL_WINDOWS_FROM_BACK(w) {
00739 if (w->window_class == cls) return w;
00740 }
00741
00742 return NULL;
00743 }
00744
00751 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00752 {
00753 Window *w = FindWindowById(cls, number);
00754 if (force || w == NULL ||
00755 (w->flags4 & WF_STICKY) == 0) {
00756 delete w;
00757 }
00758 }
00759
00764 void DeleteWindowByClass(WindowClass cls)
00765 {
00766 Window *w;
00767
00768 restart_search:
00769
00770
00771
00772 FOR_ALL_WINDOWS_FROM_BACK(w) {
00773 if (w->window_class == cls) {
00774 delete w;
00775 goto restart_search;
00776 }
00777 }
00778 }
00779
00786 void DeleteCompanyWindows(CompanyID id)
00787 {
00788 Window *w;
00789
00790 restart_search:
00791
00792
00793
00794 FOR_ALL_WINDOWS_FROM_BACK(w) {
00795 if (w->owner == id) {
00796 delete w;
00797 goto restart_search;
00798 }
00799 }
00800
00801
00802 DeleteWindowById(WC_BUY_COMPANY, id);
00803 }
00804
00812 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00813 {
00814 Window *w;
00815 FOR_ALL_WINDOWS_FROM_BACK(w) {
00816 if (w->owner != old_owner) continue;
00817
00818 switch (w->window_class) {
00819 case WC_COMPANY_COLOUR:
00820 case WC_FINANCES:
00821 case WC_STATION_LIST:
00822 case WC_TRAINS_LIST:
00823 case WC_ROADVEH_LIST:
00824 case WC_SHIPS_LIST:
00825 case WC_AIRCRAFT_LIST:
00826 case WC_BUY_COMPANY:
00827 case WC_COMPANY:
00828 case WC_COMPANY_INFRASTRUCTURE:
00829 continue;
00830
00831 default:
00832 w->owner = new_owner;
00833 break;
00834 }
00835 }
00836 }
00837
00838 static void BringWindowToFront(Window *w);
00839
00847 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00848 {
00849 Window *w = FindWindowById(cls, number);
00850
00851 if (w != NULL) {
00852 if (w->IsShaded()) w->SetShaded(false);
00853
00854 w->flags4 |= WF_WHITE_BORDER_MASK;
00855 BringWindowToFront(w);
00856 w->SetDirty();
00857 }
00858
00859 return w;
00860 }
00861
00862 static inline bool IsVitalWindow(const Window *w)
00863 {
00864 switch (w->window_class) {
00865 case WC_MAIN_TOOLBAR:
00866 case WC_STATUS_BAR:
00867 case WC_NEWS_WINDOW:
00868 case WC_SEND_NETWORK_MSG:
00869 return true;
00870
00871 default:
00872 return false;
00873 }
00874 }
00875
00884 static uint GetWindowZPriority(const Window *w)
00885 {
00886 assert(w->window_class != WC_INVALID);
00887
00888 uint z_priority = 0;
00889
00890 switch (w->window_class) {
00891 case WC_ENDSCREEN:
00892 ++z_priority;
00893
00894 case WC_HIGHSCORE:
00895 ++z_priority;
00896
00897 case WC_TOOLTIPS:
00898 ++z_priority;
00899
00900 case WC_DROPDOWN_MENU:
00901 ++z_priority;
00902
00903 case WC_MAIN_TOOLBAR:
00904 case WC_STATUS_BAR:
00905 ++z_priority;
00906
00907 case WC_QUERY_STRING:
00908 ++z_priority;
00909
00910 case WC_ERRMSG:
00911 case WC_CONFIRM_POPUP_QUERY:
00912 case WC_MODAL_PROGRESS:
00913 case WC_NETWORK_STATUS_WINDOW:
00914 ++z_priority;
00915
00916 case WC_SAVELOAD:
00917 case WC_GAME_OPTIONS:
00918 case WC_CUSTOM_CURRENCY:
00919 case WC_NETWORK_WINDOW:
00920 case WC_GRF_PARAMETERS:
00921 case WC_NEWGRF_TEXTFILE:
00922 case WC_AI_LIST:
00923 case WC_AI_SETTINGS:
00924 ++z_priority;
00925
00926 case WC_CONSOLE:
00927 ++z_priority;
00928
00929 case WC_SEND_NETWORK_MSG:
00930 case WC_NEWS_WINDOW:
00931 ++z_priority;
00932
00933 default:
00934 ++z_priority;
00935
00936 case WC_MAIN_WINDOW:
00937 return z_priority;
00938 }
00939 }
00940
00945 static void AddWindowToZOrdering(Window *w)
00946 {
00947 assert(w->z_front == NULL && w->z_back == NULL);
00948
00949 if (_z_front_window == NULL) {
00950
00951 _z_front_window = _z_back_window = w;
00952 w->z_front = w->z_back = NULL;
00953 } else {
00954
00955 Window *v = _z_front_window;
00956 uint last_z_priority = UINT_MAX;
00957 while (v != NULL && (v->window_class == WC_INVALID || GetWindowZPriority(v) > GetWindowZPriority(w))) {
00958 if (v->window_class != WC_INVALID) {
00959
00960 assert(last_z_priority >= GetWindowZPriority(v));
00961 last_z_priority = GetWindowZPriority(v);
00962 }
00963
00964 v = v->z_back;
00965 }
00966
00967 if (v == NULL) {
00968
00969 w->z_front = _z_back_window;
00970 w->z_back = NULL;
00971 _z_back_window->z_back = w;
00972 _z_back_window = w;
00973 } else if (v == _z_front_window) {
00974
00975 w->z_front = NULL;
00976 w->z_back = _z_front_window;
00977 _z_front_window->z_front = w;
00978 _z_front_window = w;
00979 } else {
00980
00981 w->z_front = v->z_front;
00982 w->z_back = v;
00983 v->z_front->z_back = w;
00984 v->z_front = w;
00985 }
00986 }
00987 }
00988
00989
00994 static void RemoveWindowFromZOrdering(Window *w)
00995 {
00996 if (w->z_front == NULL) {
00997 assert(_z_front_window == w);
00998 _z_front_window = w->z_back;
00999 } else {
01000 w->z_front->z_back = w->z_back;
01001 }
01002
01003 if (w->z_back == NULL) {
01004 assert(_z_back_window == w);
01005 _z_back_window = w->z_front;
01006 } else {
01007 w->z_back->z_front = w->z_front;
01008 }
01009
01010 w->z_front = w->z_back = NULL;
01011 }
01012
01018 static void BringWindowToFront(Window *w)
01019 {
01020 RemoveWindowFromZOrdering(w);
01021 AddWindowToZOrdering(w);
01022
01023 w->SetDirty();
01024 }
01025
01034 void Window::InitializeData(const WindowDesc *desc, WindowNumber window_number)
01035 {
01036
01037 this->window_class = desc->cls;
01038 this->flags4 |= WF_WHITE_BORDER_MASK;
01039 if (desc->default_pos == WDP_CENTER) this->flags4 |= WF_CENTERED;
01040 this->owner = INVALID_OWNER;
01041 this->nested_focus = NULL;
01042 this->window_number = window_number;
01043 this->desc_flags = desc->flags;
01044
01045 this->OnInit();
01046
01047 if (this->nested_array == NULL) {
01048 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01049 this->nested_root->SetupSmallestSize(this, true);
01050 } else {
01051 this->nested_root->SetupSmallestSize(this, false);
01052 }
01053
01054 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
01055
01056
01057
01058 this->resize.step_width = this->nested_root->resize_x;
01059 this->resize.step_height = this->nested_root->resize_y;
01060
01061
01062
01063
01064 if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL)) SetFocusedWindow(this);
01065
01066
01067 AddWindowToZOrdering(this);
01068 }
01069
01077 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
01078 {
01079 this->left = x;
01080 this->top = y;
01081 this->width = sm_width;
01082 this->height = sm_height;
01083 }
01084
01095 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
01096 {
01097 def_width = max(def_width, this->width);
01098 def_height = max(def_height, this->height);
01099
01100
01101
01102
01103
01104 if (this->width != def_width || this->height != def_height) {
01105
01106 int free_height = _screen.height;
01107 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
01108 if (wt != NULL) free_height -= wt->height;
01109 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01110 if (wt != NULL) free_height -= wt->height;
01111
01112 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
01113 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
01114
01115
01116
01117
01118 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
01119 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
01120
01121 ResizeWindow(this, enlarge_x, enlarge_y);
01122
01123 } else {
01124
01125 this->OnResize();
01126 }
01127
01128 int nx = this->left;
01129 int ny = this->top;
01130
01131 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
01132
01133 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01134 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
01135 nx = max(nx, 0);
01136
01137 if (this->viewport != NULL) {
01138 this->viewport->left += nx - this->left;
01139 this->viewport->top += ny - this->top;
01140 }
01141 this->left = nx;
01142 this->top = ny;
01143
01144 this->SetDirty();
01145 }
01146
01158 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
01159 {
01160 int right = width + left;
01161 int bottom = height + top;
01162
01163 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01164 if (left < 0 || (main_toolbar != NULL && top < main_toolbar->height) || right > _screen.width || bottom > _screen.height) return false;
01165
01166
01167 const Window *w;
01168 FOR_ALL_WINDOWS_FROM_BACK(w) {
01169 if (w->window_class == WC_MAIN_WINDOW) continue;
01170
01171 if (right > w->left &&
01172 w->left + w->width > left &&
01173 bottom > w->top &&
01174 w->top + w->height > top) {
01175 return false;
01176 }
01177 }
01178
01179 pos.x = left;
01180 pos.y = top;
01181 return true;
01182 }
01183
01195 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
01196 {
01197
01198
01199
01200 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
01201
01202 if (top < 22 || top > _screen.height - (height >> 2)) return false;
01203
01204
01205 const Window *w;
01206 FOR_ALL_WINDOWS_FROM_BACK(w) {
01207 if (w->window_class == WC_MAIN_WINDOW) continue;
01208
01209 if (left + width > w->left &&
01210 w->left + w->width > left &&
01211 top + height > w->top &&
01212 w->top + w->height > top) {
01213 return false;
01214 }
01215 }
01216
01217 pos.x = left;
01218 pos.y = top;
01219 return true;
01220 }
01221
01228 static Point GetAutoPlacePosition(int width, int height)
01229 {
01230 Point pt;
01231
01232
01233 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01234 if (IsGoodAutoPlace1(0, main_toolbar != NULL ? main_toolbar->height + 2 : 2, width, height, pt)) return pt;
01235
01236
01237
01238
01239
01240 const Window *w;
01241 FOR_ALL_WINDOWS_FROM_BACK(w) {
01242 if (w->window_class == WC_MAIN_WINDOW) continue;
01243
01244 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01245 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01246 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01247 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01248 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01249 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01250 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01251 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01252 }
01253
01254
01255
01256
01257
01258 FOR_ALL_WINDOWS_FROM_BACK(w) {
01259 if (w->window_class == WC_MAIN_WINDOW) continue;
01260
01261 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01262 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01263 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01264 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01265 }
01266
01267
01268
01269
01270 int left = 0, top = 24;
01271
01272 restart:
01273 FOR_ALL_WINDOWS_FROM_BACK(w) {
01274 if (w->left == left && w->top == top) {
01275 left += 5;
01276 top += 5;
01277 goto restart;
01278 }
01279 }
01280
01281 pt.x = left;
01282 pt.y = top;
01283 return pt;
01284 }
01285
01292 Point GetToolbarAlignedWindowPosition(int window_width)
01293 {
01294 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01295 assert(w != NULL);
01296 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
01297 return pt;
01298 }
01299
01317 static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01318 {
01319 Point pt;
01320 const Window *w;
01321
01322 int16 default_width = max(desc->default_width, sm_width);
01323 int16 default_height = max(desc->default_height, sm_height);
01324
01325 if (desc->parent_cls != 0 &&
01326 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01327 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01328
01329 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01330 if (pt.x > _screen.width + 10 - default_width) {
01331 pt.x = (_screen.width + 10 - default_width) - 20;
01332 }
01333 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? w->height : 10);
01334 return pt;
01335 }
01336
01337 switch (desc->default_pos) {
01338 case WDP_ALIGN_TOOLBAR:
01339 return GetToolbarAlignedWindowPosition(default_width);
01340
01341 case WDP_AUTO:
01342 return GetAutoPlacePosition(default_width, default_height);
01343
01344 case WDP_CENTER:
01345 pt.x = (_screen.width - default_width) / 2;
01346 pt.y = (_screen.height - default_height) / 2;
01347 break;
01348
01349 case WDP_MANUAL:
01350 pt.x = 0;
01351 pt.y = 0;
01352 break;
01353
01354 default:
01355 NOT_REACHED();
01356 }
01357
01358 return pt;
01359 }
01360
01361 Point Window::OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01362 {
01363 return LocalGetWindowPlacement(desc, sm_width, sm_height, window_number);
01364 }
01365
01374 void Window::CreateNestedTree(const WindowDesc *desc, bool fill_nested)
01375 {
01376 int biggest_index = -1;
01377 this->nested_root = MakeWindowNWidgetTree(desc->nwid_parts, desc->nwid_length, &biggest_index, &this->shade_select);
01378 this->nested_array_size = (uint)(biggest_index + 1);
01379
01380 if (fill_nested) {
01381 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01382 this->nested_root->FillNestedArray(this->nested_array, this->nested_array_size);
01383 }
01384 }
01385
01391 void Window::FinishInitNested(const WindowDesc *desc, WindowNumber window_number)
01392 {
01393 this->InitializeData(desc, window_number);
01394 Point pt = this->OnInitialPosition(desc, this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
01395 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
01396 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
01397 }
01398
01404 void Window::InitNested(const WindowDesc *desc, WindowNumber window_number)
01405 {
01406 this->CreateNestedTree(desc, false);
01407 this->FinishInitNested(desc, window_number);
01408 }
01409
01411 Window::Window() : scrolling_scrollbar(-1)
01412 {
01413 }
01414
01422 Window *FindWindowFromPt(int x, int y)
01423 {
01424 Window *w;
01425 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01426 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01427 return w;
01428 }
01429 }
01430
01431 return NULL;
01432 }
01433
01437 void InitWindowSystem()
01438 {
01439 IConsoleClose();
01440
01441 _z_back_window = NULL;
01442 _z_front_window = NULL;
01443 _focused_window = NULL;
01444 _mouseover_last_w = NULL;
01445 _last_scroll_window = NULL;
01446 _scrolling_viewport = false;
01447 _mouse_hovering = false;
01448
01449 NWidgetLeaf::InvalidateDimensionCache();
01450 NWidgetScrollbar::InvalidateDimensionCache();
01451 }
01452
01456 void UnInitWindowSystem()
01457 {
01458 Window *w;
01459 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01460
01461 for (w = _z_front_window; w != NULL; ) {
01462 Window *to_del = w;
01463 w = w->z_back;
01464 free(to_del);
01465 }
01466
01467 _z_front_window = NULL;
01468 _z_back_window = NULL;
01469 }
01470
01474 void ResetWindowSystem()
01475 {
01476 UnInitWindowSystem();
01477 InitWindowSystem();
01478 _thd.Reset();
01479 }
01480
01481 static void DecreaseWindowCounters()
01482 {
01483 Window *w;
01484 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01485 if (_scroller_click_timeout == 0) {
01486
01487 for (uint i = 0; i < w->nested_array_size; i++) {
01488 NWidgetBase *nwid = w->nested_array[i];
01489 if (nwid != NULL && (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR)) {
01490 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
01491 if (sb->disp_flags & (ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN)) {
01492 sb->disp_flags &= ~(ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN);
01493 w->scrolling_scrollbar = -1;
01494 sb->SetDirty(w);
01495 }
01496 }
01497 }
01498 }
01499 w->OnMouseLoop();
01500 }
01501
01502 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01503 if ((w->flags4 & WF_TIMEOUT_MASK) && !(--w->flags4 & WF_TIMEOUT_MASK)) {
01504 w->OnTimeout();
01505 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons(true);
01506 }
01507 }
01508 }
01509
01510 static void HandlePlacePresize()
01511 {
01512 if (_special_mouse_mode != WSM_PRESIZE) return;
01513
01514 Window *w = _thd.GetCallbackWnd();
01515 if (w == NULL) return;
01516
01517 Point pt = GetTileBelowCursor();
01518 if (pt.x == -1) {
01519 _thd.selend.x = -1;
01520 return;
01521 }
01522
01523 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01524 }
01525
01530 static EventState HandleMouseDragDrop()
01531 {
01532 if (_special_mouse_mode != WSM_DRAGDROP) return ES_NOT_HANDLED;
01533
01534 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01535
01536 Window *w = _thd.GetCallbackWnd();
01537 if (w != NULL) {
01538
01539 Point pt;
01540 pt.x = _cursor.pos.x - w->left;
01541 pt.y = _cursor.pos.y - w->top;
01542 if (_left_button_down) {
01543 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
01544 } else {
01545 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01546 }
01547 }
01548
01549 if (!_left_button_down) ResetObjectToPlace();
01550 return ES_HANDLED;
01551 }
01552
01554 static void HandleMouseOver()
01555 {
01556 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01557
01558
01559 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01560
01561 Point pt = { -1, -1 };
01562 _mouseover_last_w->OnMouseOver(pt, 0);
01563 }
01564
01565
01566 _mouseover_last_w = w;
01567
01568 if (w != NULL) {
01569
01570 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01571 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
01572 if (widget != NULL) w->OnMouseOver(pt, widget->index);
01573 }
01574 }
01575
01577 static const int MIN_VISIBLE_TITLE_BAR = 13;
01578
01580 enum PreventHideDirection {
01581 PHD_UP,
01582 PHD_DOWN,
01583 };
01584
01595 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
01596 {
01597 if (v == NULL) return;
01598
01599 int v_bottom = v->top + v->height;
01600 int v_right = v->left + v->width;
01601 int safe_y = (dir == PHD_UP) ? (v->top - MIN_VISIBLE_TITLE_BAR - rect.top) : (v_bottom + MIN_VISIBLE_TITLE_BAR - rect.bottom);
01602
01603 if (*ny + rect.top <= v->top - MIN_VISIBLE_TITLE_BAR) return;
01604 if (*ny + rect.bottom >= v_bottom + MIN_VISIBLE_TITLE_BAR) return;
01605
01606
01607 if (*nx + rect.left + MIN_VISIBLE_TITLE_BAR < v->left) {
01608 if (v->left < MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01609 return;
01610 }
01611 if (*nx + rect.right - MIN_VISIBLE_TITLE_BAR > v_right) {
01612 if (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01613 return;
01614 }
01615
01616
01617 if (px + rect.left < v->left && v->left >= MIN_VISIBLE_TITLE_BAR) {
01618 *nx = v->left - MIN_VISIBLE_TITLE_BAR - rect.left;
01619 } else if (px + rect.right > v_right && v_right <= _screen.width - MIN_VISIBLE_TITLE_BAR) {
01620 *nx = v_right + MIN_VISIBLE_TITLE_BAR - rect.right;
01621 } else {
01622 *ny = safe_y;
01623 }
01624 }
01625
01633 static void EnsureVisibleCaption(Window *w, int nx, int ny)
01634 {
01635
01636 Rect caption_rect;
01637 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
01638 if (caption != NULL) {
01639 caption_rect.left = caption->pos_x;
01640 caption_rect.right = caption->pos_x + caption->current_x;
01641 caption_rect.top = caption->pos_y;
01642 caption_rect.bottom = caption->pos_y + caption->current_y;
01643
01644
01645 nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
01646 ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
01647
01648
01649 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
01650 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
01651 }
01652
01653 if (w->viewport != NULL) {
01654 w->viewport->left += nx - w->left;
01655 w->viewport->top += ny - w->top;
01656 }
01657
01658 w->left = nx;
01659 w->top = ny;
01660 }
01661
01671 void ResizeWindow(Window *w, int delta_x, int delta_y)
01672 {
01673 if (delta_x != 0 || delta_y != 0) {
01674
01675
01676 int new_right = w->left + w->width + delta_x;
01677 int new_bottom = w->top + w->height + delta_y;
01678 if (new_right >= (int)_cur_resolution.width) delta_x -= new_right - _cur_resolution.width;
01679 if (new_bottom >= (int)_cur_resolution.height) delta_y -= new_bottom - _cur_resolution.height;
01680
01681 w->SetDirty();
01682
01683 uint new_xinc = max(0, (w->nested_root->resize_x == 0) ? 0 : (int)(w->nested_root->current_x - w->nested_root->smallest_x) + delta_x);
01684 uint new_yinc = max(0, (w->nested_root->resize_y == 0) ? 0 : (int)(w->nested_root->current_y - w->nested_root->smallest_y) + delta_y);
01685 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
01686 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
01687
01688 w->nested_root->AssignSizePosition(ST_RESIZE, 0, 0, w->nested_root->smallest_x + new_xinc, w->nested_root->smallest_y + new_yinc, _current_text_dir == TD_RTL);
01689 w->width = w->nested_root->current_x;
01690 w->height = w->nested_root->current_y;
01691 }
01692
01693 EnsureVisibleCaption(w, w->left, w->top);
01694
01695
01696 w->OnResize();
01697 w->SetDirty();
01698 }
01699
01705 int GetMainViewTop()
01706 {
01707 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01708 return (w == NULL) ? 0 : w->top + w->height;
01709 }
01710
01716 int GetMainViewBottom()
01717 {
01718 Window *w = FindWindowById(WC_STATUS_BAR, 0);
01719 return (w == NULL) ? _screen.height : w->top;
01720 }
01721
01722 static bool _dragging_window;
01723
01728 static EventState HandleWindowDragging()
01729 {
01730
01731 if (!_dragging_window) return ES_NOT_HANDLED;
01732
01733
01734 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01735
01736
01737 Window *w;
01738 FOR_ALL_WINDOWS_FROM_BACK(w) {
01739 if (w->flags4 & WF_DRAGGING) {
01740
01741 if (!_left_button_down) {
01742 w->flags4 &= ~WF_DRAGGING;
01743 break;
01744 }
01745
01746 w->SetDirty();
01747
01748 int x = _cursor.pos.x + _drag_delta.x;
01749 int y = _cursor.pos.y + _drag_delta.y;
01750 int nx = x;
01751 int ny = y;
01752
01753 if (_settings_client.gui.window_snap_radius != 0) {
01754 const Window *v;
01755
01756 int hsnap = _settings_client.gui.window_snap_radius;
01757 int vsnap = _settings_client.gui.window_snap_radius;
01758 int delta;
01759
01760 FOR_ALL_WINDOWS_FROM_BACK(v) {
01761 if (v == w) continue;
01762
01763 if (y + w->height > v->top && y < v->top + v->height) {
01764
01765 delta = abs(v->left + v->width - x);
01766 if (delta <= hsnap) {
01767 nx = v->left + v->width;
01768 hsnap = delta;
01769 }
01770
01771
01772 delta = abs(v->left - x - w->width);
01773 if (delta <= hsnap) {
01774 nx = v->left - w->width;
01775 hsnap = delta;
01776 }
01777 }
01778
01779 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01780
01781 delta = abs(v->left - x);
01782 if (delta <= hsnap) {
01783 nx = v->left;
01784 hsnap = delta;
01785 }
01786
01787
01788 delta = abs(v->left + v->width - x - w->width);
01789 if (delta <= hsnap) {
01790 nx = v->left + v->width - w->width;
01791 hsnap = delta;
01792 }
01793 }
01794
01795 if (x + w->width > v->left && x < v->left + v->width) {
01796
01797 delta = abs(v->top + v->height - y);
01798 if (delta <= vsnap) {
01799 ny = v->top + v->height;
01800 vsnap = delta;
01801 }
01802
01803
01804 delta = abs(v->top - y - w->height);
01805 if (delta <= vsnap) {
01806 ny = v->top - w->height;
01807 vsnap = delta;
01808 }
01809 }
01810
01811 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01812
01813 delta = abs(v->top - y);
01814 if (delta <= vsnap) {
01815 ny = v->top;
01816 vsnap = delta;
01817 }
01818
01819
01820 delta = abs(v->top + v->height - y - w->height);
01821 if (delta <= vsnap) {
01822 ny = v->top + v->height - w->height;
01823 vsnap = delta;
01824 }
01825 }
01826 }
01827 }
01828
01829 EnsureVisibleCaption(w, nx, ny);
01830
01831 w->SetDirty();
01832 return ES_HANDLED;
01833 } else if (w->flags4 & WF_SIZING) {
01834
01835 if (!_left_button_down) {
01836 w->flags4 &= ~WF_SIZING;
01837 w->SetDirty();
01838 break;
01839 }
01840
01841
01842
01843
01844 int x, y = _cursor.pos.y - _drag_delta.y;
01845 if (w->flags4 & WF_SIZING_LEFT) {
01846 x = _drag_delta.x - _cursor.pos.x;
01847 } else {
01848 x = _cursor.pos.x - _drag_delta.x;
01849 }
01850
01851
01852 if (w->resize.step_width == 0) x = 0;
01853 if (w->resize.step_height == 0) y = 0;
01854
01855
01856 if (w->top + w->height + y > _screen.height) {
01857 y = _screen.height - w->height - w->top;
01858 }
01859
01860
01861
01862
01863 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
01864 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
01865
01866
01867 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
01868 x = w->nested_root->smallest_x - w->width;
01869 }
01870 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
01871 y = w->nested_root->smallest_y - w->height;
01872 }
01873
01874
01875 if (x == 0 && y == 0) return ES_HANDLED;
01876
01877
01878 _drag_delta.y += y;
01879 if ((w->flags4 & WF_SIZING_LEFT) && x != 0) {
01880 _drag_delta.x -= x;
01881 w->SetDirty();
01882 w->left -= x;
01883
01884 } else {
01885 _drag_delta.x += x;
01886 }
01887
01888
01889 ResizeWindow(w, x, y);
01890 return ES_HANDLED;
01891 }
01892 }
01893
01894 _dragging_window = false;
01895 return ES_HANDLED;
01896 }
01897
01902 static void StartWindowDrag(Window *w)
01903 {
01904 w->flags4 |= WF_DRAGGING;
01905 w->flags4 &= ~WF_CENTERED;
01906 _dragging_window = true;
01907
01908 _drag_delta.x = w->left - _cursor.pos.x;
01909 _drag_delta.y = w->top - _cursor.pos.y;
01910
01911 BringWindowToFront(w);
01912 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01913 }
01914
01920 static void StartWindowSizing(Window *w, bool to_left)
01921 {
01922 w->flags4 |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
01923 w->flags4 &= ~WF_CENTERED;
01924 _dragging_window = true;
01925
01926 _drag_delta.x = _cursor.pos.x;
01927 _drag_delta.y = _cursor.pos.y;
01928
01929 BringWindowToFront(w);
01930 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01931 }
01932
01937 static EventState HandleScrollbarScrolling()
01938 {
01939 Window *w;
01940 FOR_ALL_WINDOWS_FROM_BACK(w) {
01941 if (w->scrolling_scrollbar >= 0) {
01942
01943 if (!_left_button_down) {
01944 w->scrolling_scrollbar = -1;
01945 w->SetDirty();
01946 return ES_HANDLED;
01947 }
01948
01949 int i;
01950 NWidgetScrollbar *sb = w->GetWidget<NWidgetScrollbar>(w->scrolling_scrollbar);
01951 bool rtl = false;
01952
01953 if (sb->type == NWID_HSCROLLBAR) {
01954 i = _cursor.pos.x - _cursorpos_drag_start.x;
01955 rtl = _current_text_dir == TD_RTL;
01956 } else {
01957 i = _cursor.pos.y - _cursorpos_drag_start.y;
01958 }
01959
01960 if (sb->disp_flags & ND_SCROLLBAR_BTN) {
01961 if (_scroller_click_timeout == 1) {
01962 _scroller_click_timeout = 3;
01963 sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1);
01964 w->SetDirty();
01965 }
01966 return ES_HANDLED;
01967 }
01968
01969
01970 int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity()));
01971 if (rtl) pos = max(0, sb->GetCount() - sb->GetCapacity() - pos);
01972 if (pos != sb->GetPosition()) {
01973 sb->SetPosition(pos);
01974 w->SetDirty();
01975 }
01976 return ES_HANDLED;
01977 }
01978 }
01979
01980 return ES_NOT_HANDLED;
01981 }
01982
01987 static EventState HandleViewportScroll()
01988 {
01989 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
01990
01991 if (!_scrolling_viewport) return ES_NOT_HANDLED;
01992
01993
01994
01995
01996 if (_last_scroll_window == NULL) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01997
01998 if (_last_scroll_window == NULL || !(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down))) {
01999 _cursor.fix_at = false;
02000 _scrolling_viewport = false;
02001 _last_scroll_window = NULL;
02002 return ES_NOT_HANDLED;
02003 }
02004
02005 if (_last_scroll_window == FindWindowById(WC_MAIN_WINDOW, 0) && _last_scroll_window->viewport->follow_vehicle != INVALID_VEHICLE) {
02006
02007 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
02008 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
02009 return ES_NOT_HANDLED;
02010 }
02011
02012 Point delta;
02013 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
02014 delta.x = -_cursor.delta.x;
02015 delta.y = -_cursor.delta.y;
02016 } else {
02017 delta.x = _cursor.delta.x;
02018 delta.y = _cursor.delta.y;
02019 }
02020
02021 if (scrollwheel_scrolling) {
02022
02023 delta.x = _cursor.h_wheel;
02024 delta.y = _cursor.v_wheel;
02025 _cursor.v_wheel = 0;
02026 _cursor.h_wheel = 0;
02027 }
02028
02029
02030 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
02031
02032 _cursor.delta.x = 0;
02033 _cursor.delta.y = 0;
02034 return ES_HANDLED;
02035 }
02036
02047 static bool MaybeBringWindowToFront(Window *w)
02048 {
02049 bool bring_to_front = false;
02050
02051 if (w->window_class == WC_MAIN_WINDOW ||
02052 IsVitalWindow(w) ||
02053 w->window_class == WC_TOOLTIPS ||
02054 w->window_class == WC_DROPDOWN_MENU) {
02055 return true;
02056 }
02057
02058
02059 int w_width = w->width;
02060 int w_height = w->height;
02061 if (w->IsShaded()) {
02062 w_width = w->unshaded_size.width;
02063 w_height = w->unshaded_size.height;
02064 }
02065
02066 Window *u;
02067 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
02068
02069 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
02070 u->flags4 |= WF_WHITE_BORDER_MASK;
02071 u->SetDirty();
02072 return false;
02073 }
02074
02075 if (u->window_class == WC_MAIN_WINDOW ||
02076 IsVitalWindow(u) ||
02077 u->window_class == WC_TOOLTIPS ||
02078 u->window_class == WC_DROPDOWN_MENU) {
02079 continue;
02080 }
02081
02082
02083 if (w->left + w_width <= u->left ||
02084 u->left + u->width <= w->left ||
02085 w->top + w_height <= u->top ||
02086 u->top + u->height <= w->top) {
02087 continue;
02088 }
02089
02090 bring_to_front = true;
02091 }
02092
02093 if (bring_to_front) BringWindowToFront(w);
02094 return true;
02095 }
02096
02101 void HandleKeypress(uint32 raw_key)
02102 {
02103
02104
02105 assert(HasModalProgress() || IsLocalCompany());
02106
02107
02108 uint16 key = GB(raw_key, 0, 16);
02109 uint16 keycode = GB(raw_key, 16, 16);
02110
02111
02112
02113
02114
02115
02116
02117
02118 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
02119
02120
02121
02122
02123 if (key == 0 && keycode == 0) return;
02124
02125
02126 if (EditBoxInGlobalFocus()) {
02127
02128 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
02129 }
02130
02131
02132 Window *w;
02133 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02134 if (w->window_class == WC_MAIN_TOOLBAR) continue;
02135 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02136 }
02137
02138 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02139
02140 if (w != NULL && w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02141
02142 HandleGlobalHotkeys(key, keycode);
02143 }
02144
02148 void HandleCtrlChanged()
02149 {
02150
02151 Window *w;
02152 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02153 if (w->OnCTRLStateChange() == ES_HANDLED) return;
02154 }
02155 }
02156
02163 static int _input_events_this_tick = 0;
02164
02169 static void HandleAutoscroll()
02170 {
02171 if (_settings_client.gui.autoscroll && _game_mode != GM_MENU && !HasModalProgress()) {
02172 int x = _cursor.pos.x;
02173 int y = _cursor.pos.y;
02174 Window *w = FindWindowFromPt(x, y);
02175 if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
02176 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02177 if (vp != NULL) {
02178 x -= vp->left;
02179 y -= vp->top;
02180
02181
02182 #define scrollspeed 3
02183 if (x - 15 < 0) {
02184 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
02185 } else if (15 - (vp->width - x) > 0) {
02186 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
02187 }
02188 if (y - 15 < 0) {
02189 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
02190 } else if (15 - (vp->height - y) > 0) {
02191 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
02192 }
02193 #undef scrollspeed
02194 }
02195 }
02196 }
02197
02198 enum MouseClick {
02199 MC_NONE = 0,
02200 MC_LEFT,
02201 MC_RIGHT,
02202 MC_DOUBLE_LEFT,
02203 MC_HOVER,
02204
02205 MAX_OFFSET_DOUBLE_CLICK = 5,
02206 TIME_BETWEEN_DOUBLE_CLICK = 500,
02207 MAX_OFFSET_HOVER = 5,
02208 };
02209 extern EventState VpHandlePlaceSizingDrag();
02210
02211 static void ScrollMainViewport(int x, int y)
02212 {
02213 if (_game_mode != GM_MENU) {
02214 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02215 assert(w);
02216
02217 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
02218 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
02219 }
02220 }
02221
02231 static const int8 scrollamt[16][2] = {
02232 { 0, 0},
02233 {-2, 0},
02234 { 0, -2},
02235 {-2, -1},
02236 { 2, 0},
02237 { 0, 0},
02238 { 2, -1},
02239 { 0, -2},
02240 { 0, 2},
02241 {-2, 1},
02242 { 0, 0},
02243 {-2, 0},
02244 { 2, 1},
02245 { 0, 2},
02246 { 2, 0},
02247 { 0, 0},
02248 };
02249
02250 static void HandleKeyScrolling()
02251 {
02252
02253
02254
02255
02256 if (_dirkeys && !EditBoxInGlobalFocus()) {
02257 int factor = _shift_pressed ? 50 : 10;
02258 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
02259 }
02260 }
02261
02262 static void MouseLoop(MouseClick click, int mousewheel)
02263 {
02264
02265
02266 assert(HasModalProgress() || IsLocalCompany());
02267
02268 HandlePlacePresize();
02269 UpdateTileSelection();
02270
02271 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
02272 if (HandleMouseDragDrop() == ES_HANDLED) return;
02273 if (HandleWindowDragging() == ES_HANDLED) return;
02274 if (HandleScrollbarScrolling() == ES_HANDLED) return;
02275 if (HandleViewportScroll() == ES_HANDLED) return;
02276
02277 HandleMouseOver();
02278
02279 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02280 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
02281
02282 int x = _cursor.pos.x;
02283 int y = _cursor.pos.y;
02284 Window *w = FindWindowFromPt(x, y);
02285 if (w == NULL) return;
02286
02287 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
02288 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02289
02290
02291 if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
02292
02293 if (mousewheel != 0) {
02294
02295 w->OnMouseWheel(mousewheel);
02296
02297
02298 if (vp == NULL) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
02299 }
02300
02301 if (vp != NULL) {
02302 if (scrollwheel_scrolling) click = MC_RIGHT;
02303 switch (click) {
02304 case MC_DOUBLE_LEFT:
02305 case MC_LEFT:
02306 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
02307 if (!HandleViewportClicked(vp, x, y) &&
02308 !(w->flags4 & WF_DISABLE_VP_SCROLL) &&
02309 _settings_client.gui.left_mouse_btn_scrolling) {
02310 _scrolling_viewport = true;
02311 _cursor.fix_at = false;
02312 }
02313 break;
02314
02315 case MC_RIGHT:
02316 if (!(w->flags4 & WF_DISABLE_VP_SCROLL)) {
02317 _scrolling_viewport = true;
02318 _cursor.fix_at = true;
02319
02320
02321 _cursor.h_wheel = 0;
02322 _cursor.v_wheel = 0;
02323 }
02324 break;
02325
02326 default:
02327 break;
02328 }
02329 } else {
02330 switch (click) {
02331 case MC_LEFT:
02332 case MC_DOUBLE_LEFT:
02333 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
02334 break;
02335
02336 default:
02337 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
02338
02339
02340
02341
02342 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
02343
02344 case MC_HOVER: DispatchHoverEvent(w, x - w->left, y - w->top); break;
02345 }
02346 }
02347 }
02348
02352 void HandleMouseEvents()
02353 {
02354
02355
02356 assert(HasModalProgress() || IsLocalCompany());
02357
02358 static int double_click_time = 0;
02359 static Point double_click_pos = {0, 0};
02360
02361
02362 MouseClick click = MC_NONE;
02363 if (_left_button_down && !_left_button_clicked) {
02364 click = MC_LEFT;
02365 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02366 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
02367 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
02368 click = MC_DOUBLE_LEFT;
02369 }
02370 double_click_time = _realtime_tick;
02371 double_click_pos = _cursor.pos;
02372 _left_button_clicked = true;
02373 _input_events_this_tick++;
02374 } else if (_right_button_clicked) {
02375 _right_button_clicked = false;
02376 click = MC_RIGHT;
02377 _input_events_this_tick++;
02378 }
02379
02380 int mousewheel = 0;
02381 if (_cursor.wheel) {
02382 mousewheel = _cursor.wheel;
02383 _cursor.wheel = 0;
02384 _input_events_this_tick++;
02385 }
02386
02387 static uint32 hover_time = 0;
02388 static Point hover_pos = {0, 0};
02389
02390 if (_settings_client.gui.hover_delay > 0) {
02391 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
02392 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
02393 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
02394 hover_pos = _cursor.pos;
02395 hover_time = _realtime_tick;
02396 _mouse_hovering = false;
02397 } else {
02398 if (hover_time != 0 && _realtime_tick > hover_time + _settings_client.gui.hover_delay * 1000) {
02399 click = MC_HOVER;
02400 _input_events_this_tick++;
02401 _mouse_hovering = true;
02402 }
02403 }
02404 }
02405
02406
02407 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _newgrf_debug_sprite_picker.click_time != _realtime_tick) {
02408
02409 _newgrf_debug_sprite_picker.mode = SPM_NONE;
02410 InvalidateWindowData(WC_SPRITE_ALIGNER, 0, 1);
02411 }
02412
02413 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
02414
02415 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
02416 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
02417 _newgrf_debug_sprite_picker.click_time = _realtime_tick;
02418 _newgrf_debug_sprite_picker.sprites.Clear();
02419 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
02420 MarkWholeScreenDirty();
02421 } else {
02422 MouseLoop(click, mousewheel);
02423 }
02424
02425
02426
02427 _cursor.delta.x = 0;
02428 _cursor.delta.y = 0;
02429 }
02430
02434 static void CheckSoftLimit()
02435 {
02436 if (_settings_client.gui.window_soft_limit == 0) return;
02437
02438 for (;;) {
02439 uint deletable_count = 0;
02440 Window *w, *last_deletable = NULL;
02441 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02442 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags4 & WF_STICKY)) continue;
02443
02444 last_deletable = w;
02445 deletable_count++;
02446 }
02447
02448
02449 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02450
02451 assert(last_deletable != NULL);
02452 delete last_deletable;
02453 }
02454 }
02455
02459 void InputLoop()
02460 {
02461
02462
02463 assert(HasModalProgress() || IsLocalCompany());
02464
02465 CheckSoftLimit();
02466 HandleKeyScrolling();
02467
02468
02469 for (Window *v = _z_front_window; v != NULL; ) {
02470 Window *w = v;
02471 v = v->z_back;
02472
02473 if (w->window_class != WC_INVALID) continue;
02474
02475 RemoveWindowFromZOrdering(w);
02476 free(w);
02477 }
02478
02479 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
02480 DecreaseWindowCounters();
02481
02482 if (_input_events_this_tick != 0) {
02483
02484 _input_events_this_tick = 0;
02485
02486 return;
02487 }
02488
02489
02490 HandleMouseEvents();
02491 HandleAutoscroll();
02492 }
02493
02497 void UpdateWindows()
02498 {
02499 Window *w;
02500
02501 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02502 w->ProcessScheduledInvalidations();
02503 }
02504
02505 static int we4_timer = 0;
02506 int t = we4_timer + 1;
02507
02508 if (t >= 100) {
02509 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02510 w->OnHundredthTick();
02511 }
02512 t = 0;
02513 }
02514 we4_timer = t;
02515
02516 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02517 if (w->flags4 & WF_WHITE_BORDER_MASK) {
02518 w->flags4 -= WF_WHITE_BORDER_ONE;
02519
02520 if (!(w->flags4 & WF_WHITE_BORDER_MASK)) w->SetDirty();
02521 }
02522 }
02523
02524 DrawDirtyBlocks();
02525
02526 FOR_ALL_WINDOWS_FROM_BACK(w) {
02527
02528 if (w->viewport != NULL && !w->IsShaded()) UpdateViewportPosition(w);
02529 }
02530 NetworkDrawChatMessage();
02531
02532 DrawMouseCursor();
02533 }
02534
02540 void SetWindowDirty(WindowClass cls, WindowNumber number)
02541 {
02542 const Window *w;
02543 FOR_ALL_WINDOWS_FROM_BACK(w) {
02544 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02545 }
02546 }
02547
02554 void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
02555 {
02556 const Window *w;
02557 FOR_ALL_WINDOWS_FROM_BACK(w) {
02558 if (w->window_class == cls && w->window_number == number) {
02559 w->SetWidgetDirty(widget_index);
02560 }
02561 }
02562 }
02563
02568 void SetWindowClassesDirty(WindowClass cls)
02569 {
02570 Window *w;
02571 FOR_ALL_WINDOWS_FROM_BACK(w) {
02572 if (w->window_class == cls) w->SetDirty();
02573 }
02574 }
02575
02602 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
02603 {
02604 Window *w;
02605 FOR_ALL_WINDOWS_FROM_BACK(w) {
02606 if (w->window_class == cls && w->window_number == number) {
02607 w->InvalidateData(data, gui_scope);
02608 }
02609 }
02610 }
02611
02620 void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
02621 {
02622 Window *w;
02623
02624 FOR_ALL_WINDOWS_FROM_BACK(w) {
02625 if (w->window_class == cls) {
02626 w->InvalidateData(data, gui_scope);
02627 }
02628 }
02629 }
02630
02634 void CallWindowTickEvent()
02635 {
02636 Window *w;
02637 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02638 w->OnTick();
02639 }
02640 }
02641
02648 void DeleteNonVitalWindows()
02649 {
02650 Window *w;
02651
02652 restart_search:
02653
02654
02655
02656 FOR_ALL_WINDOWS_FROM_BACK(w) {
02657 if (w->window_class != WC_MAIN_WINDOW &&
02658 w->window_class != WC_SELECT_GAME &&
02659 w->window_class != WC_MAIN_TOOLBAR &&
02660 w->window_class != WC_STATUS_BAR &&
02661 w->window_class != WC_TOOLTIPS &&
02662 (w->flags4 & WF_STICKY) == 0) {
02663
02664 delete w;
02665 goto restart_search;
02666 }
02667 }
02668 }
02669
02677 void DeleteAllNonVitalWindows()
02678 {
02679 Window *w;
02680
02681
02682 DeleteNonVitalWindows();
02683
02684 restart_search:
02685
02686
02687
02688 FOR_ALL_WINDOWS_FROM_BACK(w) {
02689 if (w->flags4 & WF_STICKY) {
02690 delete w;
02691 goto restart_search;
02692 }
02693 }
02694 }
02695
02700 void DeleteConstructionWindows()
02701 {
02702 Window *w;
02703
02704 restart_search:
02705
02706
02707
02708 FOR_ALL_WINDOWS_FROM_BACK(w) {
02709 if (w->desc_flags & WDF_CONSTRUCTION) {
02710 delete w;
02711 goto restart_search;
02712 }
02713 }
02714
02715 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02716 }
02717
02719 void HideVitalWindows()
02720 {
02721 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02722 DeleteWindowById(WC_STATUS_BAR, 0);
02723 }
02724
02726 void ReInitAllWindows()
02727 {
02728 NWidgetLeaf::InvalidateDimensionCache();
02729 NWidgetScrollbar::InvalidateDimensionCache();
02730
02731 Window *w;
02732 FOR_ALL_WINDOWS_FROM_BACK(w) {
02733 w->ReInit();
02734 }
02735 #ifdef ENABLE_NETWORK
02736 void NetworkReInitChatBoxSize();
02737 NetworkReInitChatBoxSize();
02738 #endif
02739
02740
02741 RelocateAllWindows(_cur_resolution.width, _cur_resolution.height);
02742 MarkWholeScreenDirty();
02743 }
02744
02752 static int PositionWindow(Window *w, WindowClass clss, int setting)
02753 {
02754 if (w == NULL || w->window_class != clss) {
02755 w = FindWindowById(clss, 0);
02756 }
02757 if (w == NULL) return 0;
02758
02759 int old_left = w->left;
02760 switch (setting) {
02761 case 1: w->left = (_screen.width - w->width) / 2; break;
02762 case 2: w->left = _screen.width - w->width; break;
02763 default: w->left = 0; break;
02764 }
02765 if (w->viewport != NULL) w->viewport->left += w->left - old_left;
02766 SetDirtyBlocks(0, w->top, _screen.width, w->top + w->height);
02767 return w->left;
02768 }
02769
02775 int PositionMainToolbar(Window *w)
02776 {
02777 DEBUG(misc, 5, "Repositioning Main Toolbar...");
02778 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
02779 }
02780
02786 int PositionStatusbar(Window *w)
02787 {
02788 DEBUG(misc, 5, "Repositioning statusbar...");
02789 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
02790 }
02791
02797 int PositionNewsMessage(Window *w)
02798 {
02799 DEBUG(misc, 5, "Repositioning news message...");
02800 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
02801 }
02802
02808 int PositionNetworkChatWindow(Window *w)
02809 {
02810 DEBUG(misc, 5, "Repositioning network chat window...");
02811 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
02812 }
02813
02814
02820 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
02821 {
02822 Window *w;
02823 FOR_ALL_WINDOWS_FROM_BACK(w) {
02824 if (w->viewport != NULL && w->viewport->follow_vehicle == from_index) {
02825 w->viewport->follow_vehicle = to_index;
02826 w->SetDirty();
02827 }
02828 }
02829 }
02830
02831
02837 void RelocateAllWindows(int neww, int newh)
02838 {
02839 Window *w;
02840
02841 FOR_ALL_WINDOWS_FROM_BACK(w) {
02842 int left, top;
02843
02844 if (w->window_class == WC_MAIN_WINDOW) {
02845 ViewPort *vp = w->viewport;
02846 vp->width = w->width = neww;
02847 vp->height = w->height = newh;
02848 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
02849 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
02850 continue;
02851 }
02852
02853
02854
02855 switch (w->window_class) {
02856 case WC_BOOTSTRAP:
02857 ResizeWindow(w, neww, newh);
02858 continue;
02859
02860 case WC_MAIN_TOOLBAR:
02861 ResizeWindow(w, min(neww, *_preferred_toolbar_size) - w->width, 0);
02862
02863 top = w->top;
02864 left = PositionMainToolbar(w);
02865 break;
02866
02867 case WC_NEWS_WINDOW:
02868 top = newh - w->height;
02869 left = PositionNewsMessage(w);
02870 break;
02871
02872 case WC_STATUS_BAR:
02873 ResizeWindow(w, min(neww, *_preferred_statusbar_size) - w->width, 0);
02874
02875 top = newh - w->height;
02876 left = PositionStatusbar(w);
02877 break;
02878
02879 case WC_SEND_NETWORK_MSG:
02880 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
02881 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
02882 left = PositionNetworkChatWindow(w);
02883 break;
02884
02885 case WC_CONSOLE:
02886 IConsoleResize(w);
02887 continue;
02888
02889 default: {
02890 if (w->flags4 & WF_CENTERED) {
02891 top = (newh - w->height) >> 1;
02892 left = (neww - w->width) >> 1;
02893 break;
02894 }
02895
02896 left = w->left;
02897 if (left + (w->width >> 1) >= neww) left = neww - w->width;
02898 if (left < 0) left = 0;
02899
02900 top = w->top;
02901 if (top + (w->height >> 1) >= newh) top = newh - w->height;
02902 break;
02903 }
02904 }
02905
02906 EnsureVisibleCaption(w, left, top);
02907 }
02908 }
02909
02915 PickerWindowBase::~PickerWindowBase()
02916 {
02917 this->window_class = WC_INVALID;
02918 ResetObjectToPlace();
02919 }