00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include <stdarg.h>
00014 #include "openttd.h"
00015 #include "company_func.h"
00016 #include "gfx_func.h"
00017 #include "console_func.h"
00018 #include "console_gui.h"
00019 #include "viewport_func.h"
00020 #include "variables.h"
00021 #include "genworld.h"
00022 #include "blitter/factory.hpp"
00023 #include "zoom_func.h"
00024 #include "map_func.h"
00025 #include "vehicle_base.h"
00026 #include "cheat_type.h"
00027 #include "window_func.h"
00028 #include "tilehighlight_func.h"
00029 #include "network/network.h"
00030 #include "querystring_gui.h"
00031 #include "widgets/dropdown_func.h"
00032 #include "strings_func.h"
00033
00034 #include "table/sprites.h"
00035
00036 static Point _drag_delta;
00037 static Window *_mouseover_last_w = NULL;
00038
00040 Window *_z_front_window = NULL;
00042 Window *_z_back_window = NULL;
00043
00044
00045
00046
00047
00048
00049 Window *_focused_window;
00050
00051 Point _cursorpos_drag_start;
00052
00053 int _scrollbar_start_pos;
00054 int _scrollbar_size;
00055 byte _scroller_click_timeout;
00056
00057 bool _scrolling_scrollbar;
00058 bool _scrolling_viewport;
00059
00060 byte _special_mouse_mode;
00061
00063 WindowDesc::WindowDesc(WindowPosition def_pos, int16 def_width, int16 def_height,
00064 WindowClass window_class, WindowClass parent_class, uint32 flags,
00065 const NWidgetPart *nwid_parts, int16 nwid_length) :
00066 default_pos(def_pos),
00067 default_width(def_width),
00068 default_height(def_height),
00069 cls(window_class),
00070 parent_cls(parent_class),
00071 flags(flags),
00072 nwid_parts(nwid_parts),
00073 nwid_length(nwid_length)
00074 {
00075 }
00076
00077 WindowDesc::~WindowDesc()
00078 {
00079 }
00080
00088 void Scrollbar::SetCapacityFromWidget(Window *w, int widget, int padding)
00089 {
00090 NWidgetBase *nwid = w->GetWidget<NWidgetBase>(widget);
00091 if (this->is_vertical) {
00092 this->SetCapacity(((int)nwid->current_y - padding) / (int)nwid->resize_y);
00093 } else {
00094 this->SetCapacity(((int)nwid->current_x - padding) / (int)nwid->resize_x);
00095 }
00096 }
00097
00102 void SetFocusedWindow(Window *w)
00103 {
00104 if (_focused_window == w) return;
00105
00106
00107 if (_focused_window != NULL) {
00108 if (_focused_window->nested_focus != NULL) _focused_window->nested_focus->SetDirty(_focused_window);
00109 }
00110
00111
00112 Window *old_focused = _focused_window;
00113 _focused_window = w;
00114
00115
00116 if (old_focused != NULL) old_focused->OnFocusLost();
00117 if (_focused_window != NULL) _focused_window->OnFocus();
00118 }
00119
00125 bool EditBoxInGlobalFocus()
00126 {
00127 if (_focused_window == NULL) return false;
00128
00129
00130 if (_focused_window->window_class == WC_CONSOLE) return true;
00131
00132 return _focused_window->nested_focus != NULL && _focused_window->nested_focus->type == WWT_EDITBOX;
00133 }
00134
00138 void Window::UnfocusFocusedWidget()
00139 {
00140 if (this->nested_focus != NULL) {
00141
00142 this->nested_focus->SetDirty(this);
00143 this->nested_focus = NULL;
00144 }
00145 }
00146
00152 bool Window::SetFocusedWidget(byte widget_index)
00153 {
00154
00155 if (widget_index >= this->nested_array_size) return false;
00156
00157 assert(this->nested_array[widget_index] != NULL);
00158 if (this->nested_focus != NULL) {
00159 if (this->GetWidget<NWidgetCore>(widget_index) == this->nested_focus) return false;
00160
00161
00162 this->nested_focus->SetDirty(this);
00163 }
00164 this->nested_focus = this->GetWidget<NWidgetCore>(widget_index);
00165 return true;
00166 }
00167
00175 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
00176 {
00177 va_list wdg_list;
00178
00179 va_start(wdg_list, widgets);
00180
00181 while (widgets != WIDGET_LIST_END) {
00182 SetWidgetDisabledState(widgets, disab_stat);
00183 widgets = va_arg(wdg_list, int);
00184 }
00185
00186 va_end(wdg_list);
00187 }
00188
00194 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
00195 {
00196 va_list wdg_list;
00197
00198 va_start(wdg_list, widgets);
00199
00200 while (widgets != WIDGET_LIST_END) {
00201 SetWidgetLoweredState(widgets, lowered_stat);
00202 widgets = va_arg(wdg_list, int);
00203 }
00204
00205 va_end(wdg_list);
00206 }
00207
00212 void Window::RaiseButtons(bool autoraise)
00213 {
00214 for (uint i = 0; i < this->nested_array_size; i++) {
00215 if (this->nested_array[i] != NULL && (this->nested_array[i]->type & ~WWB_PUSHBUTTON) < WWT_LAST &&
00216 (!autoraise || (this->nested_array[i]->type & WWB_PUSHBUTTON)) && this->IsWidgetLowered(i)) {
00217 this->RaiseWidget(i);
00218 this->SetWidgetDirty(i);
00219 }
00220 }
00221 }
00222
00227 void Window::SetWidgetDirty(byte widget_index) const
00228 {
00229
00230 if (this->nested_array == NULL) return;
00231
00232 this->nested_array[widget_index]->SetDirty(this);
00233 }
00234
00240 void Window::HandleButtonClick(byte widget)
00241 {
00242 this->LowerWidget(widget);
00243 this->flags4 |= WF_TIMEOUT_BEGIN;
00244 this->SetWidgetDirty(widget);
00245 }
00246
00247 static void StartWindowDrag(Window *w);
00248 static void StartWindowSizing(Window *w, bool to_left);
00249
00257 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click)
00258 {
00259 const NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
00260 WidgetType widget_type = (nw != NULL) ? nw->type : WWT_EMPTY;
00261
00262 bool focused_widget_changed = false;
00263
00264 if (_focused_window != w &&
00265 (w->desc_flags & WDF_NO_FOCUS) == 0 &&
00266 widget_type != WWT_CLOSEBOX) {
00267 focused_widget_changed = true;
00268 if (_focused_window != NULL) {
00269 _focused_window->OnFocusLost();
00270
00271
00272 if (w->window_class != WC_OSK) DeleteWindowById(WC_OSK, 0);
00273 }
00274 SetFocusedWindow(w);
00275 w->OnFocus();
00276 }
00277
00278 if (nw == NULL) return;
00279
00280
00281 if (nw->IsDisabled()) return;
00282
00283 int widget_index = nw->index;
00284
00285
00286
00287 if (widget_type != WWT_CAPTION) {
00288
00289 if (w->nested_focus != NULL && w->nested_focus->type == WWT_EDITBOX && w->nested_focus != nw && w->window_class != WC_OSK) {
00290 DeleteWindowById(WC_OSK, 0);
00291 }
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302 focused_widget_changed |= w->SetFocusedWidget(widget_index);
00303 }
00304
00305
00306
00307 if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
00308
00309 switch (widget_type) {
00310
00311 case WWT_PANEL | WWB_PUSHBUTTON:
00312 case WWT_IMGBTN | WWB_PUSHBUTTON:
00313 case WWT_TEXTBTN | WWB_PUSHBUTTON:
00314 w->HandleButtonClick(widget_index);
00315 break;
00316
00317 case WWT_SCROLLBAR:
00318 case WWT_SCROLL2BAR:
00319 case WWT_HSCROLLBAR:
00320 ScrollbarClickHandler(w, nw, x, y);
00321 break;
00322
00323 case WWT_EDITBOX:
00324 if (!focused_widget_changed) {
00325
00326 QueryStringBaseWindow *qs = dynamic_cast<QueryStringBaseWindow *>(w);
00327 if (qs != NULL) {
00328 qs->OnOpenOSKWindow(widget_index);
00329 }
00330 }
00331 break;
00332
00333 case WWT_CLOSEBOX:
00334 delete w;
00335 return;
00336
00337 case WWT_CAPTION:
00338 StartWindowDrag(w);
00339 return;
00340
00341 case WWT_RESIZEBOX:
00342
00343
00344 StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
00345 nw->SetDirty(w);
00346 return;
00347
00348 case WWT_SHADEBOX:
00349 nw->SetDirty(w);
00350 w->SetShaded(!w->IsShaded());
00351 return;
00352
00353 case WWT_STICKYBOX:
00354 w->flags4 ^= WF_STICKY;
00355 nw->SetDirty(w);
00356 return;
00357
00358 default:
00359 break;
00360 }
00361
00362
00363 if (widget_index < 0) return;
00364
00365 Point pt = { x, y };
00366
00367 if (double_click) {
00368 w->OnDoubleClick(pt, widget_index);
00369 } else {
00370 w->OnClick(pt, widget_index);
00371 }
00372 }
00373
00380 static void DispatchRightClickEvent(Window *w, int x, int y)
00381 {
00382 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00383
00384
00385 if (wid == NULL) return;
00386
00387
00388 if (wid->tool_tip != 0) {
00389 GuiShowTooltips(wid->tool_tip);
00390 return;
00391 }
00392
00393
00394 if (wid->index < 0) return;
00395
00396 Point pt = { x, y };
00397 w->OnRightClick(pt, wid->index);
00398 }
00399
00407 static void DispatchMouseWheelEvent(Window *w, const NWidgetCore *nwid, int wheel)
00408 {
00409 if (nwid == NULL) return;
00410
00411
00412 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
00413 w->SetShaded(!w->IsShaded());
00414 return;
00415 }
00416
00417
00418 Scrollbar *sb = nwid->FindScrollbar(w);
00419 if (sb != NULL && sb->GetCount() > sb->GetCapacity()) {
00420 sb->UpdatePosition(wheel);
00421 w->SetDirty();
00422 }
00423 }
00424
00437 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00438 {
00439 const Window *v;
00440 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00441 if (right > v->left &&
00442 bottom > v->top &&
00443 left < v->left + v->width &&
00444 top < v->top + v->height) {
00445
00446 int x;
00447
00448 if (left < (x = v->left)) {
00449 DrawOverlappedWindow(w, left, top, x, bottom);
00450 DrawOverlappedWindow(w, x, top, right, bottom);
00451 return;
00452 }
00453
00454 if (right > (x = v->left + v->width)) {
00455 DrawOverlappedWindow(w, left, top, x, bottom);
00456 DrawOverlappedWindow(w, x, top, right, bottom);
00457 return;
00458 }
00459
00460 if (top < (x = v->top)) {
00461 DrawOverlappedWindow(w, left, top, right, x);
00462 DrawOverlappedWindow(w, left, x, right, bottom);
00463 return;
00464 }
00465
00466 if (bottom > (x = v->top + v->height)) {
00467 DrawOverlappedWindow(w, left, top, right, x);
00468 DrawOverlappedWindow(w, left, x, right, bottom);
00469 return;
00470 }
00471
00472 return;
00473 }
00474 }
00475
00476
00477 DrawPixelInfo *dp = _cur_dpi;
00478 dp->width = right - left;
00479 dp->height = bottom - top;
00480 dp->left = left - w->left;
00481 dp->top = top - w->top;
00482 dp->pitch = _screen.pitch;
00483 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00484 dp->zoom = ZOOM_LVL_NORMAL;
00485 w->OnPaint();
00486 }
00487
00496 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00497 {
00498 Window *w;
00499 DrawPixelInfo bk;
00500 _cur_dpi = &bk;
00501
00502 FOR_ALL_WINDOWS_FROM_BACK(w) {
00503 if (right > w->left &&
00504 bottom > w->top &&
00505 left < w->left + w->width &&
00506 top < w->top + w->height) {
00507
00508 DrawOverlappedWindow(w, left, top, right, bottom);
00509 }
00510 }
00511 }
00512
00517 void Window::SetDirty() const
00518 {
00519 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00520 }
00521
00527 void Window::ReInit(int rx, int ry)
00528 {
00529 this->SetDirty();
00530
00531
00532 int window_width = this->width;
00533 int window_height = this->height;
00534
00535 this->OnInit();
00536
00537 this->nested_root->SetupSmallestSize(this, false);
00538 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _dynlang.text_dir == TD_RTL);
00539 this->width = this->nested_root->smallest_x;
00540 this->height = this->nested_root->smallest_y;
00541 this->resize.step_width = this->nested_root->resize_x;
00542 this->resize.step_height = this->nested_root->resize_y;
00543
00544
00545 window_width = max(window_width + rx, this->width);
00546 window_height = max(window_height + ry, this->height);
00547 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
00548 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
00549
00550
00551 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
00552 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
00553
00554 ResizeWindow(this, dx, dy);
00555 this->OnResize();
00556 this->SetDirty();
00557 }
00558
00563 void Window::SetShaded(bool make_shaded)
00564 {
00565 if (this->shade_select == NULL) return;
00566
00567 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
00568 if (this->shade_select->shown_plane != desired) {
00569 if (make_shaded) {
00570 this->unshaded_size.width = this->width;
00571 this->unshaded_size.height = this->height;
00572 this->shade_select->SetDisplayedPlane(desired);
00573 this->ReInit(0, -this->height);
00574 } else {
00575 this->shade_select->SetDisplayedPlane(desired);
00576 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
00577 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
00578 this->ReInit(dx, dy);
00579 }
00580 }
00581 }
00582
00588 static Window *FindChildWindow(const Window *w, WindowClass wc)
00589 {
00590 Window *v;
00591 FOR_ALL_WINDOWS_FROM_BACK(v) {
00592 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
00593 }
00594
00595 return NULL;
00596 }
00597
00602 void Window::DeleteChildWindows(WindowClass wc) const
00603 {
00604 Window *child = FindChildWindow(this, wc);
00605 while (child != NULL) {
00606 delete child;
00607 child = FindChildWindow(this, wc);
00608 }
00609 }
00610
00614 Window::~Window()
00615 {
00616 if (_thd.place_mode != HT_NONE &&
00617 _thd.window_class == this->window_class &&
00618 _thd.window_number == this->window_number) {
00619 ResetObjectToPlace();
00620 }
00621
00622
00623 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00624
00625
00626 if (_focused_window == this) _focused_window = NULL;
00627
00628 this->DeleteChildWindows();
00629
00630 if (this->viewport != NULL) DeleteWindowViewport(this);
00631
00632 this->SetDirty();
00633
00634 free(this->nested_array);
00635 delete this->nested_root;
00636
00637 this->window_class = WC_INVALID;
00638 }
00639
00646 Window *FindWindowById(WindowClass cls, WindowNumber number)
00647 {
00648 Window *w;
00649 FOR_ALL_WINDOWS_FROM_BACK(w) {
00650 if (w->window_class == cls && w->window_number == number) return w;
00651 }
00652
00653 return NULL;
00654 }
00655
00662 Window *FindWindowByClass(WindowClass cls)
00663 {
00664 Window *w;
00665 FOR_ALL_WINDOWS_FROM_BACK(w) {
00666 if (w->window_class == cls) return w;
00667 }
00668
00669 return NULL;
00670 }
00671
00678 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00679 {
00680 Window *w = FindWindowById(cls, number);
00681 if (force || w == NULL ||
00682 (w->flags4 & WF_STICKY) == 0) {
00683 delete w;
00684 }
00685 }
00686
00691 void DeleteWindowByClass(WindowClass cls)
00692 {
00693 Window *w;
00694
00695 restart_search:
00696
00697
00698
00699 FOR_ALL_WINDOWS_FROM_BACK(w) {
00700 if (w->window_class == cls) {
00701 delete w;
00702 goto restart_search;
00703 }
00704 }
00705 }
00706
00711 void DeleteCompanyWindows(CompanyID id)
00712 {
00713 Window *w;
00714
00715 restart_search:
00716
00717
00718
00719 FOR_ALL_WINDOWS_FROM_BACK(w) {
00720 if (w->owner == id) {
00721 delete w;
00722 goto restart_search;
00723 }
00724 }
00725
00726
00727 DeleteWindowById(WC_BUY_COMPANY, id);
00728 }
00729
00735 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00736 {
00737 Window *w;
00738 FOR_ALL_WINDOWS_FROM_BACK(w) {
00739 if (w->owner != old_owner) continue;
00740
00741 switch (w->window_class) {
00742 case WC_COMPANY_COLOUR:
00743 case WC_FINANCES:
00744 case WC_STATION_LIST:
00745 case WC_TRAINS_LIST:
00746 case WC_ROADVEH_LIST:
00747 case WC_SHIPS_LIST:
00748 case WC_AIRCRAFT_LIST:
00749 case WC_BUY_COMPANY:
00750 case WC_COMPANY:
00751 continue;
00752
00753 default:
00754 w->owner = new_owner;
00755 break;
00756 }
00757 }
00758 }
00759
00760 static void BringWindowToFront(Window *w);
00761
00767 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00768 {
00769 Window *w = FindWindowById(cls, number);
00770
00771 if (w != NULL) {
00772 if (w->IsShaded()) w->SetShaded(false);
00773
00774 w->flags4 |= WF_WHITE_BORDER_MASK;
00775 BringWindowToFront(w);
00776 w->SetDirty();
00777 }
00778
00779 return w;
00780 }
00781
00782 static inline bool IsVitalWindow(const Window *w)
00783 {
00784 switch (w->window_class) {
00785 case WC_MAIN_TOOLBAR:
00786 case WC_STATUS_BAR:
00787 case WC_NEWS_WINDOW:
00788 case WC_SEND_NETWORK_MSG:
00789 return true;
00790
00791 default:
00792 return false;
00793 }
00794 }
00795
00804 static void BringWindowToFront(Window *w)
00805 {
00806 Window *v = _z_front_window;
00807
00808
00809 for (; v != NULL && v != w && IsVitalWindow(v); v = v->z_back) { }
00810
00811 if (v == NULL || w == v) return;
00812
00813
00814 assert(w != _z_front_window);
00815
00816 if (w->z_back == NULL) {
00817 _z_back_window = w->z_front;
00818 } else {
00819 w->z_back->z_front = w->z_front;
00820 }
00821 w->z_front->z_back = w->z_back;
00822
00823 w->z_front = v->z_front;
00824 w->z_back = v;
00825
00826 if (v->z_front == NULL) {
00827 _z_front_window = w;
00828 } else {
00829 v->z_front->z_back = w;
00830 }
00831 v->z_front = w;
00832
00833 w->SetDirty();
00834 }
00835
00845 void Window::InitializeData(WindowClass cls, int window_number, uint32 desc_flags)
00846 {
00847
00848 this->window_class = cls;
00849 this->flags4 |= WF_WHITE_BORDER_MASK;
00850 this->owner = INVALID_OWNER;
00851 this->nested_focus = NULL;
00852 this->window_number = window_number;
00853 this->desc_flags = desc_flags;
00854
00855 this->OnInit();
00856
00857 if (this->nested_array == NULL) {
00858 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
00859 this->nested_root->SetupSmallestSize(this, true);
00860 } else {
00861 this->nested_root->SetupSmallestSize(this, false);
00862 }
00863
00864 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _dynlang.text_dir == TD_RTL);
00865
00866
00867
00868 this->resize.step_width = this->nested_root->resize_x;
00869 this->resize.step_height = this->nested_root->resize_y;
00870
00871
00872
00873
00874 if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL)) SetFocusedWindow(this);
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884 Window *w = _z_front_window;
00885 if (w != NULL && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) {
00886 if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) w = w->z_back;
00887 if (FindWindowById(WC_STATUS_BAR, 0) != NULL) w = w->z_back;
00888 if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) w = w->z_back;
00889 if (FindWindowByClass(WC_SEND_NETWORK_MSG) != NULL) w = w->z_back;
00890
00891 if (w == NULL) {
00892 _z_back_window->z_front = this;
00893 this->z_back = _z_back_window;
00894 _z_back_window = this;
00895 } else {
00896 if (w->z_front == NULL) {
00897 _z_front_window = this;
00898 } else {
00899 this->z_front = w->z_front;
00900 w->z_front->z_back = this;
00901 }
00902
00903 this->z_back = w;
00904 w->z_front = this;
00905 }
00906 } else {
00907 this->z_back = _z_front_window;
00908 if (_z_front_window != NULL) {
00909 _z_front_window->z_front = this;
00910 } else {
00911 _z_back_window = this;
00912 }
00913 _z_front_window = this;
00914 }
00915 }
00916
00924 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
00925 {
00926 this->left = x;
00927 this->top = y;
00928 this->width = sm_width;
00929 this->height = sm_height;
00930 }
00931
00942 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
00943 {
00944 def_width = max(def_width, this->width);
00945 def_height = max(def_height, this->height);
00946
00947
00948
00949
00950
00951 if (this->width != def_width || this->height != def_height) {
00952
00953 int free_height = _screen.height;
00954 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
00955 if (wt != NULL) free_height -= wt->height;
00956 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
00957 if (wt != NULL) free_height -= wt->height;
00958
00959 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
00960 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
00961
00962
00963
00964
00965 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
00966 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
00967
00968 ResizeWindow(this, enlarge_x, enlarge_y);
00969 }
00970
00971
00972 this->OnResize();
00973
00974 int nx = this->left;
00975 int ny = this->top;
00976
00977 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
00978
00979 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
00980 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
00981 nx = max(nx, 0);
00982
00983 if (this->viewport != NULL) {
00984 this->viewport->left += nx - this->left;
00985 this->viewport->top += ny - this->top;
00986 }
00987 this->left = nx;
00988 this->top = ny;
00989
00990 this->SetDirty();
00991 }
00992
01004 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
01005 {
01006 int right = width + left;
01007 int bottom = height + top;
01008
01009 if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height) return false;
01010
01011
01012 const Window *w;
01013 FOR_ALL_WINDOWS_FROM_BACK(w) {
01014 if (w->window_class == WC_MAIN_WINDOW) continue;
01015
01016 if (right > w->left &&
01017 w->left + w->width > left &&
01018 bottom > w->top &&
01019 w->top + w->height > top) {
01020 return false;
01021 }
01022 }
01023
01024 pos.x = left;
01025 pos.y = top;
01026 return true;
01027 }
01028
01040 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
01041 {
01042
01043
01044
01045 if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
01046
01047 if (top < 22 || top > _screen.height - (height>>2)) return false;
01048
01049
01050 const Window *w;
01051 FOR_ALL_WINDOWS_FROM_BACK(w) {
01052 if (w->window_class == WC_MAIN_WINDOW) continue;
01053
01054 if (left + width > w->left &&
01055 w->left + w->width > left &&
01056 top + height > w->top &&
01057 w->top + w->height > top) {
01058 return false;
01059 }
01060 }
01061
01062 pos.x = left;
01063 pos.y = top;
01064 return true;
01065 }
01066
01073 static Point GetAutoPlacePosition(int width, int height)
01074 {
01075 Point pt;
01076
01077
01078 if (IsGoodAutoPlace1(0, 24, width, height, pt)) return pt;
01079
01080
01081
01082
01083
01084 const Window *w;
01085 FOR_ALL_WINDOWS_FROM_BACK(w) {
01086 if (w->window_class == WC_MAIN_WINDOW) continue;
01087
01088 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01089 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01090 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01091 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01092 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01093 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01094 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01095 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01096 }
01097
01098
01099
01100
01101
01102 FOR_ALL_WINDOWS_FROM_BACK(w) {
01103 if (w->window_class == WC_MAIN_WINDOW) continue;
01104
01105 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01106 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01107 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01108 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01109 }
01110
01111
01112
01113
01114 int left = 0, top = 24;
01115
01116 restart:
01117 FOR_ALL_WINDOWS_FROM_BACK(w) {
01118 if (w->left == left && w->top == top) {
01119 left += 5;
01120 top += 5;
01121 goto restart;
01122 }
01123 }
01124
01125 pt.x = left;
01126 pt.y = top;
01127 return pt;
01128 }
01129
01136 Point GetToolbarAlignedWindowPosition(int window_width)
01137 {
01138 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01139 assert(w != NULL);
01140 Point pt = { _dynlang.text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
01141 return pt;
01142 }
01143
01161 static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01162 {
01163 Point pt;
01164 const Window *w;
01165
01166 int16 default_width = max(desc->default_width, sm_width);
01167 int16 default_height = max(desc->default_height, sm_height);
01168
01169 if (desc->parent_cls != 0 &&
01170 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01171 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01172
01173 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01174 if (pt.x > _screen.width + 10 - default_width) {
01175 pt.x = (_screen.width + 10 - default_width) - 20;
01176 }
01177 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? w->height : 10);
01178 return pt;
01179 }
01180
01181 switch (desc->default_pos) {
01182 case WDP_ALIGN_TOOLBAR:
01183 return GetToolbarAlignedWindowPosition(default_width);
01184
01185 case WDP_AUTO:
01186 return GetAutoPlacePosition(default_width, default_height);
01187
01188 case WDP_CENTER:
01189 pt.x = (_screen.width - default_width) / 2;
01190 pt.y = (_screen.height - default_height) / 2;
01191 break;
01192
01193 case WDP_MANUAL:
01194 pt.x = 0;
01195 pt.y = 0;
01196 break;
01197
01198 default:
01199 NOT_REACHED();
01200 }
01201
01202 return pt;
01203 }
01204
01205 Point Window::OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01206 {
01207 return LocalGetWindowPlacement(desc, sm_width, sm_height, window_number);
01208 }
01209
01218 void Window::CreateNestedTree(const WindowDesc *desc, bool fill_nested)
01219 {
01220 int biggest_index = -1;
01221 this->nested_root = MakeWindowNWidgetTree(desc->nwid_parts, desc->nwid_length, &biggest_index, &this->shade_select);
01222 this->nested_array_size = (uint)(biggest_index + 1);
01223
01224 if (fill_nested) {
01225 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01226 this->nested_root->FillNestedArray(this->nested_array, this->nested_array_size);
01227 }
01228 }
01229
01235 void Window::FinishInitNested(const WindowDesc *desc, WindowNumber window_number)
01236 {
01237 this->InitializeData(desc->cls, window_number, desc->flags);
01238 Point pt = this->OnInitialPosition(desc, this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
01239 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
01240 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
01241 }
01242
01248 void Window::InitNested(const WindowDesc *desc, WindowNumber window_number)
01249 {
01250 this->CreateNestedTree(desc, false);
01251 this->FinishInitNested(desc, window_number);
01252 }
01253
01255 Window::Window() : hscroll(false), vscroll(true), vscroll2(true)
01256 {
01257 }
01258
01264 Window *FindWindowFromPt(int x, int y)
01265 {
01266 Window *w;
01267 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01268 if (IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01269 return w;
01270 }
01271 }
01272
01273 return NULL;
01274 }
01275
01279 void InitWindowSystem()
01280 {
01281 IConsoleClose();
01282
01283 _z_back_window = NULL;
01284 _z_front_window = NULL;
01285 _focused_window = NULL;
01286 _mouseover_last_w = NULL;
01287 _scrolling_viewport = 0;
01288
01289 NWidgetLeaf::InvalidateDimensionCache();
01290 }
01291
01295 void UnInitWindowSystem()
01296 {
01297 Window *w;
01298 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01299
01300 for (w = _z_front_window; w != NULL; ) {
01301 Window *to_del = w;
01302 w = w->z_back;
01303 free(to_del);
01304 }
01305
01306 _z_front_window = NULL;
01307 _z_back_window = NULL;
01308 }
01309
01313 void ResetWindowSystem()
01314 {
01315 UnInitWindowSystem();
01316 InitWindowSystem();
01317 _thd.pos.x = 0;
01318 _thd.pos.y = 0;
01319 _thd.new_pos.x = 0;
01320 _thd.new_pos.y = 0;
01321 }
01322
01323 static void DecreaseWindowCounters()
01324 {
01325 Window *w;
01326 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01327
01328 if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
01329 w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
01330 w->SetDirty();
01331 }
01332 w->OnMouseLoop();
01333 }
01334
01335 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01336 if ((w->flags4 & WF_TIMEOUT_MASK) && !(--w->flags4 & WF_TIMEOUT_MASK)) {
01337 w->OnTimeout();
01338 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons(true);
01339 }
01340 }
01341 }
01342
01343 Window *GetCallbackWnd()
01344 {
01345 return FindWindowById(_thd.window_class, _thd.window_number);
01346 }
01347
01348 static void HandlePlacePresize()
01349 {
01350 if (_special_mouse_mode != WSM_PRESIZE) return;
01351
01352 Window *w = GetCallbackWnd();
01353 if (w == NULL) return;
01354
01355 Point pt = GetTileBelowCursor();
01356 if (pt.x == -1) {
01357 _thd.selend.x = -1;
01358 return;
01359 }
01360
01361 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01362 }
01363
01364 static bool HandleDragDrop()
01365 {
01366 if (_special_mouse_mode != WSM_DRAGDROP) return true;
01367 if (_left_button_down) return false;
01368
01369 Window *w = GetCallbackWnd();
01370
01371 if (w != NULL) {
01372
01373 Point pt;
01374 pt.x = _cursor.pos.x - w->left;
01375 pt.y = _cursor.pos.y - w->top;
01376 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01377 }
01378
01379 ResetObjectToPlace();
01380
01381 return false;
01382 }
01383
01384 static bool HandleMouseOver()
01385 {
01386 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01387
01388
01389 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01390
01391 Point pt = { -1, -1 };
01392 _mouseover_last_w->OnMouseOver(pt, 0);
01393 }
01394
01395
01396 _mouseover_last_w = w;
01397
01398 if (w != NULL) {
01399
01400 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01401 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
01402 if (widget != NULL) w->OnMouseOver(pt, widget->index);
01403 }
01404
01405
01406 return true;
01407 }
01408
01418 void ResizeWindow(Window *w, int delta_x, int delta_y)
01419 {
01420 if (delta_x == 0 && delta_y == 0) return;
01421
01422 w->SetDirty();
01423
01424 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);
01425 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);
01426 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
01427 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
01428
01429 w->nested_root->AssignSizePosition(ST_RESIZE, 0, 0, w->nested_root->smallest_x + new_xinc, w->nested_root->smallest_y + new_yinc, _dynlang.text_dir == TD_RTL);
01430 w->width = w->nested_root->current_x;
01431 w->height = w->nested_root->current_y;
01432 w->SetDirty();
01433 }
01434
01439 int GetMainViewTop()
01440 {
01441 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01442 return (w == NULL) ? 0 : w->top + w->height;
01443 }
01444
01449 int GetMainViewBottom()
01450 {
01451 Window *w = FindWindowById(WC_STATUS_BAR, 0);
01452 return (w == NULL) ? _screen.height : w->top;
01453 }
01454
01456 static const int MIN_VISIBLE_TITLE_BAR = 13;
01457
01459 enum PreventHideDirection {
01460 PHD_UP,
01461 PHD_DOWN,
01462 };
01463
01474 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
01475 {
01476 if (v == NULL) return;
01477
01478 int v_bottom = v->top + v->height;
01479 int v_right = v->left + v->width;
01480 int safe_y = (dir == PHD_UP) ? (v->top - MIN_VISIBLE_TITLE_BAR - rect.top) : (v_bottom + MIN_VISIBLE_TITLE_BAR - rect.bottom);
01481
01482 if (*ny + rect.top <= v->top - MIN_VISIBLE_TITLE_BAR) return;
01483 if (*ny + rect.bottom >= v_bottom + MIN_VISIBLE_TITLE_BAR) return;
01484
01485
01486 if (*nx + rect.left + MIN_VISIBLE_TITLE_BAR < v->left) {
01487 if (v->left < MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01488 return;
01489 }
01490 if (*nx + rect.right - MIN_VISIBLE_TITLE_BAR > v_right) {
01491 if (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01492 return;
01493 }
01494
01495
01496 if (px + rect.left < v->left && v->left >= MIN_VISIBLE_TITLE_BAR) {
01497 *nx = v->left - MIN_VISIBLE_TITLE_BAR - rect.left;
01498 } else if (px + rect.right > v_right && v_right <= _screen.width - MIN_VISIBLE_TITLE_BAR) {
01499 *nx = v_right + MIN_VISIBLE_TITLE_BAR - rect.right;
01500 } else {
01501 *ny = safe_y;
01502 }
01503 }
01504
01505 static bool _dragging_window;
01506
01507 static bool HandleWindowDragging()
01508 {
01509
01510 if (!_dragging_window) return true;
01511
01512
01513 Window *w;
01514 FOR_ALL_WINDOWS_FROM_BACK(w) {
01515 if (w->flags4 & WF_DRAGGING) {
01516
01517 if (!_left_button_down) {
01518 w->flags4 &= ~WF_DRAGGING;
01519 break;
01520 }
01521
01522 w->SetDirty();
01523
01524 int x = _cursor.pos.x + _drag_delta.x;
01525 int y = _cursor.pos.y + _drag_delta.y;
01526 int nx = x;
01527 int ny = y;
01528
01529 if (_settings_client.gui.window_snap_radius != 0) {
01530 const Window *v;
01531
01532 int hsnap = _settings_client.gui.window_snap_radius;
01533 int vsnap = _settings_client.gui.window_snap_radius;
01534 int delta;
01535
01536 FOR_ALL_WINDOWS_FROM_BACK(v) {
01537 if (v == w) continue;
01538
01539 if (y + w->height > v->top && y < v->top + v->height) {
01540
01541 delta = abs(v->left + v->width - x);
01542 if (delta <= hsnap) {
01543 nx = v->left + v->width;
01544 hsnap = delta;
01545 }
01546
01547
01548 delta = abs(v->left - x - w->width);
01549 if (delta <= hsnap) {
01550 nx = v->left - w->width;
01551 hsnap = delta;
01552 }
01553 }
01554
01555 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01556
01557 delta = abs(v->left - x);
01558 if (delta <= hsnap) {
01559 nx = v->left;
01560 hsnap = delta;
01561 }
01562
01563
01564 delta = abs(v->left + v->width - x - w->width);
01565 if (delta <= hsnap) {
01566 nx = v->left + v->width - w->width;
01567 hsnap = delta;
01568 }
01569 }
01570
01571 if (x + w->width > v->left && x < v->left + v->width) {
01572
01573 delta = abs(v->top + v->height - y);
01574 if (delta <= vsnap) {
01575 ny = v->top + v->height;
01576 vsnap = delta;
01577 }
01578
01579
01580 delta = abs(v->top - y - w->height);
01581 if (delta <= vsnap) {
01582 ny = v->top - w->height;
01583 vsnap = delta;
01584 }
01585 }
01586
01587 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01588
01589 delta = abs(v->top - y);
01590 if (delta <= vsnap) {
01591 ny = v->top;
01592 vsnap = delta;
01593 }
01594
01595
01596 delta = abs(v->top + v->height - y - w->height);
01597 if (delta <= vsnap) {
01598 ny = v->top + v->height - w->height;
01599 vsnap = delta;
01600 }
01601 }
01602 }
01603 }
01604
01605
01606 Rect caption_rect;
01607 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
01608 assert(caption != NULL);
01609 caption_rect.left = caption->pos_x;
01610 caption_rect.right = caption->pos_x + caption->current_x;
01611 caption_rect.top = caption->pos_y;
01612 caption_rect.bottom = caption->pos_y + caption->current_y;
01613
01614
01615 nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
01616 ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
01617
01618
01619 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
01620 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
01621
01622 if (w->viewport != NULL) {
01623 w->viewport->left += nx - w->left;
01624 w->viewport->top += ny - w->top;
01625 }
01626 w->left = nx;
01627 w->top = ny;
01628
01629 w->SetDirty();
01630 return false;
01631 } else if (w->flags4 & WF_SIZING) {
01632
01633 if (!_left_button_down) {
01634 w->flags4 &= ~WF_SIZING;
01635 w->SetDirty();
01636 break;
01637 }
01638
01639
01640
01641
01642 int x, y = _cursor.pos.y - _drag_delta.y;
01643 if (w->flags4 & WF_SIZING_LEFT) {
01644 x = _drag_delta.x - _cursor.pos.x;
01645 } else {
01646 x = _cursor.pos.x - _drag_delta.x;
01647 }
01648
01649
01650 if (w->resize.step_width == 0) x = 0;
01651 if (w->resize.step_height == 0) y = 0;
01652
01653
01654
01655
01656 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
01657 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
01658
01659
01660 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
01661 x = w->nested_root->smallest_x - w->width;
01662 }
01663 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
01664 y = w->nested_root->smallest_y - w->height;
01665 }
01666
01667
01668 if (x == 0 && y == 0) return false;
01669
01670
01671 _drag_delta.y += y;
01672 if ((w->flags4 & WF_SIZING_LEFT) && x != 0) {
01673 _drag_delta.x -= x;
01674 w->SetDirty();
01675 w->left -= x;
01676
01677 } else {
01678 _drag_delta.x += x;
01679 }
01680
01681
01682 ResizeWindow(w, x, y);
01683 w->OnResize();
01684 return false;
01685 }
01686 }
01687
01688 _dragging_window = false;
01689 return false;
01690 }
01691
01696 static void StartWindowDrag(Window *w)
01697 {
01698 w->flags4 |= WF_DRAGGING;
01699 _dragging_window = true;
01700
01701 _drag_delta.x = w->left - _cursor.pos.x;
01702 _drag_delta.y = w->top - _cursor.pos.y;
01703
01704 BringWindowToFront(w);
01705 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01706 }
01707
01713 static void StartWindowSizing(Window *w, bool to_left)
01714 {
01715 w->flags4 |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
01716 _dragging_window = true;
01717
01718 _drag_delta.x = _cursor.pos.x;
01719 _drag_delta.y = _cursor.pos.y;
01720
01721 BringWindowToFront(w);
01722 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01723 }
01724
01725
01726 static bool HandleScrollbarScrolling()
01727 {
01728 Window *w;
01729
01730
01731 if (!_scrolling_scrollbar) return true;
01732
01733
01734 FOR_ALL_WINDOWS_FROM_BACK(w) {
01735 if (w->flags4 & WF_SCROLL_MIDDLE) {
01736
01737 if (!_left_button_down) {
01738 w->flags4 &= ~WF_SCROLL_MIDDLE;
01739 w->SetDirty();
01740 break;
01741 }
01742
01743 int i;
01744 Scrollbar *sb;
01745 bool rtl = false;
01746
01747 if (w->flags4 & WF_HSCROLL) {
01748 sb = &w->hscroll;
01749 i = _cursor.pos.x - _cursorpos_drag_start.x;
01750 rtl = _dynlang.text_dir == TD_RTL;
01751 } else if (w->flags4 & WF_SCROLL2) {
01752 sb = &w->vscroll2;
01753 i = _cursor.pos.y - _cursorpos_drag_start.y;
01754 } else {
01755 sb = &w->vscroll;
01756 i = _cursor.pos.y - _cursorpos_drag_start.y;
01757 }
01758
01759
01760 int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity()));
01761 if (rtl) pos = sb->GetCount() - sb->GetCapacity() - pos;
01762 if (pos != sb->GetPosition()) {
01763 sb->SetPosition(pos);
01764 w->SetDirty();
01765 }
01766 return false;
01767 }
01768 }
01769
01770 _scrolling_scrollbar = false;
01771 return false;
01772 }
01773
01774 static bool HandleViewportScroll()
01775 {
01776 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
01777
01778 if (!_scrolling_viewport) return true;
01779
01780 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01781
01782 if (!(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) || w == NULL) {
01783 _cursor.fix_at = false;
01784 _scrolling_viewport = false;
01785 return true;
01786 }
01787
01788 if (w == FindWindowById(WC_MAIN_WINDOW, 0) && w->viewport->follow_vehicle != INVALID_VEHICLE) {
01789
01790 const Vehicle *veh = Vehicle::Get(w->viewport->follow_vehicle);
01791 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
01792 return true;
01793 }
01794
01795 Point delta;
01796 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
01797 delta.x = -_cursor.delta.x;
01798 delta.y = -_cursor.delta.y;
01799 } else {
01800 delta.x = _cursor.delta.x;
01801 delta.y = _cursor.delta.y;
01802 }
01803
01804 if (scrollwheel_scrolling) {
01805
01806 delta.x = _cursor.h_wheel;
01807 delta.y = _cursor.v_wheel;
01808 _cursor.v_wheel = 0;
01809 _cursor.h_wheel = 0;
01810 }
01811
01812
01813 if (delta.x != 0 || delta.y != 0) w->OnScroll(delta);
01814
01815 _cursor.delta.x = 0;
01816 _cursor.delta.y = 0;
01817 return false;
01818 }
01819
01828 static bool MaybeBringWindowToFront(Window *w)
01829 {
01830 bool bring_to_front = false;
01831
01832 if (w->window_class == WC_MAIN_WINDOW ||
01833 IsVitalWindow(w) ||
01834 w->window_class == WC_TOOLTIPS ||
01835 w->window_class == WC_DROPDOWN_MENU) {
01836 return true;
01837 }
01838
01839
01840 int w_width = w->width;
01841 int w_height = w->height;
01842 if (w->IsShaded()) {
01843 w_width = w->unshaded_size.width;
01844 w_height = w->unshaded_size.height;
01845 }
01846
01847 Window *u;
01848 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
01849
01850 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
01851 u->flags4 |= WF_WHITE_BORDER_MASK;
01852 u->SetDirty();
01853 return false;
01854 }
01855
01856 if (u->window_class == WC_MAIN_WINDOW ||
01857 IsVitalWindow(u) ||
01858 u->window_class == WC_TOOLTIPS ||
01859 u->window_class == WC_DROPDOWN_MENU) {
01860 continue;
01861 }
01862
01863
01864 if (w->left + w_width <= u->left ||
01865 u->left + u->width <= w->left ||
01866 w->top + w_height <= u->top ||
01867 u->top + u->height <= w->top) {
01868 continue;
01869 }
01870
01871 bring_to_front = true;
01872 }
01873
01874 if (bring_to_front) BringWindowToFront(w);
01875 return true;
01876 }
01877
01881 void HandleKeypress(uint32 raw_key)
01882 {
01883
01884
01885
01886
01887
01888
01889
01890
01891
01892 if (!IsGeneratingWorld()) _current_company = _local_company;
01893
01894
01895 uint16 key = GB(raw_key, 0, 16);
01896 uint16 keycode = GB(raw_key, 16, 16);
01897
01898
01899
01900
01901
01902
01903
01904
01905 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
01906
01907
01908
01909
01910 if (key == 0 && keycode == 0) return;
01911
01912
01913 if (EditBoxInGlobalFocus()) {
01914
01915 if (_focused_window->OnKeyPress(key, keycode) == Window::ES_HANDLED) return;
01916 }
01917
01918
01919 Window *w;
01920 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01921 if (w->window_class == WC_MAIN_TOOLBAR) continue;
01922 if (w->OnKeyPress(key, keycode) == Window::ES_HANDLED) return;
01923 }
01924
01925 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01926
01927 if (w != NULL) w->OnKeyPress(key, keycode);
01928 }
01929
01933 void HandleCtrlChanged()
01934 {
01935
01936 Window *w;
01937 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01938 if (w->OnCTRLStateChange() == Window::ES_HANDLED) return;
01939 }
01940 }
01941
01948 static int _input_events_this_tick = 0;
01949
01954 static void HandleAutoscroll()
01955 {
01956 if (_settings_client.gui.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
01957 int x = _cursor.pos.x;
01958 int y = _cursor.pos.y;
01959 Window *w = FindWindowFromPt(x, y);
01960 if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
01961 ViewPort *vp = IsPtInWindowViewport(w, x, y);
01962 if (vp != NULL) {
01963 x -= vp->left;
01964 y -= vp->top;
01965
01966
01967 #define scrollspeed 3
01968 if (x - 15 < 0) {
01969 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
01970 } else if (15 - (vp->width - x) > 0) {
01971 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
01972 }
01973 if (y - 15 < 0) {
01974 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
01975 } else if (15 - (vp->height - y) > 0) {
01976 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
01977 }
01978 #undef scrollspeed
01979 }
01980 }
01981 }
01982
01983 enum MouseClick {
01984 MC_NONE = 0,
01985 MC_LEFT,
01986 MC_RIGHT,
01987 MC_DOUBLE_LEFT,
01988
01989 MAX_OFFSET_DOUBLE_CLICK = 5,
01990 TIME_BETWEEN_DOUBLE_CLICK = 500,
01991 };
01992
01993 extern bool VpHandlePlaceSizingDrag();
01994
01995 static void ScrollMainViewport(int x, int y)
01996 {
01997 if (_game_mode != GM_MENU) {
01998 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01999 assert(w);
02000
02001 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
02002 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
02003 }
02004 }
02005
02015 static const int8 scrollamt[16][2] = {
02016 { 0, 0},
02017 {-2, 0},
02018 { 0, -2},
02019 {-2, -1},
02020 { 2, 0},
02021 { 0, 0},
02022 { 2, -1},
02023 { 0, -2},
02024 { 0, 2},
02025 {-2, 1},
02026 { 0, 0},
02027 {-2, 0},
02028 { 2, 1},
02029 { 0, 2},
02030 { 2, 0},
02031 { 0, 0},
02032 };
02033
02034 static void HandleKeyScrolling()
02035 {
02036
02037
02038
02039
02040 if (_dirkeys && !EditBoxInGlobalFocus()) {
02041 int factor = _shift_pressed ? 50 : 10;
02042 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
02043 }
02044 }
02045
02046 static void MouseLoop(MouseClick click, int mousewheel)
02047 {
02048 HandlePlacePresize();
02049 UpdateTileSelection();
02050
02051 if (!VpHandlePlaceSizingDrag()) return;
02052 if (!HandleDragDrop()) return;
02053 if (!HandleWindowDragging()) return;
02054 if (!HandleScrollbarScrolling()) return;
02055 if (!HandleViewportScroll()) return;
02056 if (!HandleMouseOver()) return;
02057
02058 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02059 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
02060
02061 int x = _cursor.pos.x;
02062 int y = _cursor.pos.y;
02063 Window *w = FindWindowFromPt(x, y);
02064 if (w == NULL) return;
02065
02066 if (!MaybeBringWindowToFront(w)) return;
02067 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02068
02069
02070 if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
02071
02072 if (mousewheel != 0) {
02073 if (_settings_client.gui.scrollwheel_scrolling == 0) {
02074
02075 w->OnMouseWheel(mousewheel);
02076 }
02077
02078
02079 if (vp == NULL) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
02080 }
02081
02082 if (vp != NULL) {
02083 if (scrollwheel_scrolling) click = MC_RIGHT;
02084 switch (click) {
02085 case MC_DOUBLE_LEFT:
02086 case MC_LEFT:
02087 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
02088 if (_thd.place_mode != HT_NONE &&
02089
02090 _cursor.sprite != SPR_CURSOR_QUERY &&
02091 _cursor.sprite != SPR_CURSOR_SIGN &&
02092 _pause_mode != PM_UNPAUSED &&
02093 !_cheats.build_in_pause.value) {
02094 return;
02095 }
02096
02097 if (_thd.place_mode == HT_NONE) {
02098 if (!HandleViewportClicked(vp, x, y) &&
02099 !(w->flags4 & WF_DISABLE_VP_SCROLL) &&
02100 _settings_client.gui.left_mouse_btn_scrolling) {
02101 _scrolling_viewport = true;
02102 _cursor.fix_at = false;
02103 }
02104 } else {
02105 PlaceObject();
02106 }
02107 break;
02108
02109 case MC_RIGHT:
02110 if (!(w->flags4 & WF_DISABLE_VP_SCROLL)) {
02111 _scrolling_viewport = true;
02112 _cursor.fix_at = true;
02113 }
02114 break;
02115
02116 default:
02117 break;
02118 }
02119 } else {
02120 switch (click) {
02121 case MC_DOUBLE_LEFT:
02122 DispatchLeftClickEvent(w, x - w->left, y - w->top, true);
02123 if (_mouseover_last_w == NULL) break;
02124
02125 case MC_LEFT:
02126 DispatchLeftClickEvent(w, x - w->left, y - w->top, false);
02127 break;
02128
02129 default:
02130 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
02131
02132
02133
02134
02135 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
02136 }
02137 }
02138 }
02139
02143 void HandleMouseEvents()
02144 {
02145 static int double_click_time = 0;
02146 static int double_click_x = 0;
02147 static int double_click_y = 0;
02148
02149
02150
02151
02152
02153
02154
02155
02156
02157
02158 if (!IsGeneratingWorld()) _current_company = _local_company;
02159
02160
02161 MouseClick click = MC_NONE;
02162 if (_left_button_down && !_left_button_clicked) {
02163 click = MC_LEFT;
02164 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02165 double_click_x != 0 && abs(_cursor.pos.x - double_click_x) < MAX_OFFSET_DOUBLE_CLICK &&
02166 double_click_y != 0 && abs(_cursor.pos.y - double_click_y) < MAX_OFFSET_DOUBLE_CLICK) {
02167 click = MC_DOUBLE_LEFT;
02168 }
02169 double_click_time = _realtime_tick;
02170 double_click_x = _cursor.pos.x;
02171 double_click_y = _cursor.pos.y;
02172 _left_button_clicked = true;
02173 _input_events_this_tick++;
02174 } else if (_right_button_clicked) {
02175 _right_button_clicked = false;
02176 click = MC_RIGHT;
02177 _input_events_this_tick++;
02178 }
02179
02180 int mousewheel = 0;
02181 if (_cursor.wheel) {
02182 mousewheel = _cursor.wheel;
02183 _cursor.wheel = 0;
02184 _input_events_this_tick++;
02185 }
02186
02187 MouseLoop(click, mousewheel);
02188
02189
02190
02191 _cursor.delta.x = 0;
02192 _cursor.delta.y = 0;
02193 }
02194
02198 static void CheckSoftLimit()
02199 {
02200 if (_settings_client.gui.window_soft_limit == 0) return;
02201
02202 for (;;) {
02203 uint deletable_count = 0;
02204 Window *w, *last_deletable = NULL;
02205 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02206 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags4 & WF_STICKY)) continue;
02207
02208 last_deletable = w;
02209 deletable_count++;
02210 }
02211
02212
02213 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02214
02215 assert(last_deletable != NULL);
02216 delete last_deletable;
02217 }
02218 }
02219
02223 void InputLoop()
02224 {
02225 CheckSoftLimit();
02226 HandleKeyScrolling();
02227
02228
02229 for (Window *v = _z_front_window; v != NULL; ) {
02230 Window *w = v;
02231 v = v->z_back;
02232
02233 if (w->window_class != WC_INVALID) continue;
02234
02235
02236
02237
02238
02239 if (w->z_front == NULL) {
02240 _z_front_window = w->z_back;
02241 } else {
02242 w->z_front->z_back = w->z_back;
02243 }
02244 if (w->z_back == NULL) {
02245 _z_back_window = w->z_front;
02246 } else {
02247 w->z_back->z_front = w->z_front;
02248 }
02249 free(w);
02250 }
02251
02252 DecreaseWindowCounters();
02253
02254 if (_input_events_this_tick != 0) {
02255
02256 _input_events_this_tick = 0;
02257
02258 return;
02259 }
02260
02261
02262 HandleMouseEvents();
02263 HandleAutoscroll();
02264 }
02265
02269 void UpdateWindows()
02270 {
02271 Window *w;
02272 static int we4_timer = 0;
02273 int t = we4_timer + 1;
02274
02275 if (t >= 100) {
02276 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02277 w->OnHundredthTick();
02278 }
02279 t = 0;
02280 }
02281 we4_timer = t;
02282
02283 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02284 if (w->flags4 & WF_WHITE_BORDER_MASK) {
02285 w->flags4 -= WF_WHITE_BORDER_ONE;
02286
02287 if (!(w->flags4 & WF_WHITE_BORDER_MASK)) w->SetDirty();
02288 }
02289 }
02290
02291 DrawDirtyBlocks();
02292
02293 FOR_ALL_WINDOWS_FROM_BACK(w) {
02294
02295 if (w->viewport != NULL && !w->IsShaded()) UpdateViewportPosition(w);
02296 }
02297 NetworkDrawChatMessage();
02298
02299 DrawMouseCursor();
02300 }
02301
02307 void SetWindowDirty(WindowClass cls, WindowNumber number)
02308 {
02309 const Window *w;
02310 FOR_ALL_WINDOWS_FROM_BACK(w) {
02311 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02312 }
02313 }
02314
02321 void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
02322 {
02323 const Window *w;
02324 FOR_ALL_WINDOWS_FROM_BACK(w) {
02325 if (w->window_class == cls && w->window_number == number) {
02326 w->SetWidgetDirty(widget_index);
02327 }
02328 }
02329 }
02330
02335 void SetWindowClassesDirty(WindowClass cls)
02336 {
02337 Window *w;
02338 FOR_ALL_WINDOWS_FROM_BACK(w) {
02339 if (w->window_class == cls) w->SetDirty();
02340 }
02341 }
02342
02349 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data)
02350 {
02351 Window *w;
02352 FOR_ALL_WINDOWS_FROM_BACK(w) {
02353 if (w->window_class == cls && w->window_number == number) w->InvalidateData(data);
02354 }
02355 }
02356
02362 void InvalidateWindowClassesData(WindowClass cls, int data)
02363 {
02364 Window *w;
02365
02366 FOR_ALL_WINDOWS_FROM_BACK(w) {
02367 if (w->window_class == cls) w->InvalidateData(data);
02368 }
02369 }
02370
02374 void CallWindowTickEvent()
02375 {
02376 if (_scroller_click_timeout > 3) {
02377 _scroller_click_timeout -= 3;
02378 } else {
02379 _scroller_click_timeout = 0;
02380 }
02381
02382 Window *w;
02383 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02384 w->OnTick();
02385 }
02386 }
02387
02394 void DeleteNonVitalWindows()
02395 {
02396 Window *w;
02397
02398 restart_search:
02399
02400
02401
02402 FOR_ALL_WINDOWS_FROM_BACK(w) {
02403 if (w->window_class != WC_MAIN_WINDOW &&
02404 w->window_class != WC_SELECT_GAME &&
02405 w->window_class != WC_MAIN_TOOLBAR &&
02406 w->window_class != WC_STATUS_BAR &&
02407 w->window_class != WC_TOOLBAR_MENU &&
02408 w->window_class != WC_TOOLTIPS &&
02409 (w->flags4 & WF_STICKY) == 0) {
02410
02411 delete w;
02412 goto restart_search;
02413 }
02414 }
02415 }
02416
02422 void DeleteAllNonVitalWindows()
02423 {
02424 Window *w;
02425
02426
02427 DeleteNonVitalWindows();
02428
02429 restart_search:
02430
02431
02432
02433 FOR_ALL_WINDOWS_FROM_BACK(w) {
02434 if (w->flags4 & WF_STICKY) {
02435 delete w;
02436 goto restart_search;
02437 }
02438 }
02439 }
02440
02445 void DeleteConstructionWindows()
02446 {
02447 Window *w;
02448
02449 restart_search:
02450
02451
02452
02453 FOR_ALL_WINDOWS_FROM_BACK(w) {
02454 if (w->desc_flags & WDF_CONSTRUCTION) {
02455 delete w;
02456 goto restart_search;
02457 }
02458 }
02459
02460 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02461 }
02462
02464 void HideVitalWindows()
02465 {
02466 DeleteWindowById(WC_TOOLBAR_MENU, 0);
02467 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02468 DeleteWindowById(WC_STATUS_BAR, 0);
02469 }
02470
02472 void ReInitAllWindows()
02473 {
02474 NWidgetLeaf::InvalidateDimensionCache();
02475
02476 Window *w;
02477 FOR_ALL_WINDOWS_FROM_BACK(w) {
02478 w->ReInit();
02479 }
02480
02481
02482 RelocateAllWindows(_cur_resolution.width, _cur_resolution.height);
02483 MarkWholeScreenDirty();
02484 }
02485
02491 int PositionMainToolbar(Window *w)
02492 {
02493 DEBUG(misc, 5, "Repositioning Main Toolbar...");
02494
02495 if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
02496 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02497 }
02498
02499 switch (_settings_client.gui.toolbar_pos) {
02500 case 1: w->left = (_screen.width - w->width) / 2; break;
02501 case 2: w->left = _screen.width - w->width; break;
02502 default: w->left = 0;
02503 }
02504 SetDirtyBlocks(0, 0, _screen.width, w->height);
02505 return w->left;
02506 }
02507
02508
02514 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
02515 {
02516 Window *w;
02517 FOR_ALL_WINDOWS_FROM_BACK(w) {
02518 if (w->viewport != NULL && w->viewport->follow_vehicle == from_index) {
02519 w->viewport->follow_vehicle = to_index;
02520 w->SetDirty();
02521 }
02522 }
02523 }
02524
02525
02531 void RelocateAllWindows(int neww, int newh)
02532 {
02533 Window *w;
02534
02535 FOR_ALL_WINDOWS_FROM_BACK(w) {
02536 int left, top;
02537
02538 if (w->window_class == WC_MAIN_WINDOW) {
02539 ViewPort *vp = w->viewport;
02540 vp->width = w->width = neww;
02541 vp->height = w->height = newh;
02542 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
02543 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
02544 continue;
02545 }
02546
02547
02548
02549 switch (w->window_class) {
02550 case WC_MAIN_TOOLBAR:
02551 if (neww - w->width != 0) {
02552 ResizeWindow(w, min(neww, 640) - w->width, 0);
02553 w->OnResize();
02554 }
02555
02556 top = w->top;
02557 left = PositionMainToolbar(w);
02558 break;
02559
02560 case WC_SELECT_GAME:
02561 case WC_GAME_OPTIONS:
02562 case WC_NETWORK_WINDOW:
02563 top = (newh - w->height) >> 1;
02564 left = (neww - w->width) >> 1;
02565 break;
02566
02567 case WC_NEWS_WINDOW:
02568 top = newh - w->height;
02569 left = (neww - w->width) >> 1;
02570 break;
02571
02572 case WC_STATUS_BAR:
02573 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
02574 top = newh - w->height;
02575 left = (neww - w->width) >> 1;
02576 break;
02577
02578 case WC_SEND_NETWORK_MSG:
02579 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
02580 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
02581 left = (neww - w->width) >> 1;
02582 break;
02583
02584 case WC_CONSOLE:
02585 IConsoleResize(w);
02586 continue;
02587
02588 default: {
02589 left = w->left;
02590 if (left + (w->width >> 1) >= neww) left = neww - w->width;
02591 if (left < 0) left = 0;
02592
02593 top = w->top;
02594 if (top + (w->height >> 1) >= newh) top = newh - w->height;
02595
02596 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
02597 if (wt != NULL) {
02598 if (top < wt->height && wt->left < (w->left + w->width) && (wt->left + wt->width) > w->left) top = wt->height;
02599 if (top >= newh) top = newh - 1;
02600 } else {
02601 if (top < 0) top = 0;
02602 }
02603 } break;
02604 }
02605
02606 if (w->viewport != NULL) {
02607 w->viewport->left += left - w->left;
02608 w->viewport->top += top - w->top;
02609 }
02610
02611 w->left = left;
02612 w->top = top;
02613 }
02614 }
02615
02620 PickerWindowBase::~PickerWindowBase()
02621 {
02622 this->window_class = WC_INVALID;
02623 ResetObjectToPlace();
02624 }