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