00001
00002
00005 #include "stdafx.h"
00006 #include <stdarg.h>
00007 #include "openttd.h"
00008 #include "company_func.h"
00009 #include "gfx_func.h"
00010 #include "console_func.h"
00011 #include "console_gui.h"
00012 #include "viewport_func.h"
00013 #include "variables.h"
00014 #include "genworld.h"
00015 #include "blitter/factory.hpp"
00016 #include "zoom_func.h"
00017 #include "map_func.h"
00018 #include "vehicle_base.h"
00019 #include "settings_type.h"
00020 #include "cheat_type.h"
00021 #include "window_func.h"
00022 #include "tilehighlight_func.h"
00023 #include "network/network.h"
00024 #include "querystring_gui.h"
00025 #include "widgets/dropdown_func.h"
00026
00027 #include "table/sprites.h"
00028
00029 static Point _drag_delta;
00030 static Window *_mouseover_last_w = NULL;
00031
00033 Window *_z_front_window = NULL;
00035 Window *_z_back_window = NULL;
00036
00037
00038
00039
00040
00041
00042 Window *_focused_window;
00043
00044 Point _cursorpos_drag_start;
00045
00046 int _scrollbar_start_pos;
00047 int _scrollbar_size;
00048 byte _scroller_click_timeout;
00049
00050 bool _scrolling_scrollbar;
00051 bool _scrolling_viewport;
00052
00053 byte _special_mouse_mode;
00054
00056 WindowDesc::WindowDesc(int16 left, int16 top, int16 min_width, int16 min_height, int16 def_width, int16 def_height,
00057 WindowClass window_class, WindowClass parent_class, uint32 flags, const Widget *widgets)
00058 {
00059 this->left = left;
00060 this->top = top;
00061 this->minimum_width = min_width;
00062 this->minimum_height = min_height;
00063 this->default_width = def_width;
00064 this->default_height = def_height;
00065 this->cls = window_class;
00066 this->parent_cls = parent_class;
00067 this->flags = flags;
00068 this->widgets = widgets;
00069 }
00070
00071
00076 void SetFocusedWindow(Window *w)
00077 {
00078 if (_focused_window == w) return;
00079
00080
00081 if (_focused_window != NULL && _focused_window->focused_widget != NULL) {
00082 uint focused_widget_id = _focused_window->focused_widget - _focused_window->widget;
00083 _focused_window->InvalidateWidget(focused_widget_id);
00084 }
00085
00086
00087 Window *old_focused = _focused_window;
00088 _focused_window = w;
00089
00090
00091 if (old_focused != NULL) old_focused->OnFocusLost();
00092 if (_focused_window != NULL) _focused_window->OnFocus();
00093 }
00094
00099 const Widget *GetGloballyFocusedWidget()
00100 {
00101 return _focused_window != NULL ? _focused_window->focused_widget : NULL;
00102 }
00103
00109 bool EditBoxInGlobalFocus()
00110 {
00111 const Widget *wi = GetGloballyFocusedWidget();
00112
00113
00114 return (wi != NULL && wi->type == WWT_EDITBOX) ||
00115 (_focused_window != NULL && _focused_window->window_class == WC_CONSOLE);
00116 }
00117
00125 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
00126 {
00127 va_list wdg_list;
00128
00129 va_start(wdg_list, widgets);
00130
00131 while (widgets != WIDGET_LIST_END) {
00132 SetWidgetDisabledState(widgets, disab_stat);
00133 widgets = va_arg(wdg_list, int);
00134 }
00135
00136 va_end(wdg_list);
00137 }
00138
00146 void CDECL Window::SetWidgetsHiddenState(bool hidden_stat, int widgets, ...)
00147 {
00148 va_list wdg_list;
00149
00150 va_start(wdg_list, widgets);
00151
00152 while (widgets != WIDGET_LIST_END) {
00153 SetWidgetHiddenState(widgets, hidden_stat);
00154 widgets = va_arg(wdg_list, int);
00155 }
00156
00157 va_end(wdg_list);
00158 }
00159
00165 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
00166 {
00167 va_list wdg_list;
00168
00169 va_start(wdg_list, widgets);
00170
00171 while (widgets != WIDGET_LIST_END) {
00172 SetWidgetLoweredState(widgets, lowered_stat);
00173 widgets = va_arg(wdg_list, int);
00174 }
00175
00176 va_end(wdg_list);
00177 }
00178
00182 void Window::RaiseButtons()
00183 {
00184 for (uint i = 0; i < this->widget_count; i++) {
00185 if (this->IsWidgetLowered(i)) {
00186 this->RaiseWidget(i);
00187 this->InvalidateWidget(i);
00188 }
00189 }
00190 }
00191
00196 void Window::InvalidateWidget(byte widget_index) const
00197 {
00198 const Widget *wi = &this->widget[widget_index];
00199
00200
00201 if (wi->type == WWT_EMPTY || IsWidgetHidden(widget_index)) return;
00202
00203 SetDirtyBlocks(this->left + wi->left, this->top + wi->top, this->left + wi->right + 1, this->top + wi->bottom + 1);
00204 }
00205
00211 void Window::HandleButtonClick(byte widget)
00212 {
00213 this->LowerWidget(widget);
00214 this->flags4 |= WF_TIMEOUT_BEGIN;
00215 this->InvalidateWidget(widget);
00216 }
00217
00222 bool Window::HasWidgetOfType(WidgetType widget_type) const
00223 {
00224 for (uint i = 0; i < this->widget_count; i++) {
00225 if (this->widget[i].type == widget_type) return true;
00226 }
00227 return false;
00228 }
00229
00230 static void StartWindowDrag(Window *w);
00231 static void StartWindowSizing(Window *w);
00232
00240 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click)
00241 {
00242 bool focused_widget_changed = false;
00243 int widget = 0;
00244 if (w->desc_flags & WDF_DEF_WIDGET) {
00245 widget = GetWidgetFromPos(w, x, y);
00246
00247
00248 if (_focused_window != w &&
00249 (w->desc_flags & WDF_NO_FOCUS) == 0 &&
00250 !(w->desc_flags & WDF_STD_BTN && widget == 0)) {
00251 focused_widget_changed = true;
00252 if (_focused_window != NULL) {
00253 _focused_window->OnFocusLost();
00254
00255
00256 if (w->window_class != WC_OSK) DeleteWindowById(WC_OSK, 0);
00257 }
00258 SetFocusedWindow(w);
00259 w->OnFocus();
00260 }
00261
00262 if (widget < 0) return;
00263
00264
00265 if (w->IsWidgetDisabled(widget)) return;
00266
00267 const Widget *wi = &w->widget[widget];
00268
00269
00270
00271 if (wi->type != WWT_CAPTION) {
00272
00273 if (w->focused_widget && w->focused_widget->type == WWT_EDITBOX &&
00274 w->focused_widget != wi &&
00275 w->window_class != WC_OSK) {
00276 DeleteWindowById(WC_OSK, 0);
00277 }
00278
00279 if (w->focused_widget != wi) {
00280
00281 if (w->focused_widget) w->InvalidateWidget(w->focused_widget - w->widget);
00282 focused_widget_changed = true;
00283 w->focused_widget = wi;
00284 }
00285 }
00286
00287 if (wi->type & WWB_MASK) {
00288
00289 switch (wi->type) {
00290 default: NOT_REACHED();
00291 case WWT_PANEL | WWB_PUSHBUTTON:
00292 case WWT_IMGBTN | WWB_PUSHBUTTON:
00293 case WWT_TEXTBTN | WWB_PUSHBUTTON:
00294 w->HandleButtonClick(widget);
00295 break;
00296 }
00297 } else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
00298 ScrollbarClickHandler(w, wi, x, y);
00299 } else if (wi->type == WWT_EDITBOX && !focused_widget_changed) {
00300
00301 QueryStringBaseWindow *qs = dynamic_cast<QueryStringBaseWindow*>(w);
00302 if (qs != NULL) {
00303 const int widget_index = wi - w->widget;
00304 qs->OnOpenOSKWindow(widget_index);
00305 }
00306 }
00307
00308
00309
00310 if (HideDropDownMenu(w) == widget) return;
00311
00312 if (w->desc_flags & WDF_STD_BTN) {
00313 if (widget == 0) {
00314 delete w;
00315 return;
00316 }
00317
00318 if (widget == 1) {
00319 StartWindowDrag(w);
00320 return;
00321 }
00322 }
00323
00324 if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
00325 StartWindowSizing(w);
00326 w->InvalidateWidget(widget);
00327 return;
00328 }
00329
00330 if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) {
00331 w->flags4 ^= WF_STICKY;
00332 w->InvalidateWidget(widget);
00333 return;
00334 }
00335 }
00336
00337 Point pt = { x, y };
00338
00339 if (double_click) {
00340 w->OnDoubleClick(pt, widget);
00341 } else {
00342 w->OnClick(pt, widget);
00343 }
00344 }
00345
00352 static void DispatchRightClickEvent(Window *w, int x, int y)
00353 {
00354 int widget = 0;
00355
00356
00357 if (w->desc_flags & WDF_STD_TOOLTIPS) {
00358 widget = GetWidgetFromPos(w, x, y);
00359 if (widget < 0) return;
00360
00361 if (w->widget[widget].tooltips != 0) {
00362 GuiShowTooltips(w->widget[widget].tooltips);
00363 return;
00364 }
00365 }
00366
00367 Point pt = { x, y };
00368 w->OnRightClick(pt, widget);
00369 }
00370
00378 static void DispatchMouseWheelEvent(Window *w, int widget, int wheel)
00379 {
00380 if (widget < 0) return;
00381
00382 const Widget *wi1 = &w->widget[widget];
00383 const Widget *wi2 = &w->widget[widget + 1];
00384
00385
00386
00387
00388
00389 Scrollbar *sb;
00390 if ((sb = &w->vscroll, wi1->type == WWT_SCROLLBAR) || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR) ||
00391 (sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) {
00392
00393 if (sb->count > sb->cap) {
00394 int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap);
00395 if (pos != sb->pos) {
00396 sb->pos = pos;
00397 w->SetDirty();
00398 }
00399 }
00400 }
00401 }
00402
00415 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00416 {
00417 const Window *v;
00418 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00419 if (right > v->left &&
00420 bottom > v->top &&
00421 left < v->left + v->width &&
00422 top < v->top + v->height) {
00423
00424 int x;
00425
00426 if (left < (x = v->left)) {
00427 DrawOverlappedWindow(w, left, top, x, bottom);
00428 DrawOverlappedWindow(w, x, top, right, bottom);
00429 return;
00430 }
00431
00432 if (right > (x = v->left + v->width)) {
00433 DrawOverlappedWindow(w, left, top, x, bottom);
00434 DrawOverlappedWindow(w, x, top, right, bottom);
00435 return;
00436 }
00437
00438 if (top < (x = v->top)) {
00439 DrawOverlappedWindow(w, left, top, right, x);
00440 DrawOverlappedWindow(w, left, x, right, bottom);
00441 return;
00442 }
00443
00444 if (bottom > (x = v->top + v->height)) {
00445 DrawOverlappedWindow(w, left, top, right, x);
00446 DrawOverlappedWindow(w, left, x, right, bottom);
00447 return;
00448 }
00449
00450 return;
00451 }
00452 }
00453
00454
00455 DrawPixelInfo *dp = _cur_dpi;
00456 dp->width = right - left;
00457 dp->height = bottom - top;
00458 dp->left = left - w->left;
00459 dp->top = top - w->top;
00460 dp->pitch = _screen.pitch;
00461 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00462 dp->zoom = ZOOM_LVL_NORMAL;
00463 w->OnPaint();
00464 }
00465
00474 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00475 {
00476 Window *w;
00477 DrawPixelInfo bk;
00478 _cur_dpi = &bk;
00479
00480 FOR_ALL_WINDOWS_FROM_BACK(w) {
00481 if (right > w->left &&
00482 bottom > w->top &&
00483 left < w->left + w->width &&
00484 top < w->top + w->height) {
00485
00486 DrawOverlappedWindow(w, left, top, right, bottom);
00487 }
00488 }
00489 }
00490
00495 void Window::SetDirty() const
00496 {
00497 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00498 }
00499
00505 void SetWindowDirty(const Window *w)
00506 {
00507 if (w != NULL) w->SetDirty();
00508 }
00509
00513 static Window *FindChildWindow(const Window *w)
00514 {
00515 Window *v;
00516 FOR_ALL_WINDOWS_FROM_BACK(v) {
00517 if (v->parent == w) return v;
00518 }
00519
00520 return NULL;
00521 }
00522
00526 void Window::DeleteChildWindows() const
00527 {
00528 Window *child = FindChildWindow(this);
00529 while (child != NULL) {
00530 delete child;
00531 child = FindChildWindow(this);
00532 }
00533 }
00534
00538 Window::~Window()
00539 {
00540 if (_thd.place_mode != VHM_NONE &&
00541 _thd.window_class == this->window_class &&
00542 _thd.window_number == this->window_number) {
00543 ResetObjectToPlace();
00544 }
00545
00546
00547 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00548
00549
00550 if (_focused_window == this) _focused_window = NULL;
00551
00552 this->DeleteChildWindows();
00553
00554 if (this->viewport != NULL) DeleteWindowViewport(this);
00555
00556 this->SetDirty();
00557
00558 free(this->widget);
00559
00560 this->window_class = WC_INVALID;
00561 }
00562
00569 Window *FindWindowById(WindowClass cls, WindowNumber number)
00570 {
00571 Window *w;
00572 FOR_ALL_WINDOWS_FROM_BACK(w) {
00573 if (w->window_class == cls && w->window_number == number) return w;
00574 }
00575
00576 return NULL;
00577 }
00578
00585 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00586 {
00587 Window *w = FindWindowById(cls, number);
00588 if (force || w == NULL ||
00589 (w->desc_flags & WDF_STICKY_BUTTON) == 0 ||
00590 (w->flags4 & WF_STICKY) == 0) {
00591 delete w;
00592 }
00593 }
00594
00599 void DeleteWindowByClass(WindowClass cls)
00600 {
00601 Window *w;
00602
00603 restart_search:
00604
00605
00606
00607 FOR_ALL_WINDOWS_FROM_BACK(w) {
00608 if (w->window_class == cls) {
00609 delete w;
00610 goto restart_search;
00611 }
00612 }
00613 }
00614
00619 void DeleteCompanyWindows(CompanyID id)
00620 {
00621 Window *w;
00622
00623 restart_search:
00624
00625
00626
00627 FOR_ALL_WINDOWS_FROM_BACK(w) {
00628 if (w->owner == id) {
00629 delete w;
00630 goto restart_search;
00631 }
00632 }
00633
00634
00635 DeleteWindowById(WC_BUY_COMPANY, id);
00636 }
00637
00643 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00644 {
00645 Window *w;
00646 FOR_ALL_WINDOWS_FROM_BACK(w) {
00647 if (w->owner != old_owner) continue;
00648
00649 switch (w->window_class) {
00650 case WC_COMPANY_COLOUR:
00651 case WC_FINANCES:
00652 case WC_STATION_LIST:
00653 case WC_TRAINS_LIST:
00654 case WC_ROADVEH_LIST:
00655 case WC_SHIPS_LIST:
00656 case WC_AIRCRAFT_LIST:
00657 case WC_BUY_COMPANY:
00658 case WC_COMPANY:
00659 continue;
00660
00661 default:
00662 w->owner = new_owner;
00663 break;
00664 }
00665 }
00666 }
00667
00668 static void BringWindowToFront(Window *w);
00669
00675 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00676 {
00677 Window *w = FindWindowById(cls, number);
00678
00679 if (w != NULL) {
00680 w->flags4 |= WF_WHITE_BORDER_MASK;
00681 BringWindowToFront(w);
00682 w->SetDirty();
00683 }
00684
00685 return w;
00686 }
00687
00688 static inline bool IsVitalWindow(const Window *w)
00689 {
00690 switch (w->window_class) {
00691 case WC_MAIN_TOOLBAR:
00692 case WC_STATUS_BAR:
00693 case WC_NEWS_WINDOW:
00694 case WC_SEND_NETWORK_MSG:
00695 return true;
00696
00697 default:
00698 return false;
00699 }
00700 }
00701
00710 static void BringWindowToFront(Window *w)
00711 {
00712 Window *v = _z_front_window;
00713
00714
00715 for (; v != NULL && v != w && IsVitalWindow(v); v = v->z_back) { }
00716
00717 if (v == NULL || w == v) return;
00718
00719
00720 assert(w != _z_front_window);
00721
00722 if (w->z_back == NULL) {
00723 _z_back_window = w->z_front;
00724 } else {
00725 w->z_back->z_front = w->z_front;
00726 }
00727 w->z_front->z_back = w->z_back;
00728
00729 w->z_front = v->z_front;
00730 w->z_back = v;
00731
00732 if (v->z_front == NULL) {
00733 _z_front_window = w;
00734 } else {
00735 v->z_front->z_back = w;
00736 }
00737 v->z_front = w;
00738
00739 w->SetDirty();
00740 }
00741
00752 static void AssignWidgetToWindow(Window *w, const Widget *widget)
00753 {
00754 if (widget != NULL) {
00755 uint index = 1;
00756
00757 for (const Widget *wi = widget; wi->type != WWT_LAST; wi++) index++;
00758
00759 w->widget = MallocT<Widget>(index);
00760 memcpy(w->widget, widget, sizeof(*w->widget) * index);
00761 w->widget_count = index - 1;
00762 } else {
00763 w->widget = NULL;
00764 w->widget_count = 0;
00765 }
00766 }
00767
00782 void Window::Initialize(int x, int y, int min_width, int min_height,
00783 WindowClass cls, const Widget *widget, int window_number)
00784 {
00785
00786 this->window_class = cls;
00787 this->flags4 = WF_WHITE_BORDER_MASK;
00788 this->owner = INVALID_OWNER;
00789 this->left = x;
00790 this->top = y;
00791 this->width = min_width;
00792 this->height = min_height;
00793 AssignWidgetToWindow(this, widget);
00794 this->focused_widget = 0;
00795 this->resize.width = min_width;
00796 this->resize.height = min_height;
00797 this->resize.step_width = 1;
00798 this->resize.step_height = 1;
00799 this->window_number = window_number;
00800
00801
00802
00803
00804 if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->HasWidgetOfType(WWT_EDITBOX))) SetFocusedWindow(this);
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814 Window *w = _z_front_window;
00815 if (w != NULL && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) {
00816 if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) w = w->z_back;
00817 if (FindWindowById(WC_STATUS_BAR, 0) != NULL) w = w->z_back;
00818 if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) w = w->z_back;
00819 if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) w = w->z_back;
00820
00821 if (w == NULL) {
00822 _z_back_window->z_front = this;
00823 this->z_back = _z_back_window;
00824 _z_back_window = this;
00825 } else {
00826 if (w->z_front == NULL) {
00827 _z_front_window = this;
00828 } else {
00829 this->z_front = w->z_front;
00830 w->z_front->z_back = this;
00831 }
00832
00833 this->z_back = w;
00834 w->z_front = this;
00835 }
00836 } else {
00837 this->z_back = _z_front_window;
00838 if (_z_front_window != NULL) {
00839 _z_front_window->z_front = this;
00840 } else {
00841 _z_back_window = this;
00842 }
00843 _z_front_window = this;
00844 }
00845 }
00846
00857 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
00858 {
00859
00860
00861
00862
00863
00864 if (this->width != def_width || this->height != def_height) {
00865
00866 int free_height = _screen.height;
00867 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
00868 if (wt != NULL) free_height -= wt->height;
00869 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
00870 if (wt != NULL) free_height -= wt->height;
00871
00872 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
00873 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
00874
00875
00876
00877
00878 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
00879 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
00880
00881 ResizeWindow(this, enlarge_x, enlarge_y);
00882
00883 Point size;
00884 Point diff;
00885 size.x = this->width;
00886 size.y = this->height;
00887 diff.x = enlarge_x;
00888 diff.y = enlarge_y;
00889 this->OnResize(size, diff);
00890 }
00891
00892 int nx = this->left;
00893 int ny = this->top;
00894
00895 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
00896
00897 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
00898 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
00899 nx = max(nx, 0);
00900
00901 if (this->viewport != NULL) {
00902 this->viewport->left += nx - this->left;
00903 this->viewport->top += ny - this->top;
00904 }
00905 this->left = nx;
00906 this->top = ny;
00907
00908 this->SetDirty();
00909 }
00910
00915 void Window::FindWindowPlacementAndResize(const WindowDesc *desc)
00916 {
00917 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
00918 }
00919
00933 Window::Window(int x, int y, int width, int height, WindowClass cls, const Widget *widget)
00934 {
00935 this->Initialize(x, y, width, height, cls, widget, 0);
00936 }
00937
00949 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
00950 {
00951 int right = width + left;
00952 int bottom = height + top;
00953
00954 if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height) return false;
00955
00956
00957 const Window *w;
00958 FOR_ALL_WINDOWS_FROM_BACK(w) {
00959 if (w->window_class == WC_MAIN_WINDOW) continue;
00960
00961 if (right > w->left &&
00962 w->left + w->width > left &&
00963 bottom > w->top &&
00964 w->top + w->height > top) {
00965 return false;
00966 }
00967 }
00968
00969 pos.x = left;
00970 pos.y = top;
00971 return true;
00972 }
00973
00985 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
00986 {
00987
00988
00989
00990 if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
00991
00992 if (top < 22 || top > _screen.height - (height>>2)) return false;
00993
00994
00995 const Window *w;
00996 FOR_ALL_WINDOWS_FROM_BACK(w) {
00997 if (w->window_class == WC_MAIN_WINDOW) continue;
00998
00999 if (left + width > w->left &&
01000 w->left + w->width > left &&
01001 top + height > w->top &&
01002 w->top + w->height > top) {
01003 return false;
01004 }
01005 }
01006
01007 pos.x = left;
01008 pos.y = top;
01009 return true;
01010 }
01011
01018 static Point GetAutoPlacePosition(int width, int height)
01019 {
01020 Point pt;
01021
01022
01023 if (IsGoodAutoPlace1(0, 24, width, height, pt)) return pt;
01024
01025
01026
01027
01028
01029 const Window *w;
01030 FOR_ALL_WINDOWS_FROM_BACK(w) {
01031 if (w->window_class == WC_MAIN_WINDOW) continue;
01032
01033 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01034 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01035 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01036 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01037 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01038 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01039 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01040 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01041 }
01042
01043
01044
01045
01046
01047 FOR_ALL_WINDOWS_FROM_BACK(w) {
01048 if (w->window_class == WC_MAIN_WINDOW) continue;
01049
01050 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01051 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01052 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01053 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01054 }
01055
01056
01057
01058
01059 int left = 0, top = 24;
01060
01061 restart:
01062 FOR_ALL_WINDOWS_FROM_BACK(w) {
01063 if (w->left == left && w->top == top) {
01064 left += 5;
01065 top += 5;
01066 goto restart;
01067 }
01068 }
01069
01070 pt.x = left;
01071 pt.y = top;
01072 return pt;
01073 }
01074
01090 static Point LocalGetWindowPlacement(const WindowDesc *desc, int window_number)
01091 {
01092 Point pt;
01093 Window *w;
01094
01095 if (desc->parent_cls != 0 &&
01096 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01097 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01098
01099 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01100 if (pt.x > _screen.width + 10 - desc->default_width) {
01101 pt.x = (_screen.width + 10 - desc->default_width) - 20;
01102 }
01103 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 36 : 10);
01104 } else {
01105 switch (desc->left) {
01106 case WDP_ALIGN_TBR:
01107 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01108 pt.x = (w->left + w->width) - desc->default_width;
01109 break;
01110
01111 case WDP_ALIGN_TBL:
01112 pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left;
01113 break;
01114
01115 case WDP_AUTO:
01116 return GetAutoPlacePosition(desc->default_width, desc->default_height);
01117
01118 case WDP_CENTER:
01119 pt.x = (_screen.width - desc->default_width) / 2;
01120 break;
01121
01122 default:
01123 pt.x = desc->left;
01124 if (pt.x < 0) pt.x += _screen.width;
01125 }
01126
01127 switch (desc->top) {
01128 case WDP_CENTER:
01129 pt.y = (_screen.height - desc->default_height) / 2;
01130 break;
01131
01132
01133
01134 case WDP_AUTO:
01135 NOT_REACHED();
01136 assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO);
01137
01138
01139 default:
01140 pt.y = desc->top;
01141 if (pt.y < 0) pt.y += _screen.height;
01142 break;
01143 }
01144 }
01145
01146 return pt;
01147 }
01148
01157 Window::Window(const WindowDesc *desc, WindowNumber window_number)
01158 {
01159 Point pt = LocalGetWindowPlacement(desc, window_number);
01160 this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->cls, desc->widgets, window_number);
01161 this->desc_flags = desc->flags;
01162 }
01163
01169 Window *FindWindowFromPt(int x, int y)
01170 {
01171 Window *w;
01172 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01173 if (IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01174 return w;
01175 }
01176 }
01177
01178 return NULL;
01179 }
01180
01184 void InitWindowSystem()
01185 {
01186 IConsoleClose();
01187
01188 _z_back_window = NULL;
01189 _z_front_window = NULL;
01190 _focused_window = NULL;
01191 _mouseover_last_w = NULL;
01192 _scrolling_viewport = 0;
01193 }
01194
01198 void UnInitWindowSystem()
01199 {
01200 Window *w;
01201 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01202
01203 for (w = _z_front_window; w != NULL; ) {
01204 Window *to_del = w;
01205 w = w->z_back;
01206 free(to_del);
01207 }
01208
01209 _z_front_window = NULL;
01210 _z_back_window = NULL;
01211 }
01212
01216 void ResetWindowSystem()
01217 {
01218 UnInitWindowSystem();
01219 InitWindowSystem();
01220 _thd.pos.x = 0;
01221 _thd.pos.y = 0;
01222 _thd.new_pos.x = 0;
01223 _thd.new_pos.y = 0;
01224 }
01225
01226 static void DecreaseWindowCounters()
01227 {
01228 Window *w;
01229 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01230
01231 if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
01232 w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
01233 w->SetDirty();
01234 }
01235 w->OnMouseLoop();
01236 }
01237
01238 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01239 if (w->flags4 & WF_TIMEOUT_MASK && !(--w->flags4 & WF_TIMEOUT_MASK)) {
01240 w->OnTimeout();
01241 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons();
01242 }
01243 }
01244 }
01245
01246 Window *GetCallbackWnd()
01247 {
01248 return FindWindowById(_thd.window_class, _thd.window_number);
01249 }
01250
01251 static void HandlePlacePresize()
01252 {
01253 if (_special_mouse_mode != WSM_PRESIZE) return;
01254
01255 Window *w = GetCallbackWnd();
01256 if (w == NULL) return;
01257
01258 Point pt = GetTileBelowCursor();
01259 if (pt.x == -1) {
01260 _thd.selend.x = -1;
01261 return;
01262 }
01263
01264 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01265 }
01266
01267 static bool HandleDragDrop()
01268 {
01269 if (_special_mouse_mode != WSM_DRAGDROP) return true;
01270 if (_left_button_down) return false;
01271
01272 Window *w = GetCallbackWnd();
01273
01274 if (w != NULL) {
01275
01276 Point pt;
01277 pt.x = _cursor.pos.x - w->left;
01278 pt.y = _cursor.pos.y - w->top;
01279 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01280 }
01281
01282 ResetObjectToPlace();
01283
01284 return false;
01285 }
01286
01287 static bool HandleMouseOver()
01288 {
01289 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01290
01291
01292 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01293
01294 Point pt = { -1, -1 };
01295 _mouseover_last_w->OnMouseOver(pt, 0);
01296 }
01297
01298
01299 _mouseover_last_w = w;
01300
01301 if (w != NULL) {
01302
01303 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01304 int widget = 0;
01305 if (w->widget != NULL) {
01306 widget = GetWidgetFromPos(w, pt.x, pt.y);
01307 }
01308 w->OnMouseOver(pt, widget);
01309 }
01310
01311
01312 return true;
01313 }
01314
01324 void ResizeWindow(Window *w, int x, int y)
01325 {
01326 bool resize_height = false;
01327 bool resize_width = false;
01328
01329 if (x == 0 && y == 0) return;
01330
01331 w->SetDirty();
01332 for (Widget *wi = w->widget; wi->type != WWT_LAST; wi++) {
01333
01334 byte rsizeflag = GB(wi->display_flags, 0, 4);
01335
01336 if (rsizeflag == RESIZE_NONE) continue;
01337
01338
01339 if (rsizeflag & RESIZE_LEFT) {
01340 wi->left += x;
01341 resize_width = true;
01342 }
01343
01344 if (rsizeflag & RESIZE_RIGHT) {
01345 wi->right += x;
01346 resize_width = true;
01347 }
01348
01349 if (rsizeflag & RESIZE_TOP) {
01350 wi->top += y;
01351 resize_height = true;
01352 }
01353
01354 if (rsizeflag & RESIZE_BOTTOM) {
01355 wi->bottom += y;
01356 resize_height = true;
01357 }
01358 }
01359
01360
01361 if (resize_width) w->width += x;
01362 if (resize_height) w->height += y;
01363
01364 w->SetDirty();
01365 }
01366
01367 static bool _dragging_window;
01368
01369 static bool HandleWindowDragging()
01370 {
01371
01372 if (!_dragging_window) return true;
01373
01374
01375 Window *w;
01376 FOR_ALL_WINDOWS_FROM_BACK(w) {
01377 if (w->flags4 & WF_DRAGGING) {
01378 const Widget *t = &w->widget[1];
01379
01380
01381 if (!_left_button_down) {
01382 w->flags4 &= ~WF_DRAGGING;
01383 break;
01384 }
01385
01386 w->SetDirty();
01387
01388 int x = _cursor.pos.x + _drag_delta.x;
01389 int y = _cursor.pos.y + _drag_delta.y;
01390 int nx = x;
01391 int ny = y;
01392
01393 if (_settings_client.gui.window_snap_radius != 0) {
01394 const Window *v;
01395
01396 int hsnap = _settings_client.gui.window_snap_radius;
01397 int vsnap = _settings_client.gui.window_snap_radius;
01398 int delta;
01399
01400 FOR_ALL_WINDOWS_FROM_BACK(v) {
01401 if (v == w) continue;
01402
01403 if (y + w->height > v->top && y < v->top + v->height) {
01404
01405 delta = abs(v->left + v->width - x);
01406 if (delta <= hsnap) {
01407 nx = v->left + v->width;
01408 hsnap = delta;
01409 }
01410
01411
01412 delta = abs(v->left - x - w->width);
01413 if (delta <= hsnap) {
01414 nx = v->left - w->width;
01415 hsnap = delta;
01416 }
01417 }
01418
01419 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01420
01421 delta = abs(v->left - x);
01422 if (delta <= hsnap) {
01423 nx = v->left;
01424 hsnap = delta;
01425 }
01426
01427
01428 delta = abs(v->left + v->width - x - w->width);
01429 if (delta <= hsnap) {
01430 nx = v->left + v->width - w->width;
01431 hsnap = delta;
01432 }
01433 }
01434
01435 if (x + w->width > v->left && x < v->left + v->width) {
01436
01437 delta = abs(v->top + v->height - y);
01438 if (delta <= vsnap) {
01439 ny = v->top + v->height;
01440 vsnap = delta;
01441 }
01442
01443
01444 delta = abs(v->top - y - w->height);
01445 if (delta <= vsnap) {
01446 ny = v->top - w->height;
01447 vsnap = delta;
01448 }
01449 }
01450
01451 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01452
01453 delta = abs(v->top - y);
01454 if (delta <= vsnap) {
01455 ny = v->top;
01456 vsnap = delta;
01457 }
01458
01459
01460 delta = abs(v->top + v->height - y - w->height);
01461 if (delta <= vsnap) {
01462 ny = v->top + v->height - w->height;
01463 vsnap = delta;
01464 }
01465 }
01466 }
01467 }
01468
01469
01470
01471 nx = Clamp(nx, 13 - t->right, _screen.width - 13 - t->left);
01472 ny = Clamp(ny, 0, _screen.height - 13);
01473
01474
01475 Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0);
01476 if (v != NULL) {
01477 int v_bottom = v->top + v->height;
01478 int v_right = v->left + v->width;
01479 if (ny + t->top >= v->top && ny + t->top < v_bottom) {
01480 if ((v->left < 13 && nx + t->left < v->left) ||
01481 (v_right > _screen.width - 13 && nx + t->right > v_right)) {
01482 ny = v_bottom;
01483 } else {
01484 if (nx + t->left > v->left - 13 &&
01485 nx + t->right < v_right + 13) {
01486 if (w->top >= v_bottom) {
01487 ny = v_bottom;
01488 } else if (w->left < nx) {
01489 nx = v->left - 13 - t->left;
01490 } else {
01491 nx = v_right + 13 - t->right;
01492 }
01493 }
01494 }
01495 }
01496 }
01497
01498 if (w->viewport != NULL) {
01499 w->viewport->left += nx - w->left;
01500 w->viewport->top += ny - w->top;
01501 }
01502 w->left = nx;
01503 w->top = ny;
01504
01505 w->SetDirty();
01506 return false;
01507 } else if (w->flags4 & WF_SIZING) {
01508 int x, y;
01509
01510
01511 if (!_left_button_down) {
01512 w->flags4 &= ~WF_SIZING;
01513 w->SetDirty();
01514 break;
01515 }
01516
01517 x = _cursor.pos.x - _drag_delta.x;
01518 y = _cursor.pos.y - _drag_delta.y;
01519
01520
01521
01522
01523 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
01524
01525 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
01526
01527
01528 if ((int)w->width + x < (int)w->resize.width)
01529 x = w->resize.width - w->width;
01530 if ((int)w->height + y < (int)w->resize.height)
01531 y = w->resize.height - w->height;
01532
01533
01534 if (x == 0 && y == 0) return false;
01535
01536
01537
01538 _drag_delta.x += x;
01539 _drag_delta.y += y;
01540
01541
01542 ResizeWindow(w, x, y);
01543
01544 Point size;
01545 Point diff;
01546 size.x = x + w->width;
01547 size.y = y + w->height;
01548 diff.x = x;
01549 diff.y = y;
01550 w->OnResize(size, diff);
01551 return false;
01552 }
01553 }
01554
01555 _dragging_window = false;
01556 return false;
01557 }
01558
01563 static void StartWindowDrag(Window *w)
01564 {
01565 w->flags4 |= WF_DRAGGING;
01566 _dragging_window = true;
01567
01568 _drag_delta.x = w->left - _cursor.pos.x;
01569 _drag_delta.y = w->top - _cursor.pos.y;
01570
01571 BringWindowToFront(w);
01572 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01573 }
01574
01579 static void StartWindowSizing(Window *w)
01580 {
01581 w->flags4 |= WF_SIZING;
01582 _dragging_window = true;
01583
01584 _drag_delta.x = _cursor.pos.x;
01585 _drag_delta.y = _cursor.pos.y;
01586
01587 BringWindowToFront(w);
01588 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01589 }
01590
01591
01592 static bool HandleScrollbarScrolling()
01593 {
01594 Window *w;
01595
01596
01597 if (!_scrolling_scrollbar) return true;
01598
01599
01600 FOR_ALL_WINDOWS_FROM_BACK(w) {
01601 if (w->flags4 & WF_SCROLL_MIDDLE) {
01602
01603 if (!_left_button_down) {
01604 w->flags4 &= ~WF_SCROLL_MIDDLE;
01605 w->SetDirty();
01606 break;
01607 }
01608
01609 int i;
01610 Scrollbar *sb;
01611
01612 if (w->flags4 & WF_HSCROLL) {
01613 sb = &w->hscroll;
01614 i = _cursor.pos.x - _cursorpos_drag_start.x;
01615 } else if (w->flags4 & WF_SCROLL2){
01616 sb = &w->vscroll2;
01617 i = _cursor.pos.y - _cursorpos_drag_start.y;
01618 } else {
01619 sb = &w->vscroll;
01620 i = _cursor.pos.y - _cursorpos_drag_start.y;
01621 }
01622
01623
01624 int pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap));
01625 if (pos != sb->pos) {
01626 sb->pos = pos;
01627 w->SetDirty();
01628 }
01629 return false;
01630 }
01631 }
01632
01633 _scrolling_scrollbar = false;
01634 return false;
01635 }
01636
01637 static bool HandleViewportScroll()
01638 {
01639 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
01640
01641 if (!_scrolling_viewport) return true;
01642
01643 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01644
01645 if (!(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) || w == NULL) {
01646 _cursor.fix_at = false;
01647 _scrolling_viewport = false;
01648 return true;
01649 }
01650
01651 if (w == FindWindowById(WC_MAIN_WINDOW, 0) && w->viewport->follow_vehicle != INVALID_VEHICLE) {
01652
01653 const Vehicle *veh = GetVehicle(w->viewport->follow_vehicle);
01654 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
01655 return true;
01656 }
01657
01658 Point delta;
01659 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
01660 delta.x = -_cursor.delta.x;
01661 delta.y = -_cursor.delta.y;
01662 } else {
01663 delta.x = _cursor.delta.x;
01664 delta.y = _cursor.delta.y;
01665 }
01666
01667 if (scrollwheel_scrolling) {
01668
01669 delta.x = _cursor.h_wheel;
01670 delta.y = _cursor.v_wheel;
01671 _cursor.v_wheel = 0;
01672 _cursor.h_wheel = 0;
01673 }
01674
01675
01676 w->OnScroll(delta);
01677
01678 _cursor.delta.x = 0;
01679 _cursor.delta.y = 0;
01680 return false;
01681 }
01682
01691 static bool MaybeBringWindowToFront(Window *w)
01692 {
01693 bool bring_to_front = false;
01694
01695 if (w->window_class == WC_MAIN_WINDOW ||
01696 IsVitalWindow(w) ||
01697 w->window_class == WC_TOOLTIPS ||
01698 w->window_class == WC_DROPDOWN_MENU) {
01699 return true;
01700 }
01701
01702 Window *u;
01703 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
01704
01705 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
01706 u->flags4 |= WF_WHITE_BORDER_MASK;
01707 u->SetDirty();
01708 return false;
01709 }
01710
01711 if (u->window_class == WC_MAIN_WINDOW ||
01712 IsVitalWindow(u) ||
01713 u->window_class == WC_TOOLTIPS ||
01714 u->window_class == WC_DROPDOWN_MENU) {
01715 continue;
01716 }
01717
01718
01719 if (w->left + w->width <= u->left ||
01720 u->left + u->width <= w->left ||
01721 w->top + w->height <= u->top ||
01722 u->top + u->height <= w->top) {
01723 continue;
01724 }
01725
01726 bring_to_front = true;
01727 }
01728
01729 if (bring_to_front) BringWindowToFront(w);
01730 return true;
01731 }
01732
01736 void HandleKeypress(uint32 raw_key)
01737 {
01738
01739
01740
01741
01742
01743
01744
01745
01746
01747 if (!IsGeneratingWorld()) _current_company = _local_company;
01748
01749
01750 uint16 key = GB(raw_key, 0, 16);
01751 uint16 keycode = GB(raw_key, 16, 16);
01752
01753
01754
01755
01756
01757
01758
01759
01760 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
01761
01762
01763
01764
01765 if (key == 0 && keycode == 0) return;
01766
01767
01768 if (EditBoxInGlobalFocus()) {
01769
01770 if (_focused_window->OnKeyPress(key, keycode) == Window::ES_HANDLED) return;
01771 }
01772
01773
01774 Window *w;
01775 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01776 if (w->OnKeyPress(key, keycode) == Window::ES_HANDLED) return;
01777 }
01778
01779 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01780
01781 if (w != NULL) w->OnKeyPress(key, keycode);
01782 }
01783
01787 void HandleCtrlChanged()
01788 {
01789
01790 Window *w;
01791 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01792 if (w->OnCTRLStateChange() == Window::ES_HANDLED) return;
01793 }
01794 }
01795
01802 static int _input_events_this_tick = 0;
01803
01808 static void HandleAutoscroll()
01809 {
01810 if (_settings_client.gui.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
01811 int x = _cursor.pos.x;
01812 int y = _cursor.pos.y;
01813 Window *w = FindWindowFromPt(x, y);
01814 if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
01815 ViewPort *vp = IsPtInWindowViewport(w, x, y);
01816 if (vp != NULL) {
01817 x -= vp->left;
01818 y -= vp->top;
01819
01820
01821 #define scrollspeed 3
01822 if (x - 15 < 0) {
01823 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
01824 } else if (15 - (vp->width - x) > 0) {
01825 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
01826 }
01827 if (y - 15 < 0) {
01828 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
01829 } else if (15 - (vp->height - y) > 0) {
01830 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
01831 }
01832 #undef scrollspeed
01833 }
01834 }
01835 }
01836
01837 enum MouseClick {
01838 MC_NONE = 0,
01839 MC_LEFT,
01840 MC_RIGHT,
01841 MC_DOUBLE_LEFT,
01842
01843 MAX_OFFSET_DOUBLE_CLICK = 5,
01844 TIME_BETWEEN_DOUBLE_CLICK = 500,
01845 };
01846
01847 extern bool VpHandlePlaceSizingDrag();
01848
01849 static void ScrollMainViewport(int x, int y)
01850 {
01851 if (_game_mode != GM_MENU) {
01852 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01853 assert(w);
01854
01855 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
01856 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
01857 }
01858 }
01859
01869 static const int8 scrollamt[16][2] = {
01870 { 0, 0},
01871 {-2, 0},
01872 { 0, -2},
01873 {-2, -1},
01874 { 2, 0},
01875 { 0, 0},
01876 { 2, -1},
01877 { 0, -2},
01878 { 0 ,2},
01879 {-2 ,1},
01880 { 0, 0},
01881 {-2, 0},
01882 { 2, 1},
01883 { 0, 2},
01884 { 2, 0},
01885 { 0, 0},
01886 };
01887
01888 static void HandleKeyScrolling()
01889 {
01890
01891
01892
01893
01894 if (_dirkeys && !EditBoxInGlobalFocus()) {
01895 int factor = _shift_pressed ? 50 : 10;
01896 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
01897 }
01898 }
01899
01900 void MouseLoop(MouseClick click, int mousewheel)
01901 {
01902 DecreaseWindowCounters();
01903 HandlePlacePresize();
01904 UpdateTileSelection();
01905
01906 if (!VpHandlePlaceSizingDrag()) return;
01907 if (!HandleDragDrop()) return;
01908 if (!HandleWindowDragging()) return;
01909 if (!HandleScrollbarScrolling()) return;
01910 if (!HandleViewportScroll()) return;
01911 if (!HandleMouseOver()) return;
01912
01913 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
01914 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
01915
01916 int x = _cursor.pos.x;
01917 int y = _cursor.pos.y;
01918 Window *w = FindWindowFromPt(x, y);
01919 if (w == NULL) return;
01920
01921 if (!MaybeBringWindowToFront(w)) return;
01922 ViewPort *vp = IsPtInWindowViewport(w, x, y);
01923
01924
01925 if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
01926
01927 if (mousewheel != 0) {
01928 if (_settings_client.gui.scrollwheel_scrolling == 0) {
01929
01930 w->OnMouseWheel(mousewheel);
01931 }
01932
01933
01934 if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel);
01935 }
01936
01937 if (vp != NULL) {
01938 if (scrollwheel_scrolling) click = MC_RIGHT;
01939 switch (click) {
01940 case MC_DOUBLE_LEFT:
01941 case MC_LEFT:
01942 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
01943 if (_thd.place_mode != VHM_NONE &&
01944
01945 _cursor.sprite != SPR_CURSOR_QUERY &&
01946 _cursor.sprite != SPR_CURSOR_SIGN &&
01947 _pause_game != 0 &&
01948 !_cheats.build_in_pause.value) {
01949 return;
01950 }
01951
01952 if (_thd.place_mode == VHM_NONE) {
01953 if (!HandleViewportClicked(vp, x, y) &&
01954 !(w->flags4 & WF_DISABLE_VP_SCROLL) &&
01955 _settings_client.gui.left_mouse_btn_scrolling) {
01956 _scrolling_viewport = true;
01957 _cursor.fix_at = false;
01958 }
01959 } else {
01960 PlaceObject();
01961 }
01962 break;
01963
01964 case MC_RIGHT:
01965 if (!(w->flags4 & WF_DISABLE_VP_SCROLL)) {
01966 _scrolling_viewport = true;
01967 _cursor.fix_at = true;
01968 }
01969 break;
01970
01971 default:
01972 break;
01973 }
01974 } else {
01975 switch (click) {
01976 case MC_DOUBLE_LEFT:
01977 DispatchLeftClickEvent(w, x - w->left, y - w->top, true);
01978 if (_mouseover_last_w == NULL) break;
01979
01980 case MC_LEFT:
01981 DispatchLeftClickEvent(w, x - w->left, y - w->top, false);
01982 break;
01983
01984 default:
01985 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
01986
01987
01988
01989
01990 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
01991 }
01992 }
01993 }
01994
01998 void HandleMouseEvents()
01999 {
02000 static int double_click_time = 0;
02001 static int double_click_x = 0;
02002 static int double_click_y = 0;
02003
02004
02005
02006
02007
02008
02009
02010
02011
02012
02013 if (!IsGeneratingWorld()) _current_company = _local_company;
02014
02015
02016 MouseClick click = MC_NONE;
02017 if (_left_button_down && !_left_button_clicked) {
02018 click = MC_LEFT;
02019 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02020 double_click_x != 0 && abs(_cursor.pos.x - double_click_x) < MAX_OFFSET_DOUBLE_CLICK &&
02021 double_click_y != 0 && abs(_cursor.pos.y - double_click_y) < MAX_OFFSET_DOUBLE_CLICK) {
02022 click = MC_DOUBLE_LEFT;
02023 }
02024 double_click_time = _realtime_tick;
02025 double_click_x = _cursor.pos.x;
02026 double_click_y = _cursor.pos.y;
02027 _left_button_clicked = true;
02028 _input_events_this_tick++;
02029 } else if (_right_button_clicked) {
02030 _right_button_clicked = false;
02031 click = MC_RIGHT;
02032 _input_events_this_tick++;
02033 }
02034
02035 int mousewheel = 0;
02036 if (_cursor.wheel) {
02037 mousewheel = _cursor.wheel;
02038 _cursor.wheel = 0;
02039 _input_events_this_tick++;
02040 }
02041
02042 MouseLoop(click, mousewheel);
02043 }
02044
02048 static void CheckSoftLimit()
02049 {
02050 if (_settings_client.gui.window_soft_limit == 0) return;
02051
02052 for (;;) {
02053 uint deletable_count = 0;
02054 Window *w, *last_deletable = NULL;
02055 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02056 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags4 & WF_STICKY)) continue;
02057
02058 last_deletable = w;
02059 deletable_count++;
02060 }
02061
02062
02063 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02064
02065 assert(last_deletable != NULL);
02066 delete last_deletable;
02067 }
02068 }
02069
02073 void InputLoop()
02074 {
02075 CheckSoftLimit();
02076 HandleKeyScrolling();
02077
02078
02079 for (Window *v = _z_front_window; v != NULL; ) {
02080 Window *w = v;
02081 v = v->z_back;
02082
02083 if (w->window_class != WC_INVALID) continue;
02084
02085
02086
02087
02088
02089 if (w->z_front == NULL) {
02090 _z_front_window = w->z_back;
02091 } else {
02092 w->z_front->z_back = w->z_back;
02093 }
02094 if (w->z_back == NULL) {
02095 _z_back_window = w->z_front;
02096 } else {
02097 w->z_back->z_front = w->z_front;
02098 }
02099 free(w);
02100 }
02101
02102 if (_input_events_this_tick != 0) {
02103
02104 _input_events_this_tick = 0;
02105
02106 return;
02107 }
02108
02109
02110 HandleMouseEvents();
02111 HandleAutoscroll();
02112 }
02113
02117 void UpdateWindows()
02118 {
02119 Window *w;
02120 static int we4_timer = 0;
02121 int t = we4_timer + 1;
02122
02123 if (t >= 100) {
02124 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02125 w->OnHundredthTick();
02126 }
02127 t = 0;
02128 }
02129 we4_timer = t;
02130
02131 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02132 if (w->flags4 & WF_WHITE_BORDER_MASK) {
02133 w->flags4 -= WF_WHITE_BORDER_ONE;
02134
02135 if (!(w->flags4 & WF_WHITE_BORDER_MASK)) w->SetDirty();
02136 }
02137 }
02138
02139 DrawDirtyBlocks();
02140
02141 FOR_ALL_WINDOWS_FROM_BACK(w) {
02142 if (w->viewport != NULL) UpdateViewportPosition(w);
02143 }
02144 NetworkDrawChatMessage();
02145
02146 DrawMouseCursor();
02147 }
02148
02154 void InvalidateWindow(WindowClass cls, WindowNumber number)
02155 {
02156 const Window *w;
02157 FOR_ALL_WINDOWS_FROM_BACK(w) {
02158 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02159 }
02160 }
02161
02168 void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index)
02169 {
02170 const Window *w;
02171 FOR_ALL_WINDOWS_FROM_BACK(w) {
02172 if (w->window_class == cls && w->window_number == number) {
02173 w->InvalidateWidget(widget_index);
02174 }
02175 }
02176 }
02177
02182 void InvalidateWindowClasses(WindowClass cls)
02183 {
02184 Window *w;
02185 FOR_ALL_WINDOWS_FROM_BACK(w) {
02186 if (w->window_class == cls) w->SetDirty();
02187 }
02188 }
02189
02194 void InvalidateThisWindowData(Window *w, int data)
02195 {
02196 w->OnInvalidateData(data);
02197 w->SetDirty();
02198 }
02199
02205 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data)
02206 {
02207 Window *w;
02208 FOR_ALL_WINDOWS_FROM_BACK(w) {
02209 if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w, data);
02210 }
02211 }
02212
02217 void InvalidateWindowClassesData(WindowClass cls, int data)
02218 {
02219 Window *w;
02220
02221 FOR_ALL_WINDOWS_FROM_BACK(w) {
02222 if (w->window_class == cls) InvalidateThisWindowData(w, data);
02223 }
02224 }
02225
02229 void CallWindowTickEvent()
02230 {
02231 if (_scroller_click_timeout > 3) {
02232 _scroller_click_timeout -= 3;
02233 } else {
02234 _scroller_click_timeout = 0;
02235 }
02236
02237 Window *w;
02238 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02239 w->OnTick();
02240 }
02241 }
02242
02249 void DeleteNonVitalWindows()
02250 {
02251 Window *w;
02252
02253 restart_search:
02254
02255
02256
02257 FOR_ALL_WINDOWS_FROM_BACK(w) {
02258 if (w->window_class != WC_MAIN_WINDOW &&
02259 w->window_class != WC_SELECT_GAME &&
02260 w->window_class != WC_MAIN_TOOLBAR &&
02261 w->window_class != WC_STATUS_BAR &&
02262 w->window_class != WC_TOOLBAR_MENU &&
02263 w->window_class != WC_TOOLTIPS &&
02264 (w->flags4 & WF_STICKY) == 0) {
02265
02266 delete w;
02267 goto restart_search;
02268 }
02269 }
02270 }
02271
02277 void DeleteAllNonVitalWindows()
02278 {
02279 Window *w;
02280
02281
02282 DeleteNonVitalWindows();
02283
02284 restart_search:
02285
02286
02287
02288 FOR_ALL_WINDOWS_FROM_BACK(w) {
02289 if (w->flags4 & WF_STICKY) {
02290 delete w;
02291 goto restart_search;
02292 }
02293 }
02294 }
02295
02300 void DeleteConstructionWindows()
02301 {
02302 Window *w;
02303
02304 restart_search:
02305
02306
02307
02308 FOR_ALL_WINDOWS_FROM_BACK(w) {
02309 if (w->desc_flags & WDF_CONSTRUCTION) {
02310 delete w;
02311 goto restart_search;
02312 }
02313 }
02314
02315 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02316 }
02317
02319 void HideVitalWindows()
02320 {
02321 DeleteWindowById(WC_TOOLBAR_MENU, 0);
02322 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02323 DeleteWindowById(WC_STATUS_BAR, 0);
02324 }
02325
02331 int PositionMainToolbar(Window *w)
02332 {
02333 DEBUG(misc, 5, "Repositioning Main Toolbar...");
02334
02335 if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
02336 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02337 }
02338
02339 switch (_settings_client.gui.toolbar_pos) {
02340 case 1: w->left = (_screen.width - w->width) / 2; break;
02341 case 2: w->left = _screen.width - w->width; break;
02342 default: w->left = 0;
02343 }
02344 SetDirtyBlocks(0, 0, _screen.width, w->height);
02345 return w->left;
02346 }
02347
02355 void SetVScrollCount(Window *w, int num)
02356 {
02357 w->vscroll.count = num;
02358 num -= w->vscroll.cap;
02359 if (num < 0) num = 0;
02360 if (num < w->vscroll.pos) w->vscroll.pos = num;
02361 }
02362
02370 void SetVScroll2Count(Window *w, int num)
02371 {
02372 w->vscroll2.count = num;
02373 num -= w->vscroll2.cap;
02374 if (num < 0) num = 0;
02375 if (num < w->vscroll2.pos) w->vscroll2.pos = num;
02376 }
02377
02385 void SetHScrollCount(Window *w, int num)
02386 {
02387 w->hscroll.count = num;
02388 num -= w->hscroll.cap;
02389 if (num < 0) num = 0;
02390 if (num < w->hscroll.pos) w->hscroll.pos = num;
02391 }
02392
02398 void RelocateAllWindows(int neww, int newh)
02399 {
02400 Window *w;
02401
02402 FOR_ALL_WINDOWS_FROM_BACK(w) {
02403 int left, top;
02404
02405 if (w->window_class == WC_MAIN_WINDOW) {
02406 ViewPort *vp = w->viewport;
02407 vp->width = w->width = neww;
02408 vp->height = w->height = newh;
02409 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
02410 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
02411 continue;
02412 }
02413
02414
02415
02416 switch (w->window_class) {
02417 case WC_MAIN_TOOLBAR:
02418 if (neww - w->width != 0) {
02419 ResizeWindow(w, min(neww, 640) - w->width, 0);
02420
02421 Point size;
02422 Point delta;
02423 size.x = w->width;
02424 size.y = w->height;
02425 delta.x = neww - w->width;
02426 delta.y = 0;
02427 w->OnResize(size, delta);
02428 }
02429
02430 top = w->top;
02431 left = PositionMainToolbar(w);
02432 break;
02433
02434 case WC_SELECT_GAME:
02435 case WC_GAME_OPTIONS:
02436 case WC_NETWORK_WINDOW:
02437 top = (newh - w->height) >> 1;
02438 left = (neww - w->width) >> 1;
02439 break;
02440
02441 case WC_NEWS_WINDOW:
02442 top = newh - w->height;
02443 left = (neww - w->width) >> 1;
02444 break;
02445
02446 case WC_STATUS_BAR:
02447 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
02448 top = newh - w->height;
02449 left = (neww - w->width) >> 1;
02450 break;
02451
02452 case WC_SEND_NETWORK_MSG:
02453 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
02454 top = (newh - 26);
02455 left = (neww - w->width) >> 1;
02456 break;
02457
02458 case WC_CONSOLE:
02459 IConsoleResize(w);
02460 continue;
02461
02462 default: {
02463 left = w->left;
02464 if (left + (w->width >> 1) >= neww) left = neww - w->width;
02465 if (left < 0) left = 0;
02466
02467 top = w->top;
02468 if (top + (w->height >> 1) >= newh) top = newh - w->height;
02469
02470 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
02471 if (wt != NULL) {
02472 if (top < wt->height && wt->left < (w->left + w->width) && (wt->left + wt->width) > w->left) top = wt->height;
02473 if (top >= newh) top = newh - 1;
02474 } else {
02475 if (top < 0) top = 0;
02476 }
02477 } break;
02478 }
02479
02480 if (w->viewport != NULL) {
02481 w->viewport->left += left - w->left;
02482 w->viewport->top += top - w->top;
02483 }
02484
02485 w->left = left;
02486 w->top = top;
02487 }
02488 }
02489
02494 PickerWindowBase::~PickerWindowBase()
02495 {
02496 this->window_class = WC_INVALID;
02497 ResetObjectToPlace();
02498 }