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 "progress.h"
00020 #include "blitter/factory.hpp"
00021 #include "zoom_func.h"
00022 #include "vehicle_base.h"
00023 #include "window_func.h"
00024 #include "tilehighlight_func.h"
00025 #include "network/network.h"
00026 #include "querystring_gui.h"
00027 #include "widgets/dropdown_func.h"
00028 #include "strings_func.h"
00029 #include "settings_type.h"
00030 #include "newgrf_debug.h"
00031 #include "hotkeys.h"
00032 #include "toolbar_gui.h"
00033 #include "statusbar_gui.h"
00034 #include "error.h"
00035 #include "game/game.hpp"
00036
00038 enum ViewportAutoscrolling {
00039 VA_DISABLED,
00040 VA_MAIN_VIEWPORT_FULLSCREEN,
00041 VA_MAIN_VIEWPORT,
00042 VA_EVERY_VIEWPORT,
00043 };
00044
00045 static Point _drag_delta;
00046 static Window *_mouseover_last_w = NULL;
00047 static Window *_last_scroll_window = NULL;
00048
00050 Window *_z_front_window = NULL;
00052 Window *_z_back_window = NULL;
00053
00055 bool _window_highlight_colour = false;
00056
00057
00058
00059
00060
00061
00062 Window *_focused_window;
00063
00064 Point _cursorpos_drag_start;
00065
00066 int _scrollbar_start_pos;
00067 int _scrollbar_size;
00068 byte _scroller_click_timeout = 0;
00069
00070 bool _scrolling_viewport;
00071 bool _mouse_hovering;
00072
00073 SpecialMouseMode _special_mouse_mode;
00074
00076 WindowDesc::WindowDesc(WindowPosition def_pos, int16 def_width, int16 def_height,
00077 WindowClass window_class, WindowClass parent_class, uint32 flags,
00078 const NWidgetPart *nwid_parts, int16 nwid_length) :
00079 default_pos(def_pos),
00080 default_width(def_width),
00081 default_height(def_height),
00082 cls(window_class),
00083 parent_cls(parent_class),
00084 flags(flags),
00085 nwid_parts(nwid_parts),
00086 nwid_length(nwid_length)
00087 {
00088 }
00089
00090 WindowDesc::~WindowDesc()
00091 {
00092 }
00093
00103 int Window::GetRowFromWidget(int clickpos, int widget, int padding, int line_height) const
00104 {
00105 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
00106 if (line_height < 0) line_height = wid->resize_y;
00107 if (clickpos < (int)wid->pos_y + padding) return INT_MAX;
00108 return (clickpos - (int)wid->pos_y - padding) / line_height;
00109 }
00110
00114 void Window::DisableAllWidgetHighlight()
00115 {
00116 for (uint i = 0; i < this->nested_array_size; i++) {
00117 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(i);
00118 if (nwid == NULL) continue;
00119
00120 if (nwid->IsHighlighted()) {
00121 nwid->SetHighlighted(TC_INVALID);
00122 this->SetWidgetDirty(i);
00123 }
00124 }
00125
00126 CLRBITS(this->flags, WF_HIGHLIGHTED);
00127 }
00128
00134 void Window::SetWidgetHighlight(byte widget_index, TextColour highlighted_colour)
00135 {
00136 assert(widget_index < this->nested_array_size);
00137
00138 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
00139 if (nwid == NULL) return;
00140
00141 nwid->SetHighlighted(highlighted_colour);
00142 this->SetWidgetDirty(widget_index);
00143
00144 if (highlighted_colour != TC_INVALID) {
00145
00146 this->flags |= WF_HIGHLIGHTED;
00147 } else {
00148
00149 bool valid = false;
00150 for (uint i = 0; i < this->nested_array_size; i++) {
00151 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(i);
00152 if (nwid == NULL) continue;
00153 if (!nwid->IsHighlighted()) continue;
00154
00155 valid = true;
00156 }
00157
00158 if (!valid) CLRBITS(this->flags, WF_HIGHLIGHTED);
00159 }
00160 }
00161
00167 bool Window::IsWidgetHighlighted(byte widget_index) const
00168 {
00169 assert(widget_index < this->nested_array_size);
00170
00171 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
00172 if (nwid == NULL) return false;
00173
00174 return nwid->IsHighlighted();
00175 }
00176
00184 void Window::OnDropdownClose(Point pt, int widget, int index, bool instant_close)
00185 {
00186 if (widget < 0) return;
00187
00188 if (instant_close) {
00189
00190
00191 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
00192 this->OnDropdownSelect(widget, index);
00193 }
00194 }
00195
00196
00197 if (this->nested_array != NULL) {
00198 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
00199 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
00200 nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
00201 } else {
00202 this->RaiseWidget(widget);
00203 }
00204 } else {
00205 this->RaiseWidget(widget);
00206 }
00207 this->SetWidgetDirty(widget);
00208 }
00209
00215 const Scrollbar *Window::GetScrollbar(uint widnum) const
00216 {
00217 return this->GetWidget<NWidgetScrollbar>(widnum);
00218 }
00219
00225 Scrollbar *Window::GetScrollbar(uint widnum)
00226 {
00227 return this->GetWidget<NWidgetScrollbar>(widnum);
00228 }
00229
00230
00235 void SetFocusedWindow(Window *w)
00236 {
00237 if (_focused_window == w) return;
00238
00239
00240 if (_focused_window != NULL) {
00241 if (_focused_window->nested_focus != NULL) _focused_window->nested_focus->SetDirty(_focused_window);
00242 }
00243
00244
00245 Window *old_focused = _focused_window;
00246 _focused_window = w;
00247
00248
00249 if (old_focused != NULL) old_focused->OnFocusLost();
00250 if (_focused_window != NULL) _focused_window->OnFocus();
00251 }
00252
00258 static bool EditBoxInGlobalFocus()
00259 {
00260 if (_focused_window == NULL) return false;
00261
00262
00263 if (_focused_window->window_class == WC_CONSOLE) return true;
00264
00265 return _focused_window->nested_focus != NULL && _focused_window->nested_focus->type == WWT_EDITBOX;
00266 }
00267
00271 void Window::UnfocusFocusedWidget()
00272 {
00273 if (this->nested_focus != NULL) {
00274
00275 this->nested_focus->SetDirty(this);
00276 this->nested_focus = NULL;
00277 }
00278 }
00279
00285 bool Window::SetFocusedWidget(byte widget_index)
00286 {
00287
00288 if (widget_index >= this->nested_array_size) return false;
00289
00290 assert(this->nested_array[widget_index] != NULL);
00291 if (this->nested_focus != NULL) {
00292 if (this->GetWidget<NWidgetCore>(widget_index) == this->nested_focus) return false;
00293
00294
00295 this->nested_focus->SetDirty(this);
00296 }
00297 this->nested_focus = this->GetWidget<NWidgetCore>(widget_index);
00298 return true;
00299 }
00300
00308 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
00309 {
00310 va_list wdg_list;
00311
00312 va_start(wdg_list, widgets);
00313
00314 while (widgets != WIDGET_LIST_END) {
00315 SetWidgetDisabledState(widgets, disab_stat);
00316 widgets = va_arg(wdg_list, int);
00317 }
00318
00319 va_end(wdg_list);
00320 }
00321
00327 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
00328 {
00329 va_list wdg_list;
00330
00331 va_start(wdg_list, widgets);
00332
00333 while (widgets != WIDGET_LIST_END) {
00334 SetWidgetLoweredState(widgets, lowered_stat);
00335 widgets = va_arg(wdg_list, int);
00336 }
00337
00338 va_end(wdg_list);
00339 }
00340
00345 void Window::RaiseButtons(bool autoraise)
00346 {
00347 for (uint i = 0; i < this->nested_array_size; i++) {
00348 if (this->nested_array[i] != NULL && ((this->nested_array[i]->type & ~WWB_PUSHBUTTON) < WWT_LAST || this->nested_array[i]->type == NWID_PUSHBUTTON_DROPDOWN) &&
00349 (!autoraise || (this->nested_array[i]->type & WWB_PUSHBUTTON)) && this->IsWidgetLowered(i)) {
00350 this->RaiseWidget(i);
00351 this->SetWidgetDirty(i);
00352 }
00353 }
00354 }
00355
00360 void Window::SetWidgetDirty(byte widget_index) const
00361 {
00362
00363 if (this->nested_array == NULL) return;
00364
00365 this->nested_array[widget_index]->SetDirty(this);
00366 }
00367
00373 void Window::HandleButtonClick(byte widget)
00374 {
00375 this->LowerWidget(widget);
00376 this->SetTimeout();
00377 this->SetWidgetDirty(widget);
00378 }
00379
00380 static void StartWindowDrag(Window *w);
00381 static void StartWindowSizing(Window *w, bool to_left);
00382
00390 static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
00391 {
00392 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
00393 WidgetType widget_type = (nw != NULL) ? nw->type : WWT_EMPTY;
00394
00395 bool focused_widget_changed = false;
00396
00397 if (_focused_window != w &&
00398 (w->desc_flags & WDF_NO_FOCUS) == 0 &&
00399 widget_type != WWT_CLOSEBOX) {
00400 focused_widget_changed = true;
00401 if (_focused_window != NULL) {
00402 _focused_window->OnFocusLost();
00403
00404
00405 if (w->window_class != WC_OSK) DeleteWindowById(WC_OSK, 0);
00406 }
00407 SetFocusedWindow(w);
00408 w->OnFocus();
00409 }
00410
00411 if (nw == NULL) return;
00412
00413
00414 if (nw->IsDisabled()) return;
00415
00416 int widget_index = nw->index;
00417
00418
00419
00420 if (widget_type != WWT_CAPTION) {
00421
00422 if (w->nested_focus != NULL && w->nested_focus->type == WWT_EDITBOX && w->nested_focus != nw && w->window_class != WC_OSK) {
00423 DeleteWindowById(WC_OSK, 0);
00424 }
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435 focused_widget_changed |= w->SetFocusedWidget(widget_index);
00436 }
00437
00438
00439
00440 if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
00441
00442 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
00443
00444 switch (widget_type) {
00445 case NWID_VSCROLLBAR:
00446 case NWID_HSCROLLBAR:
00447 ScrollbarClickHandler(w, nw, x, y);
00448 break;
00449
00450 case WWT_EDITBOX:
00451 if (!focused_widget_changed) {
00452
00453 QueryStringBaseWindow *qs = dynamic_cast<QueryStringBaseWindow *>(w);
00454 if (qs != NULL) {
00455 qs->OnOpenOSKWindow(widget_index);
00456 }
00457 }
00458 break;
00459
00460 case WWT_CLOSEBOX:
00461 delete w;
00462 return;
00463
00464 case WWT_CAPTION:
00465 StartWindowDrag(w);
00466 return;
00467
00468 case WWT_RESIZEBOX:
00469
00470
00471 StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
00472 nw->SetDirty(w);
00473 return;
00474
00475 case WWT_DEBUGBOX:
00476 w->ShowNewGRFInspectWindow();
00477 break;
00478
00479 case WWT_SHADEBOX:
00480 nw->SetDirty(w);
00481 w->SetShaded(!w->IsShaded());
00482 return;
00483
00484 case WWT_STICKYBOX:
00485 w->flags ^= WF_STICKY;
00486 nw->SetDirty(w);
00487 return;
00488
00489 default:
00490 break;
00491 }
00492
00493
00494 if (widget_index < 0) return;
00495
00496
00497 if (w->IsWidgetHighlighted(widget_index)) {
00498 w->SetWidgetHighlight(widget_index, TC_INVALID);
00499 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
00500 }
00501
00502 Point pt = { x, y };
00503 w->OnClick(pt, widget_index, click_count);
00504 }
00505
00512 static void DispatchRightClickEvent(Window *w, int x, int y)
00513 {
00514 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00515 if (wid == NULL) return;
00516
00517
00518 if (wid->index >= 0) {
00519 Point pt = { x, y };
00520 if (w->OnRightClick(pt, wid->index)) return;
00521 }
00522
00523 if (_settings_client.gui.hover_delay == 0 && wid->tool_tip != 0) GuiShowTooltips(w, wid->tool_tip, 0, NULL, TCC_RIGHT_CLICK);
00524 }
00525
00532 static void DispatchHoverEvent(Window *w, int x, int y)
00533 {
00534 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00535
00536
00537 if (wid == NULL) return;
00538
00539
00540 if (wid->tool_tip != 0) {
00541 GuiShowTooltips(w, wid->tool_tip);
00542 return;
00543 }
00544
00545
00546 if (wid->index < 0) return;
00547
00548 Point pt = { x, y };
00549 w->OnHover(pt, wid->index);
00550 }
00551
00559 static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
00560 {
00561 if (nwid == NULL) return;
00562
00563
00564 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
00565 w->SetShaded(wheel < 0);
00566 return;
00567 }
00568
00569
00570 if (nwid->type == NWID_VSCROLLBAR) {
00571 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
00572 if (sb->GetCount() > sb->GetCapacity()) {
00573 sb->UpdatePosition(wheel);
00574 w->SetDirty();
00575 }
00576 return;
00577 }
00578
00579
00580 Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : NULL);
00581 if (sb != NULL && sb->GetCount() > sb->GetCapacity()) {
00582 sb->UpdatePosition(wheel);
00583 w->SetDirty();
00584 }
00585 }
00586
00592 static bool MayBeShown(const Window *w)
00593 {
00594
00595 if (!HasModalProgress()) return true;
00596
00597 switch (w->window_class) {
00598 case WC_MAIN_WINDOW:
00599 case WC_MODAL_PROGRESS:
00600 case WC_CONFIRM_POPUP_QUERY:
00601 return true;
00602
00603 default:
00604 return false;
00605 }
00606 }
00607
00620 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00621 {
00622 const Window *v;
00623 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00624 if (MayBeShown(v) &&
00625 right > v->left &&
00626 bottom > v->top &&
00627 left < v->left + v->width &&
00628 top < v->top + v->height) {
00629
00630 int x;
00631
00632 if (left < (x = v->left)) {
00633 DrawOverlappedWindow(w, left, top, x, bottom);
00634 DrawOverlappedWindow(w, x, top, right, bottom);
00635 return;
00636 }
00637
00638 if (right > (x = v->left + v->width)) {
00639 DrawOverlappedWindow(w, left, top, x, bottom);
00640 DrawOverlappedWindow(w, x, top, right, bottom);
00641 return;
00642 }
00643
00644 if (top < (x = v->top)) {
00645 DrawOverlappedWindow(w, left, top, right, x);
00646 DrawOverlappedWindow(w, left, x, right, bottom);
00647 return;
00648 }
00649
00650 if (bottom > (x = v->top + v->height)) {
00651 DrawOverlappedWindow(w, left, top, right, x);
00652 DrawOverlappedWindow(w, left, x, right, bottom);
00653 return;
00654 }
00655
00656 return;
00657 }
00658 }
00659
00660
00661 DrawPixelInfo *dp = _cur_dpi;
00662 dp->width = right - left;
00663 dp->height = bottom - top;
00664 dp->left = left - w->left;
00665 dp->top = top - w->top;
00666 dp->pitch = _screen.pitch;
00667 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00668 dp->zoom = ZOOM_LVL_NORMAL;
00669 w->OnPaint();
00670 }
00671
00680 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00681 {
00682 Window *w;
00683 DrawPixelInfo bk;
00684 _cur_dpi = &bk;
00685
00686 FOR_ALL_WINDOWS_FROM_BACK(w) {
00687 if (MayBeShown(w) &&
00688 right > w->left &&
00689 bottom > w->top &&
00690 left < w->left + w->width &&
00691 top < w->top + w->height) {
00692
00693 DrawOverlappedWindow(w, left, top, right, bottom);
00694 }
00695 }
00696 }
00697
00702 void Window::SetDirty() const
00703 {
00704 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00705 }
00706
00713 void Window::ReInit(int rx, int ry)
00714 {
00715 this->SetDirty();
00716
00717
00718 int window_width = this->width;
00719 int window_height = this->height;
00720
00721 this->OnInit();
00722
00723 this->nested_root->SetupSmallestSize(this, false);
00724 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
00725 this->width = this->nested_root->smallest_x;
00726 this->height = this->nested_root->smallest_y;
00727 this->resize.step_width = this->nested_root->resize_x;
00728 this->resize.step_height = this->nested_root->resize_y;
00729
00730
00731 window_width = max(window_width + rx, this->width);
00732 window_height = max(window_height + ry, this->height);
00733 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
00734 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
00735
00736
00737 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
00738 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
00739
00740 ResizeWindow(this, dx, dy);
00741
00742 }
00743
00749 void Window::SetShaded(bool make_shaded)
00750 {
00751 if (this->shade_select == NULL) return;
00752
00753 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
00754 if (this->shade_select->shown_plane != desired) {
00755 if (make_shaded) {
00756 this->unshaded_size.width = this->width;
00757 this->unshaded_size.height = this->height;
00758 this->shade_select->SetDisplayedPlane(desired);
00759 this->ReInit(0, -this->height);
00760 } else {
00761 this->shade_select->SetDisplayedPlane(desired);
00762 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
00763 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
00764 this->ReInit(dx, dy);
00765 }
00766 }
00767 }
00768
00775 static Window *FindChildWindow(const Window *w, WindowClass wc)
00776 {
00777 Window *v;
00778 FOR_ALL_WINDOWS_FROM_BACK(v) {
00779 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
00780 }
00781
00782 return NULL;
00783 }
00784
00789 void Window::DeleteChildWindows(WindowClass wc) const
00790 {
00791 Window *child = FindChildWindow(this, wc);
00792 while (child != NULL) {
00793 delete child;
00794 child = FindChildWindow(this, wc);
00795 }
00796 }
00797
00801 Window::~Window()
00802 {
00803 if (_thd.window_class == this->window_class &&
00804 _thd.window_number == this->window_number) {
00805 ResetObjectToPlace();
00806 }
00807
00808
00809 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00810
00811
00812 if (_last_scroll_window == this) _last_scroll_window = NULL;
00813
00814
00815 if (_focused_window == this) _focused_window = NULL;
00816
00817 this->DeleteChildWindows();
00818
00819 if (this->viewport != NULL) DeleteWindowViewport(this);
00820
00821 this->SetDirty();
00822
00823 free(this->nested_array);
00824 delete this->nested_root;
00825
00826 this->window_class = WC_INVALID;
00827 }
00828
00835 Window *FindWindowById(WindowClass cls, WindowNumber number)
00836 {
00837 Window *w;
00838 FOR_ALL_WINDOWS_FROM_BACK(w) {
00839 if (w->window_class == cls && w->window_number == number) return w;
00840 }
00841
00842 return NULL;
00843 }
00844
00851 Window *FindWindowByClass(WindowClass cls)
00852 {
00853 Window *w;
00854 FOR_ALL_WINDOWS_FROM_BACK(w) {
00855 if (w->window_class == cls) return w;
00856 }
00857
00858 return NULL;
00859 }
00860
00867 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00868 {
00869 Window *w = FindWindowById(cls, number);
00870 if (force || w == NULL ||
00871 (w->flags & WF_STICKY) == 0) {
00872 delete w;
00873 }
00874 }
00875
00880 void DeleteWindowByClass(WindowClass cls)
00881 {
00882 Window *w;
00883
00884 restart_search:
00885
00886
00887
00888 FOR_ALL_WINDOWS_FROM_BACK(w) {
00889 if (w->window_class == cls) {
00890 delete w;
00891 goto restart_search;
00892 }
00893 }
00894 }
00895
00902 void DeleteCompanyWindows(CompanyID id)
00903 {
00904 Window *w;
00905
00906 restart_search:
00907
00908
00909
00910 FOR_ALL_WINDOWS_FROM_BACK(w) {
00911 if (w->owner == id) {
00912 delete w;
00913 goto restart_search;
00914 }
00915 }
00916
00917
00918 DeleteWindowById(WC_BUY_COMPANY, id);
00919 }
00920
00928 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00929 {
00930 Window *w;
00931 FOR_ALL_WINDOWS_FROM_BACK(w) {
00932 if (w->owner != old_owner) continue;
00933
00934 switch (w->window_class) {
00935 case WC_COMPANY_COLOUR:
00936 case WC_FINANCES:
00937 case WC_STATION_LIST:
00938 case WC_TRAINS_LIST:
00939 case WC_ROADVEH_LIST:
00940 case WC_SHIPS_LIST:
00941 case WC_AIRCRAFT_LIST:
00942 case WC_BUY_COMPANY:
00943 case WC_COMPANY:
00944 case WC_COMPANY_INFRASTRUCTURE:
00945 continue;
00946
00947 default:
00948 w->owner = new_owner;
00949 break;
00950 }
00951 }
00952 }
00953
00954 static void BringWindowToFront(Window *w);
00955
00963 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00964 {
00965 Window *w = FindWindowById(cls, number);
00966
00967 if (w != NULL) {
00968 if (w->IsShaded()) w->SetShaded(false);
00969
00970 w->SetWhiteBorder();
00971 BringWindowToFront(w);
00972 w->SetDirty();
00973 }
00974
00975 return w;
00976 }
00977
00978 static inline bool IsVitalWindow(const Window *w)
00979 {
00980 switch (w->window_class) {
00981 case WC_MAIN_TOOLBAR:
00982 case WC_STATUS_BAR:
00983 case WC_NEWS_WINDOW:
00984 case WC_SEND_NETWORK_MSG:
00985 return true;
00986
00987 default:
00988 return false;
00989 }
00990 }
00991
01000 static uint GetWindowZPriority(const Window *w)
01001 {
01002 assert(w->window_class != WC_INVALID);
01003
01004 uint z_priority = 0;
01005
01006 switch (w->window_class) {
01007 case WC_ENDSCREEN:
01008 ++z_priority;
01009
01010 case WC_HIGHSCORE:
01011 ++z_priority;
01012
01013 case WC_TOOLTIPS:
01014 ++z_priority;
01015
01016 case WC_DROPDOWN_MENU:
01017 ++z_priority;
01018
01019 case WC_MAIN_TOOLBAR:
01020 case WC_STATUS_BAR:
01021 ++z_priority;
01022
01023 case WC_OSK:
01024 ++z_priority;
01025
01026 case WC_QUERY_STRING:
01027 case WC_SEND_NETWORK_MSG:
01028 ++z_priority;
01029
01030 case WC_ERRMSG:
01031 case WC_CONFIRM_POPUP_QUERY:
01032 case WC_MODAL_PROGRESS:
01033 case WC_NETWORK_STATUS_WINDOW:
01034 ++z_priority;
01035
01036 case WC_GENERATE_LANDSCAPE:
01037 case WC_SAVELOAD:
01038 case WC_GAME_OPTIONS:
01039 case WC_CUSTOM_CURRENCY:
01040 case WC_NETWORK_WINDOW:
01041 case WC_GRF_PARAMETERS:
01042 case WC_AI_LIST:
01043 case WC_AI_SETTINGS:
01044 case WC_TEXTFILE:
01045 ++z_priority;
01046
01047 case WC_CONSOLE:
01048 ++z_priority;
01049
01050 case WC_NEWS_WINDOW:
01051 ++z_priority;
01052
01053 default:
01054 ++z_priority;
01055
01056 case WC_MAIN_WINDOW:
01057 return z_priority;
01058 }
01059 }
01060
01065 static void AddWindowToZOrdering(Window *w)
01066 {
01067 assert(w->z_front == NULL && w->z_back == NULL);
01068
01069 if (_z_front_window == NULL) {
01070
01071 _z_front_window = _z_back_window = w;
01072 w->z_front = w->z_back = NULL;
01073 } else {
01074
01075 Window *v = _z_front_window;
01076 uint last_z_priority = UINT_MAX;
01077 while (v != NULL && (v->window_class == WC_INVALID || GetWindowZPriority(v) > GetWindowZPriority(w))) {
01078 if (v->window_class != WC_INVALID) {
01079
01080 assert(last_z_priority >= GetWindowZPriority(v));
01081 last_z_priority = GetWindowZPriority(v);
01082 }
01083
01084 v = v->z_back;
01085 }
01086
01087 if (v == NULL) {
01088
01089 w->z_front = _z_back_window;
01090 w->z_back = NULL;
01091 _z_back_window->z_back = w;
01092 _z_back_window = w;
01093 } else if (v == _z_front_window) {
01094
01095 w->z_front = NULL;
01096 w->z_back = _z_front_window;
01097 _z_front_window->z_front = w;
01098 _z_front_window = w;
01099 } else {
01100
01101 w->z_front = v->z_front;
01102 w->z_back = v;
01103 v->z_front->z_back = w;
01104 v->z_front = w;
01105 }
01106 }
01107 }
01108
01109
01114 static void RemoveWindowFromZOrdering(Window *w)
01115 {
01116 if (w->z_front == NULL) {
01117 assert(_z_front_window == w);
01118 _z_front_window = w->z_back;
01119 } else {
01120 w->z_front->z_back = w->z_back;
01121 }
01122
01123 if (w->z_back == NULL) {
01124 assert(_z_back_window == w);
01125 _z_back_window = w->z_front;
01126 } else {
01127 w->z_back->z_front = w->z_front;
01128 }
01129
01130 w->z_front = w->z_back = NULL;
01131 }
01132
01138 static void BringWindowToFront(Window *w)
01139 {
01140 RemoveWindowFromZOrdering(w);
01141 AddWindowToZOrdering(w);
01142
01143 w->SetDirty();
01144 }
01145
01154 void Window::InitializeData(const WindowDesc *desc, WindowNumber window_number)
01155 {
01156
01157 this->window_class = desc->cls;
01158 this->SetWhiteBorder();
01159 if (desc->default_pos == WDP_CENTER) this->flags |= WF_CENTERED;
01160 this->owner = INVALID_OWNER;
01161 this->nested_focus = NULL;
01162 this->window_number = window_number;
01163 this->desc_flags = desc->flags;
01164
01165 this->OnInit();
01166
01167 if (this->nested_array == NULL) {
01168 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01169 this->nested_root->SetupSmallestSize(this, true);
01170 } else {
01171 this->nested_root->SetupSmallestSize(this, false);
01172 }
01173
01174 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
01175
01176
01177
01178 this->resize.step_width = this->nested_root->resize_x;
01179 this->resize.step_height = this->nested_root->resize_y;
01180
01181
01182
01183
01184 if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL)) SetFocusedWindow(this);
01185
01186
01187 AddWindowToZOrdering(this);
01188 }
01189
01197 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
01198 {
01199 this->left = x;
01200 this->top = y;
01201 this->width = sm_width;
01202 this->height = sm_height;
01203 }
01204
01215 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
01216 {
01217 def_width = max(def_width, this->width);
01218 def_height = max(def_height, this->height);
01219
01220
01221
01222
01223
01224 if (this->width != def_width || this->height != def_height) {
01225
01226 int free_height = _screen.height;
01227 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
01228 if (wt != NULL) free_height -= wt->height;
01229 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01230 if (wt != NULL) free_height -= wt->height;
01231
01232 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
01233 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
01234
01235
01236
01237
01238 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
01239 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
01240
01241 ResizeWindow(this, enlarge_x, enlarge_y);
01242
01243 } else {
01244
01245 this->OnResize();
01246 }
01247
01248 int nx = this->left;
01249 int ny = this->top;
01250
01251 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
01252
01253 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01254 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
01255 nx = max(nx, 0);
01256
01257 if (this->viewport != NULL) {
01258 this->viewport->left += nx - this->left;
01259 this->viewport->top += ny - this->top;
01260 }
01261 this->left = nx;
01262 this->top = ny;
01263
01264 this->SetDirty();
01265 }
01266
01278 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
01279 {
01280 int right = width + left;
01281 int bottom = height + top;
01282
01283 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01284 if (left < 0 || (main_toolbar != NULL && top < main_toolbar->height) || right > _screen.width || bottom > _screen.height) return false;
01285
01286
01287 const Window *w;
01288 FOR_ALL_WINDOWS_FROM_BACK(w) {
01289 if (w->window_class == WC_MAIN_WINDOW) continue;
01290
01291 if (right > w->left &&
01292 w->left + w->width > left &&
01293 bottom > w->top &&
01294 w->top + w->height > top) {
01295 return false;
01296 }
01297 }
01298
01299 pos.x = left;
01300 pos.y = top;
01301 return true;
01302 }
01303
01315 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
01316 {
01317
01318
01319
01320 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
01321
01322 if (top < 22 || top > _screen.height - (height >> 2)) return false;
01323
01324
01325 const Window *w;
01326 FOR_ALL_WINDOWS_FROM_BACK(w) {
01327 if (w->window_class == WC_MAIN_WINDOW) continue;
01328
01329 if (left + width > w->left &&
01330 w->left + w->width > left &&
01331 top + height > w->top &&
01332 w->top + w->height > top) {
01333 return false;
01334 }
01335 }
01336
01337 pos.x = left;
01338 pos.y = top;
01339 return true;
01340 }
01341
01348 static Point GetAutoPlacePosition(int width, int height)
01349 {
01350 Point pt;
01351
01352
01353 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01354 if (IsGoodAutoPlace1(0, main_toolbar != NULL ? main_toolbar->height + 2 : 2, width, height, pt)) return pt;
01355
01356
01357
01358
01359
01360 const Window *w;
01361 FOR_ALL_WINDOWS_FROM_BACK(w) {
01362 if (w->window_class == WC_MAIN_WINDOW) continue;
01363
01364 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01365 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01366 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01367 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01368 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01369 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01370 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01371 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01372 }
01373
01374
01375
01376
01377
01378 FOR_ALL_WINDOWS_FROM_BACK(w) {
01379 if (w->window_class == WC_MAIN_WINDOW) continue;
01380
01381 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01382 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01383 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01384 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01385 }
01386
01387
01388
01389
01390 int left = 0, top = 24;
01391
01392 restart:
01393 FOR_ALL_WINDOWS_FROM_BACK(w) {
01394 if (w->left == left && w->top == top) {
01395 left += 5;
01396 top += 5;
01397 goto restart;
01398 }
01399 }
01400
01401 pt.x = left;
01402 pt.y = top;
01403 return pt;
01404 }
01405
01412 Point GetToolbarAlignedWindowPosition(int window_width)
01413 {
01414 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01415 assert(w != NULL);
01416 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
01417 return pt;
01418 }
01419
01437 static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01438 {
01439 Point pt;
01440 const Window *w;
01441
01442 int16 default_width = max(desc->default_width, sm_width);
01443 int16 default_height = max(desc->default_height, sm_height);
01444
01445 if (desc->parent_cls != 0 &&
01446 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01447 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01448
01449 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01450 if (pt.x > _screen.width + 10 - default_width) {
01451 pt.x = (_screen.width + 10 - default_width) - 20;
01452 }
01453 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? w->height : 10);
01454 return pt;
01455 }
01456
01457 switch (desc->default_pos) {
01458 case WDP_ALIGN_TOOLBAR:
01459 return GetToolbarAlignedWindowPosition(default_width);
01460
01461 case WDP_AUTO:
01462 return GetAutoPlacePosition(default_width, default_height);
01463
01464 case WDP_CENTER:
01465 pt.x = (_screen.width - default_width) / 2;
01466 pt.y = (_screen.height - default_height) / 2;
01467 break;
01468
01469 case WDP_MANUAL:
01470 pt.x = 0;
01471 pt.y = 0;
01472 break;
01473
01474 default:
01475 NOT_REACHED();
01476 }
01477
01478 return pt;
01479 }
01480
01481 Point Window::OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01482 {
01483 return LocalGetWindowPlacement(desc, sm_width, sm_height, window_number);
01484 }
01485
01494 void Window::CreateNestedTree(const WindowDesc *desc, bool fill_nested)
01495 {
01496 int biggest_index = -1;
01497 this->nested_root = MakeWindowNWidgetTree(desc->nwid_parts, desc->nwid_length, &biggest_index, &this->shade_select);
01498 this->nested_array_size = (uint)(biggest_index + 1);
01499
01500 if (fill_nested) {
01501 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01502 this->nested_root->FillNestedArray(this->nested_array, this->nested_array_size);
01503 }
01504 }
01505
01511 void Window::FinishInitNested(const WindowDesc *desc, WindowNumber window_number)
01512 {
01513 this->InitializeData(desc, window_number);
01514 Point pt = this->OnInitialPosition(desc, this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
01515 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
01516 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
01517 }
01518
01524 void Window::InitNested(const WindowDesc *desc, WindowNumber window_number)
01525 {
01526 this->CreateNestedTree(desc, false);
01527 this->FinishInitNested(desc, window_number);
01528 }
01529
01531 Window::Window() : scrolling_scrollbar(-1)
01532 {
01533 }
01534
01542 Window *FindWindowFromPt(int x, int y)
01543 {
01544 Window *w;
01545 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01546 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01547 return w;
01548 }
01549 }
01550
01551 return NULL;
01552 }
01553
01557 void InitWindowSystem()
01558 {
01559 IConsoleClose();
01560
01561 _z_back_window = NULL;
01562 _z_front_window = NULL;
01563 _focused_window = NULL;
01564 _mouseover_last_w = NULL;
01565 _last_scroll_window = NULL;
01566 _scrolling_viewport = false;
01567 _mouse_hovering = false;
01568
01569 NWidgetLeaf::InvalidateDimensionCache();
01570 NWidgetScrollbar::InvalidateDimensionCache();
01571
01572 ShowFirstError();
01573 }
01574
01578 void UnInitWindowSystem()
01579 {
01580 UnshowCriticalError();
01581
01582 Window *w;
01583 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01584
01585 for (w = _z_front_window; w != NULL; ) {
01586 Window *to_del = w;
01587 w = w->z_back;
01588 free(to_del);
01589 }
01590
01591 _z_front_window = NULL;
01592 _z_back_window = NULL;
01593 }
01594
01598 void ResetWindowSystem()
01599 {
01600 UnInitWindowSystem();
01601 InitWindowSystem();
01602 _thd.Reset();
01603 }
01604
01605 static void DecreaseWindowCounters()
01606 {
01607 Window *w;
01608 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01609 if (_scroller_click_timeout == 0) {
01610
01611 for (uint i = 0; i < w->nested_array_size; i++) {
01612 NWidgetBase *nwid = w->nested_array[i];
01613 if (nwid != NULL && (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR)) {
01614 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
01615 if (sb->disp_flags & (ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN)) {
01616 sb->disp_flags &= ~(ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN);
01617 w->scrolling_scrollbar = -1;
01618 sb->SetDirty(w);
01619 }
01620 }
01621 }
01622 }
01623 w->OnMouseLoop();
01624 }
01625
01626 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01627 if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) {
01628 CLRBITS(w->flags, WF_TIMEOUT);
01629
01630 w->OnTimeout();
01631 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons(true);
01632 }
01633 }
01634 }
01635
01636 static void HandlePlacePresize()
01637 {
01638 if (_special_mouse_mode != WSM_PRESIZE) return;
01639
01640 Window *w = _thd.GetCallbackWnd();
01641 if (w == NULL) return;
01642
01643 Point pt = GetTileBelowCursor();
01644 if (pt.x == -1) {
01645 _thd.selend.x = -1;
01646 return;
01647 }
01648
01649 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01650 }
01651
01656 static EventState HandleMouseDragDrop()
01657 {
01658 if (_special_mouse_mode != WSM_DRAGDROP) return ES_NOT_HANDLED;
01659
01660 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01661
01662 Window *w = _thd.GetCallbackWnd();
01663 if (w != NULL) {
01664
01665 Point pt;
01666 pt.x = _cursor.pos.x - w->left;
01667 pt.y = _cursor.pos.y - w->top;
01668 if (_left_button_down) {
01669 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
01670 } else {
01671 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01672 }
01673 }
01674
01675 if (!_left_button_down) ResetObjectToPlace();
01676 return ES_HANDLED;
01677 }
01678
01680 static void HandleMouseOver()
01681 {
01682 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01683
01684
01685 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01686
01687 Point pt = { -1, -1 };
01688 _mouseover_last_w->OnMouseOver(pt, 0);
01689 }
01690
01691
01692 _mouseover_last_w = w;
01693
01694 if (w != NULL) {
01695
01696 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01697 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
01698 if (widget != NULL) w->OnMouseOver(pt, widget->index);
01699 }
01700 }
01701
01703 static const int MIN_VISIBLE_TITLE_BAR = 13;
01704
01706 enum PreventHideDirection {
01707 PHD_UP,
01708 PHD_DOWN,
01709 };
01710
01721 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
01722 {
01723 if (v == NULL) return;
01724
01725 int v_bottom = v->top + v->height;
01726 int v_right = v->left + v->width;
01727 int safe_y = (dir == PHD_UP) ? (v->top - MIN_VISIBLE_TITLE_BAR - rect.top) : (v_bottom + MIN_VISIBLE_TITLE_BAR - rect.bottom);
01728
01729 if (*ny + rect.top <= v->top - MIN_VISIBLE_TITLE_BAR) return;
01730 if (*ny + rect.bottom >= v_bottom + MIN_VISIBLE_TITLE_BAR) return;
01731
01732
01733 if (*nx + rect.left + MIN_VISIBLE_TITLE_BAR < v->left) {
01734 if (v->left < MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01735 return;
01736 }
01737 if (*nx + rect.right - MIN_VISIBLE_TITLE_BAR > v_right) {
01738 if (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01739 return;
01740 }
01741
01742
01743 if (px + rect.left < v->left && v->left >= MIN_VISIBLE_TITLE_BAR) {
01744 *nx = v->left - MIN_VISIBLE_TITLE_BAR - rect.left;
01745 } else if (px + rect.right > v_right && v_right <= _screen.width - MIN_VISIBLE_TITLE_BAR) {
01746 *nx = v_right + MIN_VISIBLE_TITLE_BAR - rect.right;
01747 } else {
01748 *ny = safe_y;
01749 }
01750 }
01751
01759 static void EnsureVisibleCaption(Window *w, int nx, int ny)
01760 {
01761
01762 Rect caption_rect;
01763 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
01764 if (caption != NULL) {
01765 caption_rect.left = caption->pos_x;
01766 caption_rect.right = caption->pos_x + caption->current_x;
01767 caption_rect.top = caption->pos_y;
01768 caption_rect.bottom = caption->pos_y + caption->current_y;
01769
01770
01771 nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
01772 ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
01773
01774
01775 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
01776 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
01777 }
01778
01779 if (w->viewport != NULL) {
01780 w->viewport->left += nx - w->left;
01781 w->viewport->top += ny - w->top;
01782 }
01783
01784 w->left = nx;
01785 w->top = ny;
01786 }
01787
01798 void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
01799 {
01800 if (delta_x != 0 || delta_y != 0) {
01801 if (clamp_to_screen) {
01802
01803
01804 int new_right = w->left + w->width + delta_x;
01805 int new_bottom = w->top + w->height + delta_y;
01806 if (new_right >= (int)_cur_resolution.width) delta_x -= Ceil(new_right - _cur_resolution.width, max(1U, w->nested_root->resize_x));
01807 if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, max(1U, w->nested_root->resize_y));
01808 }
01809
01810 w->SetDirty();
01811
01812 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);
01813 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);
01814 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
01815 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
01816
01817 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);
01818 w->width = w->nested_root->current_x;
01819 w->height = w->nested_root->current_y;
01820 }
01821
01822 EnsureVisibleCaption(w, w->left, w->top);
01823
01824
01825 w->OnResize();
01826 w->SetDirty();
01827 }
01828
01834 int GetMainViewTop()
01835 {
01836 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01837 return (w == NULL) ? 0 : w->top + w->height;
01838 }
01839
01845 int GetMainViewBottom()
01846 {
01847 Window *w = FindWindowById(WC_STATUS_BAR, 0);
01848 return (w == NULL) ? _screen.height : w->top;
01849 }
01850
01851 static bool _dragging_window;
01852
01857 static EventState HandleWindowDragging()
01858 {
01859
01860 if (!_dragging_window) return ES_NOT_HANDLED;
01861
01862
01863 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01864
01865
01866 Window *w;
01867 FOR_ALL_WINDOWS_FROM_BACK(w) {
01868 if (w->flags & WF_DRAGGING) {
01869
01870 if (!_left_button_down) {
01871 w->flags &= ~WF_DRAGGING;
01872 break;
01873 }
01874
01875 w->SetDirty();
01876
01877 int x = _cursor.pos.x + _drag_delta.x;
01878 int y = _cursor.pos.y + _drag_delta.y;
01879 int nx = x;
01880 int ny = y;
01881
01882 if (_settings_client.gui.window_snap_radius != 0) {
01883 const Window *v;
01884
01885 int hsnap = _settings_client.gui.window_snap_radius;
01886 int vsnap = _settings_client.gui.window_snap_radius;
01887 int delta;
01888
01889 FOR_ALL_WINDOWS_FROM_BACK(v) {
01890 if (v == w) continue;
01891
01892 if (y + w->height > v->top && y < v->top + v->height) {
01893
01894 delta = abs(v->left + v->width - x);
01895 if (delta <= hsnap) {
01896 nx = v->left + v->width;
01897 hsnap = delta;
01898 }
01899
01900
01901 delta = abs(v->left - x - w->width);
01902 if (delta <= hsnap) {
01903 nx = v->left - w->width;
01904 hsnap = delta;
01905 }
01906 }
01907
01908 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01909
01910 delta = abs(v->left - x);
01911 if (delta <= hsnap) {
01912 nx = v->left;
01913 hsnap = delta;
01914 }
01915
01916
01917 delta = abs(v->left + v->width - x - w->width);
01918 if (delta <= hsnap) {
01919 nx = v->left + v->width - w->width;
01920 hsnap = delta;
01921 }
01922 }
01923
01924 if (x + w->width > v->left && x < v->left + v->width) {
01925
01926 delta = abs(v->top + v->height - y);
01927 if (delta <= vsnap) {
01928 ny = v->top + v->height;
01929 vsnap = delta;
01930 }
01931
01932
01933 delta = abs(v->top - y - w->height);
01934 if (delta <= vsnap) {
01935 ny = v->top - w->height;
01936 vsnap = delta;
01937 }
01938 }
01939
01940 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01941
01942 delta = abs(v->top - y);
01943 if (delta <= vsnap) {
01944 ny = v->top;
01945 vsnap = delta;
01946 }
01947
01948
01949 delta = abs(v->top + v->height - y - w->height);
01950 if (delta <= vsnap) {
01951 ny = v->top + v->height - w->height;
01952 vsnap = delta;
01953 }
01954 }
01955 }
01956 }
01957
01958 EnsureVisibleCaption(w, nx, ny);
01959
01960 w->SetDirty();
01961 return ES_HANDLED;
01962 } else if (w->flags & WF_SIZING) {
01963
01964 if (!_left_button_down) {
01965 w->flags &= ~WF_SIZING;
01966 w->SetDirty();
01967 break;
01968 }
01969
01970
01971
01972
01973 int x, y = _cursor.pos.y - _drag_delta.y;
01974 if (w->flags & WF_SIZING_LEFT) {
01975 x = _drag_delta.x - _cursor.pos.x;
01976 } else {
01977 x = _cursor.pos.x - _drag_delta.x;
01978 }
01979
01980
01981 if (w->resize.step_width == 0) x = 0;
01982 if (w->resize.step_height == 0) y = 0;
01983
01984
01985 if (w->top + w->height + y > _screen.height) {
01986 y = _screen.height - w->height - w->top;
01987 }
01988
01989
01990
01991
01992 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
01993 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
01994
01995
01996 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
01997 x = w->nested_root->smallest_x - w->width;
01998 }
01999 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
02000 y = w->nested_root->smallest_y - w->height;
02001 }
02002
02003
02004 if (x == 0 && y == 0) return ES_HANDLED;
02005
02006
02007 _drag_delta.y += y;
02008 if ((w->flags & WF_SIZING_LEFT) && x != 0) {
02009 _drag_delta.x -= x;
02010 w->SetDirty();
02011 w->left -= x;
02012
02013 } else {
02014 _drag_delta.x += x;
02015 }
02016
02017
02018 ResizeWindow(w, x, y);
02019 return ES_HANDLED;
02020 }
02021 }
02022
02023 _dragging_window = false;
02024 return ES_HANDLED;
02025 }
02026
02031 static void StartWindowDrag(Window *w)
02032 {
02033 w->flags |= WF_DRAGGING;
02034 w->flags &= ~WF_CENTERED;
02035 _dragging_window = true;
02036
02037 _drag_delta.x = w->left - _cursor.pos.x;
02038 _drag_delta.y = w->top - _cursor.pos.y;
02039
02040 BringWindowToFront(w);
02041 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02042 }
02043
02049 static void StartWindowSizing(Window *w, bool to_left)
02050 {
02051 w->flags |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
02052 w->flags &= ~WF_CENTERED;
02053 _dragging_window = true;
02054
02055 _drag_delta.x = _cursor.pos.x;
02056 _drag_delta.y = _cursor.pos.y;
02057
02058 BringWindowToFront(w);
02059 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02060 }
02061
02066 static EventState HandleScrollbarScrolling()
02067 {
02068 Window *w;
02069 FOR_ALL_WINDOWS_FROM_BACK(w) {
02070 if (w->scrolling_scrollbar >= 0) {
02071
02072 if (!_left_button_down) {
02073 w->scrolling_scrollbar = -1;
02074 w->SetDirty();
02075 return ES_HANDLED;
02076 }
02077
02078 int i;
02079 NWidgetScrollbar *sb = w->GetWidget<NWidgetScrollbar>(w->scrolling_scrollbar);
02080 bool rtl = false;
02081
02082 if (sb->type == NWID_HSCROLLBAR) {
02083 i = _cursor.pos.x - _cursorpos_drag_start.x;
02084 rtl = _current_text_dir == TD_RTL;
02085 } else {
02086 i = _cursor.pos.y - _cursorpos_drag_start.y;
02087 }
02088
02089 if (sb->disp_flags & ND_SCROLLBAR_BTN) {
02090 if (_scroller_click_timeout == 1) {
02091 _scroller_click_timeout = 3;
02092 sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1);
02093 w->SetDirty();
02094 }
02095 return ES_HANDLED;
02096 }
02097
02098
02099 int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity()));
02100 if (rtl) pos = max(0, sb->GetCount() - sb->GetCapacity() - pos);
02101 if (pos != sb->GetPosition()) {
02102 sb->SetPosition(pos);
02103 w->SetDirty();
02104 }
02105 return ES_HANDLED;
02106 }
02107 }
02108
02109 return ES_NOT_HANDLED;
02110 }
02111
02116 static EventState HandleViewportScroll()
02117 {
02118 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02119
02120 if (!_scrolling_viewport) return ES_NOT_HANDLED;
02121
02122
02123
02124
02125 if (_last_scroll_window == NULL) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
02126
02127 if (_last_scroll_window == NULL || !(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down))) {
02128 _cursor.fix_at = false;
02129 _scrolling_viewport = false;
02130 _last_scroll_window = NULL;
02131 return ES_NOT_HANDLED;
02132 }
02133
02134 if (_last_scroll_window == FindWindowById(WC_MAIN_WINDOW, 0) && _last_scroll_window->viewport->follow_vehicle != INVALID_VEHICLE) {
02135
02136 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
02137 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
02138 return ES_NOT_HANDLED;
02139 }
02140
02141 Point delta;
02142 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
02143 delta.x = -_cursor.delta.x;
02144 delta.y = -_cursor.delta.y;
02145 } else {
02146 delta.x = _cursor.delta.x;
02147 delta.y = _cursor.delta.y;
02148 }
02149
02150 if (scrollwheel_scrolling) {
02151
02152 delta.x = _cursor.h_wheel;
02153 delta.y = _cursor.v_wheel;
02154 _cursor.v_wheel = 0;
02155 _cursor.h_wheel = 0;
02156 }
02157
02158
02159 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
02160
02161 _cursor.delta.x = 0;
02162 _cursor.delta.y = 0;
02163 return ES_HANDLED;
02164 }
02165
02176 static bool MaybeBringWindowToFront(Window *w)
02177 {
02178 bool bring_to_front = false;
02179
02180 if (w->window_class == WC_MAIN_WINDOW ||
02181 IsVitalWindow(w) ||
02182 w->window_class == WC_TOOLTIPS ||
02183 w->window_class == WC_DROPDOWN_MENU) {
02184 return true;
02185 }
02186
02187
02188 int w_width = w->width;
02189 int w_height = w->height;
02190 if (w->IsShaded()) {
02191 w_width = w->unshaded_size.width;
02192 w_height = w->unshaded_size.height;
02193 }
02194
02195 Window *u;
02196 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
02197
02198 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
02199 u->SetWhiteBorder();
02200 u->SetDirty();
02201 return false;
02202 }
02203
02204 if (u->window_class == WC_MAIN_WINDOW ||
02205 IsVitalWindow(u) ||
02206 u->window_class == WC_TOOLTIPS ||
02207 u->window_class == WC_DROPDOWN_MENU) {
02208 continue;
02209 }
02210
02211
02212 if (w->left + w_width <= u->left ||
02213 u->left + u->width <= w->left ||
02214 w->top + w_height <= u->top ||
02215 u->top + u->height <= w->top) {
02216 continue;
02217 }
02218
02219 bring_to_front = true;
02220 }
02221
02222 if (bring_to_front) BringWindowToFront(w);
02223 return true;
02224 }
02225
02230 void HandleKeypress(uint32 raw_key)
02231 {
02232
02233
02234 assert(HasModalProgress() || IsLocalCompany());
02235
02236
02237 uint16 key = GB(raw_key, 0, 16);
02238 uint16 keycode = GB(raw_key, 16, 16);
02239
02240
02241
02242
02243
02244
02245
02246
02247 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
02248
02249
02250
02251
02252 if (key == 0 && keycode == 0) return;
02253
02254
02255 if (EditBoxInGlobalFocus()) {
02256
02257 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
02258 }
02259
02260
02261 Window *w;
02262 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02263 if (w->window_class == WC_MAIN_TOOLBAR) continue;
02264 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02265 }
02266
02267 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02268
02269 if (w != NULL && w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02270
02271 HandleGlobalHotkeys(key, keycode);
02272 }
02273
02277 void HandleCtrlChanged()
02278 {
02279
02280 Window *w;
02281 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02282 if (w->OnCTRLStateChange() == ES_HANDLED) return;
02283 }
02284 }
02285
02292 static int _input_events_this_tick = 0;
02293
02298 static void HandleAutoscroll()
02299 {
02300 if (_game_mode == GM_MENU || HasModalProgress()) return;
02301 if (_settings_client.gui.auto_scrolling == VA_DISABLED) return;
02302 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
02303
02304 int x = _cursor.pos.x;
02305 int y = _cursor.pos.y;
02306 Window *w = FindWindowFromPt(x, y);
02307 if (w == NULL || w->flags & WF_DISABLE_VP_SCROLL) return;
02308 if (_settings_client.gui.auto_scrolling != VA_EVERY_VIEWPORT && w->window_class != WC_MAIN_WINDOW) return;
02309
02310 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02311 if (vp == NULL) return;
02312
02313 x -= vp->left;
02314 y -= vp->top;
02315
02316
02317 #define scrollspeed 3
02318 if (x - 15 < 0) {
02319 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
02320 } else if (15 - (vp->width - x) > 0) {
02321 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
02322 }
02323 if (y - 15 < 0) {
02324 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
02325 } else if (15 - (vp->height - y) > 0) {
02326 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
02327 }
02328 #undef scrollspeed
02329 }
02330
02331 enum MouseClick {
02332 MC_NONE = 0,
02333 MC_LEFT,
02334 MC_RIGHT,
02335 MC_DOUBLE_LEFT,
02336 MC_HOVER,
02337
02338 MAX_OFFSET_DOUBLE_CLICK = 5,
02339 TIME_BETWEEN_DOUBLE_CLICK = 500,
02340 MAX_OFFSET_HOVER = 5,
02341 };
02342 extern EventState VpHandlePlaceSizingDrag();
02343
02344 static void ScrollMainViewport(int x, int y)
02345 {
02346 if (_game_mode != GM_MENU) {
02347 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02348 assert(w);
02349
02350 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
02351 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
02352 }
02353 }
02354
02364 static const int8 scrollamt[16][2] = {
02365 { 0, 0},
02366 {-2, 0},
02367 { 0, -2},
02368 {-2, -1},
02369 { 2, 0},
02370 { 0, 0},
02371 { 2, -1},
02372 { 0, -2},
02373 { 0, 2},
02374 {-2, 1},
02375 { 0, 0},
02376 {-2, 0},
02377 { 2, 1},
02378 { 0, 2},
02379 { 2, 0},
02380 { 0, 0},
02381 };
02382
02383 static void HandleKeyScrolling()
02384 {
02385
02386
02387
02388
02389 if (_dirkeys && !EditBoxInGlobalFocus()) {
02390 int factor = _shift_pressed ? 50 : 10;
02391 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
02392 }
02393 }
02394
02395 static void MouseLoop(MouseClick click, int mousewheel)
02396 {
02397
02398
02399 assert(HasModalProgress() || IsLocalCompany());
02400
02401 HandlePlacePresize();
02402 UpdateTileSelection();
02403
02404 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
02405 if (HandleMouseDragDrop() == ES_HANDLED) return;
02406 if (HandleWindowDragging() == ES_HANDLED) return;
02407 if (HandleScrollbarScrolling() == ES_HANDLED) return;
02408 if (HandleViewportScroll() == ES_HANDLED) return;
02409
02410 HandleMouseOver();
02411
02412 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02413 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
02414
02415 int x = _cursor.pos.x;
02416 int y = _cursor.pos.y;
02417 Window *w = FindWindowFromPt(x, y);
02418 if (w == NULL) return;
02419
02420 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
02421 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02422
02423
02424 if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
02425
02426 if (mousewheel != 0) {
02427
02428 w->OnMouseWheel(mousewheel);
02429
02430
02431 if (vp == NULL) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
02432 }
02433
02434 if (vp != NULL) {
02435 if (scrollwheel_scrolling) click = MC_RIGHT;
02436 switch (click) {
02437 case MC_DOUBLE_LEFT:
02438 case MC_LEFT:
02439 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
02440 if (!HandleViewportClicked(vp, x, y) &&
02441 !(w->flags & WF_DISABLE_VP_SCROLL) &&
02442 _settings_client.gui.left_mouse_btn_scrolling) {
02443 _scrolling_viewport = true;
02444 _cursor.fix_at = false;
02445 }
02446 break;
02447
02448 case MC_RIGHT:
02449 if (!(w->flags & WF_DISABLE_VP_SCROLL)) {
02450 _scrolling_viewport = true;
02451 _cursor.fix_at = true;
02452
02453
02454 _cursor.h_wheel = 0;
02455 _cursor.v_wheel = 0;
02456 }
02457 break;
02458
02459 default:
02460 break;
02461 }
02462 } else {
02463 switch (click) {
02464 case MC_LEFT:
02465 case MC_DOUBLE_LEFT:
02466 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
02467 break;
02468
02469 default:
02470 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
02471
02472
02473
02474
02475 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
02476
02477 case MC_HOVER: DispatchHoverEvent(w, x - w->left, y - w->top); break;
02478 }
02479 }
02480 }
02481
02485 void HandleMouseEvents()
02486 {
02487
02488
02489 assert(HasModalProgress() || IsLocalCompany());
02490
02491 static int double_click_time = 0;
02492 static Point double_click_pos = {0, 0};
02493
02494
02495 MouseClick click = MC_NONE;
02496 if (_left_button_down && !_left_button_clicked) {
02497 click = MC_LEFT;
02498 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02499 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
02500 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
02501 click = MC_DOUBLE_LEFT;
02502 }
02503 double_click_time = _realtime_tick;
02504 double_click_pos = _cursor.pos;
02505 _left_button_clicked = true;
02506 _input_events_this_tick++;
02507 } else if (_right_button_clicked) {
02508 _right_button_clicked = false;
02509 click = MC_RIGHT;
02510 _input_events_this_tick++;
02511 }
02512
02513 int mousewheel = 0;
02514 if (_cursor.wheel) {
02515 mousewheel = _cursor.wheel;
02516 _cursor.wheel = 0;
02517 _input_events_this_tick++;
02518 }
02519
02520 static uint32 hover_time = 0;
02521 static Point hover_pos = {0, 0};
02522
02523 if (_settings_client.gui.hover_delay > 0) {
02524 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
02525 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
02526 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
02527 hover_pos = _cursor.pos;
02528 hover_time = _realtime_tick;
02529 _mouse_hovering = false;
02530 } else {
02531 if (hover_time != 0 && _realtime_tick > hover_time + _settings_client.gui.hover_delay * 1000) {
02532 click = MC_HOVER;
02533 _input_events_this_tick++;
02534 _mouse_hovering = true;
02535 }
02536 }
02537 }
02538
02539
02540 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _newgrf_debug_sprite_picker.click_time != _realtime_tick) {
02541
02542 _newgrf_debug_sprite_picker.mode = SPM_NONE;
02543 InvalidateWindowData(WC_SPRITE_ALIGNER, 0, 1);
02544 }
02545
02546 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
02547
02548 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
02549 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
02550 _newgrf_debug_sprite_picker.click_time = _realtime_tick;
02551 _newgrf_debug_sprite_picker.sprites.Clear();
02552 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
02553 MarkWholeScreenDirty();
02554 } else {
02555 MouseLoop(click, mousewheel);
02556 }
02557
02558
02559
02560 _cursor.delta.x = 0;
02561 _cursor.delta.y = 0;
02562 }
02563
02567 static void CheckSoftLimit()
02568 {
02569 if (_settings_client.gui.window_soft_limit == 0) return;
02570
02571 for (;;) {
02572 uint deletable_count = 0;
02573 Window *w, *last_deletable = NULL;
02574 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02575 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue;
02576
02577 last_deletable = w;
02578 deletable_count++;
02579 }
02580
02581
02582 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02583
02584 assert(last_deletable != NULL);
02585 delete last_deletable;
02586 }
02587 }
02588
02592 void InputLoop()
02593 {
02594
02595
02596 assert(HasModalProgress() || IsLocalCompany());
02597
02598 CheckSoftLimit();
02599 HandleKeyScrolling();
02600
02601
02602 for (Window *v = _z_front_window; v != NULL; ) {
02603 Window *w = v;
02604 v = v->z_back;
02605
02606 if (w->window_class != WC_INVALID) continue;
02607
02608 RemoveWindowFromZOrdering(w);
02609 free(w);
02610 }
02611
02612 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
02613 DecreaseWindowCounters();
02614
02615 if (_input_events_this_tick != 0) {
02616
02617 _input_events_this_tick = 0;
02618
02619 return;
02620 }
02621
02622
02623 HandleMouseEvents();
02624 HandleAutoscroll();
02625 }
02626
02630 void UpdateWindows()
02631 {
02632 Window *w;
02633
02634 static int highlight_timer = 1;
02635 if (--highlight_timer == 0) {
02636 highlight_timer = 15;
02637 _window_highlight_colour = !_window_highlight_colour;
02638 }
02639
02640 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02641 w->ProcessScheduledInvalidations();
02642 w->ProcessHighlightedInvalidations();
02643 }
02644
02645 static int we4_timer = 0;
02646 int t = we4_timer + 1;
02647
02648 if (t >= 100) {
02649 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02650 w->OnHundredthTick();
02651 }
02652 t = 0;
02653 }
02654 we4_timer = t;
02655
02656 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02657 if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
02658 CLRBITS(w->flags, WF_WHITE_BORDER);
02659 w->SetDirty();
02660 }
02661 }
02662
02663 DrawDirtyBlocks();
02664
02665 FOR_ALL_WINDOWS_FROM_BACK(w) {
02666
02667 if (w->viewport != NULL && !w->IsShaded()) UpdateViewportPosition(w);
02668 }
02669 NetworkDrawChatMessage();
02670
02671 DrawMouseCursor();
02672 }
02673
02679 void SetWindowDirty(WindowClass cls, WindowNumber number)
02680 {
02681 const Window *w;
02682 FOR_ALL_WINDOWS_FROM_BACK(w) {
02683 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02684 }
02685 }
02686
02693 void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
02694 {
02695 const Window *w;
02696 FOR_ALL_WINDOWS_FROM_BACK(w) {
02697 if (w->window_class == cls && w->window_number == number) {
02698 w->SetWidgetDirty(widget_index);
02699 }
02700 }
02701 }
02702
02707 void SetWindowClassesDirty(WindowClass cls)
02708 {
02709 Window *w;
02710 FOR_ALL_WINDOWS_FROM_BACK(w) {
02711 if (w->window_class == cls) w->SetDirty();
02712 }
02713 }
02714
02720 void Window::InvalidateData(int data, bool gui_scope)
02721 {
02722 this->SetDirty();
02723 if (!gui_scope) {
02724
02725 *this->scheduled_invalidation_data.Append() = data;
02726 }
02727 this->OnInvalidateData(data, gui_scope);
02728 }
02729
02733 void Window::ProcessScheduledInvalidations()
02734 {
02735 for (int *data = this->scheduled_invalidation_data.Begin(); this->window_class != WC_INVALID && data != this->scheduled_invalidation_data.End(); data++) {
02736 this->OnInvalidateData(*data, true);
02737 }
02738 this->scheduled_invalidation_data.Clear();
02739 }
02740
02744 void Window::ProcessHighlightedInvalidations()
02745 {
02746 if ((this->flags & WF_HIGHLIGHTED) == 0) return;
02747
02748 for (uint i = 0; i < this->nested_array_size; i++) {
02749 if (this->IsWidgetHighlighted(i)) this->SetWidgetDirty(i);
02750 }
02751 }
02752
02779 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
02780 {
02781 Window *w;
02782 FOR_ALL_WINDOWS_FROM_BACK(w) {
02783 if (w->window_class == cls && w->window_number == number) {
02784 w->InvalidateData(data, gui_scope);
02785 }
02786 }
02787 }
02788
02797 void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
02798 {
02799 Window *w;
02800
02801 FOR_ALL_WINDOWS_FROM_BACK(w) {
02802 if (w->window_class == cls) {
02803 w->InvalidateData(data, gui_scope);
02804 }
02805 }
02806 }
02807
02811 void CallWindowTickEvent()
02812 {
02813 Window *w;
02814 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02815 w->OnTick();
02816 }
02817 }
02818
02825 void DeleteNonVitalWindows()
02826 {
02827 Window *w;
02828
02829 restart_search:
02830
02831
02832
02833 FOR_ALL_WINDOWS_FROM_BACK(w) {
02834 if (w->window_class != WC_MAIN_WINDOW &&
02835 w->window_class != WC_SELECT_GAME &&
02836 w->window_class != WC_MAIN_TOOLBAR &&
02837 w->window_class != WC_STATUS_BAR &&
02838 w->window_class != WC_TOOLTIPS &&
02839 (w->flags & WF_STICKY) == 0) {
02840
02841 delete w;
02842 goto restart_search;
02843 }
02844 }
02845 }
02846
02854 void DeleteAllNonVitalWindows()
02855 {
02856 Window *w;
02857
02858
02859 DeleteNonVitalWindows();
02860
02861 restart_search:
02862
02863
02864
02865 FOR_ALL_WINDOWS_FROM_BACK(w) {
02866 if (w->flags & WF_STICKY) {
02867 delete w;
02868 goto restart_search;
02869 }
02870 }
02871 }
02872
02877 void DeleteConstructionWindows()
02878 {
02879 Window *w;
02880
02881 restart_search:
02882
02883
02884
02885 FOR_ALL_WINDOWS_FROM_BACK(w) {
02886 if (w->desc_flags & WDF_CONSTRUCTION) {
02887 delete w;
02888 goto restart_search;
02889 }
02890 }
02891
02892 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02893 }
02894
02896 void HideVitalWindows()
02897 {
02898 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02899 DeleteWindowById(WC_STATUS_BAR, 0);
02900 }
02901
02903 void ReInitAllWindows()
02904 {
02905 NWidgetLeaf::InvalidateDimensionCache();
02906 NWidgetScrollbar::InvalidateDimensionCache();
02907
02908 Window *w;
02909 FOR_ALL_WINDOWS_FROM_BACK(w) {
02910 w->ReInit();
02911 }
02912 #ifdef ENABLE_NETWORK
02913 void NetworkReInitChatBoxSize();
02914 NetworkReInitChatBoxSize();
02915 #endif
02916
02917
02918 RelocateAllWindows(_cur_resolution.width, _cur_resolution.height);
02919 MarkWholeScreenDirty();
02920 }
02921
02929 static int PositionWindow(Window *w, WindowClass clss, int setting)
02930 {
02931 if (w == NULL || w->window_class != clss) {
02932 w = FindWindowById(clss, 0);
02933 }
02934 if (w == NULL) return 0;
02935
02936 int old_left = w->left;
02937 switch (setting) {
02938 case 1: w->left = (_screen.width - w->width) / 2; break;
02939 case 2: w->left = _screen.width - w->width; break;
02940 default: w->left = 0; break;
02941 }
02942 if (w->viewport != NULL) w->viewport->left += w->left - old_left;
02943 SetDirtyBlocks(0, w->top, _screen.width, w->top + w->height);
02944 return w->left;
02945 }
02946
02952 int PositionMainToolbar(Window *w)
02953 {
02954 DEBUG(misc, 5, "Repositioning Main Toolbar...");
02955 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
02956 }
02957
02963 int PositionStatusbar(Window *w)
02964 {
02965 DEBUG(misc, 5, "Repositioning statusbar...");
02966 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
02967 }
02968
02974 int PositionNewsMessage(Window *w)
02975 {
02976 DEBUG(misc, 5, "Repositioning news message...");
02977 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
02978 }
02979
02985 int PositionNetworkChatWindow(Window *w)
02986 {
02987 DEBUG(misc, 5, "Repositioning network chat window...");
02988 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
02989 }
02990
02991
02997 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
02998 {
02999 Window *w;
03000 FOR_ALL_WINDOWS_FROM_BACK(w) {
03001 if (w->viewport != NULL && w->viewport->follow_vehicle == from_index) {
03002 w->viewport->follow_vehicle = to_index;
03003 w->SetDirty();
03004 }
03005 }
03006 }
03007
03008
03014 void RelocateAllWindows(int neww, int newh)
03015 {
03016 Window *w;
03017
03018 FOR_ALL_WINDOWS_FROM_BACK(w) {
03019 int left, top;
03020
03021 if (w->window_class == WC_MAIN_WINDOW) {
03022 ViewPort *vp = w->viewport;
03023 vp->width = w->width = neww;
03024 vp->height = w->height = newh;
03025 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
03026 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
03027 continue;
03028 }
03029
03030
03031
03032 switch (w->window_class) {
03033 case WC_BOOTSTRAP:
03034 ResizeWindow(w, neww, newh);
03035 continue;
03036
03037 case WC_MAIN_TOOLBAR:
03038 ResizeWindow(w, min(neww, *_preferred_toolbar_size) - w->width, 0, false);
03039
03040 top = w->top;
03041 left = PositionMainToolbar(w);
03042 break;
03043
03044 case WC_NEWS_WINDOW:
03045 top = newh - w->height;
03046 left = PositionNewsMessage(w);
03047 break;
03048
03049 case WC_STATUS_BAR:
03050 ResizeWindow(w, min(neww, *_preferred_statusbar_size) - w->width, 0, false);
03051
03052 top = newh - w->height;
03053 left = PositionStatusbar(w);
03054 break;
03055
03056 case WC_SEND_NETWORK_MSG:
03057 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0, false);
03058 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
03059 left = PositionNetworkChatWindow(w);
03060 break;
03061
03062 case WC_CONSOLE:
03063 IConsoleResize(w);
03064 continue;
03065
03066 default: {
03067 if (w->flags & WF_CENTERED) {
03068 top = (newh - w->height) >> 1;
03069 left = (neww - w->width) >> 1;
03070 break;
03071 }
03072
03073 left = w->left;
03074 if (left + (w->width >> 1) >= neww) left = neww - w->width;
03075 if (left < 0) left = 0;
03076
03077 top = w->top;
03078 if (top + (w->height >> 1) >= newh) top = newh - w->height;
03079 break;
03080 }
03081 }
03082
03083 EnsureVisibleCaption(w, left, top);
03084 }
03085 }
03086
03092 PickerWindowBase::~PickerWindowBase()
03093 {
03094 this->window_class = WC_INVALID;
03095 ResetObjectToPlace();
03096 }