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
00515 static Window *FindChildWindow(const Window *w, WindowClass wc)
00516 {
00517 Window *v;
00518 FOR_ALL_WINDOWS_FROM_BACK(v) {
00519 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
00520 }
00521
00522 return NULL;
00523 }
00524
00529 void Window::DeleteChildWindows(WindowClass wc) const
00530 {
00531 Window *child = FindChildWindow(this, wc);
00532 while (child != NULL) {
00533 delete child;
00534 child = FindChildWindow(this, wc);
00535 }
00536 }
00537
00541 Window::~Window()
00542 {
00543 if (_thd.place_mode != VHM_NONE &&
00544 _thd.window_class == this->window_class &&
00545 _thd.window_number == this->window_number) {
00546 ResetObjectToPlace();
00547 }
00548
00549
00550 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00551
00552
00553 if (_focused_window == this) _focused_window = NULL;
00554
00555 this->DeleteChildWindows();
00556
00557 if (this->viewport != NULL) DeleteWindowViewport(this);
00558
00559 this->SetDirty();
00560
00561 free(this->widget);
00562
00563 this->window_class = WC_INVALID;
00564 }
00565
00572 Window *FindWindowById(WindowClass cls, WindowNumber number)
00573 {
00574 Window *w;
00575 FOR_ALL_WINDOWS_FROM_BACK(w) {
00576 if (w->window_class == cls && w->window_number == number) return w;
00577 }
00578
00579 return NULL;
00580 }
00581
00588 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00589 {
00590 Window *w = FindWindowById(cls, number);
00591 if (force || w == NULL ||
00592 (w->desc_flags & WDF_STICKY_BUTTON) == 0 ||
00593 (w->flags4 & WF_STICKY) == 0) {
00594 delete w;
00595 }
00596 }
00597
00602 void DeleteWindowByClass(WindowClass cls)
00603 {
00604 Window *w;
00605
00606 restart_search:
00607
00608
00609
00610 FOR_ALL_WINDOWS_FROM_BACK(w) {
00611 if (w->window_class == cls) {
00612 delete w;
00613 goto restart_search;
00614 }
00615 }
00616 }
00617
00622 void DeleteCompanyWindows(CompanyID id)
00623 {
00624 Window *w;
00625
00626 restart_search:
00627
00628
00629
00630 FOR_ALL_WINDOWS_FROM_BACK(w) {
00631 if (w->owner == id) {
00632 delete w;
00633 goto restart_search;
00634 }
00635 }
00636
00637
00638 DeleteWindowById(WC_BUY_COMPANY, id);
00639 }
00640
00646 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00647 {
00648 Window *w;
00649 FOR_ALL_WINDOWS_FROM_BACK(w) {
00650 if (w->owner != old_owner) continue;
00651
00652 switch (w->window_class) {
00653 case WC_COMPANY_COLOUR:
00654 case WC_FINANCES:
00655 case WC_STATION_LIST:
00656 case WC_TRAINS_LIST:
00657 case WC_ROADVEH_LIST:
00658 case WC_SHIPS_LIST:
00659 case WC_AIRCRAFT_LIST:
00660 case WC_BUY_COMPANY:
00661 case WC_COMPANY:
00662 continue;
00663
00664 default:
00665 w->owner = new_owner;
00666 break;
00667 }
00668 }
00669 }
00670
00671 static void BringWindowToFront(Window *w);
00672
00678 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00679 {
00680 Window *w = FindWindowById(cls, number);
00681
00682 if (w != NULL) {
00683 w->flags4 |= WF_WHITE_BORDER_MASK;
00684 BringWindowToFront(w);
00685 w->SetDirty();
00686 }
00687
00688 return w;
00689 }
00690
00691 static inline bool IsVitalWindow(const Window *w)
00692 {
00693 switch (w->window_class) {
00694 case WC_MAIN_TOOLBAR:
00695 case WC_STATUS_BAR:
00696 case WC_NEWS_WINDOW:
00697 case WC_SEND_NETWORK_MSG:
00698 return true;
00699
00700 default:
00701 return false;
00702 }
00703 }
00704
00713 static void BringWindowToFront(Window *w)
00714 {
00715 Window *v = _z_front_window;
00716
00717
00718 for (; v != NULL && v != w && IsVitalWindow(v); v = v->z_back) { }
00719
00720 if (v == NULL || w == v) return;
00721
00722
00723 assert(w != _z_front_window);
00724
00725 if (w->z_back == NULL) {
00726 _z_back_window = w->z_front;
00727 } else {
00728 w->z_back->z_front = w->z_front;
00729 }
00730 w->z_front->z_back = w->z_back;
00731
00732 w->z_front = v->z_front;
00733 w->z_back = v;
00734
00735 if (v->z_front == NULL) {
00736 _z_front_window = w;
00737 } else {
00738 v->z_front->z_back = w;
00739 }
00740 v->z_front = w;
00741
00742 w->SetDirty();
00743 }
00744
00755 static void AssignWidgetToWindow(Window *w, const Widget *widget)
00756 {
00757 if (widget != NULL) {
00758 uint index = 1;
00759
00760 for (const Widget *wi = widget; wi->type != WWT_LAST; wi++) index++;
00761
00762 w->widget = MallocT<Widget>(index);
00763 memcpy(w->widget, widget, sizeof(*w->widget) * index);
00764 w->widget_count = index - 1;
00765 } else {
00766 w->widget = NULL;
00767 w->widget_count = 0;
00768 }
00769 }
00770
00785 void Window::Initialize(int x, int y, int min_width, int min_height,
00786 WindowClass cls, const Widget *widget, int window_number)
00787 {
00788
00789 this->window_class = cls;
00790 this->flags4 = WF_WHITE_BORDER_MASK;
00791 this->owner = INVALID_OWNER;
00792 this->left = x;
00793 this->top = y;
00794 this->width = min_width;
00795 this->height = min_height;
00796 AssignWidgetToWindow(this, widget);
00797 this->focused_widget = 0;
00798 this->resize.width = min_width;
00799 this->resize.height = min_height;
00800 this->resize.step_width = 1;
00801 this->resize.step_height = 1;
00802 this->window_number = window_number;
00803
00804
00805
00806
00807 if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->HasWidgetOfType(WWT_EDITBOX))) SetFocusedWindow(this);
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817 Window *w = _z_front_window;
00818 if (w != NULL && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) {
00819 if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) w = w->z_back;
00820 if (FindWindowById(WC_STATUS_BAR, 0) != NULL) w = w->z_back;
00821 if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) w = w->z_back;
00822 if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) w = w->z_back;
00823
00824 if (w == NULL) {
00825 _z_back_window->z_front = this;
00826 this->z_back = _z_back_window;
00827 _z_back_window = this;
00828 } else {
00829 if (w->z_front == NULL) {
00830 _z_front_window = this;
00831 } else {
00832 this->z_front = w->z_front;
00833 w->z_front->z_back = this;
00834 }
00835
00836 this->z_back = w;
00837 w->z_front = this;
00838 }
00839 } else {
00840 this->z_back = _z_front_window;
00841 if (_z_front_window != NULL) {
00842 _z_front_window->z_front = this;
00843 } else {
00844 _z_back_window = this;
00845 }
00846 _z_front_window = this;
00847 }
00848 }
00849
00860 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
00861 {
00862
00863
00864
00865
00866
00867 if (this->width != def_width || this->height != def_height) {
00868
00869 int free_height = _screen.height;
00870 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
00871 if (wt != NULL) free_height -= wt->height;
00872 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
00873 if (wt != NULL) free_height -= wt->height;
00874
00875 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
00876 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
00877
00878
00879
00880
00881 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
00882 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
00883
00884 ResizeWindow(this, enlarge_x, enlarge_y);
00885
00886 Point size;
00887 Point diff;
00888 size.x = this->width;
00889 size.y = this->height;
00890 diff.x = enlarge_x;
00891 diff.y = enlarge_y;
00892 this->OnResize(size, diff);
00893 }
00894
00895 int nx = this->left;
00896 int ny = this->top;
00897
00898 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
00899
00900 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
00901 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
00902 nx = max(nx, 0);
00903
00904 if (this->viewport != NULL) {
00905 this->viewport->left += nx - this->left;
00906 this->viewport->top += ny - this->top;
00907 }
00908 this->left = nx;
00909 this->top = ny;
00910
00911 this->SetDirty();
00912 }
00913
00918 void Window::FindWindowPlacementAndResize(const WindowDesc *desc)
00919 {
00920 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
00921 }
00922
00936 Window::Window(int x, int y, int width, int height, WindowClass cls, const Widget *widget)
00937 {
00938 this->Initialize(x, y, width, height, cls, widget, 0);
00939 }
00940
00952 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
00953 {
00954 int right = width + left;
00955 int bottom = height + top;
00956
00957 if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height) return false;
00958
00959
00960 const Window *w;
00961 FOR_ALL_WINDOWS_FROM_BACK(w) {
00962 if (w->window_class == WC_MAIN_WINDOW) continue;
00963
00964 if (right > w->left &&
00965 w->left + w->width > left &&
00966 bottom > w->top &&
00967 w->top + w->height > top) {
00968 return false;
00969 }
00970 }
00971
00972 pos.x = left;
00973 pos.y = top;
00974 return true;
00975 }
00976
00988 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
00989 {
00990
00991
00992
00993 if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
00994
00995 if (top < 22 || top > _screen.height - (height>>2)) return false;
00996
00997
00998 const Window *w;
00999 FOR_ALL_WINDOWS_FROM_BACK(w) {
01000 if (w->window_class == WC_MAIN_WINDOW) continue;
01001
01002 if (left + width > w->left &&
01003 w->left + w->width > left &&
01004 top + height > w->top &&
01005 w->top + w->height > top) {
01006 return false;
01007 }
01008 }
01009
01010 pos.x = left;
01011 pos.y = top;
01012 return true;
01013 }
01014
01021 static Point GetAutoPlacePosition(int width, int height)
01022 {
01023 Point pt;
01024
01025
01026 if (IsGoodAutoPlace1(0, 24, width, height, pt)) return pt;
01027
01028
01029
01030
01031
01032 const Window *w;
01033 FOR_ALL_WINDOWS_FROM_BACK(w) {
01034 if (w->window_class == WC_MAIN_WINDOW) continue;
01035
01036 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01037 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01038 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01039 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01040 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01041 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01042 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01043 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01044 }
01045
01046
01047
01048
01049
01050 FOR_ALL_WINDOWS_FROM_BACK(w) {
01051 if (w->window_class == WC_MAIN_WINDOW) continue;
01052
01053 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01054 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01055 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01056 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01057 }
01058
01059
01060
01061
01062 int left = 0, top = 24;
01063
01064 restart:
01065 FOR_ALL_WINDOWS_FROM_BACK(w) {
01066 if (w->left == left && w->top == top) {
01067 left += 5;
01068 top += 5;
01069 goto restart;
01070 }
01071 }
01072
01073 pt.x = left;
01074 pt.y = top;
01075 return pt;
01076 }
01077
01093 static Point LocalGetWindowPlacement(const WindowDesc *desc, int window_number)
01094 {
01095 Point pt;
01096 Window *w;
01097
01098 if (desc->parent_cls != 0 &&
01099 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01100 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01101
01102 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01103 if (pt.x > _screen.width + 10 - desc->default_width) {
01104 pt.x = (_screen.width + 10 - desc->default_width) - 20;
01105 }
01106 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 36 : 10);
01107 } else {
01108 switch (desc->left) {
01109 case WDP_ALIGN_TBR:
01110 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01111 pt.x = (w->left + w->width) - desc->default_width;
01112 break;
01113
01114 case WDP_ALIGN_TBL:
01115 pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left;
01116 break;
01117
01118 case WDP_AUTO:
01119 return GetAutoPlacePosition(desc->default_width, desc->default_height);
01120
01121 case WDP_CENTER:
01122 pt.x = (_screen.width - desc->default_width) / 2;
01123 break;
01124
01125 default:
01126 pt.x = desc->left;
01127 if (pt.x < 0) pt.x += _screen.width;
01128 }
01129
01130 switch (desc->top) {
01131 case WDP_CENTER:
01132 pt.y = (_screen.height - desc->default_height) / 2;
01133 break;
01134
01135
01136
01137 case WDP_AUTO:
01138 NOT_REACHED();
01139 assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO);
01140
01141
01142 default:
01143 pt.y = desc->top;
01144 if (pt.y < 0) pt.y += _screen.height;
01145 break;
01146 }
01147 }
01148
01149 return pt;
01150 }
01151
01160 Window::Window(const WindowDesc *desc, WindowNumber window_number)
01161 {
01162 Point pt = LocalGetWindowPlacement(desc, window_number);
01163 this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->cls, desc->widgets, window_number);
01164 this->desc_flags = desc->flags;
01165 }
01166
01172 Window *FindWindowFromPt(int x, int y)
01173 {
01174 Window *w;
01175 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01176 if (IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01177 return w;
01178 }
01179 }
01180
01181 return NULL;
01182 }
01183
01187 void InitWindowSystem()
01188 {
01189 IConsoleClose();
01190
01191 _z_back_window = NULL;
01192 _z_front_window = NULL;
01193 _focused_window = NULL;
01194 _mouseover_last_w = NULL;
01195 _scrolling_viewport = 0;
01196 }
01197
01201 void UnInitWindowSystem()
01202 {
01203 Window *w;
01204 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01205
01206 for (w = _z_front_window; w != NULL; ) {
01207 Window *to_del = w;
01208 w = w->z_back;
01209 free(to_del);
01210 }
01211
01212 _z_front_window = NULL;
01213 _z_back_window = NULL;
01214 }
01215
01219 void ResetWindowSystem()
01220 {
01221 UnInitWindowSystem();
01222 InitWindowSystem();
01223 _thd.pos.x = 0;
01224 _thd.pos.y = 0;
01225 _thd.new_pos.x = 0;
01226 _thd.new_pos.y = 0;
01227 }
01228
01229 static void DecreaseWindowCounters()
01230 {
01231 Window *w;
01232 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01233
01234 if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
01235 w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
01236 w->SetDirty();
01237 }
01238 w->OnMouseLoop();
01239 }
01240
01241 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01242 if (w->flags4 & WF_TIMEOUT_MASK && !(--w->flags4 & WF_TIMEOUT_MASK)) {
01243 w->OnTimeout();
01244 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons();
01245 }
01246 }
01247 }
01248
01249 Window *GetCallbackWnd()
01250 {
01251 return FindWindowById(_thd.window_class, _thd.window_number);
01252 }
01253
01254 static void HandlePlacePresize()
01255 {
01256 if (_special_mouse_mode != WSM_PRESIZE) return;
01257
01258 Window *w = GetCallbackWnd();
01259 if (w == NULL) return;
01260
01261 Point pt = GetTileBelowCursor();
01262 if (pt.x == -1) {
01263 _thd.selend.x = -1;
01264 return;
01265 }
01266
01267 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01268 }
01269
01270 static bool HandleDragDrop()
01271 {
01272 if (_special_mouse_mode != WSM_DRAGDROP) return true;
01273 if (_left_button_down) return false;
01274
01275 Window *w = GetCallbackWnd();
01276
01277 if (w != NULL) {
01278
01279 Point pt;
01280 pt.x = _cursor.pos.x - w->left;
01281 pt.y = _cursor.pos.y - w->top;
01282 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01283 }
01284
01285 ResetObjectToPlace();
01286
01287 return false;
01288 }
01289
01290 static bool HandleMouseOver()
01291 {
01292 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01293
01294
01295 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01296
01297 Point pt = { -1, -1 };
01298 _mouseover_last_w->OnMouseOver(pt, 0);
01299 }
01300
01301
01302 _mouseover_last_w = w;
01303
01304 if (w != NULL) {
01305
01306 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01307 int widget = 0;
01308 if (w->widget != NULL) {
01309 widget = GetWidgetFromPos(w, pt.x, pt.y);
01310 }
01311 w->OnMouseOver(pt, widget);
01312 }
01313
01314
01315 return true;
01316 }
01317
01327 void ResizeWindow(Window *w, int x, int y)
01328 {
01329 bool resize_height = false;
01330 bool resize_width = false;
01331
01332 if (x == 0 && y == 0) return;
01333
01334 w->SetDirty();
01335 for (Widget *wi = w->widget; wi->type != WWT_LAST; wi++) {
01336
01337 byte rsizeflag = GB(wi->display_flags, 0, 4);
01338
01339 if (rsizeflag == RESIZE_NONE) continue;
01340
01341
01342 if (rsizeflag & RESIZE_LEFT) {
01343 wi->left += x;
01344 resize_width = true;
01345 }
01346
01347 if (rsizeflag & RESIZE_RIGHT) {
01348 wi->right += x;
01349 resize_width = true;
01350 }
01351
01352 if (rsizeflag & RESIZE_TOP) {
01353 wi->top += y;
01354 resize_height = true;
01355 }
01356
01357 if (rsizeflag & RESIZE_BOTTOM) {
01358 wi->bottom += y;
01359 resize_height = true;
01360 }
01361 }
01362
01363
01364 if (resize_width) w->width += x;
01365 if (resize_height) w->height += y;
01366
01367 w->SetDirty();
01368 }
01369
01370 static bool _dragging_window;
01371
01372 static bool HandleWindowDragging()
01373 {
01374
01375 if (!_dragging_window) return true;
01376
01377
01378 Window *w;
01379 FOR_ALL_WINDOWS_FROM_BACK(w) {
01380 if (w->flags4 & WF_DRAGGING) {
01381 const Widget *t = &w->widget[1];
01382
01383
01384 if (!_left_button_down) {
01385 w->flags4 &= ~WF_DRAGGING;
01386 break;
01387 }
01388
01389 w->SetDirty();
01390
01391 int x = _cursor.pos.x + _drag_delta.x;
01392 int y = _cursor.pos.y + _drag_delta.y;
01393 int nx = x;
01394 int ny = y;
01395
01396 if (_settings_client.gui.window_snap_radius != 0) {
01397 const Window *v;
01398
01399 int hsnap = _settings_client.gui.window_snap_radius;
01400 int vsnap = _settings_client.gui.window_snap_radius;
01401 int delta;
01402
01403 FOR_ALL_WINDOWS_FROM_BACK(v) {
01404 if (v == w) continue;
01405
01406 if (y + w->height > v->top && y < v->top + v->height) {
01407
01408 delta = abs(v->left + v->width - x);
01409 if (delta <= hsnap) {
01410 nx = v->left + v->width;
01411 hsnap = delta;
01412 }
01413
01414
01415 delta = abs(v->left - x - w->width);
01416 if (delta <= hsnap) {
01417 nx = v->left - w->width;
01418 hsnap = delta;
01419 }
01420 }
01421
01422 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01423
01424 delta = abs(v->left - x);
01425 if (delta <= hsnap) {
01426 nx = v->left;
01427 hsnap = delta;
01428 }
01429
01430
01431 delta = abs(v->left + v->width - x - w->width);
01432 if (delta <= hsnap) {
01433 nx = v->left + v->width - w->width;
01434 hsnap = delta;
01435 }
01436 }
01437
01438 if (x + w->width > v->left && x < v->left + v->width) {
01439
01440 delta = abs(v->top + v->height - y);
01441 if (delta <= vsnap) {
01442 ny = v->top + v->height;
01443 vsnap = delta;
01444 }
01445
01446
01447 delta = abs(v->top - y - w->height);
01448 if (delta <= vsnap) {
01449 ny = v->top - w->height;
01450 vsnap = delta;
01451 }
01452 }
01453
01454 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01455
01456 delta = abs(v->top - y);
01457 if (delta <= vsnap) {
01458 ny = v->top;
01459 vsnap = delta;
01460 }
01461
01462
01463 delta = abs(v->top + v->height - y - w->height);
01464 if (delta <= vsnap) {
01465 ny = v->top + v->height - w->height;
01466 vsnap = delta;
01467 }
01468 }
01469 }
01470 }
01471
01472
01473
01474 nx = Clamp(nx, 13 - t->right, _screen.width - 13 - t->left);
01475 ny = Clamp(ny, 0, _screen.height - 13);
01476
01477
01478 Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0);
01479 if (v != NULL) {
01480 int v_bottom = v->top + v->height;
01481 int v_right = v->left + v->width;
01482 if (ny + t->top >= v->top && ny + t->top < v_bottom) {
01483 if ((v->left < 13 && nx + t->left < v->left) ||
01484 (v_right > _screen.width - 13 && nx + t->right > v_right)) {
01485 ny = v_bottom;
01486 } else {
01487 if (nx + t->left > v->left - 13 &&
01488 nx + t->right < v_right + 13) {
01489 if (w->top >= v_bottom) {
01490 ny = v_bottom;
01491 } else if (w->left < nx) {
01492 nx = v->left - 13 - t->left;
01493 } else {
01494 nx = v_right + 13 - t->right;
01495 }
01496 }
01497 }
01498 }
01499 }
01500
01501 if (w->viewport != NULL) {
01502 w->viewport->left += nx - w->left;
01503 w->viewport->top += ny - w->top;
01504 }
01505 w->left = nx;
01506 w->top = ny;
01507
01508 w->SetDirty();
01509 return false;
01510 } else if (w->flags4 & WF_SIZING) {
01511 int x, y;
01512
01513
01514 if (!_left_button_down) {
01515 w->flags4 &= ~WF_SIZING;
01516 w->SetDirty();
01517 break;
01518 }
01519
01520 x = _cursor.pos.x - _drag_delta.x;
01521 y = _cursor.pos.y - _drag_delta.y;
01522
01523
01524
01525
01526 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
01527
01528 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
01529
01530
01531 if ((int)w->width + x < (int)w->resize.width)
01532 x = w->resize.width - w->width;
01533 if ((int)w->height + y < (int)w->resize.height)
01534 y = w->resize.height - w->height;
01535
01536
01537 if (x == 0 && y == 0) return false;
01538
01539
01540
01541 _drag_delta.x += x;
01542 _drag_delta.y += y;
01543
01544
01545 ResizeWindow(w, x, y);
01546
01547 Point size;
01548 Point diff;
01549 size.x = x + w->width;
01550 size.y = y + w->height;
01551 diff.x = x;
01552 diff.y = y;
01553 w->OnResize(size, diff);
01554 return false;
01555 }
01556 }
01557
01558 _dragging_window = false;
01559 return false;
01560 }
01561
01566 static void StartWindowDrag(Window *w)
01567 {
01568 w->flags4 |= WF_DRAGGING;
01569 _dragging_window = true;
01570
01571 _drag_delta.x = w->left - _cursor.pos.x;
01572 _drag_delta.y = w->top - _cursor.pos.y;
01573
01574 BringWindowToFront(w);
01575 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01576 }
01577
01582 static void StartWindowSizing(Window *w)
01583 {
01584 w->flags4 |= WF_SIZING;
01585 _dragging_window = true;
01586
01587 _drag_delta.x = _cursor.pos.x;
01588 _drag_delta.y = _cursor.pos.y;
01589
01590 BringWindowToFront(w);
01591 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01592 }
01593
01594
01595 static bool HandleScrollbarScrolling()
01596 {
01597 Window *w;
01598
01599
01600 if (!_scrolling_scrollbar) return true;
01601
01602
01603 FOR_ALL_WINDOWS_FROM_BACK(w) {
01604 if (w->flags4 & WF_SCROLL_MIDDLE) {
01605
01606 if (!_left_button_down) {
01607 w->flags4 &= ~WF_SCROLL_MIDDLE;
01608 w->SetDirty();
01609 break;
01610 }
01611
01612 int i;
01613 Scrollbar *sb;
01614
01615 if (w->flags4 & WF_HSCROLL) {
01616 sb = &w->hscroll;
01617 i = _cursor.pos.x - _cursorpos_drag_start.x;
01618 } else if (w->flags4 & WF_SCROLL2){
01619 sb = &w->vscroll2;
01620 i = _cursor.pos.y - _cursorpos_drag_start.y;
01621 } else {
01622 sb = &w->vscroll;
01623 i = _cursor.pos.y - _cursorpos_drag_start.y;
01624 }
01625
01626
01627 int pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap));
01628 if (pos != sb->pos) {
01629 sb->pos = pos;
01630 w->SetDirty();
01631 }
01632 return false;
01633 }
01634 }
01635
01636 _scrolling_scrollbar = false;
01637 return false;
01638 }
01639
01640 static bool HandleViewportScroll()
01641 {
01642 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
01643
01644 if (!_scrolling_viewport) return true;
01645
01646 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01647
01648 if (!(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) || w == NULL) {
01649 _cursor.fix_at = false;
01650 _scrolling_viewport = false;
01651 return true;
01652 }
01653
01654 if (w == FindWindowById(WC_MAIN_WINDOW, 0) && w->viewport->follow_vehicle != INVALID_VEHICLE) {
01655
01656 const Vehicle *veh = GetVehicle(w->viewport->follow_vehicle);
01657 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
01658 return true;
01659 }
01660
01661 Point delta;
01662 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
01663 delta.x = -_cursor.delta.x;
01664 delta.y = -_cursor.delta.y;
01665 } else {
01666 delta.x = _cursor.delta.x;
01667 delta.y = _cursor.delta.y;
01668 }
01669
01670 if (scrollwheel_scrolling) {
01671
01672 delta.x = _cursor.h_wheel;
01673 delta.y = _cursor.v_wheel;
01674 _cursor.v_wheel = 0;
01675 _cursor.h_wheel = 0;
01676 }
01677
01678
01679 w->OnScroll(delta);
01680
01681 _cursor.delta.x = 0;
01682 _cursor.delta.y = 0;
01683 return false;
01684 }
01685
01694 static bool MaybeBringWindowToFront(Window *w)
01695 {
01696 bool bring_to_front = false;
01697
01698 if (w->window_class == WC_MAIN_WINDOW ||
01699 IsVitalWindow(w) ||
01700 w->window_class == WC_TOOLTIPS ||
01701 w->window_class == WC_DROPDOWN_MENU) {
01702 return true;
01703 }
01704
01705 Window *u;
01706 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
01707
01708 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
01709 u->flags4 |= WF_WHITE_BORDER_MASK;
01710 u->SetDirty();
01711 return false;
01712 }
01713
01714 if (u->window_class == WC_MAIN_WINDOW ||
01715 IsVitalWindow(u) ||
01716 u->window_class == WC_TOOLTIPS ||
01717 u->window_class == WC_DROPDOWN_MENU) {
01718 continue;
01719 }
01720
01721
01722 if (w->left + w->width <= u->left ||
01723 u->left + u->width <= w->left ||
01724 w->top + w->height <= u->top ||
01725 u->top + u->height <= w->top) {
01726 continue;
01727 }
01728
01729 bring_to_front = true;
01730 }
01731
01732 if (bring_to_front) BringWindowToFront(w);
01733 return true;
01734 }
01735
01739 void HandleKeypress(uint32 raw_key)
01740 {
01741
01742
01743
01744
01745
01746
01747
01748
01749
01750 if (!IsGeneratingWorld()) _current_company = _local_company;
01751
01752
01753 uint16 key = GB(raw_key, 0, 16);
01754 uint16 keycode = GB(raw_key, 16, 16);
01755
01756
01757
01758
01759
01760
01761
01762
01763 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
01764
01765
01766
01767
01768 if (key == 0 && keycode == 0) return;
01769
01770
01771 if (EditBoxInGlobalFocus()) {
01772
01773 if (_focused_window->OnKeyPress(key, keycode) == Window::ES_HANDLED) return;
01774 }
01775
01776
01777 Window *w;
01778 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01779 if (w->OnKeyPress(key, keycode) == Window::ES_HANDLED) return;
01780 }
01781
01782 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01783
01784 if (w != NULL) w->OnKeyPress(key, keycode);
01785 }
01786
01790 void HandleCtrlChanged()
01791 {
01792
01793 Window *w;
01794 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01795 if (w->OnCTRLStateChange() == Window::ES_HANDLED) return;
01796 }
01797 }
01798
01805 static int _input_events_this_tick = 0;
01806
01811 static void HandleAutoscroll()
01812 {
01813 if (_settings_client.gui.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
01814 int x = _cursor.pos.x;
01815 int y = _cursor.pos.y;
01816 Window *w = FindWindowFromPt(x, y);
01817 if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
01818 ViewPort *vp = IsPtInWindowViewport(w, x, y);
01819 if (vp != NULL) {
01820 x -= vp->left;
01821 y -= vp->top;
01822
01823
01824 #define scrollspeed 3
01825 if (x - 15 < 0) {
01826 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
01827 } else if (15 - (vp->width - x) > 0) {
01828 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
01829 }
01830 if (y - 15 < 0) {
01831 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
01832 } else if (15 - (vp->height - y) > 0) {
01833 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
01834 }
01835 #undef scrollspeed
01836 }
01837 }
01838 }
01839
01840 enum MouseClick {
01841 MC_NONE = 0,
01842 MC_LEFT,
01843 MC_RIGHT,
01844 MC_DOUBLE_LEFT,
01845
01846 MAX_OFFSET_DOUBLE_CLICK = 5,
01847 TIME_BETWEEN_DOUBLE_CLICK = 500,
01848 };
01849
01850 extern bool VpHandlePlaceSizingDrag();
01851
01852 static void ScrollMainViewport(int x, int y)
01853 {
01854 if (_game_mode != GM_MENU) {
01855 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01856 assert(w);
01857
01858 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
01859 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
01860 }
01861 }
01862
01872 static const int8 scrollamt[16][2] = {
01873 { 0, 0},
01874 {-2, 0},
01875 { 0, -2},
01876 {-2, -1},
01877 { 2, 0},
01878 { 0, 0},
01879 { 2, -1},
01880 { 0, -2},
01881 { 0 ,2},
01882 {-2 ,1},
01883 { 0, 0},
01884 {-2, 0},
01885 { 2, 1},
01886 { 0, 2},
01887 { 2, 0},
01888 { 0, 0},
01889 };
01890
01891 static void HandleKeyScrolling()
01892 {
01893
01894
01895
01896
01897 if (_dirkeys && !EditBoxInGlobalFocus()) {
01898 int factor = _shift_pressed ? 50 : 10;
01899 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
01900 }
01901 }
01902
01903 static void MouseLoop(MouseClick click, int mousewheel)
01904 {
01905 DecreaseWindowCounters();
01906 HandlePlacePresize();
01907 UpdateTileSelection();
01908
01909 if (!VpHandlePlaceSizingDrag()) return;
01910 if (!HandleDragDrop()) return;
01911 if (!HandleWindowDragging()) return;
01912 if (!HandleScrollbarScrolling()) return;
01913 if (!HandleViewportScroll()) return;
01914 if (!HandleMouseOver()) return;
01915
01916 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
01917 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
01918
01919 int x = _cursor.pos.x;
01920 int y = _cursor.pos.y;
01921 Window *w = FindWindowFromPt(x, y);
01922 if (w == NULL) return;
01923
01924 if (!MaybeBringWindowToFront(w)) return;
01925 ViewPort *vp = IsPtInWindowViewport(w, x, y);
01926
01927
01928 if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
01929
01930 if (mousewheel != 0) {
01931 if (_settings_client.gui.scrollwheel_scrolling == 0) {
01932
01933 w->OnMouseWheel(mousewheel);
01934 }
01935
01936
01937 if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel);
01938 }
01939
01940 if (vp != NULL) {
01941 if (scrollwheel_scrolling) click = MC_RIGHT;
01942 switch (click) {
01943 case MC_DOUBLE_LEFT:
01944 case MC_LEFT:
01945 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
01946 if (_thd.place_mode != VHM_NONE &&
01947
01948 _cursor.sprite != SPR_CURSOR_QUERY &&
01949 _cursor.sprite != SPR_CURSOR_SIGN &&
01950 _pause_game != 0 &&
01951 !_cheats.build_in_pause.value) {
01952 return;
01953 }
01954
01955 if (_thd.place_mode == VHM_NONE) {
01956 if (!HandleViewportClicked(vp, x, y) &&
01957 !(w->flags4 & WF_DISABLE_VP_SCROLL) &&
01958 _settings_client.gui.left_mouse_btn_scrolling) {
01959 _scrolling_viewport = true;
01960 _cursor.fix_at = false;
01961 }
01962 } else {
01963 PlaceObject();
01964 }
01965 break;
01966
01967 case MC_RIGHT:
01968 if (!(w->flags4 & WF_DISABLE_VP_SCROLL)) {
01969 _scrolling_viewport = true;
01970 _cursor.fix_at = true;
01971 }
01972 break;
01973
01974 default:
01975 break;
01976 }
01977 } else {
01978 switch (click) {
01979 case MC_DOUBLE_LEFT:
01980 DispatchLeftClickEvent(w, x - w->left, y - w->top, true);
01981 if (_mouseover_last_w == NULL) break;
01982
01983 case MC_LEFT:
01984 DispatchLeftClickEvent(w, x - w->left, y - w->top, false);
01985 break;
01986
01987 default:
01988 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
01989
01990
01991
01992
01993 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
01994 }
01995 }
01996 }
01997
02001 void HandleMouseEvents()
02002 {
02003 static int double_click_time = 0;
02004 static int double_click_x = 0;
02005 static int double_click_y = 0;
02006
02007
02008
02009
02010
02011
02012
02013
02014
02015
02016 if (!IsGeneratingWorld()) _current_company = _local_company;
02017
02018
02019 MouseClick click = MC_NONE;
02020 if (_left_button_down && !_left_button_clicked) {
02021 click = MC_LEFT;
02022 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02023 double_click_x != 0 && abs(_cursor.pos.x - double_click_x) < MAX_OFFSET_DOUBLE_CLICK &&
02024 double_click_y != 0 && abs(_cursor.pos.y - double_click_y) < MAX_OFFSET_DOUBLE_CLICK) {
02025 click = MC_DOUBLE_LEFT;
02026 }
02027 double_click_time = _realtime_tick;
02028 double_click_x = _cursor.pos.x;
02029 double_click_y = _cursor.pos.y;
02030 _left_button_clicked = true;
02031 _input_events_this_tick++;
02032 } else if (_right_button_clicked) {
02033 _right_button_clicked = false;
02034 click = MC_RIGHT;
02035 _input_events_this_tick++;
02036 }
02037
02038 int mousewheel = 0;
02039 if (_cursor.wheel) {
02040 mousewheel = _cursor.wheel;
02041 _cursor.wheel = 0;
02042 _input_events_this_tick++;
02043 }
02044
02045 MouseLoop(click, mousewheel);
02046
02047
02048
02049 _cursor.delta.x = 0;
02050 _cursor.delta.y = 0;
02051 }
02052
02056 static void CheckSoftLimit()
02057 {
02058 if (_settings_client.gui.window_soft_limit == 0) return;
02059
02060 for (;;) {
02061 uint deletable_count = 0;
02062 Window *w, *last_deletable = NULL;
02063 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02064 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags4 & WF_STICKY)) continue;
02065
02066 last_deletable = w;
02067 deletable_count++;
02068 }
02069
02070
02071 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02072
02073 assert(last_deletable != NULL);
02074 delete last_deletable;
02075 }
02076 }
02077
02081 void InputLoop()
02082 {
02083 CheckSoftLimit();
02084 HandleKeyScrolling();
02085
02086
02087 for (Window *v = _z_front_window; v != NULL; ) {
02088 Window *w = v;
02089 v = v->z_back;
02090
02091 if (w->window_class != WC_INVALID) continue;
02092
02093
02094
02095
02096
02097 if (w->z_front == NULL) {
02098 _z_front_window = w->z_back;
02099 } else {
02100 w->z_front->z_back = w->z_back;
02101 }
02102 if (w->z_back == NULL) {
02103 _z_back_window = w->z_front;
02104 } else {
02105 w->z_back->z_front = w->z_front;
02106 }
02107 free(w);
02108 }
02109
02110 if (_input_events_this_tick != 0) {
02111
02112 _input_events_this_tick = 0;
02113
02114 return;
02115 }
02116
02117
02118 HandleMouseEvents();
02119 HandleAutoscroll();
02120 }
02121
02125 void UpdateWindows()
02126 {
02127 Window *w;
02128 static int we4_timer = 0;
02129 int t = we4_timer + 1;
02130
02131 if (t >= 100) {
02132 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02133 w->OnHundredthTick();
02134 }
02135 t = 0;
02136 }
02137 we4_timer = t;
02138
02139 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02140 if (w->flags4 & WF_WHITE_BORDER_MASK) {
02141 w->flags4 -= WF_WHITE_BORDER_ONE;
02142
02143 if (!(w->flags4 & WF_WHITE_BORDER_MASK)) w->SetDirty();
02144 }
02145 }
02146
02147 DrawDirtyBlocks();
02148
02149 FOR_ALL_WINDOWS_FROM_BACK(w) {
02150 if (w->viewport != NULL) UpdateViewportPosition(w);
02151 }
02152 NetworkDrawChatMessage();
02153
02154 DrawMouseCursor();
02155 }
02156
02162 void InvalidateWindow(WindowClass cls, WindowNumber number)
02163 {
02164 const Window *w;
02165 FOR_ALL_WINDOWS_FROM_BACK(w) {
02166 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02167 }
02168 }
02169
02176 void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index)
02177 {
02178 const Window *w;
02179 FOR_ALL_WINDOWS_FROM_BACK(w) {
02180 if (w->window_class == cls && w->window_number == number) {
02181 w->InvalidateWidget(widget_index);
02182 }
02183 }
02184 }
02185
02190 void InvalidateWindowClasses(WindowClass cls)
02191 {
02192 Window *w;
02193 FOR_ALL_WINDOWS_FROM_BACK(w) {
02194 if (w->window_class == cls) w->SetDirty();
02195 }
02196 }
02197
02202 void InvalidateThisWindowData(Window *w, int data)
02203 {
02204 w->OnInvalidateData(data);
02205 w->SetDirty();
02206 }
02207
02213 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data)
02214 {
02215 Window *w;
02216 FOR_ALL_WINDOWS_FROM_BACK(w) {
02217 if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w, data);
02218 }
02219 }
02220
02225 void InvalidateWindowClassesData(WindowClass cls, int data)
02226 {
02227 Window *w;
02228
02229 FOR_ALL_WINDOWS_FROM_BACK(w) {
02230 if (w->window_class == cls) InvalidateThisWindowData(w, data);
02231 }
02232 }
02233
02237 void CallWindowTickEvent()
02238 {
02239 if (_scroller_click_timeout > 3) {
02240 _scroller_click_timeout -= 3;
02241 } else {
02242 _scroller_click_timeout = 0;
02243 }
02244
02245 Window *w;
02246 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02247 w->OnTick();
02248 }
02249 }
02250
02257 void DeleteNonVitalWindows()
02258 {
02259 Window *w;
02260
02261 restart_search:
02262
02263
02264
02265 FOR_ALL_WINDOWS_FROM_BACK(w) {
02266 if (w->window_class != WC_MAIN_WINDOW &&
02267 w->window_class != WC_SELECT_GAME &&
02268 w->window_class != WC_MAIN_TOOLBAR &&
02269 w->window_class != WC_STATUS_BAR &&
02270 w->window_class != WC_TOOLBAR_MENU &&
02271 w->window_class != WC_TOOLTIPS &&
02272 (w->flags4 & WF_STICKY) == 0) {
02273
02274 delete w;
02275 goto restart_search;
02276 }
02277 }
02278 }
02279
02285 void DeleteAllNonVitalWindows()
02286 {
02287 Window *w;
02288
02289
02290 DeleteNonVitalWindows();
02291
02292 restart_search:
02293
02294
02295
02296 FOR_ALL_WINDOWS_FROM_BACK(w) {
02297 if (w->flags4 & WF_STICKY) {
02298 delete w;
02299 goto restart_search;
02300 }
02301 }
02302 }
02303
02308 void DeleteConstructionWindows()
02309 {
02310 Window *w;
02311
02312 restart_search:
02313
02314
02315
02316 FOR_ALL_WINDOWS_FROM_BACK(w) {
02317 if (w->desc_flags & WDF_CONSTRUCTION) {
02318 delete w;
02319 goto restart_search;
02320 }
02321 }
02322
02323 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02324 }
02325
02327 void HideVitalWindows()
02328 {
02329 DeleteWindowById(WC_TOOLBAR_MENU, 0);
02330 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02331 DeleteWindowById(WC_STATUS_BAR, 0);
02332 }
02333
02339 int PositionMainToolbar(Window *w)
02340 {
02341 DEBUG(misc, 5, "Repositioning Main Toolbar...");
02342
02343 if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
02344 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02345 }
02346
02347 switch (_settings_client.gui.toolbar_pos) {
02348 case 1: w->left = (_screen.width - w->width) / 2; break;
02349 case 2: w->left = _screen.width - w->width; break;
02350 default: w->left = 0;
02351 }
02352 SetDirtyBlocks(0, 0, _screen.width, w->height);
02353 return w->left;
02354 }
02355
02363 void SetVScrollCount(Window *w, int num)
02364 {
02365 w->vscroll.count = num;
02366 num -= w->vscroll.cap;
02367 if (num < 0) num = 0;
02368 if (num < w->vscroll.pos) w->vscroll.pos = num;
02369 }
02370
02378 void SetVScroll2Count(Window *w, int num)
02379 {
02380 w->vscroll2.count = num;
02381 num -= w->vscroll2.cap;
02382 if (num < 0) num = 0;
02383 if (num < w->vscroll2.pos) w->vscroll2.pos = num;
02384 }
02385
02393 void SetHScrollCount(Window *w, int num)
02394 {
02395 w->hscroll.count = num;
02396 num -= w->hscroll.cap;
02397 if (num < 0) num = 0;
02398 if (num < w->hscroll.pos) w->hscroll.pos = num;
02399 }
02400
02406 void RelocateAllWindows(int neww, int newh)
02407 {
02408 Window *w;
02409
02410 FOR_ALL_WINDOWS_FROM_BACK(w) {
02411 int left, top;
02412
02413 if (w->window_class == WC_MAIN_WINDOW) {
02414 ViewPort *vp = w->viewport;
02415 vp->width = w->width = neww;
02416 vp->height = w->height = newh;
02417 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
02418 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
02419 continue;
02420 }
02421
02422
02423
02424 switch (w->window_class) {
02425 case WC_MAIN_TOOLBAR:
02426 if (neww - w->width != 0) {
02427 ResizeWindow(w, min(neww, 640) - w->width, 0);
02428
02429 Point size;
02430 Point delta;
02431 size.x = w->width;
02432 size.y = w->height;
02433 delta.x = neww - w->width;
02434 delta.y = 0;
02435 w->OnResize(size, delta);
02436 }
02437
02438 top = w->top;
02439 left = PositionMainToolbar(w);
02440 break;
02441
02442 case WC_SELECT_GAME:
02443 case WC_GAME_OPTIONS:
02444 case WC_NETWORK_WINDOW:
02445 top = (newh - w->height) >> 1;
02446 left = (neww - w->width) >> 1;
02447 break;
02448
02449 case WC_NEWS_WINDOW:
02450 top = newh - w->height;
02451 left = (neww - w->width) >> 1;
02452 break;
02453
02454 case WC_STATUS_BAR:
02455 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
02456 top = newh - w->height;
02457 left = (neww - w->width) >> 1;
02458 break;
02459
02460 case WC_SEND_NETWORK_MSG:
02461 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
02462 top = (newh - 26);
02463 left = (neww - w->width) >> 1;
02464 break;
02465
02466 case WC_CONSOLE:
02467 IConsoleResize(w);
02468 continue;
02469
02470 default: {
02471 left = w->left;
02472 if (left + (w->width >> 1) >= neww) left = neww - w->width;
02473 if (left < 0) left = 0;
02474
02475 top = w->top;
02476 if (top + (w->height >> 1) >= newh) top = newh - w->height;
02477
02478 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
02479 if (wt != NULL) {
02480 if (top < wt->height && wt->left < (w->left + w->width) && (wt->left + wt->width) > w->left) top = wt->height;
02481 if (top >= newh) top = newh - 1;
02482 } else {
02483 if (top < 0) top = 0;
02484 }
02485 } break;
02486 }
02487
02488 if (w->viewport != NULL) {
02489 w->viewport->left += left - w->left;
02490 w->viewport->top += top - w->top;
02491 }
02492
02493 w->left = left;
02494 w->top = top;
02495 }
02496 }
02497
02502 PickerWindowBase::~PickerWindowBase()
02503 {
02504 this->window_class = WC_INVALID;
02505 ResetObjectToPlace();
02506 }