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