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
00037
00038 static Point _drag_delta;
00039 static Window *_mouseover_last_w = NULL;
00040 static Window *_last_scroll_window = NULL;
00041
00043 Window *_z_front_window = NULL;
00045 Window *_z_back_window = NULL;
00046
00048 bool _window_highlight_colour = false;
00049
00050
00051
00052
00053
00054
00055 Window *_focused_window;
00056
00057 Point _cursorpos_drag_start;
00058
00059 int _scrollbar_start_pos;
00060 int _scrollbar_size;
00061 byte _scroller_click_timeout = 0;
00062
00063 bool _scrolling_viewport;
00064 bool _mouse_hovering;
00065
00066 SpecialMouseMode _special_mouse_mode;
00067
00069 WindowDesc::WindowDesc(WindowPosition def_pos, int16 def_width, int16 def_height,
00070 WindowClass window_class, WindowClass parent_class, uint32 flags,
00071 const NWidgetPart *nwid_parts, int16 nwid_length) :
00072 default_pos(def_pos),
00073 default_width(def_width),
00074 default_height(def_height),
00075 cls(window_class),
00076 parent_cls(parent_class),
00077 flags(flags),
00078 nwid_parts(nwid_parts),
00079 nwid_length(nwid_length)
00080 {
00081 }
00082
00083 WindowDesc::~WindowDesc()
00084 {
00085 }
00086
00096 int Window::GetRowFromWidget(int clickpos, int widget, int padding, int line_height) const
00097 {
00098 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
00099 if (line_height < 0) line_height = wid->resize_y;
00100 if (clickpos < (int)wid->pos_y + padding) return INT_MAX;
00101 return (clickpos - (int)wid->pos_y - padding) / line_height;
00102 }
00103
00107 void Window::DisableAllWidgetHighlight()
00108 {
00109 for (uint i = 0; i < this->nested_array_size; i++) {
00110 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(i);
00111 if (nwid == NULL) continue;
00112
00113 if (nwid->IsHighlighted()) {
00114 nwid->SetHighlighted(TC_INVALID);
00115 this->SetWidgetDirty(i);
00116 }
00117 }
00118
00119 CLRBITS(this->flags, WF_HIGHLIGHTED);
00120 }
00121
00127 void Window::SetWidgetHighlight(byte widget_index, TextColour highlighted_colour)
00128 {
00129 assert(widget_index < this->nested_array_size);
00130
00131 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
00132 if (nwid == NULL) return;
00133
00134 nwid->SetHighlighted(highlighted_colour);
00135 this->SetWidgetDirty(widget_index);
00136
00137 if (highlighted_colour != TC_INVALID) {
00138
00139 this->flags |= WF_HIGHLIGHTED;
00140 } else {
00141
00142 bool valid = false;
00143 for (uint i = 0; i < this->nested_array_size; i++) {
00144 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(i);
00145 if (nwid == NULL) continue;
00146 if (!nwid->IsHighlighted()) continue;
00147
00148 valid = true;
00149 }
00150
00151 if (!valid) CLRBITS(this->flags, WF_HIGHLIGHTED);
00152 }
00153 }
00154
00160 bool Window::IsWidgetHighlighted(byte widget_index) const
00161 {
00162 assert(widget_index < this->nested_array_size);
00163
00164 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
00165 if (nwid == NULL) return false;
00166
00167 return nwid->IsHighlighted();
00168 }
00169
00177 void Window::OnDropdownClose(Point pt, int widget, int index, bool instant_close)
00178 {
00179 if (widget < 0) return;
00180
00181 if (instant_close) {
00182
00183
00184 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
00185 this->OnDropdownSelect(widget, index);
00186 }
00187 }
00188
00189
00190 if (this->nested_array != NULL) {
00191 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
00192 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
00193 nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
00194 } else {
00195 this->RaiseWidget(widget);
00196 }
00197 } else {
00198 this->RaiseWidget(widget);
00199 }
00200 this->SetWidgetDirty(widget);
00201 }
00202
00208 const Scrollbar *Window::GetScrollbar(uint widnum) const
00209 {
00210 return this->GetWidget<NWidgetScrollbar>(widnum);
00211 }
00212
00218 Scrollbar *Window::GetScrollbar(uint widnum)
00219 {
00220 return this->GetWidget<NWidgetScrollbar>(widnum);
00221 }
00222
00223
00228 void SetFocusedWindow(Window *w)
00229 {
00230 if (_focused_window == w) return;
00231
00232
00233 if (_focused_window != NULL) {
00234 if (_focused_window->nested_focus != NULL) _focused_window->nested_focus->SetDirty(_focused_window);
00235 }
00236
00237
00238 Window *old_focused = _focused_window;
00239 _focused_window = w;
00240
00241
00242 if (old_focused != NULL) old_focused->OnFocusLost();
00243 if (_focused_window != NULL) _focused_window->OnFocus();
00244 }
00245
00251 static bool EditBoxInGlobalFocus()
00252 {
00253 if (_focused_window == NULL) return false;
00254
00255
00256 if (_focused_window->window_class == WC_CONSOLE) return true;
00257
00258 return _focused_window->nested_focus != NULL && _focused_window->nested_focus->type == WWT_EDITBOX;
00259 }
00260
00264 void Window::UnfocusFocusedWidget()
00265 {
00266 if (this->nested_focus != NULL) {
00267
00268 this->nested_focus->SetDirty(this);
00269 this->nested_focus = NULL;
00270 }
00271 }
00272
00278 bool Window::SetFocusedWidget(byte widget_index)
00279 {
00280
00281 if (widget_index >= this->nested_array_size) return false;
00282
00283 assert(this->nested_array[widget_index] != NULL);
00284 if (this->nested_focus != NULL) {
00285 if (this->GetWidget<NWidgetCore>(widget_index) == this->nested_focus) return false;
00286
00287
00288 this->nested_focus->SetDirty(this);
00289 }
00290 this->nested_focus = this->GetWidget<NWidgetCore>(widget_index);
00291 return true;
00292 }
00293
00301 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
00302 {
00303 va_list wdg_list;
00304
00305 va_start(wdg_list, widgets);
00306
00307 while (widgets != WIDGET_LIST_END) {
00308 SetWidgetDisabledState(widgets, disab_stat);
00309 widgets = va_arg(wdg_list, int);
00310 }
00311
00312 va_end(wdg_list);
00313 }
00314
00320 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
00321 {
00322 va_list wdg_list;
00323
00324 va_start(wdg_list, widgets);
00325
00326 while (widgets != WIDGET_LIST_END) {
00327 SetWidgetLoweredState(widgets, lowered_stat);
00328 widgets = va_arg(wdg_list, int);
00329 }
00330
00331 va_end(wdg_list);
00332 }
00333
00338 void Window::RaiseButtons(bool autoraise)
00339 {
00340 for (uint i = 0; i < this->nested_array_size; i++) {
00341 if (this->nested_array[i] != NULL && ((this->nested_array[i]->type & ~WWB_PUSHBUTTON) < WWT_LAST || this->nested_array[i]->type == NWID_PUSHBUTTON_DROPDOWN) &&
00342 (!autoraise || (this->nested_array[i]->type & WWB_PUSHBUTTON)) && this->IsWidgetLowered(i)) {
00343 this->RaiseWidget(i);
00344 this->SetWidgetDirty(i);
00345 }
00346 }
00347 }
00348
00353 void Window::SetWidgetDirty(byte widget_index) const
00354 {
00355
00356 if (this->nested_array == NULL) return;
00357
00358 this->nested_array[widget_index]->SetDirty(this);
00359 }
00360
00366 void Window::HandleButtonClick(byte widget)
00367 {
00368 this->LowerWidget(widget);
00369 this->SetTimeout();
00370 this->SetWidgetDirty(widget);
00371 }
00372
00373 static void StartWindowDrag(Window *w);
00374 static void StartWindowSizing(Window *w, bool to_left);
00375
00383 static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
00384 {
00385 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
00386 WidgetType widget_type = (nw != NULL) ? nw->type : WWT_EMPTY;
00387
00388 bool focused_widget_changed = false;
00389
00390 if (_focused_window != w &&
00391 (w->desc_flags & WDF_NO_FOCUS) == 0 &&
00392 widget_type != WWT_CLOSEBOX) {
00393 focused_widget_changed = true;
00394 if (_focused_window != NULL) {
00395 _focused_window->OnFocusLost();
00396
00397
00398 if (w->window_class != WC_OSK) DeleteWindowById(WC_OSK, 0);
00399 }
00400 SetFocusedWindow(w);
00401 w->OnFocus();
00402 }
00403
00404 if (nw == NULL) return;
00405
00406
00407 if (nw->IsDisabled()) return;
00408
00409 int widget_index = nw->index;
00410
00411
00412
00413 if (widget_type != WWT_CAPTION) {
00414
00415 if (w->nested_focus != NULL && w->nested_focus->type == WWT_EDITBOX && w->nested_focus != nw && w->window_class != WC_OSK) {
00416 DeleteWindowById(WC_OSK, 0);
00417 }
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428 focused_widget_changed |= w->SetFocusedWidget(widget_index);
00429 }
00430
00431
00432
00433 if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
00434
00435 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
00436
00437 switch (widget_type) {
00438 case NWID_VSCROLLBAR:
00439 case NWID_HSCROLLBAR:
00440 ScrollbarClickHandler(w, nw, x, y);
00441 break;
00442
00443 case WWT_EDITBOX:
00444 if (!focused_widget_changed) {
00445
00446 QueryStringBaseWindow *qs = dynamic_cast<QueryStringBaseWindow *>(w);
00447 if (qs != NULL) {
00448 qs->OnOpenOSKWindow(widget_index);
00449 }
00450 }
00451 break;
00452
00453 case WWT_CLOSEBOX:
00454 delete w;
00455 return;
00456
00457 case WWT_CAPTION:
00458 StartWindowDrag(w);
00459 return;
00460
00461 case WWT_RESIZEBOX:
00462
00463
00464 StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
00465 nw->SetDirty(w);
00466 return;
00467
00468 case WWT_DEBUGBOX:
00469 w->ShowNewGRFInspectWindow();
00470 break;
00471
00472 case WWT_SHADEBOX:
00473 nw->SetDirty(w);
00474 w->SetShaded(!w->IsShaded());
00475 return;
00476
00477 case WWT_STICKYBOX:
00478 w->flags ^= WF_STICKY;
00479 nw->SetDirty(w);
00480 return;
00481
00482 default:
00483 break;
00484 }
00485
00486
00487 if (widget_index < 0) return;
00488
00489
00490 if (w->IsWidgetHighlighted(widget_index)) {
00491 w->SetWidgetHighlight(widget_index, TC_INVALID);
00492 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
00493 }
00494
00495 Point pt = { x, y };
00496 w->OnClick(pt, widget_index, click_count);
00497 }
00498
00505 static void DispatchRightClickEvent(Window *w, int x, int y)
00506 {
00507 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00508 if (wid == NULL) return;
00509
00510
00511 if (wid->index >= 0) {
00512 Point pt = { x, y };
00513 if (w->OnRightClick(pt, wid->index)) return;
00514 }
00515
00516 if (_settings_client.gui.hover_delay == 0 && wid->tool_tip != 0) GuiShowTooltips(w, wid->tool_tip, 0, NULL, TCC_RIGHT_CLICK);
00517 }
00518
00525 static void DispatchHoverEvent(Window *w, int x, int y)
00526 {
00527 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00528
00529
00530 if (wid == NULL) return;
00531
00532
00533 if (wid->tool_tip != 0) {
00534 GuiShowTooltips(w, wid->tool_tip);
00535 return;
00536 }
00537
00538
00539 if (wid->index < 0) return;
00540
00541 Point pt = { x, y };
00542 w->OnHover(pt, wid->index);
00543 }
00544
00552 static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
00553 {
00554 if (nwid == NULL) return;
00555
00556
00557 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
00558 w->SetShaded(wheel < 0);
00559 return;
00560 }
00561
00562
00563 if (nwid->type == NWID_VSCROLLBAR) {
00564 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
00565 if (sb->GetCount() > sb->GetCapacity()) {
00566 sb->UpdatePosition(wheel);
00567 w->SetDirty();
00568 }
00569 return;
00570 }
00571
00572
00573 Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : NULL);
00574 if (sb != NULL && sb->GetCount() > sb->GetCapacity()) {
00575 sb->UpdatePosition(wheel);
00576 w->SetDirty();
00577 }
00578 }
00579
00585 static bool MayBeShown(const Window *w)
00586 {
00587
00588 if (!HasModalProgress()) return true;
00589
00590 switch (w->window_class) {
00591 case WC_MAIN_WINDOW:
00592 case WC_MODAL_PROGRESS:
00593 case WC_CONFIRM_POPUP_QUERY:
00594 return true;
00595
00596 default:
00597 return false;
00598 }
00599 }
00600
00613 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00614 {
00615 const Window *v;
00616 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00617 if (MayBeShown(v) &&
00618 right > v->left &&
00619 bottom > v->top &&
00620 left < v->left + v->width &&
00621 top < v->top + v->height) {
00622
00623 int x;
00624
00625 if (left < (x = v->left)) {
00626 DrawOverlappedWindow(w, left, top, x, bottom);
00627 DrawOverlappedWindow(w, x, top, right, bottom);
00628 return;
00629 }
00630
00631 if (right > (x = v->left + v->width)) {
00632 DrawOverlappedWindow(w, left, top, x, bottom);
00633 DrawOverlappedWindow(w, x, top, right, bottom);
00634 return;
00635 }
00636
00637 if (top < (x = v->top)) {
00638 DrawOverlappedWindow(w, left, top, right, x);
00639 DrawOverlappedWindow(w, left, x, right, bottom);
00640 return;
00641 }
00642
00643 if (bottom > (x = v->top + v->height)) {
00644 DrawOverlappedWindow(w, left, top, right, x);
00645 DrawOverlappedWindow(w, left, x, right, bottom);
00646 return;
00647 }
00648
00649 return;
00650 }
00651 }
00652
00653
00654 DrawPixelInfo *dp = _cur_dpi;
00655 dp->width = right - left;
00656 dp->height = bottom - top;
00657 dp->left = left - w->left;
00658 dp->top = top - w->top;
00659 dp->pitch = _screen.pitch;
00660 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00661 dp->zoom = ZOOM_LVL_NORMAL;
00662 w->OnPaint();
00663 }
00664
00673 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00674 {
00675 Window *w;
00676 DrawPixelInfo bk;
00677 _cur_dpi = &bk;
00678
00679 FOR_ALL_WINDOWS_FROM_BACK(w) {
00680 if (MayBeShown(w) &&
00681 right > w->left &&
00682 bottom > w->top &&
00683 left < w->left + w->width &&
00684 top < w->top + w->height) {
00685
00686 DrawOverlappedWindow(w, left, top, right, bottom);
00687 }
00688 }
00689 }
00690
00695 void Window::SetDirty() const
00696 {
00697 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00698 }
00699
00706 void Window::ReInit(int rx, int ry)
00707 {
00708 this->SetDirty();
00709
00710
00711 int window_width = this->width;
00712 int window_height = this->height;
00713
00714 this->OnInit();
00715
00716 this->nested_root->SetupSmallestSize(this, false);
00717 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
00718 this->width = this->nested_root->smallest_x;
00719 this->height = this->nested_root->smallest_y;
00720 this->resize.step_width = this->nested_root->resize_x;
00721 this->resize.step_height = this->nested_root->resize_y;
00722
00723
00724 window_width = max(window_width + rx, this->width);
00725 window_height = max(window_height + ry, this->height);
00726 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
00727 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
00728
00729
00730 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
00731 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
00732
00733 ResizeWindow(this, dx, dy);
00734
00735 }
00736
00742 void Window::SetShaded(bool make_shaded)
00743 {
00744 if (this->shade_select == NULL) return;
00745
00746 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
00747 if (this->shade_select->shown_plane != desired) {
00748 if (make_shaded) {
00749 this->unshaded_size.width = this->width;
00750 this->unshaded_size.height = this->height;
00751 this->shade_select->SetDisplayedPlane(desired);
00752 this->ReInit(0, -this->height);
00753 } else {
00754 this->shade_select->SetDisplayedPlane(desired);
00755 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
00756 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
00757 this->ReInit(dx, dy);
00758 }
00759 }
00760 }
00761
00768 static Window *FindChildWindow(const Window *w, WindowClass wc)
00769 {
00770 Window *v;
00771 FOR_ALL_WINDOWS_FROM_BACK(v) {
00772 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
00773 }
00774
00775 return NULL;
00776 }
00777
00782 void Window::DeleteChildWindows(WindowClass wc) const
00783 {
00784 Window *child = FindChildWindow(this, wc);
00785 while (child != NULL) {
00786 delete child;
00787 child = FindChildWindow(this, wc);
00788 }
00789 }
00790
00794 Window::~Window()
00795 {
00796 if (_thd.window_class == this->window_class &&
00797 _thd.window_number == this->window_number) {
00798 ResetObjectToPlace();
00799 }
00800
00801
00802 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00803
00804
00805 if (_last_scroll_window == this) _last_scroll_window = NULL;
00806
00807
00808 if (_focused_window == this) _focused_window = NULL;
00809
00810 this->DeleteChildWindows();
00811
00812 if (this->viewport != NULL) DeleteWindowViewport(this);
00813
00814 this->SetDirty();
00815
00816 free(this->nested_array);
00817 delete this->nested_root;
00818
00819 this->window_class = WC_INVALID;
00820 }
00821
00828 Window *FindWindowById(WindowClass cls, WindowNumber number)
00829 {
00830 Window *w;
00831 FOR_ALL_WINDOWS_FROM_BACK(w) {
00832 if (w->window_class == cls && w->window_number == number) return w;
00833 }
00834
00835 return NULL;
00836 }
00837
00844 Window *FindWindowByClass(WindowClass cls)
00845 {
00846 Window *w;
00847 FOR_ALL_WINDOWS_FROM_BACK(w) {
00848 if (w->window_class == cls) return w;
00849 }
00850
00851 return NULL;
00852 }
00853
00860 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00861 {
00862 Window *w = FindWindowById(cls, number);
00863 if (force || w == NULL ||
00864 (w->flags & WF_STICKY) == 0) {
00865 delete w;
00866 }
00867 }
00868
00873 void DeleteWindowByClass(WindowClass cls)
00874 {
00875 Window *w;
00876
00877 restart_search:
00878
00879
00880
00881 FOR_ALL_WINDOWS_FROM_BACK(w) {
00882 if (w->window_class == cls) {
00883 delete w;
00884 goto restart_search;
00885 }
00886 }
00887 }
00888
00895 void DeleteCompanyWindows(CompanyID id)
00896 {
00897 Window *w;
00898
00899 restart_search:
00900
00901
00902
00903 FOR_ALL_WINDOWS_FROM_BACK(w) {
00904 if (w->owner == id) {
00905 delete w;
00906 goto restart_search;
00907 }
00908 }
00909
00910
00911 DeleteWindowById(WC_BUY_COMPANY, id);
00912 }
00913
00921 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00922 {
00923 Window *w;
00924 FOR_ALL_WINDOWS_FROM_BACK(w) {
00925 if (w->owner != old_owner) continue;
00926
00927 switch (w->window_class) {
00928 case WC_COMPANY_COLOUR:
00929 case WC_FINANCES:
00930 case WC_STATION_LIST:
00931 case WC_TRAINS_LIST:
00932 case WC_ROADVEH_LIST:
00933 case WC_SHIPS_LIST:
00934 case WC_AIRCRAFT_LIST:
00935 case WC_BUY_COMPANY:
00936 case WC_COMPANY:
00937 case WC_COMPANY_INFRASTRUCTURE:
00938 continue;
00939
00940 default:
00941 w->owner = new_owner;
00942 break;
00943 }
00944 }
00945 }
00946
00947 static void BringWindowToFront(Window *w);
00948
00956 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00957 {
00958 Window *w = FindWindowById(cls, number);
00959
00960 if (w != NULL) {
00961 if (w->IsShaded()) w->SetShaded(false);
00962
00963 w->SetWhiteBorder();
00964 BringWindowToFront(w);
00965 w->SetDirty();
00966 }
00967
00968 return w;
00969 }
00970
00971 static inline bool IsVitalWindow(const Window *w)
00972 {
00973 switch (w->window_class) {
00974 case WC_MAIN_TOOLBAR:
00975 case WC_STATUS_BAR:
00976 case WC_NEWS_WINDOW:
00977 case WC_SEND_NETWORK_MSG:
00978 return true;
00979
00980 default:
00981 return false;
00982 }
00983 }
00984
00993 static uint GetWindowZPriority(const Window *w)
00994 {
00995 assert(w->window_class != WC_INVALID);
00996
00997 uint z_priority = 0;
00998
00999 switch (w->window_class) {
01000 case WC_ENDSCREEN:
01001 ++z_priority;
01002
01003 case WC_HIGHSCORE:
01004 ++z_priority;
01005
01006 case WC_TOOLTIPS:
01007 ++z_priority;
01008
01009 case WC_DROPDOWN_MENU:
01010 ++z_priority;
01011
01012 case WC_MAIN_TOOLBAR:
01013 case WC_STATUS_BAR:
01014 ++z_priority;
01015
01016 case WC_OSK:
01017 ++z_priority;
01018
01019 case WC_QUERY_STRING:
01020 case WC_SEND_NETWORK_MSG:
01021 ++z_priority;
01022
01023 case WC_ERRMSG:
01024 case WC_CONFIRM_POPUP_QUERY:
01025 case WC_MODAL_PROGRESS:
01026 case WC_NETWORK_STATUS_WINDOW:
01027 ++z_priority;
01028
01029 case WC_GENERATE_LANDSCAPE:
01030 case WC_SAVELOAD:
01031 case WC_GAME_OPTIONS:
01032 case WC_CUSTOM_CURRENCY:
01033 case WC_NETWORK_WINDOW:
01034 case WC_GRF_PARAMETERS:
01035 case WC_AI_LIST:
01036 case WC_AI_SETTINGS:
01037 case WC_TEXTFILE:
01038 ++z_priority;
01039
01040 case WC_CONSOLE:
01041 ++z_priority;
01042
01043 case WC_NEWS_WINDOW:
01044 ++z_priority;
01045
01046 default:
01047 ++z_priority;
01048
01049 case WC_MAIN_WINDOW:
01050 return z_priority;
01051 }
01052 }
01053
01058 static void AddWindowToZOrdering(Window *w)
01059 {
01060 assert(w->z_front == NULL && w->z_back == NULL);
01061
01062 if (_z_front_window == NULL) {
01063
01064 _z_front_window = _z_back_window = w;
01065 w->z_front = w->z_back = NULL;
01066 } else {
01067
01068 Window *v = _z_front_window;
01069 uint last_z_priority = UINT_MAX;
01070 while (v != NULL && (v->window_class == WC_INVALID || GetWindowZPriority(v) > GetWindowZPriority(w))) {
01071 if (v->window_class != WC_INVALID) {
01072
01073 assert(last_z_priority >= GetWindowZPriority(v));
01074 last_z_priority = GetWindowZPriority(v);
01075 }
01076
01077 v = v->z_back;
01078 }
01079
01080 if (v == NULL) {
01081
01082 w->z_front = _z_back_window;
01083 w->z_back = NULL;
01084 _z_back_window->z_back = w;
01085 _z_back_window = w;
01086 } else if (v == _z_front_window) {
01087
01088 w->z_front = NULL;
01089 w->z_back = _z_front_window;
01090 _z_front_window->z_front = w;
01091 _z_front_window = w;
01092 } else {
01093
01094 w->z_front = v->z_front;
01095 w->z_back = v;
01096 v->z_front->z_back = w;
01097 v->z_front = w;
01098 }
01099 }
01100 }
01101
01102
01107 static void RemoveWindowFromZOrdering(Window *w)
01108 {
01109 if (w->z_front == NULL) {
01110 assert(_z_front_window == w);
01111 _z_front_window = w->z_back;
01112 } else {
01113 w->z_front->z_back = w->z_back;
01114 }
01115
01116 if (w->z_back == NULL) {
01117 assert(_z_back_window == w);
01118 _z_back_window = w->z_front;
01119 } else {
01120 w->z_back->z_front = w->z_front;
01121 }
01122
01123 w->z_front = w->z_back = NULL;
01124 }
01125
01131 static void BringWindowToFront(Window *w)
01132 {
01133 RemoveWindowFromZOrdering(w);
01134 AddWindowToZOrdering(w);
01135
01136 w->SetDirty();
01137 }
01138
01147 void Window::InitializeData(const WindowDesc *desc, WindowNumber window_number)
01148 {
01149
01150 this->window_class = desc->cls;
01151 this->SetWhiteBorder();
01152 if (desc->default_pos == WDP_CENTER) this->flags |= WF_CENTERED;
01153 this->owner = INVALID_OWNER;
01154 this->nested_focus = NULL;
01155 this->window_number = window_number;
01156 this->desc_flags = desc->flags;
01157
01158 this->OnInit();
01159
01160 if (this->nested_array == NULL) {
01161 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01162 this->nested_root->SetupSmallestSize(this, true);
01163 } else {
01164 this->nested_root->SetupSmallestSize(this, false);
01165 }
01166
01167 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
01168
01169
01170
01171 this->resize.step_width = this->nested_root->resize_x;
01172 this->resize.step_height = this->nested_root->resize_y;
01173
01174
01175
01176
01177 if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL)) SetFocusedWindow(this);
01178
01179
01180 AddWindowToZOrdering(this);
01181 }
01182
01190 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
01191 {
01192 this->left = x;
01193 this->top = y;
01194 this->width = sm_width;
01195 this->height = sm_height;
01196 }
01197
01208 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
01209 {
01210 def_width = max(def_width, this->width);
01211 def_height = max(def_height, this->height);
01212
01213
01214
01215
01216
01217 if (this->width != def_width || this->height != def_height) {
01218
01219 int free_height = _screen.height;
01220 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
01221 if (wt != NULL) free_height -= wt->height;
01222 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01223 if (wt != NULL) free_height -= wt->height;
01224
01225 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
01226 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
01227
01228
01229
01230
01231 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
01232 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
01233
01234 ResizeWindow(this, enlarge_x, enlarge_y);
01235
01236 } else {
01237
01238 this->OnResize();
01239 }
01240
01241 int nx = this->left;
01242 int ny = this->top;
01243
01244 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
01245
01246 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01247 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
01248 nx = max(nx, 0);
01249
01250 if (this->viewport != NULL) {
01251 this->viewport->left += nx - this->left;
01252 this->viewport->top += ny - this->top;
01253 }
01254 this->left = nx;
01255 this->top = ny;
01256
01257 this->SetDirty();
01258 }
01259
01271 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
01272 {
01273 int right = width + left;
01274 int bottom = height + top;
01275
01276 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01277 if (left < 0 || (main_toolbar != NULL && top < main_toolbar->height) || right > _screen.width || bottom > _screen.height) return false;
01278
01279
01280 const Window *w;
01281 FOR_ALL_WINDOWS_FROM_BACK(w) {
01282 if (w->window_class == WC_MAIN_WINDOW) continue;
01283
01284 if (right > w->left &&
01285 w->left + w->width > left &&
01286 bottom > w->top &&
01287 w->top + w->height > top) {
01288 return false;
01289 }
01290 }
01291
01292 pos.x = left;
01293 pos.y = top;
01294 return true;
01295 }
01296
01308 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
01309 {
01310
01311
01312
01313 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
01314
01315 if (top < 22 || top > _screen.height - (height >> 2)) return false;
01316
01317
01318 const Window *w;
01319 FOR_ALL_WINDOWS_FROM_BACK(w) {
01320 if (w->window_class == WC_MAIN_WINDOW) continue;
01321
01322 if (left + width > w->left &&
01323 w->left + w->width > left &&
01324 top + height > w->top &&
01325 w->top + w->height > top) {
01326 return false;
01327 }
01328 }
01329
01330 pos.x = left;
01331 pos.y = top;
01332 return true;
01333 }
01334
01341 static Point GetAutoPlacePosition(int width, int height)
01342 {
01343 Point pt;
01344
01345
01346 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01347 if (IsGoodAutoPlace1(0, main_toolbar != NULL ? main_toolbar->height + 2 : 2, width, height, pt)) return pt;
01348
01349
01350
01351
01352
01353 const Window *w;
01354 FOR_ALL_WINDOWS_FROM_BACK(w) {
01355 if (w->window_class == WC_MAIN_WINDOW) continue;
01356
01357 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01358 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01359 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01360 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01361 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01362 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01363 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01364 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01365 }
01366
01367
01368
01369
01370
01371 FOR_ALL_WINDOWS_FROM_BACK(w) {
01372 if (w->window_class == WC_MAIN_WINDOW) continue;
01373
01374 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01375 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01376 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01377 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01378 }
01379
01380
01381
01382
01383 int left = 0, top = 24;
01384
01385 restart:
01386 FOR_ALL_WINDOWS_FROM_BACK(w) {
01387 if (w->left == left && w->top == top) {
01388 left += 5;
01389 top += 5;
01390 goto restart;
01391 }
01392 }
01393
01394 pt.x = left;
01395 pt.y = top;
01396 return pt;
01397 }
01398
01405 Point GetToolbarAlignedWindowPosition(int window_width)
01406 {
01407 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01408 assert(w != NULL);
01409 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
01410 return pt;
01411 }
01412
01430 static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01431 {
01432 Point pt;
01433 const Window *w;
01434
01435 int16 default_width = max(desc->default_width, sm_width);
01436 int16 default_height = max(desc->default_height, sm_height);
01437
01438 if (desc->parent_cls != 0 &&
01439 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01440 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01441
01442 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01443 if (pt.x > _screen.width + 10 - default_width) {
01444 pt.x = (_screen.width + 10 - default_width) - 20;
01445 }
01446 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? w->height : 10);
01447 return pt;
01448 }
01449
01450 switch (desc->default_pos) {
01451 case WDP_ALIGN_TOOLBAR:
01452 return GetToolbarAlignedWindowPosition(default_width);
01453
01454 case WDP_AUTO:
01455 return GetAutoPlacePosition(default_width, default_height);
01456
01457 case WDP_CENTER:
01458 pt.x = (_screen.width - default_width) / 2;
01459 pt.y = (_screen.height - default_height) / 2;
01460 break;
01461
01462 case WDP_MANUAL:
01463 pt.x = 0;
01464 pt.y = 0;
01465 break;
01466
01467 default:
01468 NOT_REACHED();
01469 }
01470
01471 return pt;
01472 }
01473
01474 Point Window::OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01475 {
01476 return LocalGetWindowPlacement(desc, sm_width, sm_height, window_number);
01477 }
01478
01487 void Window::CreateNestedTree(const WindowDesc *desc, bool fill_nested)
01488 {
01489 int biggest_index = -1;
01490 this->nested_root = MakeWindowNWidgetTree(desc->nwid_parts, desc->nwid_length, &biggest_index, &this->shade_select);
01491 this->nested_array_size = (uint)(biggest_index + 1);
01492
01493 if (fill_nested) {
01494 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01495 this->nested_root->FillNestedArray(this->nested_array, this->nested_array_size);
01496 }
01497 }
01498
01504 void Window::FinishInitNested(const WindowDesc *desc, WindowNumber window_number)
01505 {
01506 this->InitializeData(desc, window_number);
01507 Point pt = this->OnInitialPosition(desc, this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
01508 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
01509 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
01510 }
01511
01517 void Window::InitNested(const WindowDesc *desc, WindowNumber window_number)
01518 {
01519 this->CreateNestedTree(desc, false);
01520 this->FinishInitNested(desc, window_number);
01521 }
01522
01524 Window::Window() : scrolling_scrollbar(-1)
01525 {
01526 }
01527
01535 Window *FindWindowFromPt(int x, int y)
01536 {
01537 Window *w;
01538 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01539 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01540 return w;
01541 }
01542 }
01543
01544 return NULL;
01545 }
01546
01550 void InitWindowSystem()
01551 {
01552 IConsoleClose();
01553
01554 _z_back_window = NULL;
01555 _z_front_window = NULL;
01556 _focused_window = NULL;
01557 _mouseover_last_w = NULL;
01558 _last_scroll_window = NULL;
01559 _scrolling_viewport = false;
01560 _mouse_hovering = false;
01561
01562 NWidgetLeaf::InvalidateDimensionCache();
01563 NWidgetScrollbar::InvalidateDimensionCache();
01564
01565 ShowFirstError();
01566 }
01567
01571 void UnInitWindowSystem()
01572 {
01573 UnshowCriticalError();
01574
01575 Window *w;
01576 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01577
01578 for (w = _z_front_window; w != NULL; ) {
01579 Window *to_del = w;
01580 w = w->z_back;
01581 free(to_del);
01582 }
01583
01584 _z_front_window = NULL;
01585 _z_back_window = NULL;
01586 }
01587
01591 void ResetWindowSystem()
01592 {
01593 UnInitWindowSystem();
01594 InitWindowSystem();
01595 _thd.Reset();
01596 }
01597
01598 static void DecreaseWindowCounters()
01599 {
01600 Window *w;
01601 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01602 if (_scroller_click_timeout == 0) {
01603
01604 for (uint i = 0; i < w->nested_array_size; i++) {
01605 NWidgetBase *nwid = w->nested_array[i];
01606 if (nwid != NULL && (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR)) {
01607 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
01608 if (sb->disp_flags & (ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN)) {
01609 sb->disp_flags &= ~(ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN);
01610 w->scrolling_scrollbar = -1;
01611 sb->SetDirty(w);
01612 }
01613 }
01614 }
01615 }
01616 w->OnMouseLoop();
01617 }
01618
01619 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01620 if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) {
01621 CLRBITS(w->flags, WF_TIMEOUT);
01622
01623 w->OnTimeout();
01624 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons(true);
01625 }
01626 }
01627 }
01628
01629 static void HandlePlacePresize()
01630 {
01631 if (_special_mouse_mode != WSM_PRESIZE) return;
01632
01633 Window *w = _thd.GetCallbackWnd();
01634 if (w == NULL) return;
01635
01636 Point pt = GetTileBelowCursor();
01637 if (pt.x == -1) {
01638 _thd.selend.x = -1;
01639 return;
01640 }
01641
01642 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01643 }
01644
01649 static EventState HandleMouseDragDrop()
01650 {
01651 if (_special_mouse_mode != WSM_DRAGDROP) return ES_NOT_HANDLED;
01652
01653 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01654
01655 Window *w = _thd.GetCallbackWnd();
01656 if (w != NULL) {
01657
01658 Point pt;
01659 pt.x = _cursor.pos.x - w->left;
01660 pt.y = _cursor.pos.y - w->top;
01661 if (_left_button_down) {
01662 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
01663 } else {
01664 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01665 }
01666 }
01667
01668 if (!_left_button_down) ResetObjectToPlace();
01669 return ES_HANDLED;
01670 }
01671
01673 static void HandleMouseOver()
01674 {
01675 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01676
01677
01678 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01679
01680 Point pt = { -1, -1 };
01681 _mouseover_last_w->OnMouseOver(pt, 0);
01682 }
01683
01684
01685 _mouseover_last_w = w;
01686
01687 if (w != NULL) {
01688
01689 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01690 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
01691 if (widget != NULL) w->OnMouseOver(pt, widget->index);
01692 }
01693 }
01694
01696 static const int MIN_VISIBLE_TITLE_BAR = 13;
01697
01699 enum PreventHideDirection {
01700 PHD_UP,
01701 PHD_DOWN,
01702 };
01703
01714 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
01715 {
01716 if (v == NULL) return;
01717
01718 int v_bottom = v->top + v->height;
01719 int v_right = v->left + v->width;
01720 int safe_y = (dir == PHD_UP) ? (v->top - MIN_VISIBLE_TITLE_BAR - rect.top) : (v_bottom + MIN_VISIBLE_TITLE_BAR - rect.bottom);
01721
01722 if (*ny + rect.top <= v->top - MIN_VISIBLE_TITLE_BAR) return;
01723 if (*ny + rect.bottom >= v_bottom + MIN_VISIBLE_TITLE_BAR) return;
01724
01725
01726 if (*nx + rect.left + MIN_VISIBLE_TITLE_BAR < v->left) {
01727 if (v->left < MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01728 return;
01729 }
01730 if (*nx + rect.right - MIN_VISIBLE_TITLE_BAR > v_right) {
01731 if (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01732 return;
01733 }
01734
01735
01736 if (px + rect.left < v->left && v->left >= MIN_VISIBLE_TITLE_BAR) {
01737 *nx = v->left - MIN_VISIBLE_TITLE_BAR - rect.left;
01738 } else if (px + rect.right > v_right && v_right <= _screen.width - MIN_VISIBLE_TITLE_BAR) {
01739 *nx = v_right + MIN_VISIBLE_TITLE_BAR - rect.right;
01740 } else {
01741 *ny = safe_y;
01742 }
01743 }
01744
01752 static void EnsureVisibleCaption(Window *w, int nx, int ny)
01753 {
01754
01755 Rect caption_rect;
01756 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
01757 if (caption != NULL) {
01758 caption_rect.left = caption->pos_x;
01759 caption_rect.right = caption->pos_x + caption->current_x;
01760 caption_rect.top = caption->pos_y;
01761 caption_rect.bottom = caption->pos_y + caption->current_y;
01762
01763
01764 nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
01765 ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
01766
01767
01768 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
01769 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
01770 }
01771
01772 if (w->viewport != NULL) {
01773 w->viewport->left += nx - w->left;
01774 w->viewport->top += ny - w->top;
01775 }
01776
01777 w->left = nx;
01778 w->top = ny;
01779 }
01780
01791 void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
01792 {
01793 if (delta_x != 0 || delta_y != 0) {
01794 if (clamp_to_screen) {
01795
01796
01797 int new_right = w->left + w->width + delta_x;
01798 int new_bottom = w->top + w->height + delta_y;
01799 if (new_right >= (int)_cur_resolution.width) delta_x -= Ceil(new_right - _cur_resolution.width, max(1U, w->nested_root->resize_x));
01800 if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, max(1U, w->nested_root->resize_y));
01801 }
01802
01803 w->SetDirty();
01804
01805 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);
01806 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);
01807 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
01808 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
01809
01810 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);
01811 w->width = w->nested_root->current_x;
01812 w->height = w->nested_root->current_y;
01813 }
01814
01815 EnsureVisibleCaption(w, w->left, w->top);
01816
01817
01818 w->OnResize();
01819 w->SetDirty();
01820 }
01821
01827 int GetMainViewTop()
01828 {
01829 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01830 return (w == NULL) ? 0 : w->top + w->height;
01831 }
01832
01838 int GetMainViewBottom()
01839 {
01840 Window *w = FindWindowById(WC_STATUS_BAR, 0);
01841 return (w == NULL) ? _screen.height : w->top;
01842 }
01843
01844 static bool _dragging_window;
01845
01850 static EventState HandleWindowDragging()
01851 {
01852
01853 if (!_dragging_window) return ES_NOT_HANDLED;
01854
01855
01856 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01857
01858
01859 Window *w;
01860 FOR_ALL_WINDOWS_FROM_BACK(w) {
01861 if (w->flags & WF_DRAGGING) {
01862
01863 if (!_left_button_down) {
01864 w->flags &= ~WF_DRAGGING;
01865 break;
01866 }
01867
01868 w->SetDirty();
01869
01870 int x = _cursor.pos.x + _drag_delta.x;
01871 int y = _cursor.pos.y + _drag_delta.y;
01872 int nx = x;
01873 int ny = y;
01874
01875 if (_settings_client.gui.window_snap_radius != 0) {
01876 const Window *v;
01877
01878 int hsnap = _settings_client.gui.window_snap_radius;
01879 int vsnap = _settings_client.gui.window_snap_radius;
01880 int delta;
01881
01882 FOR_ALL_WINDOWS_FROM_BACK(v) {
01883 if (v == w) continue;
01884
01885 if (y + w->height > v->top && y < v->top + v->height) {
01886
01887 delta = abs(v->left + v->width - x);
01888 if (delta <= hsnap) {
01889 nx = v->left + v->width;
01890 hsnap = delta;
01891 }
01892
01893
01894 delta = abs(v->left - x - w->width);
01895 if (delta <= hsnap) {
01896 nx = v->left - w->width;
01897 hsnap = delta;
01898 }
01899 }
01900
01901 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01902
01903 delta = abs(v->left - x);
01904 if (delta <= hsnap) {
01905 nx = v->left;
01906 hsnap = delta;
01907 }
01908
01909
01910 delta = abs(v->left + v->width - x - w->width);
01911 if (delta <= hsnap) {
01912 nx = v->left + v->width - w->width;
01913 hsnap = delta;
01914 }
01915 }
01916
01917 if (x + w->width > v->left && x < v->left + v->width) {
01918
01919 delta = abs(v->top + v->height - y);
01920 if (delta <= vsnap) {
01921 ny = v->top + v->height;
01922 vsnap = delta;
01923 }
01924
01925
01926 delta = abs(v->top - y - w->height);
01927 if (delta <= vsnap) {
01928 ny = v->top - w->height;
01929 vsnap = delta;
01930 }
01931 }
01932
01933 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01934
01935 delta = abs(v->top - y);
01936 if (delta <= vsnap) {
01937 ny = v->top;
01938 vsnap = delta;
01939 }
01940
01941
01942 delta = abs(v->top + v->height - y - w->height);
01943 if (delta <= vsnap) {
01944 ny = v->top + v->height - w->height;
01945 vsnap = delta;
01946 }
01947 }
01948 }
01949 }
01950
01951 EnsureVisibleCaption(w, nx, ny);
01952
01953 w->SetDirty();
01954 return ES_HANDLED;
01955 } else if (w->flags & WF_SIZING) {
01956
01957 if (!_left_button_down) {
01958 w->flags &= ~WF_SIZING;
01959 w->SetDirty();
01960 break;
01961 }
01962
01963
01964
01965
01966 int x, y = _cursor.pos.y - _drag_delta.y;
01967 if (w->flags & WF_SIZING_LEFT) {
01968 x = _drag_delta.x - _cursor.pos.x;
01969 } else {
01970 x = _cursor.pos.x - _drag_delta.x;
01971 }
01972
01973
01974 if (w->resize.step_width == 0) x = 0;
01975 if (w->resize.step_height == 0) y = 0;
01976
01977
01978 if (w->top + w->height + y > _screen.height) {
01979 y = _screen.height - w->height - w->top;
01980 }
01981
01982
01983
01984
01985 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
01986 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
01987
01988
01989 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
01990 x = w->nested_root->smallest_x - w->width;
01991 }
01992 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
01993 y = w->nested_root->smallest_y - w->height;
01994 }
01995
01996
01997 if (x == 0 && y == 0) return ES_HANDLED;
01998
01999
02000 _drag_delta.y += y;
02001 if ((w->flags & WF_SIZING_LEFT) && x != 0) {
02002 _drag_delta.x -= x;
02003 w->SetDirty();
02004 w->left -= x;
02005
02006 } else {
02007 _drag_delta.x += x;
02008 }
02009
02010
02011 ResizeWindow(w, x, y);
02012 return ES_HANDLED;
02013 }
02014 }
02015
02016 _dragging_window = false;
02017 return ES_HANDLED;
02018 }
02019
02024 static void StartWindowDrag(Window *w)
02025 {
02026 w->flags |= WF_DRAGGING;
02027 w->flags &= ~WF_CENTERED;
02028 _dragging_window = true;
02029
02030 _drag_delta.x = w->left - _cursor.pos.x;
02031 _drag_delta.y = w->top - _cursor.pos.y;
02032
02033 BringWindowToFront(w);
02034 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02035 }
02036
02042 static void StartWindowSizing(Window *w, bool to_left)
02043 {
02044 w->flags |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
02045 w->flags &= ~WF_CENTERED;
02046 _dragging_window = true;
02047
02048 _drag_delta.x = _cursor.pos.x;
02049 _drag_delta.y = _cursor.pos.y;
02050
02051 BringWindowToFront(w);
02052 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02053 }
02054
02059 static EventState HandleScrollbarScrolling()
02060 {
02061 Window *w;
02062 FOR_ALL_WINDOWS_FROM_BACK(w) {
02063 if (w->scrolling_scrollbar >= 0) {
02064
02065 if (!_left_button_down) {
02066 w->scrolling_scrollbar = -1;
02067 w->SetDirty();
02068 return ES_HANDLED;
02069 }
02070
02071 int i;
02072 NWidgetScrollbar *sb = w->GetWidget<NWidgetScrollbar>(w->scrolling_scrollbar);
02073 bool rtl = false;
02074
02075 if (sb->type == NWID_HSCROLLBAR) {
02076 i = _cursor.pos.x - _cursorpos_drag_start.x;
02077 rtl = _current_text_dir == TD_RTL;
02078 } else {
02079 i = _cursor.pos.y - _cursorpos_drag_start.y;
02080 }
02081
02082 if (sb->disp_flags & ND_SCROLLBAR_BTN) {
02083 if (_scroller_click_timeout == 1) {
02084 _scroller_click_timeout = 3;
02085 sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1);
02086 w->SetDirty();
02087 }
02088 return ES_HANDLED;
02089 }
02090
02091
02092 int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity()));
02093 if (rtl) pos = max(0, sb->GetCount() - sb->GetCapacity() - pos);
02094 if (pos != sb->GetPosition()) {
02095 sb->SetPosition(pos);
02096 w->SetDirty();
02097 }
02098 return ES_HANDLED;
02099 }
02100 }
02101
02102 return ES_NOT_HANDLED;
02103 }
02104
02109 static EventState HandleViewportScroll()
02110 {
02111 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02112
02113 if (!_scrolling_viewport) return ES_NOT_HANDLED;
02114
02115
02116
02117
02118 if (_last_scroll_window == NULL) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
02119
02120 if (_last_scroll_window == NULL || !(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down))) {
02121 _cursor.fix_at = false;
02122 _scrolling_viewport = false;
02123 _last_scroll_window = NULL;
02124 return ES_NOT_HANDLED;
02125 }
02126
02127 if (_last_scroll_window == FindWindowById(WC_MAIN_WINDOW, 0) && _last_scroll_window->viewport->follow_vehicle != INVALID_VEHICLE) {
02128
02129 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
02130 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
02131 return ES_NOT_HANDLED;
02132 }
02133
02134 Point delta;
02135 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
02136 delta.x = -_cursor.delta.x;
02137 delta.y = -_cursor.delta.y;
02138 } else {
02139 delta.x = _cursor.delta.x;
02140 delta.y = _cursor.delta.y;
02141 }
02142
02143 if (scrollwheel_scrolling) {
02144
02145 delta.x = _cursor.h_wheel;
02146 delta.y = _cursor.v_wheel;
02147 _cursor.v_wheel = 0;
02148 _cursor.h_wheel = 0;
02149 }
02150
02151
02152 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
02153
02154 _cursor.delta.x = 0;
02155 _cursor.delta.y = 0;
02156 return ES_HANDLED;
02157 }
02158
02169 static bool MaybeBringWindowToFront(Window *w)
02170 {
02171 bool bring_to_front = false;
02172
02173 if (w->window_class == WC_MAIN_WINDOW ||
02174 IsVitalWindow(w) ||
02175 w->window_class == WC_TOOLTIPS ||
02176 w->window_class == WC_DROPDOWN_MENU) {
02177 return true;
02178 }
02179
02180
02181 int w_width = w->width;
02182 int w_height = w->height;
02183 if (w->IsShaded()) {
02184 w_width = w->unshaded_size.width;
02185 w_height = w->unshaded_size.height;
02186 }
02187
02188 Window *u;
02189 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
02190
02191 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
02192 u->SetWhiteBorder();
02193 u->SetDirty();
02194 return false;
02195 }
02196
02197 if (u->window_class == WC_MAIN_WINDOW ||
02198 IsVitalWindow(u) ||
02199 u->window_class == WC_TOOLTIPS ||
02200 u->window_class == WC_DROPDOWN_MENU) {
02201 continue;
02202 }
02203
02204
02205 if (w->left + w_width <= u->left ||
02206 u->left + u->width <= w->left ||
02207 w->top + w_height <= u->top ||
02208 u->top + u->height <= w->top) {
02209 continue;
02210 }
02211
02212 bring_to_front = true;
02213 }
02214
02215 if (bring_to_front) BringWindowToFront(w);
02216 return true;
02217 }
02218
02223 void HandleKeypress(uint32 raw_key)
02224 {
02225
02226
02227 assert(HasModalProgress() || IsLocalCompany());
02228
02229
02230 uint16 key = GB(raw_key, 0, 16);
02231 uint16 keycode = GB(raw_key, 16, 16);
02232
02233
02234
02235
02236
02237
02238
02239
02240 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
02241
02242
02243
02244
02245 if (key == 0 && keycode == 0) return;
02246
02247
02248 if (EditBoxInGlobalFocus()) {
02249
02250 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
02251 }
02252
02253
02254 Window *w;
02255 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02256 if (w->window_class == WC_MAIN_TOOLBAR) continue;
02257 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02258 }
02259
02260 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02261
02262 if (w != NULL && w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02263
02264 HandleGlobalHotkeys(key, keycode);
02265 }
02266
02270 void HandleCtrlChanged()
02271 {
02272
02273 Window *w;
02274 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02275 if (w->OnCTRLStateChange() == ES_HANDLED) return;
02276 }
02277 }
02278
02285 static int _input_events_this_tick = 0;
02286
02291 static void HandleAutoscroll()
02292 {
02293 if (_settings_client.gui.autoscroll && _game_mode != GM_MENU && !HasModalProgress()) {
02294 int x = _cursor.pos.x;
02295 int y = _cursor.pos.y;
02296 Window *w = FindWindowFromPt(x, y);
02297 if (w == NULL || w->flags & WF_DISABLE_VP_SCROLL) return;
02298 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02299 if (vp != NULL) {
02300 x -= vp->left;
02301 y -= vp->top;
02302
02303
02304 #define scrollspeed 3
02305 if (x - 15 < 0) {
02306 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
02307 } else if (15 - (vp->width - x) > 0) {
02308 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
02309 }
02310 if (y - 15 < 0) {
02311 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
02312 } else if (15 - (vp->height - y) > 0) {
02313 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
02314 }
02315 #undef scrollspeed
02316 }
02317 }
02318 }
02319
02320 enum MouseClick {
02321 MC_NONE = 0,
02322 MC_LEFT,
02323 MC_RIGHT,
02324 MC_DOUBLE_LEFT,
02325 MC_HOVER,
02326
02327 MAX_OFFSET_DOUBLE_CLICK = 5,
02328 TIME_BETWEEN_DOUBLE_CLICK = 500,
02329 MAX_OFFSET_HOVER = 5,
02330 };
02331 extern EventState VpHandlePlaceSizingDrag();
02332
02333 static void ScrollMainViewport(int x, int y)
02334 {
02335 if (_game_mode != GM_MENU) {
02336 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02337 assert(w);
02338
02339 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
02340 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
02341 }
02342 }
02343
02353 static const int8 scrollamt[16][2] = {
02354 { 0, 0},
02355 {-2, 0},
02356 { 0, -2},
02357 {-2, -1},
02358 { 2, 0},
02359 { 0, 0},
02360 { 2, -1},
02361 { 0, -2},
02362 { 0, 2},
02363 {-2, 1},
02364 { 0, 0},
02365 {-2, 0},
02366 { 2, 1},
02367 { 0, 2},
02368 { 2, 0},
02369 { 0, 0},
02370 };
02371
02372 static void HandleKeyScrolling()
02373 {
02374
02375
02376
02377
02378 if (_dirkeys && !EditBoxInGlobalFocus()) {
02379 int factor = _shift_pressed ? 50 : 10;
02380 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
02381 }
02382 }
02383
02384 static void MouseLoop(MouseClick click, int mousewheel)
02385 {
02386
02387
02388 assert(HasModalProgress() || IsLocalCompany());
02389
02390 HandlePlacePresize();
02391 UpdateTileSelection();
02392
02393 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
02394 if (HandleMouseDragDrop() == ES_HANDLED) return;
02395 if (HandleWindowDragging() == ES_HANDLED) return;
02396 if (HandleScrollbarScrolling() == ES_HANDLED) return;
02397 if (HandleViewportScroll() == ES_HANDLED) return;
02398
02399 HandleMouseOver();
02400
02401 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02402 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
02403
02404 int x = _cursor.pos.x;
02405 int y = _cursor.pos.y;
02406 Window *w = FindWindowFromPt(x, y);
02407 if (w == NULL) return;
02408
02409 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
02410 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02411
02412
02413 if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
02414
02415 if (mousewheel != 0) {
02416
02417 w->OnMouseWheel(mousewheel);
02418
02419
02420 if (vp == NULL) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
02421 }
02422
02423 if (vp != NULL) {
02424 if (scrollwheel_scrolling) click = MC_RIGHT;
02425 switch (click) {
02426 case MC_DOUBLE_LEFT:
02427 case MC_LEFT:
02428 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
02429 if (!HandleViewportClicked(vp, x, y) &&
02430 !(w->flags & WF_DISABLE_VP_SCROLL) &&
02431 _settings_client.gui.left_mouse_btn_scrolling) {
02432 _scrolling_viewport = true;
02433 _cursor.fix_at = false;
02434 }
02435 break;
02436
02437 case MC_RIGHT:
02438 if (!(w->flags & WF_DISABLE_VP_SCROLL)) {
02439 _scrolling_viewport = true;
02440 _cursor.fix_at = true;
02441
02442
02443 _cursor.h_wheel = 0;
02444 _cursor.v_wheel = 0;
02445 }
02446 break;
02447
02448 default:
02449 break;
02450 }
02451 } else {
02452 switch (click) {
02453 case MC_LEFT:
02454 case MC_DOUBLE_LEFT:
02455 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
02456 break;
02457
02458 default:
02459 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
02460
02461
02462
02463
02464 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
02465
02466 case MC_HOVER: DispatchHoverEvent(w, x - w->left, y - w->top); break;
02467 }
02468 }
02469 }
02470
02474 void HandleMouseEvents()
02475 {
02476
02477
02478 assert(HasModalProgress() || IsLocalCompany());
02479
02480 static int double_click_time = 0;
02481 static Point double_click_pos = {0, 0};
02482
02483
02484 MouseClick click = MC_NONE;
02485 if (_left_button_down && !_left_button_clicked) {
02486 click = MC_LEFT;
02487 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02488 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
02489 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
02490 click = MC_DOUBLE_LEFT;
02491 }
02492 double_click_time = _realtime_tick;
02493 double_click_pos = _cursor.pos;
02494 _left_button_clicked = true;
02495 _input_events_this_tick++;
02496 } else if (_right_button_clicked) {
02497 _right_button_clicked = false;
02498 click = MC_RIGHT;
02499 _input_events_this_tick++;
02500 }
02501
02502 int mousewheel = 0;
02503 if (_cursor.wheel) {
02504 mousewheel = _cursor.wheel;
02505 _cursor.wheel = 0;
02506 _input_events_this_tick++;
02507 }
02508
02509 static uint32 hover_time = 0;
02510 static Point hover_pos = {0, 0};
02511
02512 if (_settings_client.gui.hover_delay > 0) {
02513 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
02514 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
02515 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
02516 hover_pos = _cursor.pos;
02517 hover_time = _realtime_tick;
02518 _mouse_hovering = false;
02519 } else {
02520 if (hover_time != 0 && _realtime_tick > hover_time + _settings_client.gui.hover_delay * 1000) {
02521 click = MC_HOVER;
02522 _input_events_this_tick++;
02523 _mouse_hovering = true;
02524 }
02525 }
02526 }
02527
02528
02529 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _newgrf_debug_sprite_picker.click_time != _realtime_tick) {
02530
02531 _newgrf_debug_sprite_picker.mode = SPM_NONE;
02532 InvalidateWindowData(WC_SPRITE_ALIGNER, 0, 1);
02533 }
02534
02535 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
02536
02537 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
02538 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
02539 _newgrf_debug_sprite_picker.click_time = _realtime_tick;
02540 _newgrf_debug_sprite_picker.sprites.Clear();
02541 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
02542 MarkWholeScreenDirty();
02543 } else {
02544 MouseLoop(click, mousewheel);
02545 }
02546
02547
02548
02549 _cursor.delta.x = 0;
02550 _cursor.delta.y = 0;
02551 }
02552
02556 static void CheckSoftLimit()
02557 {
02558 if (_settings_client.gui.window_soft_limit == 0) return;
02559
02560 for (;;) {
02561 uint deletable_count = 0;
02562 Window *w, *last_deletable = NULL;
02563 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02564 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue;
02565
02566 last_deletable = w;
02567 deletable_count++;
02568 }
02569
02570
02571 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02572
02573 assert(last_deletable != NULL);
02574 delete last_deletable;
02575 }
02576 }
02577
02581 void InputLoop()
02582 {
02583
02584
02585 assert(HasModalProgress() || IsLocalCompany());
02586
02587 CheckSoftLimit();
02588 HandleKeyScrolling();
02589
02590
02591 for (Window *v = _z_front_window; v != NULL; ) {
02592 Window *w = v;
02593 v = v->z_back;
02594
02595 if (w->window_class != WC_INVALID) continue;
02596
02597 RemoveWindowFromZOrdering(w);
02598 free(w);
02599 }
02600
02601 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
02602 DecreaseWindowCounters();
02603
02604 if (_input_events_this_tick != 0) {
02605
02606 _input_events_this_tick = 0;
02607
02608 return;
02609 }
02610
02611
02612 HandleMouseEvents();
02613 HandleAutoscroll();
02614 }
02615
02619 void UpdateWindows()
02620 {
02621 Window *w;
02622
02623 static int highlight_timer = 1;
02624 if (--highlight_timer == 0) {
02625 highlight_timer = 15;
02626 _window_highlight_colour = !_window_highlight_colour;
02627 }
02628
02629 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02630 w->ProcessScheduledInvalidations();
02631 w->ProcessHighlightedInvalidations();
02632 }
02633
02634 static int we4_timer = 0;
02635 int t = we4_timer + 1;
02636
02637 if (t >= 100) {
02638 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02639 w->OnHundredthTick();
02640 }
02641 t = 0;
02642 }
02643 we4_timer = t;
02644
02645 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02646 if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
02647 CLRBITS(w->flags, WF_WHITE_BORDER);
02648 w->SetDirty();
02649 }
02650 }
02651
02652 DrawDirtyBlocks();
02653
02654 FOR_ALL_WINDOWS_FROM_BACK(w) {
02655
02656 if (w->viewport != NULL && !w->IsShaded()) UpdateViewportPosition(w);
02657 }
02658 NetworkDrawChatMessage();
02659
02660 DrawMouseCursor();
02661 }
02662
02668 void SetWindowDirty(WindowClass cls, WindowNumber number)
02669 {
02670 const Window *w;
02671 FOR_ALL_WINDOWS_FROM_BACK(w) {
02672 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02673 }
02674 }
02675
02682 void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
02683 {
02684 const Window *w;
02685 FOR_ALL_WINDOWS_FROM_BACK(w) {
02686 if (w->window_class == cls && w->window_number == number) {
02687 w->SetWidgetDirty(widget_index);
02688 }
02689 }
02690 }
02691
02696 void SetWindowClassesDirty(WindowClass cls)
02697 {
02698 Window *w;
02699 FOR_ALL_WINDOWS_FROM_BACK(w) {
02700 if (w->window_class == cls) w->SetDirty();
02701 }
02702 }
02703
02709 void Window::InvalidateData(int data, bool gui_scope)
02710 {
02711 this->SetDirty();
02712 if (!gui_scope) {
02713
02714 *this->scheduled_invalidation_data.Append() = data;
02715 }
02716 this->OnInvalidateData(data, gui_scope);
02717 }
02718
02722 void Window::ProcessScheduledInvalidations()
02723 {
02724 for (int *data = this->scheduled_invalidation_data.Begin(); this->window_class != WC_INVALID && data != this->scheduled_invalidation_data.End(); data++) {
02725 this->OnInvalidateData(*data, true);
02726 }
02727 this->scheduled_invalidation_data.Clear();
02728 }
02729
02733 void Window::ProcessHighlightedInvalidations()
02734 {
02735 if ((this->flags & WF_HIGHLIGHTED) == 0) return;
02736
02737 for (uint i = 0; i < this->nested_array_size; i++) {
02738 if (this->IsWidgetHighlighted(i)) this->SetWidgetDirty(i);
02739 }
02740 }
02741
02768 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
02769 {
02770 Window *w;
02771 FOR_ALL_WINDOWS_FROM_BACK(w) {
02772 if (w->window_class == cls && w->window_number == number) {
02773 w->InvalidateData(data, gui_scope);
02774 }
02775 }
02776 }
02777
02786 void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
02787 {
02788 Window *w;
02789
02790 FOR_ALL_WINDOWS_FROM_BACK(w) {
02791 if (w->window_class == cls) {
02792 w->InvalidateData(data, gui_scope);
02793 }
02794 }
02795 }
02796
02800 void CallWindowTickEvent()
02801 {
02802 Window *w;
02803 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02804 w->OnTick();
02805 }
02806 }
02807
02814 void DeleteNonVitalWindows()
02815 {
02816 Window *w;
02817
02818 restart_search:
02819
02820
02821
02822 FOR_ALL_WINDOWS_FROM_BACK(w) {
02823 if (w->window_class != WC_MAIN_WINDOW &&
02824 w->window_class != WC_SELECT_GAME &&
02825 w->window_class != WC_MAIN_TOOLBAR &&
02826 w->window_class != WC_STATUS_BAR &&
02827 w->window_class != WC_TOOLTIPS &&
02828 (w->flags & WF_STICKY) == 0) {
02829
02830 delete w;
02831 goto restart_search;
02832 }
02833 }
02834 }
02835
02843 void DeleteAllNonVitalWindows()
02844 {
02845 Window *w;
02846
02847
02848 DeleteNonVitalWindows();
02849
02850 restart_search:
02851
02852
02853
02854 FOR_ALL_WINDOWS_FROM_BACK(w) {
02855 if (w->flags & WF_STICKY) {
02856 delete w;
02857 goto restart_search;
02858 }
02859 }
02860 }
02861
02866 void DeleteConstructionWindows()
02867 {
02868 Window *w;
02869
02870 restart_search:
02871
02872
02873
02874 FOR_ALL_WINDOWS_FROM_BACK(w) {
02875 if (w->desc_flags & WDF_CONSTRUCTION) {
02876 delete w;
02877 goto restart_search;
02878 }
02879 }
02880
02881 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02882 }
02883
02885 void HideVitalWindows()
02886 {
02887 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02888 DeleteWindowById(WC_STATUS_BAR, 0);
02889 }
02890
02892 void ReInitAllWindows()
02893 {
02894 NWidgetLeaf::InvalidateDimensionCache();
02895 NWidgetScrollbar::InvalidateDimensionCache();
02896
02897 Window *w;
02898 FOR_ALL_WINDOWS_FROM_BACK(w) {
02899 w->ReInit();
02900 }
02901 #ifdef ENABLE_NETWORK
02902 void NetworkReInitChatBoxSize();
02903 NetworkReInitChatBoxSize();
02904 #endif
02905
02906
02907 RelocateAllWindows(_cur_resolution.width, _cur_resolution.height);
02908 MarkWholeScreenDirty();
02909 }
02910
02918 static int PositionWindow(Window *w, WindowClass clss, int setting)
02919 {
02920 if (w == NULL || w->window_class != clss) {
02921 w = FindWindowById(clss, 0);
02922 }
02923 if (w == NULL) return 0;
02924
02925 int old_left = w->left;
02926 switch (setting) {
02927 case 1: w->left = (_screen.width - w->width) / 2; break;
02928 case 2: w->left = _screen.width - w->width; break;
02929 default: w->left = 0; break;
02930 }
02931 if (w->viewport != NULL) w->viewport->left += w->left - old_left;
02932 SetDirtyBlocks(0, w->top, _screen.width, w->top + w->height);
02933 return w->left;
02934 }
02935
02941 int PositionMainToolbar(Window *w)
02942 {
02943 DEBUG(misc, 5, "Repositioning Main Toolbar...");
02944 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
02945 }
02946
02952 int PositionStatusbar(Window *w)
02953 {
02954 DEBUG(misc, 5, "Repositioning statusbar...");
02955 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
02956 }
02957
02963 int PositionNewsMessage(Window *w)
02964 {
02965 DEBUG(misc, 5, "Repositioning news message...");
02966 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
02967 }
02968
02974 int PositionNetworkChatWindow(Window *w)
02975 {
02976 DEBUG(misc, 5, "Repositioning network chat window...");
02977 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
02978 }
02979
02980
02986 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
02987 {
02988 Window *w;
02989 FOR_ALL_WINDOWS_FROM_BACK(w) {
02990 if (w->viewport != NULL && w->viewport->follow_vehicle == from_index) {
02991 w->viewport->follow_vehicle = to_index;
02992 w->SetDirty();
02993 }
02994 }
02995 }
02996
02997
03003 void RelocateAllWindows(int neww, int newh)
03004 {
03005 Window *w;
03006
03007 FOR_ALL_WINDOWS_FROM_BACK(w) {
03008 int left, top;
03009
03010 if (w->window_class == WC_MAIN_WINDOW) {
03011 ViewPort *vp = w->viewport;
03012 vp->width = w->width = neww;
03013 vp->height = w->height = newh;
03014 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
03015 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
03016 continue;
03017 }
03018
03019
03020
03021 switch (w->window_class) {
03022 case WC_BOOTSTRAP:
03023 ResizeWindow(w, neww, newh);
03024 continue;
03025
03026 case WC_MAIN_TOOLBAR:
03027 ResizeWindow(w, min(neww, *_preferred_toolbar_size) - w->width, 0, false);
03028
03029 top = w->top;
03030 left = PositionMainToolbar(w);
03031 break;
03032
03033 case WC_NEWS_WINDOW:
03034 top = newh - w->height;
03035 left = PositionNewsMessage(w);
03036 break;
03037
03038 case WC_STATUS_BAR:
03039 ResizeWindow(w, min(neww, *_preferred_statusbar_size) - w->width, 0, false);
03040
03041 top = newh - w->height;
03042 left = PositionStatusbar(w);
03043 break;
03044
03045 case WC_SEND_NETWORK_MSG:
03046 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0, false);
03047 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
03048 left = PositionNetworkChatWindow(w);
03049 break;
03050
03051 case WC_CONSOLE:
03052 IConsoleResize(w);
03053 continue;
03054
03055 default: {
03056 if (w->flags & WF_CENTERED) {
03057 top = (newh - w->height) >> 1;
03058 left = (neww - w->width) >> 1;
03059 break;
03060 }
03061
03062 left = w->left;
03063 if (left + (w->width >> 1) >= neww) left = neww - w->width;
03064 if (left < 0) left = 0;
03065
03066 top = w->top;
03067 if (top + (w->height >> 1) >= newh) top = newh - w->height;
03068 break;
03069 }
03070 }
03071
03072 EnsureVisibleCaption(w, left, top);
03073 }
03074 }
03075
03081 PickerWindowBase::~PickerWindowBase()
03082 {
03083 this->window_class = WC_INVALID;
03084 ResetObjectToPlace();
03085 }