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 if (_focused_window != NULL) {
00426 _focused_window->OnFocusLost();
00427
00428
00429 if (w->window_class != WC_OSK) DeleteWindowById(WC_OSK, 0);
00430 }
00431 SetFocusedWindow(w);
00432 w->OnFocus();
00433 }
00434
00435 if (nw == NULL) return;
00436
00437
00438 if (nw->IsDisabled()) return;
00439
00440 int widget_index = nw->index;
00441
00442
00443
00444 if (widget_type != WWT_CAPTION) {
00445
00446 if (w->nested_focus != NULL && w->nested_focus->type == WWT_EDITBOX && w->nested_focus != nw && w->window_class != WC_OSK) {
00447 DeleteWindowById(WC_OSK, 0);
00448 }
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459 focused_widget_changed |= w->SetFocusedWidget(widget_index);
00460 }
00461
00462
00463
00464 if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
00465
00466 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
00467
00468 Point pt = { x, y };
00469
00470 switch (widget_type) {
00471 case NWID_VSCROLLBAR:
00472 case NWID_HSCROLLBAR:
00473 ScrollbarClickHandler(w, nw, x, y);
00474 break;
00475
00476 case WWT_EDITBOX: {
00477 QueryString *query = w->GetQueryString(widget_index);
00478 if (query != NULL) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
00479 break;
00480 }
00481
00482 case WWT_CLOSEBOX:
00483 delete w;
00484 return;
00485
00486 case WWT_CAPTION:
00487 StartWindowDrag(w);
00488 return;
00489
00490 case WWT_RESIZEBOX:
00491
00492
00493 StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
00494 nw->SetDirty(w);
00495 return;
00496
00497 case WWT_DEBUGBOX:
00498 w->ShowNewGRFInspectWindow();
00499 break;
00500
00501 case WWT_SHADEBOX:
00502 nw->SetDirty(w);
00503 w->SetShaded(!w->IsShaded());
00504 return;
00505
00506 case WWT_STICKYBOX:
00507 w->flags ^= WF_STICKY;
00508 nw->SetDirty(w);
00509 return;
00510
00511 default:
00512 break;
00513 }
00514
00515
00516 if (widget_index < 0) return;
00517
00518
00519 if (w->IsWidgetHighlighted(widget_index)) {
00520 w->SetWidgetHighlight(widget_index, TC_INVALID);
00521 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
00522 }
00523
00524 w->OnClick(pt, widget_index, click_count);
00525 }
00526
00533 static void DispatchRightClickEvent(Window *w, int x, int y)
00534 {
00535 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00536 if (wid == NULL) return;
00537
00538
00539 if (wid->index >= 0) {
00540 Point pt = { x, y };
00541 if (w->OnRightClick(pt, wid->index)) return;
00542 }
00543
00544 if (_settings_client.gui.hover_delay == 0 && wid->tool_tip != 0) GuiShowTooltips(w, wid->tool_tip, 0, NULL, TCC_RIGHT_CLICK);
00545 }
00546
00553 static void DispatchHoverEvent(Window *w, int x, int y)
00554 {
00555 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00556
00557
00558 if (wid == NULL) return;
00559
00560
00561 if (wid->tool_tip != 0) {
00562 GuiShowTooltips(w, wid->tool_tip);
00563 return;
00564 }
00565
00566
00567 if (wid->index < 0) return;
00568
00569 Point pt = { x, y };
00570 w->OnHover(pt, wid->index);
00571 }
00572
00580 static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
00581 {
00582 if (nwid == NULL) return;
00583
00584
00585 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
00586 w->SetShaded(wheel < 0);
00587 return;
00588 }
00589
00590
00591 if (nwid->type == NWID_VSCROLLBAR) {
00592 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
00593 if (sb->GetCount() > sb->GetCapacity()) {
00594 sb->UpdatePosition(wheel);
00595 w->SetDirty();
00596 }
00597 return;
00598 }
00599
00600
00601 Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : NULL);
00602 if (sb != NULL && sb->GetCount() > sb->GetCapacity()) {
00603 sb->UpdatePosition(wheel);
00604 w->SetDirty();
00605 }
00606 }
00607
00613 static bool MayBeShown(const Window *w)
00614 {
00615
00616 if (!HasModalProgress()) return true;
00617
00618 switch (w->window_class) {
00619 case WC_MAIN_WINDOW:
00620 case WC_MODAL_PROGRESS:
00621 case WC_CONFIRM_POPUP_QUERY:
00622 return true;
00623
00624 default:
00625 return false;
00626 }
00627 }
00628
00641 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00642 {
00643 const Window *v;
00644 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00645 if (MayBeShown(v) &&
00646 right > v->left &&
00647 bottom > v->top &&
00648 left < v->left + v->width &&
00649 top < v->top + v->height) {
00650
00651 int x;
00652
00653 if (left < (x = v->left)) {
00654 DrawOverlappedWindow(w, left, top, x, bottom);
00655 DrawOverlappedWindow(w, x, top, right, bottom);
00656 return;
00657 }
00658
00659 if (right > (x = v->left + v->width)) {
00660 DrawOverlappedWindow(w, left, top, x, bottom);
00661 DrawOverlappedWindow(w, x, top, right, bottom);
00662 return;
00663 }
00664
00665 if (top < (x = v->top)) {
00666 DrawOverlappedWindow(w, left, top, right, x);
00667 DrawOverlappedWindow(w, left, x, right, bottom);
00668 return;
00669 }
00670
00671 if (bottom > (x = v->top + v->height)) {
00672 DrawOverlappedWindow(w, left, top, right, x);
00673 DrawOverlappedWindow(w, left, x, right, bottom);
00674 return;
00675 }
00676
00677 return;
00678 }
00679 }
00680
00681
00682 DrawPixelInfo *dp = _cur_dpi;
00683 dp->width = right - left;
00684 dp->height = bottom - top;
00685 dp->left = left - w->left;
00686 dp->top = top - w->top;
00687 dp->pitch = _screen.pitch;
00688 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00689 dp->zoom = ZOOM_LVL_NORMAL;
00690 w->OnPaint();
00691 }
00692
00701 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00702 {
00703 Window *w;
00704 DrawPixelInfo bk;
00705 _cur_dpi = &bk;
00706
00707 FOR_ALL_WINDOWS_FROM_BACK(w) {
00708 if (MayBeShown(w) &&
00709 right > w->left &&
00710 bottom > w->top &&
00711 left < w->left + w->width &&
00712 top < w->top + w->height) {
00713
00714 DrawOverlappedWindow(w, left, top, right, bottom);
00715 }
00716 }
00717 }
00718
00723 void Window::SetDirty() const
00724 {
00725 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00726 }
00727
00734 void Window::ReInit(int rx, int ry)
00735 {
00736 this->SetDirty();
00737
00738
00739 int window_width = this->width;
00740 int window_height = this->height;
00741
00742 this->OnInit();
00743
00744 this->nested_root->SetupSmallestSize(this, false);
00745 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
00746 this->width = this->nested_root->smallest_x;
00747 this->height = this->nested_root->smallest_y;
00748 this->resize.step_width = this->nested_root->resize_x;
00749 this->resize.step_height = this->nested_root->resize_y;
00750
00751
00752 window_width = max(window_width + rx, this->width);
00753 window_height = max(window_height + ry, this->height);
00754 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
00755 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
00756
00757
00758 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
00759 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
00760
00761 ResizeWindow(this, dx, dy);
00762
00763 }
00764
00770 void Window::SetShaded(bool make_shaded)
00771 {
00772 if (this->shade_select == NULL) return;
00773
00774 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
00775 if (this->shade_select->shown_plane != desired) {
00776 if (make_shaded) {
00777 this->unshaded_size.width = this->width;
00778 this->unshaded_size.height = this->height;
00779 this->shade_select->SetDisplayedPlane(desired);
00780 this->ReInit(0, -this->height);
00781 } else {
00782 this->shade_select->SetDisplayedPlane(desired);
00783 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
00784 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
00785 this->ReInit(dx, dy);
00786 }
00787 }
00788 }
00789
00796 static Window *FindChildWindow(const Window *w, WindowClass wc)
00797 {
00798 Window *v;
00799 FOR_ALL_WINDOWS_FROM_BACK(v) {
00800 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
00801 }
00802
00803 return NULL;
00804 }
00805
00810 void Window::DeleteChildWindows(WindowClass wc) const
00811 {
00812 Window *child = FindChildWindow(this, wc);
00813 while (child != NULL) {
00814 delete child;
00815 child = FindChildWindow(this, wc);
00816 }
00817 }
00818
00822 Window::~Window()
00823 {
00824 if (_thd.window_class == this->window_class &&
00825 _thd.window_number == this->window_number) {
00826 ResetObjectToPlace();
00827 }
00828
00829
00830 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00831
00832
00833 if (_last_scroll_window == this) _last_scroll_window = NULL;
00834
00835
00836 if (_focused_window == this) _focused_window = NULL;
00837
00838 this->DeleteChildWindows();
00839
00840 if (this->viewport != NULL) DeleteWindowViewport(this);
00841
00842 this->SetDirty();
00843
00844 free(this->nested_array);
00845 delete this->nested_root;
00846
00847 this->window_class = WC_INVALID;
00848 }
00849
00856 Window *FindWindowById(WindowClass cls, WindowNumber number)
00857 {
00858 Window *w;
00859 FOR_ALL_WINDOWS_FROM_BACK(w) {
00860 if (w->window_class == cls && w->window_number == number) return w;
00861 }
00862
00863 return NULL;
00864 }
00865
00872 Window *FindWindowByClass(WindowClass cls)
00873 {
00874 Window *w;
00875 FOR_ALL_WINDOWS_FROM_BACK(w) {
00876 if (w->window_class == cls) return w;
00877 }
00878
00879 return NULL;
00880 }
00881
00888 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00889 {
00890 Window *w = FindWindowById(cls, number);
00891 if (force || w == NULL ||
00892 (w->flags & WF_STICKY) == 0) {
00893 delete w;
00894 }
00895 }
00896
00901 void DeleteWindowByClass(WindowClass cls)
00902 {
00903 Window *w;
00904
00905 restart_search:
00906
00907
00908
00909 FOR_ALL_WINDOWS_FROM_BACK(w) {
00910 if (w->window_class == cls) {
00911 delete w;
00912 goto restart_search;
00913 }
00914 }
00915 }
00916
00923 void DeleteCompanyWindows(CompanyID id)
00924 {
00925 Window *w;
00926
00927 restart_search:
00928
00929
00930
00931 FOR_ALL_WINDOWS_FROM_BACK(w) {
00932 if (w->owner == id) {
00933 delete w;
00934 goto restart_search;
00935 }
00936 }
00937
00938
00939 DeleteWindowById(WC_BUY_COMPANY, id);
00940 }
00941
00949 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00950 {
00951 Window *w;
00952 FOR_ALL_WINDOWS_FROM_BACK(w) {
00953 if (w->owner != old_owner) continue;
00954
00955 switch (w->window_class) {
00956 case WC_COMPANY_COLOUR:
00957 case WC_FINANCES:
00958 case WC_STATION_LIST:
00959 case WC_TRAINS_LIST:
00960 case WC_ROADVEH_LIST:
00961 case WC_SHIPS_LIST:
00962 case WC_AIRCRAFT_LIST:
00963 case WC_BUY_COMPANY:
00964 case WC_COMPANY:
00965 case WC_COMPANY_INFRASTRUCTURE:
00966 continue;
00967
00968 default:
00969 w->owner = new_owner;
00970 break;
00971 }
00972 }
00973 }
00974
00975 static void BringWindowToFront(Window *w);
00976
00984 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00985 {
00986 Window *w = FindWindowById(cls, number);
00987
00988 if (w != NULL) {
00989 if (w->IsShaded()) w->SetShaded(false);
00990
00991 w->SetWhiteBorder();
00992 BringWindowToFront(w);
00993 w->SetDirty();
00994 }
00995
00996 return w;
00997 }
00998
00999 static inline bool IsVitalWindow(const Window *w)
01000 {
01001 switch (w->window_class) {
01002 case WC_MAIN_TOOLBAR:
01003 case WC_STATUS_BAR:
01004 case WC_NEWS_WINDOW:
01005 case WC_SEND_NETWORK_MSG:
01006 return true;
01007
01008 default:
01009 return false;
01010 }
01011 }
01012
01021 static uint GetWindowZPriority(const Window *w)
01022 {
01023 assert(w->window_class != WC_INVALID);
01024
01025 uint z_priority = 0;
01026
01027 switch (w->window_class) {
01028 case WC_ENDSCREEN:
01029 ++z_priority;
01030
01031 case WC_HIGHSCORE:
01032 ++z_priority;
01033
01034 case WC_TOOLTIPS:
01035 ++z_priority;
01036
01037 case WC_DROPDOWN_MENU:
01038 ++z_priority;
01039
01040 case WC_MAIN_TOOLBAR:
01041 case WC_STATUS_BAR:
01042 ++z_priority;
01043
01044 case WC_OSK:
01045 ++z_priority;
01046
01047 case WC_QUERY_STRING:
01048 case WC_SEND_NETWORK_MSG:
01049 ++z_priority;
01050
01051 case WC_ERRMSG:
01052 case WC_CONFIRM_POPUP_QUERY:
01053 case WC_MODAL_PROGRESS:
01054 case WC_NETWORK_STATUS_WINDOW:
01055 ++z_priority;
01056
01057 case WC_GENERATE_LANDSCAPE:
01058 case WC_SAVELOAD:
01059 case WC_GAME_OPTIONS:
01060 case WC_CUSTOM_CURRENCY:
01061 case WC_NETWORK_WINDOW:
01062 case WC_GRF_PARAMETERS:
01063 case WC_AI_LIST:
01064 case WC_AI_SETTINGS:
01065 case WC_TEXTFILE:
01066 ++z_priority;
01067
01068 case WC_CONSOLE:
01069 ++z_priority;
01070
01071 case WC_NEWS_WINDOW:
01072 ++z_priority;
01073
01074 default:
01075 ++z_priority;
01076
01077 case WC_MAIN_WINDOW:
01078 return z_priority;
01079 }
01080 }
01081
01086 static void AddWindowToZOrdering(Window *w)
01087 {
01088 assert(w->z_front == NULL && w->z_back == NULL);
01089
01090 if (_z_front_window == NULL) {
01091
01092 _z_front_window = _z_back_window = w;
01093 w->z_front = w->z_back = NULL;
01094 } else {
01095
01096 Window *v = _z_front_window;
01097 uint last_z_priority = UINT_MAX;
01098 while (v != NULL && (v->window_class == WC_INVALID || GetWindowZPriority(v) > GetWindowZPriority(w))) {
01099 if (v->window_class != WC_INVALID) {
01100
01101 assert(last_z_priority >= GetWindowZPriority(v));
01102 last_z_priority = GetWindowZPriority(v);
01103 }
01104
01105 v = v->z_back;
01106 }
01107
01108 if (v == NULL) {
01109
01110 w->z_front = _z_back_window;
01111 w->z_back = NULL;
01112 _z_back_window->z_back = w;
01113 _z_back_window = w;
01114 } else if (v == _z_front_window) {
01115
01116 w->z_front = NULL;
01117 w->z_back = _z_front_window;
01118 _z_front_window->z_front = w;
01119 _z_front_window = w;
01120 } else {
01121
01122 w->z_front = v->z_front;
01123 w->z_back = v;
01124 v->z_front->z_back = w;
01125 v->z_front = w;
01126 }
01127 }
01128 }
01129
01130
01135 static void RemoveWindowFromZOrdering(Window *w)
01136 {
01137 if (w->z_front == NULL) {
01138 assert(_z_front_window == w);
01139 _z_front_window = w->z_back;
01140 } else {
01141 w->z_front->z_back = w->z_back;
01142 }
01143
01144 if (w->z_back == NULL) {
01145 assert(_z_back_window == w);
01146 _z_back_window = w->z_front;
01147 } else {
01148 w->z_back->z_front = w->z_front;
01149 }
01150
01151 w->z_front = w->z_back = NULL;
01152 }
01153
01159 static void BringWindowToFront(Window *w)
01160 {
01161 RemoveWindowFromZOrdering(w);
01162 AddWindowToZOrdering(w);
01163
01164 w->SetDirty();
01165 }
01166
01175 void Window::InitializeData(const WindowDesc *desc, WindowNumber window_number)
01176 {
01177
01178 this->window_class = desc->cls;
01179 this->SetWhiteBorder();
01180 if (desc->default_pos == WDP_CENTER) this->flags |= WF_CENTERED;
01181 this->owner = INVALID_OWNER;
01182 this->nested_focus = NULL;
01183 this->window_number = window_number;
01184 this->desc_flags = desc->flags;
01185
01186 this->OnInit();
01187
01188 if (this->nested_array == NULL) {
01189 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01190 this->nested_root->SetupSmallestSize(this, true);
01191 } else {
01192 this->nested_root->SetupSmallestSize(this, false);
01193 }
01194
01195 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
01196
01197
01198
01199 this->resize.step_width = this->nested_root->resize_x;
01200 this->resize.step_height = this->nested_root->resize_y;
01201
01202
01203
01204
01205 if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL)) SetFocusedWindow(this);
01206
01207
01208 AddWindowToZOrdering(this);
01209 }
01210
01218 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
01219 {
01220 this->left = x;
01221 this->top = y;
01222 this->width = sm_width;
01223 this->height = sm_height;
01224 }
01225
01236 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
01237 {
01238 def_width = max(def_width, this->width);
01239 def_height = max(def_height, this->height);
01240
01241
01242
01243
01244
01245 if (this->width != def_width || this->height != def_height) {
01246
01247 int free_height = _screen.height;
01248 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
01249 if (wt != NULL) free_height -= wt->height;
01250 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01251 if (wt != NULL) free_height -= wt->height;
01252
01253 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
01254 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
01255
01256
01257
01258
01259 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
01260 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
01261
01262 ResizeWindow(this, enlarge_x, enlarge_y);
01263
01264 } else {
01265
01266 this->OnResize();
01267 }
01268
01269 int nx = this->left;
01270 int ny = this->top;
01271
01272 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
01273
01274 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01275 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
01276 nx = max(nx, 0);
01277
01278 if (this->viewport != NULL) {
01279 this->viewport->left += nx - this->left;
01280 this->viewport->top += ny - this->top;
01281 }
01282 this->left = nx;
01283 this->top = ny;
01284
01285 this->SetDirty();
01286 }
01287
01299 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
01300 {
01301 int right = width + left;
01302 int bottom = height + top;
01303
01304 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01305 if (left < 0 || (main_toolbar != NULL && top < main_toolbar->height) || right > _screen.width || bottom > _screen.height) return false;
01306
01307
01308 const Window *w;
01309 FOR_ALL_WINDOWS_FROM_BACK(w) {
01310 if (w->window_class == WC_MAIN_WINDOW) continue;
01311
01312 if (right > w->left &&
01313 w->left + w->width > left &&
01314 bottom > w->top &&
01315 w->top + w->height > top) {
01316 return false;
01317 }
01318 }
01319
01320 pos.x = left;
01321 pos.y = top;
01322 return true;
01323 }
01324
01336 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
01337 {
01338
01339
01340
01341 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
01342
01343 if (top < 22 || top > _screen.height - (height >> 2)) return false;
01344
01345
01346 const Window *w;
01347 FOR_ALL_WINDOWS_FROM_BACK(w) {
01348 if (w->window_class == WC_MAIN_WINDOW) continue;
01349
01350 if (left + width > w->left &&
01351 w->left + w->width > left &&
01352 top + height > w->top &&
01353 w->top + w->height > top) {
01354 return false;
01355 }
01356 }
01357
01358 pos.x = left;
01359 pos.y = top;
01360 return true;
01361 }
01362
01369 static Point GetAutoPlacePosition(int width, int height)
01370 {
01371 Point pt;
01372
01373
01374 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01375 if (IsGoodAutoPlace1(0, main_toolbar != NULL ? main_toolbar->height + 2 : 2, width, height, pt)) return pt;
01376
01377
01378
01379
01380
01381 const Window *w;
01382 FOR_ALL_WINDOWS_FROM_BACK(w) {
01383 if (w->window_class == WC_MAIN_WINDOW) continue;
01384
01385 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01386 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01387 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01388 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01389 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01390 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01391 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01392 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01393 }
01394
01395
01396
01397
01398
01399 FOR_ALL_WINDOWS_FROM_BACK(w) {
01400 if (w->window_class == WC_MAIN_WINDOW) continue;
01401
01402 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01403 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01404 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01405 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01406 }
01407
01408
01409
01410
01411 int left = 0, top = 24;
01412
01413 restart:
01414 FOR_ALL_WINDOWS_FROM_BACK(w) {
01415 if (w->left == left && w->top == top) {
01416 left += 5;
01417 top += 5;
01418 goto restart;
01419 }
01420 }
01421
01422 pt.x = left;
01423 pt.y = top;
01424 return pt;
01425 }
01426
01433 Point GetToolbarAlignedWindowPosition(int window_width)
01434 {
01435 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01436 assert(w != NULL);
01437 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
01438 return pt;
01439 }
01440
01458 static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01459 {
01460 Point pt;
01461 const Window *w;
01462
01463 int16 default_width = max(desc->default_width, sm_width);
01464 int16 default_height = max(desc->default_height, sm_height);
01465
01466 if (desc->parent_cls != 0 &&
01467 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01468 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01469
01470 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01471 if (pt.x > _screen.width + 10 - default_width) {
01472 pt.x = (_screen.width + 10 - default_width) - 20;
01473 }
01474 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? w->height : 10);
01475 return pt;
01476 }
01477
01478 switch (desc->default_pos) {
01479 case WDP_ALIGN_TOOLBAR:
01480 return GetToolbarAlignedWindowPosition(default_width);
01481
01482 case WDP_AUTO:
01483 return GetAutoPlacePosition(default_width, default_height);
01484
01485 case WDP_CENTER:
01486 pt.x = (_screen.width - default_width) / 2;
01487 pt.y = (_screen.height - default_height) / 2;
01488 break;
01489
01490 case WDP_MANUAL:
01491 pt.x = 0;
01492 pt.y = 0;
01493 break;
01494
01495 default:
01496 NOT_REACHED();
01497 }
01498
01499 return pt;
01500 }
01501
01502 Point Window::OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01503 {
01504 return LocalGetWindowPlacement(desc, sm_width, sm_height, window_number);
01505 }
01506
01515 void Window::CreateNestedTree(const WindowDesc *desc, bool fill_nested)
01516 {
01517 int biggest_index = -1;
01518 this->nested_root = MakeWindowNWidgetTree(desc->nwid_parts, desc->nwid_length, &biggest_index, &this->shade_select);
01519 this->nested_array_size = (uint)(biggest_index + 1);
01520
01521 if (fill_nested) {
01522 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01523 this->nested_root->FillNestedArray(this->nested_array, this->nested_array_size);
01524 }
01525 }
01526
01532 void Window::FinishInitNested(const WindowDesc *desc, WindowNumber window_number)
01533 {
01534 this->InitializeData(desc, window_number);
01535 Point pt = this->OnInitialPosition(desc, this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
01536 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
01537 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
01538 }
01539
01545 void Window::InitNested(const WindowDesc *desc, WindowNumber window_number)
01546 {
01547 this->CreateNestedTree(desc, false);
01548 this->FinishInitNested(desc, window_number);
01549 }
01550
01552 Window::Window() : scrolling_scrollbar(-1)
01553 {
01554 }
01555
01563 Window *FindWindowFromPt(int x, int y)
01564 {
01565 Window *w;
01566 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01567 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01568 return w;
01569 }
01570 }
01571
01572 return NULL;
01573 }
01574
01578 void InitWindowSystem()
01579 {
01580 IConsoleClose();
01581
01582 _z_back_window = NULL;
01583 _z_front_window = NULL;
01584 _focused_window = NULL;
01585 _mouseover_last_w = NULL;
01586 _last_scroll_window = NULL;
01587 _scrolling_viewport = false;
01588 _mouse_hovering = false;
01589
01590 NWidgetLeaf::InvalidateDimensionCache();
01591 NWidgetScrollbar::InvalidateDimensionCache();
01592
01593 ShowFirstError();
01594 }
01595
01599 void UnInitWindowSystem()
01600 {
01601 UnshowCriticalError();
01602
01603 Window *w;
01604 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01605
01606 for (w = _z_front_window; w != NULL; ) {
01607 Window *to_del = w;
01608 w = w->z_back;
01609 free(to_del);
01610 }
01611
01612 _z_front_window = NULL;
01613 _z_back_window = NULL;
01614 }
01615
01619 void ResetWindowSystem()
01620 {
01621 UnInitWindowSystem();
01622 InitWindowSystem();
01623 _thd.Reset();
01624 }
01625
01626 static void DecreaseWindowCounters()
01627 {
01628 Window *w;
01629 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01630 if (_scroller_click_timeout == 0) {
01631
01632 for (uint i = 0; i < w->nested_array_size; i++) {
01633 NWidgetBase *nwid = w->nested_array[i];
01634 if (nwid != NULL && (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR)) {
01635 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
01636 if (sb->disp_flags & (ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN)) {
01637 sb->disp_flags &= ~(ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN);
01638 w->scrolling_scrollbar = -1;
01639 sb->SetDirty(w);
01640 }
01641 }
01642 }
01643 }
01644
01645
01646 for (SmallMap<int, QueryString*>::Pair *it = w->querystrings.Begin(); it != w->querystrings.End(); ++it) {
01647 it->second->HandleEditBox(w, it->first);
01648 }
01649
01650 w->OnMouseLoop();
01651 }
01652
01653 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01654 if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) {
01655 CLRBITS(w->flags, WF_TIMEOUT);
01656
01657 w->OnTimeout();
01658 w->RaiseButtons(true);
01659 }
01660 }
01661 }
01662
01663 static void HandlePlacePresize()
01664 {
01665 if (_special_mouse_mode != WSM_PRESIZE) return;
01666
01667 Window *w = _thd.GetCallbackWnd();
01668 if (w == NULL) return;
01669
01670 Point pt = GetTileBelowCursor();
01671 if (pt.x == -1) {
01672 _thd.selend.x = -1;
01673 return;
01674 }
01675
01676 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01677 }
01678
01683 static EventState HandleMouseDragDrop()
01684 {
01685 if (_special_mouse_mode != WSM_DRAGDROP) return ES_NOT_HANDLED;
01686
01687 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01688
01689 Window *w = _thd.GetCallbackWnd();
01690 if (w != NULL) {
01691
01692 Point pt;
01693 pt.x = _cursor.pos.x - w->left;
01694 pt.y = _cursor.pos.y - w->top;
01695 if (_left_button_down) {
01696 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
01697 } else {
01698 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01699 }
01700 }
01701
01702 if (!_left_button_down) ResetObjectToPlace();
01703 return ES_HANDLED;
01704 }
01705
01707 static void HandleMouseOver()
01708 {
01709 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01710
01711
01712 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01713
01714 Point pt = { -1, -1 };
01715 _mouseover_last_w->OnMouseOver(pt, 0);
01716 }
01717
01718
01719 _mouseover_last_w = w;
01720
01721 if (w != NULL) {
01722
01723 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01724 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
01725 if (widget != NULL) w->OnMouseOver(pt, widget->index);
01726 }
01727 }
01728
01730 static const int MIN_VISIBLE_TITLE_BAR = 13;
01731
01733 enum PreventHideDirection {
01734 PHD_UP,
01735 PHD_DOWN,
01736 };
01737
01748 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
01749 {
01750 if (v == NULL) return;
01751
01752 int v_bottom = v->top + v->height;
01753 int v_right = v->left + v->width;
01754 int safe_y = (dir == PHD_UP) ? (v->top - MIN_VISIBLE_TITLE_BAR - rect.top) : (v_bottom + MIN_VISIBLE_TITLE_BAR - rect.bottom);
01755
01756 if (*ny + rect.top <= v->top - MIN_VISIBLE_TITLE_BAR) return;
01757 if (*ny + rect.bottom >= v_bottom + MIN_VISIBLE_TITLE_BAR) return;
01758
01759
01760 if (*nx + rect.left + MIN_VISIBLE_TITLE_BAR < v->left) {
01761 if (v->left < MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01762 return;
01763 }
01764 if (*nx + rect.right - MIN_VISIBLE_TITLE_BAR > v_right) {
01765 if (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01766 return;
01767 }
01768
01769
01770 if (px + rect.left < v->left && v->left >= MIN_VISIBLE_TITLE_BAR) {
01771 *nx = v->left - MIN_VISIBLE_TITLE_BAR - rect.left;
01772 } else if (px + rect.right > v_right && v_right <= _screen.width - MIN_VISIBLE_TITLE_BAR) {
01773 *nx = v_right + MIN_VISIBLE_TITLE_BAR - rect.right;
01774 } else {
01775 *ny = safe_y;
01776 }
01777 }
01778
01786 static void EnsureVisibleCaption(Window *w, int nx, int ny)
01787 {
01788
01789 Rect caption_rect;
01790 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
01791 if (caption != NULL) {
01792 caption_rect.left = caption->pos_x;
01793 caption_rect.right = caption->pos_x + caption->current_x;
01794 caption_rect.top = caption->pos_y;
01795 caption_rect.bottom = caption->pos_y + caption->current_y;
01796
01797
01798 nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
01799 ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
01800
01801
01802 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
01803 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
01804 }
01805
01806 if (w->viewport != NULL) {
01807 w->viewport->left += nx - w->left;
01808 w->viewport->top += ny - w->top;
01809 }
01810
01811 w->left = nx;
01812 w->top = ny;
01813 }
01814
01825 void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
01826 {
01827 if (delta_x != 0 || delta_y != 0) {
01828 if (clamp_to_screen) {
01829
01830
01831 int new_right = w->left + w->width + delta_x;
01832 int new_bottom = w->top + w->height + delta_y;
01833 if (new_right >= (int)_cur_resolution.width) delta_x -= Ceil(new_right - _cur_resolution.width, max(1U, w->nested_root->resize_x));
01834 if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, max(1U, w->nested_root->resize_y));
01835 }
01836
01837 w->SetDirty();
01838
01839 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);
01840 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);
01841 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
01842 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
01843
01844 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);
01845 w->width = w->nested_root->current_x;
01846 w->height = w->nested_root->current_y;
01847 }
01848
01849 EnsureVisibleCaption(w, w->left, w->top);
01850
01851
01852 w->OnResize();
01853 w->SetDirty();
01854 }
01855
01861 int GetMainViewTop()
01862 {
01863 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01864 return (w == NULL) ? 0 : w->top + w->height;
01865 }
01866
01872 int GetMainViewBottom()
01873 {
01874 Window *w = FindWindowById(WC_STATUS_BAR, 0);
01875 return (w == NULL) ? _screen.height : w->top;
01876 }
01877
01878 static bool _dragging_window;
01879
01884 static EventState HandleWindowDragging()
01885 {
01886
01887 if (!_dragging_window) return ES_NOT_HANDLED;
01888
01889
01890 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01891
01892
01893 Window *w;
01894 FOR_ALL_WINDOWS_FROM_BACK(w) {
01895 if (w->flags & WF_DRAGGING) {
01896
01897 if (!_left_button_down) {
01898 w->flags &= ~WF_DRAGGING;
01899 break;
01900 }
01901
01902 w->SetDirty();
01903
01904 int x = _cursor.pos.x + _drag_delta.x;
01905 int y = _cursor.pos.y + _drag_delta.y;
01906 int nx = x;
01907 int ny = y;
01908
01909 if (_settings_client.gui.window_snap_radius != 0) {
01910 const Window *v;
01911
01912 int hsnap = _settings_client.gui.window_snap_radius;
01913 int vsnap = _settings_client.gui.window_snap_radius;
01914 int delta;
01915
01916 FOR_ALL_WINDOWS_FROM_BACK(v) {
01917 if (v == w) continue;
01918
01919 if (y + w->height > v->top && y < v->top + v->height) {
01920
01921 delta = abs(v->left + v->width - x);
01922 if (delta <= hsnap) {
01923 nx = v->left + v->width;
01924 hsnap = delta;
01925 }
01926
01927
01928 delta = abs(v->left - x - w->width);
01929 if (delta <= hsnap) {
01930 nx = v->left - w->width;
01931 hsnap = delta;
01932 }
01933 }
01934
01935 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01936
01937 delta = abs(v->left - x);
01938 if (delta <= hsnap) {
01939 nx = v->left;
01940 hsnap = delta;
01941 }
01942
01943
01944 delta = abs(v->left + v->width - x - w->width);
01945 if (delta <= hsnap) {
01946 nx = v->left + v->width - w->width;
01947 hsnap = delta;
01948 }
01949 }
01950
01951 if (x + w->width > v->left && x < v->left + v->width) {
01952
01953 delta = abs(v->top + v->height - y);
01954 if (delta <= vsnap) {
01955 ny = v->top + v->height;
01956 vsnap = delta;
01957 }
01958
01959
01960 delta = abs(v->top - y - w->height);
01961 if (delta <= vsnap) {
01962 ny = v->top - w->height;
01963 vsnap = delta;
01964 }
01965 }
01966
01967 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01968
01969 delta = abs(v->top - y);
01970 if (delta <= vsnap) {
01971 ny = v->top;
01972 vsnap = delta;
01973 }
01974
01975
01976 delta = abs(v->top + v->height - y - w->height);
01977 if (delta <= vsnap) {
01978 ny = v->top + v->height - w->height;
01979 vsnap = delta;
01980 }
01981 }
01982 }
01983 }
01984
01985 EnsureVisibleCaption(w, nx, ny);
01986
01987 w->SetDirty();
01988 return ES_HANDLED;
01989 } else if (w->flags & WF_SIZING) {
01990
01991 if (!_left_button_down) {
01992 w->flags &= ~WF_SIZING;
01993 w->SetDirty();
01994 break;
01995 }
01996
01997
01998
01999
02000 int x, y = _cursor.pos.y - _drag_delta.y;
02001 if (w->flags & WF_SIZING_LEFT) {
02002 x = _drag_delta.x - _cursor.pos.x;
02003 } else {
02004 x = _cursor.pos.x - _drag_delta.x;
02005 }
02006
02007
02008 if (w->resize.step_width == 0) x = 0;
02009 if (w->resize.step_height == 0) y = 0;
02010
02011
02012 if (w->top + w->height + y > _screen.height) {
02013 y = _screen.height - w->height - w->top;
02014 }
02015
02016
02017
02018
02019 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
02020 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
02021
02022
02023 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
02024 x = w->nested_root->smallest_x - w->width;
02025 }
02026 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
02027 y = w->nested_root->smallest_y - w->height;
02028 }
02029
02030
02031 if (x == 0 && y == 0) return ES_HANDLED;
02032
02033
02034 _drag_delta.y += y;
02035 if ((w->flags & WF_SIZING_LEFT) && x != 0) {
02036 _drag_delta.x -= x;
02037 w->SetDirty();
02038 w->left -= x;
02039
02040 } else {
02041 _drag_delta.x += x;
02042 }
02043
02044
02045 ResizeWindow(w, x, y);
02046 return ES_HANDLED;
02047 }
02048 }
02049
02050 _dragging_window = false;
02051 return ES_HANDLED;
02052 }
02053
02058 static void StartWindowDrag(Window *w)
02059 {
02060 w->flags |= WF_DRAGGING;
02061 w->flags &= ~WF_CENTERED;
02062 _dragging_window = true;
02063
02064 _drag_delta.x = w->left - _cursor.pos.x;
02065 _drag_delta.y = w->top - _cursor.pos.y;
02066
02067 BringWindowToFront(w);
02068 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02069 }
02070
02076 static void StartWindowSizing(Window *w, bool to_left)
02077 {
02078 w->flags |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
02079 w->flags &= ~WF_CENTERED;
02080 _dragging_window = true;
02081
02082 _drag_delta.x = _cursor.pos.x;
02083 _drag_delta.y = _cursor.pos.y;
02084
02085 BringWindowToFront(w);
02086 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02087 }
02088
02093 static EventState HandleScrollbarScrolling()
02094 {
02095 Window *w;
02096 FOR_ALL_WINDOWS_FROM_BACK(w) {
02097 if (w->scrolling_scrollbar >= 0) {
02098
02099 if (!_left_button_down) {
02100 w->scrolling_scrollbar = -1;
02101 w->SetDirty();
02102 return ES_HANDLED;
02103 }
02104
02105 int i;
02106 NWidgetScrollbar *sb = w->GetWidget<NWidgetScrollbar>(w->scrolling_scrollbar);
02107 bool rtl = false;
02108
02109 if (sb->type == NWID_HSCROLLBAR) {
02110 i = _cursor.pos.x - _cursorpos_drag_start.x;
02111 rtl = _current_text_dir == TD_RTL;
02112 } else {
02113 i = _cursor.pos.y - _cursorpos_drag_start.y;
02114 }
02115
02116 if (sb->disp_flags & ND_SCROLLBAR_BTN) {
02117 if (_scroller_click_timeout == 1) {
02118 _scroller_click_timeout = 3;
02119 sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1);
02120 w->SetDirty();
02121 }
02122 return ES_HANDLED;
02123 }
02124
02125
02126 int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity()));
02127 if (rtl) pos = max(0, sb->GetCount() - sb->GetCapacity() - pos);
02128 if (pos != sb->GetPosition()) {
02129 sb->SetPosition(pos);
02130 w->SetDirty();
02131 }
02132 return ES_HANDLED;
02133 }
02134 }
02135
02136 return ES_NOT_HANDLED;
02137 }
02138
02143 static EventState HandleViewportScroll()
02144 {
02145 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02146
02147 if (!_scrolling_viewport) return ES_NOT_HANDLED;
02148
02149
02150
02151
02152 if (_last_scroll_window == NULL) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
02153
02154 if (_last_scroll_window == NULL || !(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down))) {
02155 _cursor.fix_at = false;
02156 _scrolling_viewport = false;
02157 _last_scroll_window = NULL;
02158 return ES_NOT_HANDLED;
02159 }
02160
02161 if (_last_scroll_window == FindWindowById(WC_MAIN_WINDOW, 0) && _last_scroll_window->viewport->follow_vehicle != INVALID_VEHICLE) {
02162
02163 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
02164 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
02165 return ES_NOT_HANDLED;
02166 }
02167
02168 Point delta;
02169 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
02170 delta.x = -_cursor.delta.x;
02171 delta.y = -_cursor.delta.y;
02172 } else {
02173 delta.x = _cursor.delta.x;
02174 delta.y = _cursor.delta.y;
02175 }
02176
02177 if (scrollwheel_scrolling) {
02178
02179 delta.x = _cursor.h_wheel;
02180 delta.y = _cursor.v_wheel;
02181 _cursor.v_wheel = 0;
02182 _cursor.h_wheel = 0;
02183 }
02184
02185
02186 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
02187
02188 _cursor.delta.x = 0;
02189 _cursor.delta.y = 0;
02190 return ES_HANDLED;
02191 }
02192
02203 static bool MaybeBringWindowToFront(Window *w)
02204 {
02205 bool bring_to_front = false;
02206
02207 if (w->window_class == WC_MAIN_WINDOW ||
02208 IsVitalWindow(w) ||
02209 w->window_class == WC_TOOLTIPS ||
02210 w->window_class == WC_DROPDOWN_MENU) {
02211 return true;
02212 }
02213
02214
02215 int w_width = w->width;
02216 int w_height = w->height;
02217 if (w->IsShaded()) {
02218 w_width = w->unshaded_size.width;
02219 w_height = w->unshaded_size.height;
02220 }
02221
02222 Window *u;
02223 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
02224
02225 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
02226 u->SetWhiteBorder();
02227 u->SetDirty();
02228 return false;
02229 }
02230
02231 if (u->window_class == WC_MAIN_WINDOW ||
02232 IsVitalWindow(u) ||
02233 u->window_class == WC_TOOLTIPS ||
02234 u->window_class == WC_DROPDOWN_MENU) {
02235 continue;
02236 }
02237
02238
02239 if (w->left + w_width <= u->left ||
02240 u->left + u->width <= w->left ||
02241 w->top + w_height <= u->top ||
02242 u->top + u->height <= w->top) {
02243 continue;
02244 }
02245
02246 bring_to_front = true;
02247 }
02248
02249 if (bring_to_front) BringWindowToFront(w);
02250 return true;
02251 }
02252
02261 EventState Window::HandleEditBoxKey(int wid, uint16 key, uint16 keycode)
02262 {
02263 EventState state = ES_NOT_HANDLED;
02264
02265 QueryString *query = this->GetQueryString(wid);
02266 if (query == NULL) return state;
02267
02268 int action = QueryString::ACTION_NOTHING;
02269
02270 switch (query->HandleEditBoxKey(this, wid, key, keycode, state)) {
02271 case HEBR_EDITING:
02272 this->OnEditboxChanged(wid);
02273 break;
02274
02275 case HEBR_CONFIRM:
02276 if (query->ok_button >= 0) {
02277 this->OnClick(Point(), query->ok_button, 1);
02278 } else {
02279 action = query->ok_button;
02280 }
02281 break;
02282
02283 case HEBR_CANCEL:
02284 if (query->cancel_button >= 0) {
02285 this->OnClick(Point(), query->cancel_button, 1);
02286 } else {
02287 action = query->cancel_button;
02288 }
02289 break;
02290
02291 default: break;
02292 }
02293
02294 switch (action) {
02295 case QueryString::ACTION_DESELECT:
02296 this->UnfocusFocusedWidget();
02297 break;
02298
02299 case QueryString::ACTION_CLEAR:
02300 query->text.DeleteAll();
02301 this->SetWidgetDirty(wid);
02302 this->OnEditboxChanged(wid);
02303 break;
02304
02305 default:
02306 break;
02307 }
02308
02309 return state;
02310 }
02311
02316 void HandleKeypress(uint32 raw_key)
02317 {
02318
02319
02320 assert(HasModalProgress() || IsLocalCompany());
02321
02322
02323 uint16 key = GB(raw_key, 0, 16);
02324 uint16 keycode = GB(raw_key, 16, 16);
02325
02326
02327
02328
02329
02330
02331
02332
02333 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
02334
02335
02336
02337
02338 if (key == 0 && keycode == 0) return;
02339
02340
02341 if (EditBoxInGlobalFocus()) {
02342
02343 if (_focused_window->window_class == WC_CONSOLE) {
02344 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
02345 } else {
02346 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->index, key, keycode) == ES_HANDLED) return;
02347 }
02348 }
02349
02350
02351 Window *w;
02352 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02353 if (w->window_class == WC_MAIN_TOOLBAR) continue;
02354 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02355 }
02356
02357 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02358
02359 if (w != NULL && w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02360
02361 HandleGlobalHotkeys(key, keycode);
02362 }
02363
02367 void HandleCtrlChanged()
02368 {
02369
02370 Window *w;
02371 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02372 if (w->OnCTRLStateChange() == ES_HANDLED) return;
02373 }
02374 }
02375
02382 static int _input_events_this_tick = 0;
02383
02388 static void HandleAutoscroll()
02389 {
02390 if (_game_mode == GM_MENU || HasModalProgress()) return;
02391 if (_settings_client.gui.auto_scrolling == VA_DISABLED) return;
02392 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
02393
02394 int x = _cursor.pos.x;
02395 int y = _cursor.pos.y;
02396 Window *w = FindWindowFromPt(x, y);
02397 if (w == NULL || w->flags & WF_DISABLE_VP_SCROLL) return;
02398 if (_settings_client.gui.auto_scrolling != VA_EVERY_VIEWPORT && w->window_class != WC_MAIN_WINDOW) return;
02399
02400 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02401 if (vp == NULL) return;
02402
02403 x -= vp->left;
02404 y -= vp->top;
02405
02406
02407 #define scrollspeed 3
02408 if (x - 15 < 0) {
02409 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
02410 } else if (15 - (vp->width - x) > 0) {
02411 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
02412 }
02413 if (y - 15 < 0) {
02414 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
02415 } else if (15 - (vp->height - y) > 0) {
02416 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
02417 }
02418 #undef scrollspeed
02419 }
02420
02421 enum MouseClick {
02422 MC_NONE = 0,
02423 MC_LEFT,
02424 MC_RIGHT,
02425 MC_DOUBLE_LEFT,
02426 MC_HOVER,
02427
02428 MAX_OFFSET_DOUBLE_CLICK = 5,
02429 TIME_BETWEEN_DOUBLE_CLICK = 500,
02430 MAX_OFFSET_HOVER = 5,
02431 };
02432 extern EventState VpHandlePlaceSizingDrag();
02433
02434 static void ScrollMainViewport(int x, int y)
02435 {
02436 if (_game_mode != GM_MENU) {
02437 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02438 assert(w);
02439
02440 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
02441 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
02442 }
02443 }
02444
02454 static const int8 scrollamt[16][2] = {
02455 { 0, 0},
02456 {-2, 0},
02457 { 0, -2},
02458 {-2, -1},
02459 { 2, 0},
02460 { 0, 0},
02461 { 2, -1},
02462 { 0, -2},
02463 { 0, 2},
02464 {-2, 1},
02465 { 0, 0},
02466 {-2, 0},
02467 { 2, 1},
02468 { 0, 2},
02469 { 2, 0},
02470 { 0, 0},
02471 };
02472
02473 static void HandleKeyScrolling()
02474 {
02475
02476
02477
02478
02479 if (_dirkeys && !EditBoxInGlobalFocus()) {
02480 int factor = _shift_pressed ? 50 : 10;
02481 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
02482 }
02483 }
02484
02485 static void MouseLoop(MouseClick click, int mousewheel)
02486 {
02487
02488
02489 assert(HasModalProgress() || IsLocalCompany());
02490
02491 HandlePlacePresize();
02492 UpdateTileSelection();
02493
02494 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
02495 if (HandleMouseDragDrop() == ES_HANDLED) return;
02496 if (HandleWindowDragging() == ES_HANDLED) return;
02497 if (HandleScrollbarScrolling() == ES_HANDLED) return;
02498 if (HandleViewportScroll() == ES_HANDLED) return;
02499
02500 HandleMouseOver();
02501
02502 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02503 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
02504
02505 int x = _cursor.pos.x;
02506 int y = _cursor.pos.y;
02507 Window *w = FindWindowFromPt(x, y);
02508 if (w == NULL) return;
02509
02510 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
02511 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02512
02513
02514 if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
02515
02516 if (mousewheel != 0) {
02517
02518 w->OnMouseWheel(mousewheel);
02519
02520
02521 if (vp == NULL) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
02522 }
02523
02524 if (vp != NULL) {
02525 if (scrollwheel_scrolling) click = MC_RIGHT;
02526 switch (click) {
02527 case MC_DOUBLE_LEFT:
02528 case MC_LEFT:
02529 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
02530 if (!HandleViewportClicked(vp, x, y) &&
02531 !(w->flags & WF_DISABLE_VP_SCROLL) &&
02532 _settings_client.gui.left_mouse_btn_scrolling) {
02533 _scrolling_viewport = true;
02534 _cursor.fix_at = false;
02535 }
02536 break;
02537
02538 case MC_RIGHT:
02539 if (!(w->flags & WF_DISABLE_VP_SCROLL)) {
02540 _scrolling_viewport = true;
02541 _cursor.fix_at = true;
02542
02543
02544 _cursor.h_wheel = 0;
02545 _cursor.v_wheel = 0;
02546 }
02547 break;
02548
02549 default:
02550 break;
02551 }
02552 } else {
02553 switch (click) {
02554 case MC_LEFT:
02555 case MC_DOUBLE_LEFT:
02556 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
02557 break;
02558
02559 default:
02560 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
02561
02562
02563
02564
02565 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
02566
02567 case MC_HOVER: DispatchHoverEvent(w, x - w->left, y - w->top); break;
02568 }
02569 }
02570 }
02571
02575 void HandleMouseEvents()
02576 {
02577
02578
02579 assert(HasModalProgress() || IsLocalCompany());
02580
02581 static int double_click_time = 0;
02582 static Point double_click_pos = {0, 0};
02583
02584
02585 MouseClick click = MC_NONE;
02586 if (_left_button_down && !_left_button_clicked) {
02587 click = MC_LEFT;
02588 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02589 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
02590 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
02591 click = MC_DOUBLE_LEFT;
02592 }
02593 double_click_time = _realtime_tick;
02594 double_click_pos = _cursor.pos;
02595 _left_button_clicked = true;
02596 _input_events_this_tick++;
02597 } else if (_right_button_clicked) {
02598 _right_button_clicked = false;
02599 click = MC_RIGHT;
02600 _input_events_this_tick++;
02601 }
02602
02603 int mousewheel = 0;
02604 if (_cursor.wheel) {
02605 mousewheel = _cursor.wheel;
02606 _cursor.wheel = 0;
02607 _input_events_this_tick++;
02608 }
02609
02610 static uint32 hover_time = 0;
02611 static Point hover_pos = {0, 0};
02612
02613 if (_settings_client.gui.hover_delay > 0) {
02614 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
02615 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
02616 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
02617 hover_pos = _cursor.pos;
02618 hover_time = _realtime_tick;
02619 _mouse_hovering = false;
02620 } else {
02621 if (hover_time != 0 && _realtime_tick > hover_time + _settings_client.gui.hover_delay * 1000) {
02622 click = MC_HOVER;
02623 _input_events_this_tick++;
02624 _mouse_hovering = true;
02625 }
02626 }
02627 }
02628
02629
02630 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _newgrf_debug_sprite_picker.click_time != _realtime_tick) {
02631
02632 _newgrf_debug_sprite_picker.mode = SPM_NONE;
02633 InvalidateWindowData(WC_SPRITE_ALIGNER, 0, 1);
02634 }
02635
02636 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
02637
02638 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
02639 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
02640 _newgrf_debug_sprite_picker.click_time = _realtime_tick;
02641 _newgrf_debug_sprite_picker.sprites.Clear();
02642 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
02643 MarkWholeScreenDirty();
02644 } else {
02645 MouseLoop(click, mousewheel);
02646 }
02647
02648
02649
02650 _cursor.delta.x = 0;
02651 _cursor.delta.y = 0;
02652 }
02653
02657 static void CheckSoftLimit()
02658 {
02659 if (_settings_client.gui.window_soft_limit == 0) return;
02660
02661 for (;;) {
02662 uint deletable_count = 0;
02663 Window *w, *last_deletable = NULL;
02664 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02665 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue;
02666
02667 last_deletable = w;
02668 deletable_count++;
02669 }
02670
02671
02672 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02673
02674 assert(last_deletable != NULL);
02675 delete last_deletable;
02676 }
02677 }
02678
02682 void InputLoop()
02683 {
02684
02685
02686 assert(HasModalProgress() || IsLocalCompany());
02687
02688 CheckSoftLimit();
02689 HandleKeyScrolling();
02690
02691
02692 for (Window *v = _z_front_window; v != NULL; ) {
02693 Window *w = v;
02694 v = v->z_back;
02695
02696 if (w->window_class != WC_INVALID) continue;
02697
02698 RemoveWindowFromZOrdering(w);
02699 free(w);
02700 }
02701
02702 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
02703 DecreaseWindowCounters();
02704
02705 if (_input_events_this_tick != 0) {
02706
02707 _input_events_this_tick = 0;
02708
02709 return;
02710 }
02711
02712
02713 HandleMouseEvents();
02714 HandleAutoscroll();
02715 }
02716
02720 void UpdateWindows()
02721 {
02722 Window *w;
02723
02724 static int highlight_timer = 1;
02725 if (--highlight_timer == 0) {
02726 highlight_timer = 15;
02727 _window_highlight_colour = !_window_highlight_colour;
02728 }
02729
02730 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02731 w->ProcessScheduledInvalidations();
02732 w->ProcessHighlightedInvalidations();
02733 }
02734
02735 static int we4_timer = 0;
02736 int t = we4_timer + 1;
02737
02738 if (t >= 100) {
02739 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02740 w->OnHundredthTick();
02741 }
02742 t = 0;
02743 }
02744 we4_timer = t;
02745
02746 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02747 if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
02748 CLRBITS(w->flags, WF_WHITE_BORDER);
02749 w->SetDirty();
02750 }
02751 }
02752
02753 DrawDirtyBlocks();
02754
02755 FOR_ALL_WINDOWS_FROM_BACK(w) {
02756
02757 if (w->viewport != NULL && !w->IsShaded()) UpdateViewportPosition(w);
02758 }
02759 NetworkDrawChatMessage();
02760
02761 DrawMouseCursor();
02762 }
02763
02769 void SetWindowDirty(WindowClass cls, WindowNumber number)
02770 {
02771 const Window *w;
02772 FOR_ALL_WINDOWS_FROM_BACK(w) {
02773 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02774 }
02775 }
02776
02783 void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
02784 {
02785 const Window *w;
02786 FOR_ALL_WINDOWS_FROM_BACK(w) {
02787 if (w->window_class == cls && w->window_number == number) {
02788 w->SetWidgetDirty(widget_index);
02789 }
02790 }
02791 }
02792
02797 void SetWindowClassesDirty(WindowClass cls)
02798 {
02799 Window *w;
02800 FOR_ALL_WINDOWS_FROM_BACK(w) {
02801 if (w->window_class == cls) w->SetDirty();
02802 }
02803 }
02804
02810 void Window::InvalidateData(int data, bool gui_scope)
02811 {
02812 this->SetDirty();
02813 if (!gui_scope) {
02814
02815 *this->scheduled_invalidation_data.Append() = data;
02816 }
02817 this->OnInvalidateData(data, gui_scope);
02818 }
02819
02823 void Window::ProcessScheduledInvalidations()
02824 {
02825 for (int *data = this->scheduled_invalidation_data.Begin(); this->window_class != WC_INVALID && data != this->scheduled_invalidation_data.End(); data++) {
02826 this->OnInvalidateData(*data, true);
02827 }
02828 this->scheduled_invalidation_data.Clear();
02829 }
02830
02834 void Window::ProcessHighlightedInvalidations()
02835 {
02836 if ((this->flags & WF_HIGHLIGHTED) == 0) return;
02837
02838 for (uint i = 0; i < this->nested_array_size; i++) {
02839 if (this->IsWidgetHighlighted(i)) this->SetWidgetDirty(i);
02840 }
02841 }
02842
02869 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
02870 {
02871 Window *w;
02872 FOR_ALL_WINDOWS_FROM_BACK(w) {
02873 if (w->window_class == cls && w->window_number == number) {
02874 w->InvalidateData(data, gui_scope);
02875 }
02876 }
02877 }
02878
02887 void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
02888 {
02889 Window *w;
02890
02891 FOR_ALL_WINDOWS_FROM_BACK(w) {
02892 if (w->window_class == cls) {
02893 w->InvalidateData(data, gui_scope);
02894 }
02895 }
02896 }
02897
02901 void CallWindowTickEvent()
02902 {
02903 Window *w;
02904 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02905 w->OnTick();
02906 }
02907 }
02908
02915 void DeleteNonVitalWindows()
02916 {
02917 Window *w;
02918
02919 restart_search:
02920
02921
02922
02923 FOR_ALL_WINDOWS_FROM_BACK(w) {
02924 if (w->window_class != WC_MAIN_WINDOW &&
02925 w->window_class != WC_SELECT_GAME &&
02926 w->window_class != WC_MAIN_TOOLBAR &&
02927 w->window_class != WC_STATUS_BAR &&
02928 w->window_class != WC_TOOLTIPS &&
02929 (w->flags & WF_STICKY) == 0) {
02930
02931 delete w;
02932 goto restart_search;
02933 }
02934 }
02935 }
02936
02944 void DeleteAllNonVitalWindows()
02945 {
02946 Window *w;
02947
02948
02949 DeleteNonVitalWindows();
02950
02951 restart_search:
02952
02953
02954
02955 FOR_ALL_WINDOWS_FROM_BACK(w) {
02956 if (w->flags & WF_STICKY) {
02957 delete w;
02958 goto restart_search;
02959 }
02960 }
02961 }
02962
02967 void DeleteConstructionWindows()
02968 {
02969 Window *w;
02970
02971 restart_search:
02972
02973
02974
02975 FOR_ALL_WINDOWS_FROM_BACK(w) {
02976 if (w->desc_flags & WDF_CONSTRUCTION) {
02977 delete w;
02978 goto restart_search;
02979 }
02980 }
02981
02982 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02983 }
02984
02986 void HideVitalWindows()
02987 {
02988 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02989 DeleteWindowById(WC_STATUS_BAR, 0);
02990 }
02991
02993 void ReInitAllWindows()
02994 {
02995 NWidgetLeaf::InvalidateDimensionCache();
02996 NWidgetScrollbar::InvalidateDimensionCache();
02997
02998 Window *w;
02999 FOR_ALL_WINDOWS_FROM_BACK(w) {
03000 w->ReInit();
03001 }
03002 #ifdef ENABLE_NETWORK
03003 void NetworkReInitChatBoxSize();
03004 NetworkReInitChatBoxSize();
03005 #endif
03006
03007
03008 RelocateAllWindows(_cur_resolution.width, _cur_resolution.height);
03009 MarkWholeScreenDirty();
03010 }
03011
03019 static int PositionWindow(Window *w, WindowClass clss, int setting)
03020 {
03021 if (w == NULL || w->window_class != clss) {
03022 w = FindWindowById(clss, 0);
03023 }
03024 if (w == NULL) return 0;
03025
03026 int old_left = w->left;
03027 switch (setting) {
03028 case 1: w->left = (_screen.width - w->width) / 2; break;
03029 case 2: w->left = _screen.width - w->width; break;
03030 default: w->left = 0; break;
03031 }
03032 if (w->viewport != NULL) w->viewport->left += w->left - old_left;
03033 SetDirtyBlocks(0, w->top, _screen.width, w->top + w->height);
03034 return w->left;
03035 }
03036
03042 int PositionMainToolbar(Window *w)
03043 {
03044 DEBUG(misc, 5, "Repositioning Main Toolbar...");
03045 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
03046 }
03047
03053 int PositionStatusbar(Window *w)
03054 {
03055 DEBUG(misc, 5, "Repositioning statusbar...");
03056 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
03057 }
03058
03064 int PositionNewsMessage(Window *w)
03065 {
03066 DEBUG(misc, 5, "Repositioning news message...");
03067 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
03068 }
03069
03075 int PositionNetworkChatWindow(Window *w)
03076 {
03077 DEBUG(misc, 5, "Repositioning network chat window...");
03078 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
03079 }
03080
03081
03087 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
03088 {
03089 Window *w;
03090 FOR_ALL_WINDOWS_FROM_BACK(w) {
03091 if (w->viewport != NULL && w->viewport->follow_vehicle == from_index) {
03092 w->viewport->follow_vehicle = to_index;
03093 w->SetDirty();
03094 }
03095 }
03096 }
03097
03098
03104 void RelocateAllWindows(int neww, int newh)
03105 {
03106 Window *w;
03107
03108 FOR_ALL_WINDOWS_FROM_BACK(w) {
03109 int left, top;
03110
03111 if (w->window_class == WC_MAIN_WINDOW) {
03112 ViewPort *vp = w->viewport;
03113 vp->width = w->width = neww;
03114 vp->height = w->height = newh;
03115 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
03116 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
03117 continue;
03118 }
03119
03120
03121
03122 switch (w->window_class) {
03123 case WC_BOOTSTRAP:
03124 ResizeWindow(w, neww, newh);
03125 continue;
03126
03127 case WC_MAIN_TOOLBAR:
03128 ResizeWindow(w, min(neww, *_preferred_toolbar_size) - w->width, 0, false);
03129
03130 top = w->top;
03131 left = PositionMainToolbar(w);
03132 break;
03133
03134 case WC_NEWS_WINDOW:
03135 top = newh - w->height;
03136 left = PositionNewsMessage(w);
03137 break;
03138
03139 case WC_STATUS_BAR:
03140 ResizeWindow(w, min(neww, *_preferred_statusbar_size) - w->width, 0, false);
03141
03142 top = newh - w->height;
03143 left = PositionStatusbar(w);
03144 break;
03145
03146 case WC_SEND_NETWORK_MSG:
03147 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0, false);
03148 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
03149 left = PositionNetworkChatWindow(w);
03150 break;
03151
03152 case WC_CONSOLE:
03153 IConsoleResize(w);
03154 continue;
03155
03156 default: {
03157 if (w->flags & WF_CENTERED) {
03158 top = (newh - w->height) >> 1;
03159 left = (neww - w->width) >> 1;
03160 break;
03161 }
03162
03163 left = w->left;
03164 if (left + (w->width >> 1) >= neww) left = neww - w->width;
03165 if (left < 0) left = 0;
03166
03167 top = w->top;
03168 if (top + (w->height >> 1) >= newh) top = newh - w->height;
03169 break;
03170 }
03171 }
03172
03173 EnsureVisibleCaption(w, left, top);
03174 }
03175 }
03176
03182 PickerWindowBase::~PickerWindowBase()
03183 {
03184 this->window_class = WC_INVALID;
03185 ResetObjectToPlace();
03186 }