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
00235 const QueryString *Window::GetQueryString(uint widnum) const
00236 {
00237 const SmallMap<int, QueryString*>::Pair *query = this->querystrings.Find(widnum);
00238 return query != this->querystrings.End() ? query->second : NULL;
00239 }
00240
00246 QueryString *Window::GetQueryString(uint widnum)
00247 {
00248 SmallMap<int, QueryString*>::Pair *query = this->querystrings.Find(widnum);
00249 return query != this->querystrings.End() ? query->second : NULL;
00250 }
00251
00252
00257 void SetFocusedWindow(Window *w)
00258 {
00259 if (_focused_window == w) return;
00260
00261
00262 if (_focused_window != NULL) {
00263 if (_focused_window->nested_focus != NULL) _focused_window->nested_focus->SetDirty(_focused_window);
00264 }
00265
00266
00267 Window *old_focused = _focused_window;
00268 _focused_window = w;
00269
00270
00271 if (old_focused != NULL) old_focused->OnFocusLost();
00272 if (_focused_window != NULL) _focused_window->OnFocus();
00273 }
00274
00280 static bool EditBoxInGlobalFocus()
00281 {
00282 if (_focused_window == NULL) return false;
00283
00284
00285 if (_focused_window->window_class == WC_CONSOLE) return true;
00286
00287 return _focused_window->nested_focus != NULL && _focused_window->nested_focus->type == WWT_EDITBOX;
00288 }
00289
00293 void Window::UnfocusFocusedWidget()
00294 {
00295 if (this->nested_focus != NULL) {
00296
00297 this->nested_focus->SetDirty(this);
00298 this->nested_focus = NULL;
00299 }
00300 }
00301
00307 bool Window::SetFocusedWidget(byte widget_index)
00308 {
00309
00310 if (widget_index >= this->nested_array_size) return false;
00311
00312 assert(this->nested_array[widget_index] != NULL);
00313 if (this->nested_focus != NULL) {
00314 if (this->GetWidget<NWidgetCore>(widget_index) == this->nested_focus) return false;
00315
00316
00317 this->nested_focus->SetDirty(this);
00318 }
00319 this->nested_focus = this->GetWidget<NWidgetCore>(widget_index);
00320 return true;
00321 }
00322
00330 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
00331 {
00332 va_list wdg_list;
00333
00334 va_start(wdg_list, widgets);
00335
00336 while (widgets != WIDGET_LIST_END) {
00337 SetWidgetDisabledState(widgets, disab_stat);
00338 widgets = va_arg(wdg_list, int);
00339 }
00340
00341 va_end(wdg_list);
00342 }
00343
00349 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
00350 {
00351 va_list wdg_list;
00352
00353 va_start(wdg_list, widgets);
00354
00355 while (widgets != WIDGET_LIST_END) {
00356 SetWidgetLoweredState(widgets, lowered_stat);
00357 widgets = va_arg(wdg_list, int);
00358 }
00359
00360 va_end(wdg_list);
00361 }
00362
00367 void Window::RaiseButtons(bool autoraise)
00368 {
00369 for (uint i = 0; i < this->nested_array_size; i++) {
00370 if (this->nested_array[i] == NULL) continue;
00371 WidgetType type = this->nested_array[i]->type;
00372 if (((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
00373 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && this->IsWidgetLowered(i)) {
00374 this->RaiseWidget(i);
00375 this->SetWidgetDirty(i);
00376 }
00377 }
00378 }
00379
00384 void Window::SetWidgetDirty(byte widget_index) const
00385 {
00386
00387 if (this->nested_array == NULL) return;
00388
00389 this->nested_array[widget_index]->SetDirty(this);
00390 }
00391
00397 void Window::HandleButtonClick(byte widget)
00398 {
00399 this->LowerWidget(widget);
00400 this->SetTimeout();
00401 this->SetWidgetDirty(widget);
00402 }
00403
00404 static void StartWindowDrag(Window *w);
00405 static void StartWindowSizing(Window *w, bool to_left);
00406
00414 static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
00415 {
00416 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
00417 WidgetType widget_type = (nw != NULL) ? nw->type : WWT_EMPTY;
00418
00419 bool focused_widget_changed = false;
00420
00421 if (_focused_window != w &&
00422 (w->desc_flags & WDF_NO_FOCUS) == 0 &&
00423 widget_type != WWT_CLOSEBOX) {
00424 focused_widget_changed = true;
00425 SetFocusedWindow(w);
00426 w->OnFocus();
00427 }
00428
00429 if (nw == NULL) return;
00430
00431
00432 if (nw->IsDisabled()) return;
00433
00434 int widget_index = nw->index;
00435
00436
00437
00438
00439 if (widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449 focused_widget_changed |= w->SetFocusedWidget(widget_index);
00450 }
00451
00452
00453
00454 if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
00455
00456 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
00457
00458 Point pt = { x, y };
00459
00460 switch (widget_type) {
00461 case NWID_VSCROLLBAR:
00462 case NWID_HSCROLLBAR:
00463 ScrollbarClickHandler(w, nw, x, y);
00464 break;
00465
00466 case WWT_EDITBOX: {
00467 QueryString *query = w->GetQueryString(widget_index);
00468 if (query != NULL) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
00469 break;
00470 }
00471
00472 case WWT_CLOSEBOX:
00473 delete w;
00474 return;
00475
00476 case WWT_CAPTION:
00477 StartWindowDrag(w);
00478 return;
00479
00480 case WWT_RESIZEBOX:
00481
00482
00483 StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
00484 nw->SetDirty(w);
00485 return;
00486
00487 case WWT_DEBUGBOX:
00488 w->ShowNewGRFInspectWindow();
00489 break;
00490
00491 case WWT_SHADEBOX:
00492 nw->SetDirty(w);
00493 w->SetShaded(!w->IsShaded());
00494 return;
00495
00496 case WWT_STICKYBOX:
00497 w->flags ^= WF_STICKY;
00498 nw->SetDirty(w);
00499 return;
00500
00501 default:
00502 break;
00503 }
00504
00505
00506 if (widget_index < 0) return;
00507
00508
00509 if (w->IsWidgetHighlighted(widget_index)) {
00510 w->SetWidgetHighlight(widget_index, TC_INVALID);
00511 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
00512 }
00513
00514 w->OnClick(pt, widget_index, click_count);
00515 }
00516
00523 static void DispatchRightClickEvent(Window *w, int x, int y)
00524 {
00525 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00526 if (wid == NULL) return;
00527
00528
00529 if (wid->index >= 0) {
00530 Point pt = { x, y };
00531 if (w->OnRightClick(pt, wid->index)) return;
00532 }
00533
00534 if (_settings_client.gui.hover_delay == 0 && wid->tool_tip != 0) GuiShowTooltips(w, wid->tool_tip, 0, NULL, TCC_RIGHT_CLICK);
00535 }
00536
00543 static void DispatchHoverEvent(Window *w, int x, int y)
00544 {
00545 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00546
00547
00548 if (wid == NULL) return;
00549
00550
00551 if (wid->tool_tip != 0) {
00552 GuiShowTooltips(w, wid->tool_tip);
00553 return;
00554 }
00555
00556
00557 if (wid->index < 0) return;
00558
00559 Point pt = { x, y };
00560 w->OnHover(pt, wid->index);
00561 }
00562
00570 static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
00571 {
00572 if (nwid == NULL) return;
00573
00574
00575 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
00576 w->SetShaded(wheel < 0);
00577 return;
00578 }
00579
00580
00581 if (nwid->type == NWID_VSCROLLBAR) {
00582 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
00583 if (sb->GetCount() > sb->GetCapacity()) {
00584 sb->UpdatePosition(wheel);
00585 w->SetDirty();
00586 }
00587 return;
00588 }
00589
00590
00591 Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : NULL);
00592 if (sb != NULL && sb->GetCount() > sb->GetCapacity()) {
00593 sb->UpdatePosition(wheel);
00594 w->SetDirty();
00595 }
00596 }
00597
00603 static bool MayBeShown(const Window *w)
00604 {
00605
00606 if (!HasModalProgress()) return true;
00607
00608 switch (w->window_class) {
00609 case WC_MAIN_WINDOW:
00610 case WC_MODAL_PROGRESS:
00611 case WC_CONFIRM_POPUP_QUERY:
00612 return true;
00613
00614 default:
00615 return false;
00616 }
00617 }
00618
00631 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00632 {
00633 const Window *v;
00634 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00635 if (MayBeShown(v) &&
00636 right > v->left &&
00637 bottom > v->top &&
00638 left < v->left + v->width &&
00639 top < v->top + v->height) {
00640
00641 int x;
00642
00643 if (left < (x = v->left)) {
00644 DrawOverlappedWindow(w, left, top, x, bottom);
00645 DrawOverlappedWindow(w, x, top, right, bottom);
00646 return;
00647 }
00648
00649 if (right > (x = v->left + v->width)) {
00650 DrawOverlappedWindow(w, left, top, x, bottom);
00651 DrawOverlappedWindow(w, x, top, right, bottom);
00652 return;
00653 }
00654
00655 if (top < (x = v->top)) {
00656 DrawOverlappedWindow(w, left, top, right, x);
00657 DrawOverlappedWindow(w, left, x, right, bottom);
00658 return;
00659 }
00660
00661 if (bottom > (x = v->top + v->height)) {
00662 DrawOverlappedWindow(w, left, top, right, x);
00663 DrawOverlappedWindow(w, left, x, right, bottom);
00664 return;
00665 }
00666
00667 return;
00668 }
00669 }
00670
00671
00672 DrawPixelInfo *dp = _cur_dpi;
00673 dp->width = right - left;
00674 dp->height = bottom - top;
00675 dp->left = left - w->left;
00676 dp->top = top - w->top;
00677 dp->pitch = _screen.pitch;
00678 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00679 dp->zoom = ZOOM_LVL_NORMAL;
00680 w->OnPaint();
00681 }
00682
00691 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00692 {
00693 Window *w;
00694 DrawPixelInfo bk;
00695 _cur_dpi = &bk;
00696
00697 FOR_ALL_WINDOWS_FROM_BACK(w) {
00698 if (MayBeShown(w) &&
00699 right > w->left &&
00700 bottom > w->top &&
00701 left < w->left + w->width &&
00702 top < w->top + w->height) {
00703
00704 DrawOverlappedWindow(w, left, top, right, bottom);
00705 }
00706 }
00707 }
00708
00713 void Window::SetDirty() const
00714 {
00715 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00716 }
00717
00724 void Window::ReInit(int rx, int ry)
00725 {
00726 this->SetDirty();
00727
00728
00729 int window_width = this->width;
00730 int window_height = this->height;
00731
00732 this->OnInit();
00733
00734 this->nested_root->SetupSmallestSize(this, false);
00735 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
00736 this->width = this->nested_root->smallest_x;
00737 this->height = this->nested_root->smallest_y;
00738 this->resize.step_width = this->nested_root->resize_x;
00739 this->resize.step_height = this->nested_root->resize_y;
00740
00741
00742 window_width = max(window_width + rx, this->width);
00743 window_height = max(window_height + ry, this->height);
00744 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
00745 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
00746
00747
00748 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
00749 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
00750
00751 ResizeWindow(this, dx, dy);
00752
00753 }
00754
00760 void Window::SetShaded(bool make_shaded)
00761 {
00762 if (this->shade_select == NULL) return;
00763
00764 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
00765 if (this->shade_select->shown_plane != desired) {
00766 if (make_shaded) {
00767 this->unshaded_size.width = this->width;
00768 this->unshaded_size.height = this->height;
00769 this->shade_select->SetDisplayedPlane(desired);
00770 this->ReInit(0, -this->height);
00771 } else {
00772 this->shade_select->SetDisplayedPlane(desired);
00773 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
00774 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
00775 this->ReInit(dx, dy);
00776 }
00777 }
00778 }
00779
00786 static Window *FindChildWindow(const Window *w, WindowClass wc)
00787 {
00788 Window *v;
00789 FOR_ALL_WINDOWS_FROM_BACK(v) {
00790 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
00791 }
00792
00793 return NULL;
00794 }
00795
00800 void Window::DeleteChildWindows(WindowClass wc) const
00801 {
00802 Window *child = FindChildWindow(this, wc);
00803 while (child != NULL) {
00804 delete child;
00805 child = FindChildWindow(this, wc);
00806 }
00807 }
00808
00812 Window::~Window()
00813 {
00814 if (_thd.window_class == this->window_class &&
00815 _thd.window_number == this->window_number) {
00816 ResetObjectToPlace();
00817 }
00818
00819
00820 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00821
00822
00823 if (_last_scroll_window == this) _last_scroll_window = NULL;
00824
00825
00826 if (_focused_window == this) _focused_window = NULL;
00827
00828 this->DeleteChildWindows();
00829
00830 if (this->viewport != NULL) DeleteWindowViewport(this);
00831
00832 this->SetDirty();
00833
00834 free(this->nested_array);
00835 delete this->nested_root;
00836
00837 this->window_class = WC_INVALID;
00838 }
00839
00846 Window *FindWindowById(WindowClass cls, WindowNumber number)
00847 {
00848 Window *w;
00849 FOR_ALL_WINDOWS_FROM_BACK(w) {
00850 if (w->window_class == cls && w->window_number == number) return w;
00851 }
00852
00853 return NULL;
00854 }
00855
00862 Window *FindWindowByClass(WindowClass cls)
00863 {
00864 Window *w;
00865 FOR_ALL_WINDOWS_FROM_BACK(w) {
00866 if (w->window_class == cls) return w;
00867 }
00868
00869 return NULL;
00870 }
00871
00878 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00879 {
00880 Window *w = FindWindowById(cls, number);
00881 if (force || w == NULL ||
00882 (w->flags & WF_STICKY) == 0) {
00883 delete w;
00884 }
00885 }
00886
00891 void DeleteWindowByClass(WindowClass cls)
00892 {
00893 Window *w;
00894
00895 restart_search:
00896
00897
00898
00899 FOR_ALL_WINDOWS_FROM_BACK(w) {
00900 if (w->window_class == cls) {
00901 delete w;
00902 goto restart_search;
00903 }
00904 }
00905 }
00906
00913 void DeleteCompanyWindows(CompanyID id)
00914 {
00915 Window *w;
00916
00917 restart_search:
00918
00919
00920
00921 FOR_ALL_WINDOWS_FROM_BACK(w) {
00922 if (w->owner == id) {
00923 delete w;
00924 goto restart_search;
00925 }
00926 }
00927
00928
00929 DeleteWindowById(WC_BUY_COMPANY, id);
00930 }
00931
00939 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00940 {
00941 Window *w;
00942 FOR_ALL_WINDOWS_FROM_BACK(w) {
00943 if (w->owner != old_owner) continue;
00944
00945 switch (w->window_class) {
00946 case WC_COMPANY_COLOUR:
00947 case WC_FINANCES:
00948 case WC_STATION_LIST:
00949 case WC_TRAINS_LIST:
00950 case WC_ROADVEH_LIST:
00951 case WC_SHIPS_LIST:
00952 case WC_AIRCRAFT_LIST:
00953 case WC_BUY_COMPANY:
00954 case WC_COMPANY:
00955 case WC_COMPANY_INFRASTRUCTURE:
00956 continue;
00957
00958 default:
00959 w->owner = new_owner;
00960 break;
00961 }
00962 }
00963 }
00964
00965 static void BringWindowToFront(Window *w);
00966
00974 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00975 {
00976 Window *w = FindWindowById(cls, number);
00977
00978 if (w != NULL) {
00979 if (w->IsShaded()) w->SetShaded(false);
00980
00981 w->SetWhiteBorder();
00982 BringWindowToFront(w);
00983 w->SetDirty();
00984 }
00985
00986 return w;
00987 }
00988
00989 static inline bool IsVitalWindow(const Window *w)
00990 {
00991 switch (w->window_class) {
00992 case WC_MAIN_TOOLBAR:
00993 case WC_STATUS_BAR:
00994 case WC_NEWS_WINDOW:
00995 case WC_SEND_NETWORK_MSG:
00996 return true;
00997
00998 default:
00999 return false;
01000 }
01001 }
01002
01011 static uint GetWindowZPriority(const Window *w)
01012 {
01013 assert(w->window_class != WC_INVALID);
01014
01015 uint z_priority = 0;
01016
01017 switch (w->window_class) {
01018 case WC_ENDSCREEN:
01019 ++z_priority;
01020
01021 case WC_HIGHSCORE:
01022 ++z_priority;
01023
01024 case WC_TOOLTIPS:
01025 ++z_priority;
01026
01027 case WC_DROPDOWN_MENU:
01028 ++z_priority;
01029
01030 case WC_MAIN_TOOLBAR:
01031 case WC_STATUS_BAR:
01032 ++z_priority;
01033
01034 case WC_OSK:
01035 ++z_priority;
01036
01037 case WC_QUERY_STRING:
01038 case WC_SEND_NETWORK_MSG:
01039 ++z_priority;
01040
01041 case WC_ERRMSG:
01042 case WC_CONFIRM_POPUP_QUERY:
01043 case WC_MODAL_PROGRESS:
01044 case WC_NETWORK_STATUS_WINDOW:
01045 ++z_priority;
01046
01047 case WC_GENERATE_LANDSCAPE:
01048 case WC_SAVELOAD:
01049 case WC_GAME_OPTIONS:
01050 case WC_CUSTOM_CURRENCY:
01051 case WC_NETWORK_WINDOW:
01052 case WC_GRF_PARAMETERS:
01053 case WC_AI_LIST:
01054 case WC_AI_SETTINGS:
01055 case WC_TEXTFILE:
01056 ++z_priority;
01057
01058 case WC_CONSOLE:
01059 ++z_priority;
01060
01061 case WC_NEWS_WINDOW:
01062 ++z_priority;
01063
01064 default:
01065 ++z_priority;
01066
01067 case WC_MAIN_WINDOW:
01068 return z_priority;
01069 }
01070 }
01071
01076 static void AddWindowToZOrdering(Window *w)
01077 {
01078 assert(w->z_front == NULL && w->z_back == NULL);
01079
01080 if (_z_front_window == NULL) {
01081
01082 _z_front_window = _z_back_window = w;
01083 w->z_front = w->z_back = NULL;
01084 } else {
01085
01086 Window *v = _z_front_window;
01087 uint last_z_priority = UINT_MAX;
01088 while (v != NULL && (v->window_class == WC_INVALID || GetWindowZPriority(v) > GetWindowZPriority(w))) {
01089 if (v->window_class != WC_INVALID) {
01090
01091 assert(last_z_priority >= GetWindowZPriority(v));
01092 last_z_priority = GetWindowZPriority(v);
01093 }
01094
01095 v = v->z_back;
01096 }
01097
01098 if (v == NULL) {
01099
01100 w->z_front = _z_back_window;
01101 w->z_back = NULL;
01102 _z_back_window->z_back = w;
01103 _z_back_window = w;
01104 } else if (v == _z_front_window) {
01105
01106 w->z_front = NULL;
01107 w->z_back = _z_front_window;
01108 _z_front_window->z_front = w;
01109 _z_front_window = w;
01110 } else {
01111
01112 w->z_front = v->z_front;
01113 w->z_back = v;
01114 v->z_front->z_back = w;
01115 v->z_front = w;
01116 }
01117 }
01118 }
01119
01120
01125 static void RemoveWindowFromZOrdering(Window *w)
01126 {
01127 if (w->z_front == NULL) {
01128 assert(_z_front_window == w);
01129 _z_front_window = w->z_back;
01130 } else {
01131 w->z_front->z_back = w->z_back;
01132 }
01133
01134 if (w->z_back == NULL) {
01135 assert(_z_back_window == w);
01136 _z_back_window = w->z_front;
01137 } else {
01138 w->z_back->z_front = w->z_front;
01139 }
01140
01141 w->z_front = w->z_back = NULL;
01142 }
01143
01149 static void BringWindowToFront(Window *w)
01150 {
01151 RemoveWindowFromZOrdering(w);
01152 AddWindowToZOrdering(w);
01153
01154 w->SetDirty();
01155 }
01156
01165 void Window::InitializeData(const WindowDesc *desc, WindowNumber window_number)
01166 {
01167
01168 this->window_class = desc->cls;
01169 this->SetWhiteBorder();
01170 if (desc->default_pos == WDP_CENTER) this->flags |= WF_CENTERED;
01171 this->owner = INVALID_OWNER;
01172 this->nested_focus = NULL;
01173 this->window_number = window_number;
01174 this->desc_flags = desc->flags;
01175
01176 this->OnInit();
01177
01178 if (this->nested_array == NULL) {
01179 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01180 this->nested_root->SetupSmallestSize(this, true);
01181 } else {
01182 this->nested_root->SetupSmallestSize(this, false);
01183 }
01184
01185 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
01186
01187
01188
01189 this->resize.step_width = this->nested_root->resize_x;
01190 this->resize.step_height = this->nested_root->resize_y;
01191
01192
01193
01194
01195 if (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL) SetFocusedWindow(this);
01196
01197
01198 AddWindowToZOrdering(this);
01199 }
01200
01208 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
01209 {
01210 this->left = x;
01211 this->top = y;
01212 this->width = sm_width;
01213 this->height = sm_height;
01214 }
01215
01226 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
01227 {
01228 def_width = max(def_width, this->width);
01229 def_height = max(def_height, this->height);
01230
01231
01232
01233
01234
01235 if (this->width != def_width || this->height != def_height) {
01236
01237 int free_height = _screen.height;
01238 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
01239 if (wt != NULL) free_height -= wt->height;
01240 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01241 if (wt != NULL) free_height -= wt->height;
01242
01243 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
01244 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
01245
01246
01247
01248
01249 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
01250 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
01251
01252 ResizeWindow(this, enlarge_x, enlarge_y);
01253
01254 } else {
01255
01256 this->OnResize();
01257 }
01258
01259 int nx = this->left;
01260 int ny = this->top;
01261
01262 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
01263
01264 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01265 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
01266 nx = max(nx, 0);
01267
01268 if (this->viewport != NULL) {
01269 this->viewport->left += nx - this->left;
01270 this->viewport->top += ny - this->top;
01271 }
01272 this->left = nx;
01273 this->top = ny;
01274
01275 this->SetDirty();
01276 }
01277
01289 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
01290 {
01291 int right = width + left;
01292 int bottom = height + top;
01293
01294 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01295 if (left < 0 || (main_toolbar != NULL && top < main_toolbar->height) || right > _screen.width || bottom > _screen.height) return false;
01296
01297
01298 const Window *w;
01299 FOR_ALL_WINDOWS_FROM_BACK(w) {
01300 if (w->window_class == WC_MAIN_WINDOW) continue;
01301
01302 if (right > w->left &&
01303 w->left + w->width > left &&
01304 bottom > w->top &&
01305 w->top + w->height > top) {
01306 return false;
01307 }
01308 }
01309
01310 pos.x = left;
01311 pos.y = top;
01312 return true;
01313 }
01314
01326 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
01327 {
01328
01329
01330
01331 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
01332
01333 if (top < 22 || top > _screen.height - (height >> 2)) return false;
01334
01335
01336 const Window *w;
01337 FOR_ALL_WINDOWS_FROM_BACK(w) {
01338 if (w->window_class == WC_MAIN_WINDOW) continue;
01339
01340 if (left + width > w->left &&
01341 w->left + w->width > left &&
01342 top + height > w->top &&
01343 w->top + w->height > top) {
01344 return false;
01345 }
01346 }
01347
01348 pos.x = left;
01349 pos.y = top;
01350 return true;
01351 }
01352
01359 static Point GetAutoPlacePosition(int width, int height)
01360 {
01361 Point pt;
01362
01363
01364 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01365 if (IsGoodAutoPlace1(0, main_toolbar != NULL ? main_toolbar->height + 2 : 2, width, height, pt)) return pt;
01366
01367
01368
01369
01370
01371 const Window *w;
01372 FOR_ALL_WINDOWS_FROM_BACK(w) {
01373 if (w->window_class == WC_MAIN_WINDOW) continue;
01374
01375 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01376 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01377 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01378 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01379 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01380 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01381 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01382 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01383 }
01384
01385
01386
01387
01388
01389 FOR_ALL_WINDOWS_FROM_BACK(w) {
01390 if (w->window_class == WC_MAIN_WINDOW) continue;
01391
01392 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01393 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01394 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01395 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01396 }
01397
01398
01399
01400
01401 int left = 0, top = 24;
01402
01403 restart:
01404 FOR_ALL_WINDOWS_FROM_BACK(w) {
01405 if (w->left == left && w->top == top) {
01406 left += 5;
01407 top += 5;
01408 goto restart;
01409 }
01410 }
01411
01412 pt.x = left;
01413 pt.y = top;
01414 return pt;
01415 }
01416
01423 Point GetToolbarAlignedWindowPosition(int window_width)
01424 {
01425 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01426 assert(w != NULL);
01427 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
01428 return pt;
01429 }
01430
01448 static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01449 {
01450 Point pt;
01451 const Window *w;
01452
01453 int16 default_width = max(desc->default_width, sm_width);
01454 int16 default_height = max(desc->default_height, sm_height);
01455
01456 if (desc->parent_cls != 0 &&
01457 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01458 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01459
01460 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01461 if (pt.x > _screen.width + 10 - default_width) {
01462 pt.x = (_screen.width + 10 - default_width) - 20;
01463 }
01464 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? w->height : 10);
01465 return pt;
01466 }
01467
01468 switch (desc->default_pos) {
01469 case WDP_ALIGN_TOOLBAR:
01470 return GetToolbarAlignedWindowPosition(default_width);
01471
01472 case WDP_AUTO:
01473 return GetAutoPlacePosition(default_width, default_height);
01474
01475 case WDP_CENTER:
01476 pt.x = (_screen.width - default_width) / 2;
01477 pt.y = (_screen.height - default_height) / 2;
01478 break;
01479
01480 case WDP_MANUAL:
01481 pt.x = 0;
01482 pt.y = 0;
01483 break;
01484
01485 default:
01486 NOT_REACHED();
01487 }
01488
01489 return pt;
01490 }
01491
01492 Point Window::OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01493 {
01494 return LocalGetWindowPlacement(desc, sm_width, sm_height, window_number);
01495 }
01496
01505 void Window::CreateNestedTree(const WindowDesc *desc, bool fill_nested)
01506 {
01507 int biggest_index = -1;
01508 this->nested_root = MakeWindowNWidgetTree(desc->nwid_parts, desc->nwid_length, &biggest_index, &this->shade_select);
01509 this->nested_array_size = (uint)(biggest_index + 1);
01510
01511 if (fill_nested) {
01512 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01513 this->nested_root->FillNestedArray(this->nested_array, this->nested_array_size);
01514 }
01515 }
01516
01522 void Window::FinishInitNested(const WindowDesc *desc, WindowNumber window_number)
01523 {
01524 this->InitializeData(desc, window_number);
01525 Point pt = this->OnInitialPosition(desc, this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
01526 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
01527 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
01528 }
01529
01535 void Window::InitNested(const WindowDesc *desc, WindowNumber window_number)
01536 {
01537 this->CreateNestedTree(desc, false);
01538 this->FinishInitNested(desc, window_number);
01539 }
01540
01542 Window::Window() : scrolling_scrollbar(-1)
01543 {
01544 }
01545
01553 Window *FindWindowFromPt(int x, int y)
01554 {
01555 Window *w;
01556 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01557 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01558 return w;
01559 }
01560 }
01561
01562 return NULL;
01563 }
01564
01568 void InitWindowSystem()
01569 {
01570 IConsoleClose();
01571
01572 _z_back_window = NULL;
01573 _z_front_window = NULL;
01574 _focused_window = NULL;
01575 _mouseover_last_w = NULL;
01576 _last_scroll_window = NULL;
01577 _scrolling_viewport = false;
01578 _mouse_hovering = false;
01579
01580 NWidgetLeaf::InvalidateDimensionCache();
01581 NWidgetScrollbar::InvalidateDimensionCache();
01582
01583 ShowFirstError();
01584 }
01585
01589 void UnInitWindowSystem()
01590 {
01591 UnshowCriticalError();
01592
01593 Window *w;
01594 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01595
01596 for (w = _z_front_window; w != NULL; ) {
01597 Window *to_del = w;
01598 w = w->z_back;
01599 free(to_del);
01600 }
01601
01602 _z_front_window = NULL;
01603 _z_back_window = NULL;
01604 }
01605
01609 void ResetWindowSystem()
01610 {
01611 UnInitWindowSystem();
01612 InitWindowSystem();
01613 _thd.Reset();
01614 }
01615
01616 static void DecreaseWindowCounters()
01617 {
01618 Window *w;
01619 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01620 if (_scroller_click_timeout == 0) {
01621
01622 for (uint i = 0; i < w->nested_array_size; i++) {
01623 NWidgetBase *nwid = w->nested_array[i];
01624 if (nwid != NULL && (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR)) {
01625 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
01626 if (sb->disp_flags & (ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN)) {
01627 sb->disp_flags &= ~(ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN);
01628 w->scrolling_scrollbar = -1;
01629 sb->SetDirty(w);
01630 }
01631 }
01632 }
01633 }
01634
01635
01636 for (SmallMap<int, QueryString*>::Pair *it = w->querystrings.Begin(); it != w->querystrings.End(); ++it) {
01637 it->second->HandleEditBox(w, it->first);
01638 }
01639
01640 w->OnMouseLoop();
01641 }
01642
01643 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01644 if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) {
01645 CLRBITS(w->flags, WF_TIMEOUT);
01646
01647 w->OnTimeout();
01648 w->RaiseButtons(true);
01649 }
01650 }
01651 }
01652
01653 static void HandlePlacePresize()
01654 {
01655 if (_special_mouse_mode != WSM_PRESIZE) return;
01656
01657 Window *w = _thd.GetCallbackWnd();
01658 if (w == NULL) return;
01659
01660 Point pt = GetTileBelowCursor();
01661 if (pt.x == -1) {
01662 _thd.selend.x = -1;
01663 return;
01664 }
01665
01666 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01667 }
01668
01673 static EventState HandleMouseDragDrop()
01674 {
01675 if (_special_mouse_mode != WSM_DRAGDROP) return ES_NOT_HANDLED;
01676
01677 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01678
01679 Window *w = _thd.GetCallbackWnd();
01680 if (w != NULL) {
01681
01682 Point pt;
01683 pt.x = _cursor.pos.x - w->left;
01684 pt.y = _cursor.pos.y - w->top;
01685 if (_left_button_down) {
01686 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
01687 } else {
01688 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01689 }
01690 }
01691
01692 if (!_left_button_down) ResetObjectToPlace();
01693 return ES_HANDLED;
01694 }
01695
01697 static void HandleMouseOver()
01698 {
01699 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01700
01701
01702 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01703
01704 Point pt = { -1, -1 };
01705 _mouseover_last_w->OnMouseOver(pt, 0);
01706 }
01707
01708
01709 _mouseover_last_w = w;
01710
01711 if (w != NULL) {
01712
01713 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01714 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
01715 if (widget != NULL) w->OnMouseOver(pt, widget->index);
01716 }
01717 }
01718
01720 static const int MIN_VISIBLE_TITLE_BAR = 13;
01721
01723 enum PreventHideDirection {
01724 PHD_UP,
01725 PHD_DOWN,
01726 };
01727
01738 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
01739 {
01740 if (v == NULL) return;
01741
01742 int v_bottom = v->top + v->height;
01743 int v_right = v->left + v->width;
01744 int safe_y = (dir == PHD_UP) ? (v->top - MIN_VISIBLE_TITLE_BAR - rect.top) : (v_bottom + MIN_VISIBLE_TITLE_BAR - rect.bottom);
01745
01746 if (*ny + rect.top <= v->top - MIN_VISIBLE_TITLE_BAR) return;
01747 if (*ny + rect.bottom >= v_bottom + MIN_VISIBLE_TITLE_BAR) return;
01748
01749
01750 if (*nx + rect.left + MIN_VISIBLE_TITLE_BAR < v->left) {
01751 if (v->left < MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01752 return;
01753 }
01754 if (*nx + rect.right - MIN_VISIBLE_TITLE_BAR > v_right) {
01755 if (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01756 return;
01757 }
01758
01759
01760 if (px + rect.left < v->left && v->left >= MIN_VISIBLE_TITLE_BAR) {
01761 *nx = v->left - MIN_VISIBLE_TITLE_BAR - rect.left;
01762 } else if (px + rect.right > v_right && v_right <= _screen.width - MIN_VISIBLE_TITLE_BAR) {
01763 *nx = v_right + MIN_VISIBLE_TITLE_BAR - rect.right;
01764 } else {
01765 *ny = safe_y;
01766 }
01767 }
01768
01776 static void EnsureVisibleCaption(Window *w, int nx, int ny)
01777 {
01778
01779 Rect caption_rect;
01780 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
01781 if (caption != NULL) {
01782 caption_rect.left = caption->pos_x;
01783 caption_rect.right = caption->pos_x + caption->current_x;
01784 caption_rect.top = caption->pos_y;
01785 caption_rect.bottom = caption->pos_y + caption->current_y;
01786
01787
01788 nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
01789 ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
01790
01791
01792 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
01793 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
01794 }
01795
01796 if (w->viewport != NULL) {
01797 w->viewport->left += nx - w->left;
01798 w->viewport->top += ny - w->top;
01799 }
01800
01801 w->left = nx;
01802 w->top = ny;
01803 }
01804
01815 void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
01816 {
01817 if (delta_x != 0 || delta_y != 0) {
01818 if (clamp_to_screen) {
01819
01820
01821 int new_right = w->left + w->width + delta_x;
01822 int new_bottom = w->top + w->height + delta_y;
01823 if (new_right >= (int)_cur_resolution.width) delta_x -= Ceil(new_right - _cur_resolution.width, max(1U, w->nested_root->resize_x));
01824 if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, max(1U, w->nested_root->resize_y));
01825 }
01826
01827 w->SetDirty();
01828
01829 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);
01830 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);
01831 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
01832 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
01833
01834 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);
01835 w->width = w->nested_root->current_x;
01836 w->height = w->nested_root->current_y;
01837 }
01838
01839 EnsureVisibleCaption(w, w->left, w->top);
01840
01841
01842 w->OnResize();
01843 w->SetDirty();
01844 }
01845
01851 int GetMainViewTop()
01852 {
01853 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01854 return (w == NULL) ? 0 : w->top + w->height;
01855 }
01856
01862 int GetMainViewBottom()
01863 {
01864 Window *w = FindWindowById(WC_STATUS_BAR, 0);
01865 return (w == NULL) ? _screen.height : w->top;
01866 }
01867
01868 static bool _dragging_window;
01869
01874 static EventState HandleWindowDragging()
01875 {
01876
01877 if (!_dragging_window) return ES_NOT_HANDLED;
01878
01879
01880 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01881
01882
01883 Window *w;
01884 FOR_ALL_WINDOWS_FROM_BACK(w) {
01885 if (w->flags & WF_DRAGGING) {
01886
01887 if (!_left_button_down) {
01888 w->flags &= ~WF_DRAGGING;
01889 break;
01890 }
01891
01892 w->SetDirty();
01893
01894 int x = _cursor.pos.x + _drag_delta.x;
01895 int y = _cursor.pos.y + _drag_delta.y;
01896 int nx = x;
01897 int ny = y;
01898
01899 if (_settings_client.gui.window_snap_radius != 0) {
01900 const Window *v;
01901
01902 int hsnap = _settings_client.gui.window_snap_radius;
01903 int vsnap = _settings_client.gui.window_snap_radius;
01904 int delta;
01905
01906 FOR_ALL_WINDOWS_FROM_BACK(v) {
01907 if (v == w) continue;
01908
01909 if (y + w->height > v->top && y < v->top + v->height) {
01910
01911 delta = abs(v->left + v->width - x);
01912 if (delta <= hsnap) {
01913 nx = v->left + v->width;
01914 hsnap = delta;
01915 }
01916
01917
01918 delta = abs(v->left - x - w->width);
01919 if (delta <= hsnap) {
01920 nx = v->left - w->width;
01921 hsnap = delta;
01922 }
01923 }
01924
01925 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01926
01927 delta = abs(v->left - x);
01928 if (delta <= hsnap) {
01929 nx = v->left;
01930 hsnap = delta;
01931 }
01932
01933
01934 delta = abs(v->left + v->width - x - w->width);
01935 if (delta <= hsnap) {
01936 nx = v->left + v->width - w->width;
01937 hsnap = delta;
01938 }
01939 }
01940
01941 if (x + w->width > v->left && x < v->left + v->width) {
01942
01943 delta = abs(v->top + v->height - y);
01944 if (delta <= vsnap) {
01945 ny = v->top + v->height;
01946 vsnap = delta;
01947 }
01948
01949
01950 delta = abs(v->top - y - w->height);
01951 if (delta <= vsnap) {
01952 ny = v->top - w->height;
01953 vsnap = delta;
01954 }
01955 }
01956
01957 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01958
01959 delta = abs(v->top - y);
01960 if (delta <= vsnap) {
01961 ny = v->top;
01962 vsnap = delta;
01963 }
01964
01965
01966 delta = abs(v->top + v->height - y - w->height);
01967 if (delta <= vsnap) {
01968 ny = v->top + v->height - w->height;
01969 vsnap = delta;
01970 }
01971 }
01972 }
01973 }
01974
01975 EnsureVisibleCaption(w, nx, ny);
01976
01977 w->SetDirty();
01978 return ES_HANDLED;
01979 } else if (w->flags & WF_SIZING) {
01980
01981 if (!_left_button_down) {
01982 w->flags &= ~WF_SIZING;
01983 w->SetDirty();
01984 break;
01985 }
01986
01987
01988
01989
01990 int x, y = _cursor.pos.y - _drag_delta.y;
01991 if (w->flags & WF_SIZING_LEFT) {
01992 x = _drag_delta.x - _cursor.pos.x;
01993 } else {
01994 x = _cursor.pos.x - _drag_delta.x;
01995 }
01996
01997
01998 if (w->resize.step_width == 0) x = 0;
01999 if (w->resize.step_height == 0) y = 0;
02000
02001
02002 if (w->top + w->height + y > _screen.height) {
02003 y = _screen.height - w->height - w->top;
02004 }
02005
02006
02007
02008
02009 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
02010 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
02011
02012
02013 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
02014 x = w->nested_root->smallest_x - w->width;
02015 }
02016 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
02017 y = w->nested_root->smallest_y - w->height;
02018 }
02019
02020
02021 if (x == 0 && y == 0) return ES_HANDLED;
02022
02023
02024 _drag_delta.y += y;
02025 if ((w->flags & WF_SIZING_LEFT) && x != 0) {
02026 _drag_delta.x -= x;
02027 w->SetDirty();
02028 w->left -= x;
02029
02030 } else {
02031 _drag_delta.x += x;
02032 }
02033
02034
02035 ResizeWindow(w, x, y);
02036 return ES_HANDLED;
02037 }
02038 }
02039
02040 _dragging_window = false;
02041 return ES_HANDLED;
02042 }
02043
02048 static void StartWindowDrag(Window *w)
02049 {
02050 w->flags |= WF_DRAGGING;
02051 w->flags &= ~WF_CENTERED;
02052 _dragging_window = true;
02053
02054 _drag_delta.x = w->left - _cursor.pos.x;
02055 _drag_delta.y = w->top - _cursor.pos.y;
02056
02057 BringWindowToFront(w);
02058 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02059 }
02060
02066 static void StartWindowSizing(Window *w, bool to_left)
02067 {
02068 w->flags |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
02069 w->flags &= ~WF_CENTERED;
02070 _dragging_window = true;
02071
02072 _drag_delta.x = _cursor.pos.x;
02073 _drag_delta.y = _cursor.pos.y;
02074
02075 BringWindowToFront(w);
02076 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02077 }
02078
02083 static EventState HandleScrollbarScrolling()
02084 {
02085 Window *w;
02086 FOR_ALL_WINDOWS_FROM_BACK(w) {
02087 if (w->scrolling_scrollbar >= 0) {
02088
02089 if (!_left_button_down) {
02090 w->scrolling_scrollbar = -1;
02091 w->SetDirty();
02092 return ES_HANDLED;
02093 }
02094
02095 int i;
02096 NWidgetScrollbar *sb = w->GetWidget<NWidgetScrollbar>(w->scrolling_scrollbar);
02097 bool rtl = false;
02098
02099 if (sb->type == NWID_HSCROLLBAR) {
02100 i = _cursor.pos.x - _cursorpos_drag_start.x;
02101 rtl = _current_text_dir == TD_RTL;
02102 } else {
02103 i = _cursor.pos.y - _cursorpos_drag_start.y;
02104 }
02105
02106 if (sb->disp_flags & ND_SCROLLBAR_BTN) {
02107 if (_scroller_click_timeout == 1) {
02108 _scroller_click_timeout = 3;
02109 sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1);
02110 w->SetDirty();
02111 }
02112 return ES_HANDLED;
02113 }
02114
02115
02116 int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity()));
02117 if (rtl) pos = max(0, sb->GetCount() - sb->GetCapacity() - pos);
02118 if (pos != sb->GetPosition()) {
02119 sb->SetPosition(pos);
02120 w->SetDirty();
02121 }
02122 return ES_HANDLED;
02123 }
02124 }
02125
02126 return ES_NOT_HANDLED;
02127 }
02128
02133 static EventState HandleViewportScroll()
02134 {
02135 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02136
02137 if (!_scrolling_viewport) return ES_NOT_HANDLED;
02138
02139
02140
02141
02142 if (_last_scroll_window == NULL) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
02143
02144 if (_last_scroll_window == NULL || !(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down))) {
02145 _cursor.fix_at = false;
02146 _scrolling_viewport = false;
02147 _last_scroll_window = NULL;
02148 return ES_NOT_HANDLED;
02149 }
02150
02151 if (_last_scroll_window == FindWindowById(WC_MAIN_WINDOW, 0) && _last_scroll_window->viewport->follow_vehicle != INVALID_VEHICLE) {
02152
02153 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
02154 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
02155 return ES_NOT_HANDLED;
02156 }
02157
02158 Point delta;
02159 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
02160 delta.x = -_cursor.delta.x;
02161 delta.y = -_cursor.delta.y;
02162 } else {
02163 delta.x = _cursor.delta.x;
02164 delta.y = _cursor.delta.y;
02165 }
02166
02167 if (scrollwheel_scrolling) {
02168
02169 delta.x = _cursor.h_wheel;
02170 delta.y = _cursor.v_wheel;
02171 _cursor.v_wheel = 0;
02172 _cursor.h_wheel = 0;
02173 }
02174
02175
02176 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
02177
02178 _cursor.delta.x = 0;
02179 _cursor.delta.y = 0;
02180 return ES_HANDLED;
02181 }
02182
02193 static bool MaybeBringWindowToFront(Window *w)
02194 {
02195 bool bring_to_front = false;
02196
02197 if (w->window_class == WC_MAIN_WINDOW ||
02198 IsVitalWindow(w) ||
02199 w->window_class == WC_TOOLTIPS ||
02200 w->window_class == WC_DROPDOWN_MENU) {
02201 return true;
02202 }
02203
02204
02205 int w_width = w->width;
02206 int w_height = w->height;
02207 if (w->IsShaded()) {
02208 w_width = w->unshaded_size.width;
02209 w_height = w->unshaded_size.height;
02210 }
02211
02212 Window *u;
02213 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
02214
02215 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
02216 u->SetWhiteBorder();
02217 u->SetDirty();
02218 return false;
02219 }
02220
02221 if (u->window_class == WC_MAIN_WINDOW ||
02222 IsVitalWindow(u) ||
02223 u->window_class == WC_TOOLTIPS ||
02224 u->window_class == WC_DROPDOWN_MENU) {
02225 continue;
02226 }
02227
02228
02229 if (w->left + w_width <= u->left ||
02230 u->left + u->width <= w->left ||
02231 w->top + w_height <= u->top ||
02232 u->top + u->height <= w->top) {
02233 continue;
02234 }
02235
02236 bring_to_front = true;
02237 }
02238
02239 if (bring_to_front) BringWindowToFront(w);
02240 return true;
02241 }
02242
02251 EventState Window::HandleEditBoxKey(int wid, uint16 key, uint16 keycode)
02252 {
02253 EventState state = ES_NOT_HANDLED;
02254
02255 QueryString *query = this->GetQueryString(wid);
02256 if (query == NULL) return state;
02257
02258 int action = QueryString::ACTION_NOTHING;
02259
02260 switch (query->HandleEditBoxKey(this, wid, key, keycode, state)) {
02261 case HEBR_EDITING:
02262 this->SetWidgetDirty(wid);
02263 this->OnEditboxChanged(wid);
02264 break;
02265
02266 case HEBR_CURSOR:
02267 this->SetWidgetDirty(wid);
02268
02269 if (this->window_class == WC_OSK) this->InvalidateData();
02270 break;
02271
02272 case HEBR_CONFIRM:
02273 if (this->window_class == WC_OSK) {
02274 this->OnClick(Point(), WID_OSK_OK, 1);
02275 } else if (query->ok_button >= 0) {
02276 this->OnClick(Point(), query->ok_button, 1);
02277 } else {
02278 action = query->ok_button;
02279 }
02280 break;
02281
02282 case HEBR_CANCEL:
02283 if (this->window_class == WC_OSK) {
02284 this->OnClick(Point(), WID_OSK_CANCEL, 1);
02285 } else if (query->cancel_button >= 0) {
02286 this->OnClick(Point(), query->cancel_button, 1);
02287 } else {
02288 action = query->cancel_button;
02289 }
02290 break;
02291
02292 default: break;
02293 }
02294
02295 switch (action) {
02296 case QueryString::ACTION_DESELECT:
02297 this->UnfocusFocusedWidget();
02298 break;
02299
02300 case QueryString::ACTION_CLEAR:
02301 query->text.DeleteAll();
02302 this->SetWidgetDirty(wid);
02303 this->OnEditboxChanged(wid);
02304 break;
02305
02306 default:
02307 break;
02308 }
02309
02310 return state;
02311 }
02312
02317 void HandleKeypress(uint32 raw_key)
02318 {
02319
02320
02321 assert(HasModalProgress() || IsLocalCompany());
02322
02323
02324 uint16 key = GB(raw_key, 0, 16);
02325 uint16 keycode = GB(raw_key, 16, 16);
02326
02327
02328
02329
02330
02331
02332
02333
02334 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
02335
02336
02337
02338
02339 if (key == 0 && keycode == 0) return;
02340
02341
02342 if (EditBoxInGlobalFocus()) {
02343
02344 if (_focused_window->window_class == WC_CONSOLE) {
02345 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
02346 } else {
02347 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->index, key, keycode) == ES_HANDLED) return;
02348 }
02349 }
02350
02351
02352 Window *w;
02353 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02354 if (w->window_class == WC_MAIN_TOOLBAR) continue;
02355 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02356 }
02357
02358 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02359
02360 if (w != NULL && w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02361
02362 HandleGlobalHotkeys(key, keycode);
02363 }
02364
02368 void HandleCtrlChanged()
02369 {
02370
02371 Window *w;
02372 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02373 if (w->OnCTRLStateChange() == ES_HANDLED) return;
02374 }
02375 }
02376
02383 static int _input_events_this_tick = 0;
02384
02389 static void HandleAutoscroll()
02390 {
02391 if (_game_mode == GM_MENU || HasModalProgress()) return;
02392 if (_settings_client.gui.auto_scrolling == VA_DISABLED) return;
02393 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
02394
02395 int x = _cursor.pos.x;
02396 int y = _cursor.pos.y;
02397 Window *w = FindWindowFromPt(x, y);
02398 if (w == NULL || w->flags & WF_DISABLE_VP_SCROLL) return;
02399 if (_settings_client.gui.auto_scrolling != VA_EVERY_VIEWPORT && w->window_class != WC_MAIN_WINDOW) return;
02400
02401 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02402 if (vp == NULL) return;
02403
02404 x -= vp->left;
02405 y -= vp->top;
02406
02407
02408 #define scrollspeed 3
02409 if (x - 15 < 0) {
02410 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
02411 } else if (15 - (vp->width - x) > 0) {
02412 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
02413 }
02414 if (y - 15 < 0) {
02415 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
02416 } else if (15 - (vp->height - y) > 0) {
02417 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
02418 }
02419 #undef scrollspeed
02420 }
02421
02422 enum MouseClick {
02423 MC_NONE = 0,
02424 MC_LEFT,
02425 MC_RIGHT,
02426 MC_DOUBLE_LEFT,
02427 MC_HOVER,
02428
02429 MAX_OFFSET_DOUBLE_CLICK = 5,
02430 TIME_BETWEEN_DOUBLE_CLICK = 500,
02431 MAX_OFFSET_HOVER = 5,
02432 };
02433 extern EventState VpHandlePlaceSizingDrag();
02434
02435 static void ScrollMainViewport(int x, int y)
02436 {
02437 if (_game_mode != GM_MENU) {
02438 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02439 assert(w);
02440
02441 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
02442 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
02443 }
02444 }
02445
02455 static const int8 scrollamt[16][2] = {
02456 { 0, 0},
02457 {-2, 0},
02458 { 0, -2},
02459 {-2, -1},
02460 { 2, 0},
02461 { 0, 0},
02462 { 2, -1},
02463 { 0, -2},
02464 { 0, 2},
02465 {-2, 1},
02466 { 0, 0},
02467 {-2, 0},
02468 { 2, 1},
02469 { 0, 2},
02470 { 2, 0},
02471 { 0, 0},
02472 };
02473
02474 static void HandleKeyScrolling()
02475 {
02476
02477
02478
02479
02480 if (_dirkeys && !EditBoxInGlobalFocus()) {
02481 int factor = _shift_pressed ? 50 : 10;
02482 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
02483 }
02484 }
02485
02486 static void MouseLoop(MouseClick click, int mousewheel)
02487 {
02488
02489
02490 assert(HasModalProgress() || IsLocalCompany());
02491
02492 HandlePlacePresize();
02493 UpdateTileSelection();
02494
02495 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
02496 if (HandleMouseDragDrop() == ES_HANDLED) return;
02497 if (HandleWindowDragging() == ES_HANDLED) return;
02498 if (HandleScrollbarScrolling() == ES_HANDLED) return;
02499 if (HandleViewportScroll() == ES_HANDLED) return;
02500
02501 HandleMouseOver();
02502
02503 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02504 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
02505
02506 int x = _cursor.pos.x;
02507 int y = _cursor.pos.y;
02508 Window *w = FindWindowFromPt(x, y);
02509 if (w == NULL) return;
02510
02511 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
02512 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02513
02514
02515 if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
02516
02517 if (mousewheel != 0) {
02518
02519 w->OnMouseWheel(mousewheel);
02520
02521
02522 if (vp == NULL) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
02523 }
02524
02525 if (vp != NULL) {
02526 if (scrollwheel_scrolling) click = MC_RIGHT;
02527 switch (click) {
02528 case MC_DOUBLE_LEFT:
02529 case MC_LEFT:
02530 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
02531 if (!HandleViewportClicked(vp, x, y) &&
02532 !(w->flags & WF_DISABLE_VP_SCROLL) &&
02533 _settings_client.gui.left_mouse_btn_scrolling) {
02534 _scrolling_viewport = true;
02535 _cursor.fix_at = false;
02536 }
02537 break;
02538
02539 case MC_RIGHT:
02540 if (!(w->flags & WF_DISABLE_VP_SCROLL)) {
02541 _scrolling_viewport = true;
02542 _cursor.fix_at = true;
02543
02544
02545 _cursor.h_wheel = 0;
02546 _cursor.v_wheel = 0;
02547 }
02548 break;
02549
02550 default:
02551 break;
02552 }
02553 } else {
02554 switch (click) {
02555 case MC_LEFT:
02556 case MC_DOUBLE_LEFT:
02557 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
02558 break;
02559
02560 default:
02561 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
02562
02563
02564
02565
02566 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
02567
02568 case MC_HOVER: DispatchHoverEvent(w, x - w->left, y - w->top); break;
02569 }
02570 }
02571 }
02572
02576 void HandleMouseEvents()
02577 {
02578
02579
02580 assert(HasModalProgress() || IsLocalCompany());
02581
02582 static int double_click_time = 0;
02583 static Point double_click_pos = {0, 0};
02584
02585
02586 MouseClick click = MC_NONE;
02587 if (_left_button_down && !_left_button_clicked) {
02588 click = MC_LEFT;
02589 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02590 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
02591 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
02592 click = MC_DOUBLE_LEFT;
02593 }
02594 double_click_time = _realtime_tick;
02595 double_click_pos = _cursor.pos;
02596 _left_button_clicked = true;
02597 _input_events_this_tick++;
02598 } else if (_right_button_clicked) {
02599 _right_button_clicked = false;
02600 click = MC_RIGHT;
02601 _input_events_this_tick++;
02602 }
02603
02604 int mousewheel = 0;
02605 if (_cursor.wheel) {
02606 mousewheel = _cursor.wheel;
02607 _cursor.wheel = 0;
02608 _input_events_this_tick++;
02609 }
02610
02611 static uint32 hover_time = 0;
02612 static Point hover_pos = {0, 0};
02613
02614 if (_settings_client.gui.hover_delay > 0) {
02615 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
02616 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
02617 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
02618 hover_pos = _cursor.pos;
02619 hover_time = _realtime_tick;
02620 _mouse_hovering = false;
02621 } else {
02622 if (hover_time != 0 && _realtime_tick > hover_time + _settings_client.gui.hover_delay * 1000) {
02623 click = MC_HOVER;
02624 _input_events_this_tick++;
02625 _mouse_hovering = true;
02626 }
02627 }
02628 }
02629
02630
02631 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _newgrf_debug_sprite_picker.click_time != _realtime_tick) {
02632
02633 _newgrf_debug_sprite_picker.mode = SPM_NONE;
02634 InvalidateWindowData(WC_SPRITE_ALIGNER, 0, 1);
02635 }
02636
02637 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
02638
02639 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
02640 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
02641 _newgrf_debug_sprite_picker.click_time = _realtime_tick;
02642 _newgrf_debug_sprite_picker.sprites.Clear();
02643 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
02644 MarkWholeScreenDirty();
02645 } else {
02646 MouseLoop(click, mousewheel);
02647 }
02648
02649
02650
02651 _cursor.delta.x = 0;
02652 _cursor.delta.y = 0;
02653 }
02654
02658 static void CheckSoftLimit()
02659 {
02660 if (_settings_client.gui.window_soft_limit == 0) return;
02661
02662 for (;;) {
02663 uint deletable_count = 0;
02664 Window *w, *last_deletable = NULL;
02665 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02666 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue;
02667
02668 last_deletable = w;
02669 deletable_count++;
02670 }
02671
02672
02673 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02674
02675 assert(last_deletable != NULL);
02676 delete last_deletable;
02677 }
02678 }
02679
02683 void InputLoop()
02684 {
02685
02686
02687 assert(HasModalProgress() || IsLocalCompany());
02688
02689 CheckSoftLimit();
02690 HandleKeyScrolling();
02691
02692
02693 for (Window *v = _z_front_window; v != NULL; ) {
02694 Window *w = v;
02695 v = v->z_back;
02696
02697 if (w->window_class != WC_INVALID) continue;
02698
02699 RemoveWindowFromZOrdering(w);
02700 free(w);
02701 }
02702
02703 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
02704 DecreaseWindowCounters();
02705
02706 if (_input_events_this_tick != 0) {
02707
02708 _input_events_this_tick = 0;
02709
02710 return;
02711 }
02712
02713
02714 HandleMouseEvents();
02715 HandleAutoscroll();
02716 }
02717
02721 void UpdateWindows()
02722 {
02723 Window *w;
02724
02725 static int highlight_timer = 1;
02726 if (--highlight_timer == 0) {
02727 highlight_timer = 15;
02728 _window_highlight_colour = !_window_highlight_colour;
02729 }
02730
02731 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02732 w->ProcessScheduledInvalidations();
02733 w->ProcessHighlightedInvalidations();
02734 }
02735
02736 static int we4_timer = 0;
02737 int t = we4_timer + 1;
02738
02739 if (t >= 100) {
02740 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02741 w->OnHundredthTick();
02742 }
02743 t = 0;
02744 }
02745 we4_timer = t;
02746
02747 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02748 if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
02749 CLRBITS(w->flags, WF_WHITE_BORDER);
02750 w->SetDirty();
02751 }
02752 }
02753
02754 DrawDirtyBlocks();
02755
02756 FOR_ALL_WINDOWS_FROM_BACK(w) {
02757
02758 if (w->viewport != NULL && !w->IsShaded()) UpdateViewportPosition(w);
02759 }
02760 NetworkDrawChatMessage();
02761
02762 DrawMouseCursor();
02763 }
02764
02770 void SetWindowDirty(WindowClass cls, WindowNumber number)
02771 {
02772 const Window *w;
02773 FOR_ALL_WINDOWS_FROM_BACK(w) {
02774 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02775 }
02776 }
02777
02784 void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
02785 {
02786 const Window *w;
02787 FOR_ALL_WINDOWS_FROM_BACK(w) {
02788 if (w->window_class == cls && w->window_number == number) {
02789 w->SetWidgetDirty(widget_index);
02790 }
02791 }
02792 }
02793
02798 void SetWindowClassesDirty(WindowClass cls)
02799 {
02800 Window *w;
02801 FOR_ALL_WINDOWS_FROM_BACK(w) {
02802 if (w->window_class == cls) w->SetDirty();
02803 }
02804 }
02805
02811 void Window::InvalidateData(int data, bool gui_scope)
02812 {
02813 this->SetDirty();
02814 if (!gui_scope) {
02815
02816 *this->scheduled_invalidation_data.Append() = data;
02817 }
02818 this->OnInvalidateData(data, gui_scope);
02819 }
02820
02824 void Window::ProcessScheduledInvalidations()
02825 {
02826 for (int *data = this->scheduled_invalidation_data.Begin(); this->window_class != WC_INVALID && data != this->scheduled_invalidation_data.End(); data++) {
02827 this->OnInvalidateData(*data, true);
02828 }
02829 this->scheduled_invalidation_data.Clear();
02830 }
02831
02835 void Window::ProcessHighlightedInvalidations()
02836 {
02837 if ((this->flags & WF_HIGHLIGHTED) == 0) return;
02838
02839 for (uint i = 0; i < this->nested_array_size; i++) {
02840 if (this->IsWidgetHighlighted(i)) this->SetWidgetDirty(i);
02841 }
02842 }
02843
02870 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
02871 {
02872 Window *w;
02873 FOR_ALL_WINDOWS_FROM_BACK(w) {
02874 if (w->window_class == cls && w->window_number == number) {
02875 w->InvalidateData(data, gui_scope);
02876 }
02877 }
02878 }
02879
02888 void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
02889 {
02890 Window *w;
02891
02892 FOR_ALL_WINDOWS_FROM_BACK(w) {
02893 if (w->window_class == cls) {
02894 w->InvalidateData(data, gui_scope);
02895 }
02896 }
02897 }
02898
02902 void CallWindowTickEvent()
02903 {
02904 Window *w;
02905 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02906 w->OnTick();
02907 }
02908 }
02909
02916 void DeleteNonVitalWindows()
02917 {
02918 Window *w;
02919
02920 restart_search:
02921
02922
02923
02924 FOR_ALL_WINDOWS_FROM_BACK(w) {
02925 if (w->window_class != WC_MAIN_WINDOW &&
02926 w->window_class != WC_SELECT_GAME &&
02927 w->window_class != WC_MAIN_TOOLBAR &&
02928 w->window_class != WC_STATUS_BAR &&
02929 w->window_class != WC_TOOLTIPS &&
02930 (w->flags & WF_STICKY) == 0) {
02931
02932 delete w;
02933 goto restart_search;
02934 }
02935 }
02936 }
02937
02945 void DeleteAllNonVitalWindows()
02946 {
02947 Window *w;
02948
02949
02950 DeleteNonVitalWindows();
02951
02952 restart_search:
02953
02954
02955
02956 FOR_ALL_WINDOWS_FROM_BACK(w) {
02957 if (w->flags & WF_STICKY) {
02958 delete w;
02959 goto restart_search;
02960 }
02961 }
02962 }
02963
02968 void DeleteConstructionWindows()
02969 {
02970 Window *w;
02971
02972 restart_search:
02973
02974
02975
02976 FOR_ALL_WINDOWS_FROM_BACK(w) {
02977 if (w->desc_flags & WDF_CONSTRUCTION) {
02978 delete w;
02979 goto restart_search;
02980 }
02981 }
02982
02983 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02984 }
02985
02987 void HideVitalWindows()
02988 {
02989 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02990 DeleteWindowById(WC_STATUS_BAR, 0);
02991 }
02992
02994 void ReInitAllWindows()
02995 {
02996 NWidgetLeaf::InvalidateDimensionCache();
02997 NWidgetScrollbar::InvalidateDimensionCache();
02998
02999 Window *w;
03000 FOR_ALL_WINDOWS_FROM_BACK(w) {
03001 w->ReInit();
03002 }
03003 #ifdef ENABLE_NETWORK
03004 void NetworkReInitChatBoxSize();
03005 NetworkReInitChatBoxSize();
03006 #endif
03007
03008
03009 RelocateAllWindows(_cur_resolution.width, _cur_resolution.height);
03010 MarkWholeScreenDirty();
03011 }
03012
03020 static int PositionWindow(Window *w, WindowClass clss, int setting)
03021 {
03022 if (w == NULL || w->window_class != clss) {
03023 w = FindWindowById(clss, 0);
03024 }
03025 if (w == NULL) return 0;
03026
03027 int old_left = w->left;
03028 switch (setting) {
03029 case 1: w->left = (_screen.width - w->width) / 2; break;
03030 case 2: w->left = _screen.width - w->width; break;
03031 default: w->left = 0; break;
03032 }
03033 if (w->viewport != NULL) w->viewport->left += w->left - old_left;
03034 SetDirtyBlocks(0, w->top, _screen.width, w->top + w->height);
03035 return w->left;
03036 }
03037
03043 int PositionMainToolbar(Window *w)
03044 {
03045 DEBUG(misc, 5, "Repositioning Main Toolbar...");
03046 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
03047 }
03048
03054 int PositionStatusbar(Window *w)
03055 {
03056 DEBUG(misc, 5, "Repositioning statusbar...");
03057 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
03058 }
03059
03065 int PositionNewsMessage(Window *w)
03066 {
03067 DEBUG(misc, 5, "Repositioning news message...");
03068 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
03069 }
03070
03076 int PositionNetworkChatWindow(Window *w)
03077 {
03078 DEBUG(misc, 5, "Repositioning network chat window...");
03079 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
03080 }
03081
03082
03088 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
03089 {
03090 Window *w;
03091 FOR_ALL_WINDOWS_FROM_BACK(w) {
03092 if (w->viewport != NULL && w->viewport->follow_vehicle == from_index) {
03093 w->viewport->follow_vehicle = to_index;
03094 w->SetDirty();
03095 }
03096 }
03097 }
03098
03099
03105 void RelocateAllWindows(int neww, int newh)
03106 {
03107 Window *w;
03108
03109 FOR_ALL_WINDOWS_FROM_BACK(w) {
03110 int left, top;
03111
03112 if (w->window_class == WC_MAIN_WINDOW) {
03113 ViewPort *vp = w->viewport;
03114 vp->width = w->width = neww;
03115 vp->height = w->height = newh;
03116 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
03117 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
03118 continue;
03119 }
03120
03121
03122
03123 switch (w->window_class) {
03124 case WC_BOOTSTRAP:
03125 ResizeWindow(w, neww, newh);
03126 continue;
03127
03128 case WC_MAIN_TOOLBAR:
03129 ResizeWindow(w, min(neww, *_preferred_toolbar_size) - w->width, 0, false);
03130
03131 top = w->top;
03132 left = PositionMainToolbar(w);
03133 break;
03134
03135 case WC_NEWS_WINDOW:
03136 top = newh - w->height;
03137 left = PositionNewsMessage(w);
03138 break;
03139
03140 case WC_STATUS_BAR:
03141 ResizeWindow(w, min(neww, *_preferred_statusbar_size) - w->width, 0, false);
03142
03143 top = newh - w->height;
03144 left = PositionStatusbar(w);
03145 break;
03146
03147 case WC_SEND_NETWORK_MSG:
03148 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0, false);
03149 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
03150 left = PositionNetworkChatWindow(w);
03151 break;
03152
03153 case WC_CONSOLE:
03154 IConsoleResize(w);
03155 continue;
03156
03157 default: {
03158 if (w->flags & WF_CENTERED) {
03159 top = (newh - w->height) >> 1;
03160 left = (neww - w->width) >> 1;
03161 break;
03162 }
03163
03164 left = w->left;
03165 if (left + (w->width >> 1) >= neww) left = neww - w->width;
03166 if (left < 0) left = 0;
03167
03168 top = w->top;
03169 if (top + (w->height >> 1) >= newh) top = newh - w->height;
03170 break;
03171 }
03172 }
03173
03174 EnsureVisibleCaption(w, left, top);
03175 }
03176 }
03177
03183 PickerWindowBase::~PickerWindowBase()
03184 {
03185 this->window_class = WC_INVALID;
03186 ResetObjectToPlace();
03187 }