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
00059 void SetFocusedWindow(Window *w)
00060 {
00061 if (_focused_window == w) return;
00062
00063
00064 if (_focused_window != NULL && _focused_window->focused_widget != NULL) {
00065 uint focused_widget_id = _focused_window->focused_widget - _focused_window->widget;
00066 _focused_window->InvalidateWidget(focused_widget_id);
00067 }
00068
00069
00070 Window *old_focused = _focused_window;
00071 _focused_window = w;
00072
00073
00074 if (old_focused != NULL) old_focused->OnFocusLost();
00075 if (_focused_window != NULL) _focused_window->OnFocus();
00076 }
00077
00082 const Widget *GetGloballyFocusedWidget()
00083 {
00084 return _focused_window != NULL ? _focused_window->focused_widget : NULL;
00085 }
00086
00092 bool EditBoxInGlobalFocus()
00093 {
00094 const Widget *wi = GetGloballyFocusedWidget();
00095
00096
00097 return (wi != NULL && wi->type == WWT_EDITBOX) ||
00098 (_focused_window != NULL && _focused_window->window_class == WC_CONSOLE);
00099 }
00100
00108 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
00109 {
00110 va_list wdg_list;
00111
00112 va_start(wdg_list, widgets);
00113
00114 while (widgets != WIDGET_LIST_END) {
00115 SetWidgetDisabledState(widgets, disab_stat);
00116 widgets = va_arg(wdg_list, int);
00117 }
00118
00119 va_end(wdg_list);
00120 }
00121
00129 void CDECL Window::SetWidgetsHiddenState(bool hidden_stat, int widgets, ...)
00130 {
00131 va_list wdg_list;
00132
00133 va_start(wdg_list, widgets);
00134
00135 while (widgets != WIDGET_LIST_END) {
00136 SetWidgetHiddenState(widgets, hidden_stat);
00137 widgets = va_arg(wdg_list, int);
00138 }
00139
00140 va_end(wdg_list);
00141 }
00142
00148 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
00149 {
00150 va_list wdg_list;
00151
00152 va_start(wdg_list, widgets);
00153
00154 while (widgets != WIDGET_LIST_END) {
00155 SetWidgetLoweredState(widgets, lowered_stat);
00156 widgets = va_arg(wdg_list, int);
00157 }
00158
00159 va_end(wdg_list);
00160 }
00161
00165 void Window::RaiseButtons()
00166 {
00167 for (uint i = 0; i < this->widget_count; i++) {
00168 if (this->IsWidgetLowered(i)) {
00169 this->RaiseWidget(i);
00170 this->InvalidateWidget(i);
00171 }
00172 }
00173 }
00174
00179 void Window::InvalidateWidget(byte widget_index) const
00180 {
00181 const Widget *wi = &this->widget[widget_index];
00182
00183
00184 if (wi->type == WWT_EMPTY || IsWidgetHidden(widget_index)) return;
00185
00186 SetDirtyBlocks(this->left + wi->left, this->top + wi->top, this->left + wi->right + 1, this->top + wi->bottom + 1);
00187 }
00188
00194 void Window::HandleButtonClick(byte widget)
00195 {
00196 this->LowerWidget(widget);
00197 this->flags4 |= WF_TIMEOUT_BEGIN;
00198 this->InvalidateWidget(widget);
00199 }
00200
00205 bool Window::HasWidgetOfType(WidgetType widget_type) const
00206 {
00207 for (uint i = 0; i < this->widget_count; i++) {
00208 if (this->widget[i].type == widget_type) return true;
00209 }
00210 return false;
00211 }
00212
00213 static void StartWindowDrag(Window *w);
00214 static void StartWindowSizing(Window *w);
00215
00223 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click)
00224 {
00225 bool focused_widget_changed = false;
00226 int widget = 0;
00227 if (w->desc_flags & WDF_DEF_WIDGET) {
00228 widget = GetWidgetFromPos(w, x, y);
00229
00230
00231 if (_focused_window != w &&
00232 (w->desc_flags & WDF_NO_FOCUS) == 0 &&
00233 !(w->desc_flags & WDF_STD_BTN && widget == 0)) {
00234 focused_widget_changed = true;
00235 if (_focused_window != NULL) {
00236 _focused_window->OnFocusLost();
00237
00238
00239 if (w->window_class != WC_OSK) DeleteWindowById(WC_OSK, 0);
00240 }
00241 SetFocusedWindow(w);
00242 w->OnFocus();
00243 }
00244
00245 if (widget < 0) return;
00246
00247
00248 if (w->IsWidgetDisabled(widget)) return;
00249
00250 const Widget *wi = &w->widget[widget];
00251
00252
00253
00254 if (wi->type != WWT_CAPTION) {
00255
00256 if (w->focused_widget && w->focused_widget->type == WWT_EDITBOX &&
00257 w->focused_widget != wi &&
00258 w->window_class != WC_OSK) {
00259 DeleteWindowById(WC_OSK, 0);
00260 }
00261
00262 if (w->focused_widget != wi) {
00263
00264 if (w->focused_widget) w->InvalidateWidget(w->focused_widget - w->widget);
00265 focused_widget_changed = true;
00266 w->focused_widget = wi;
00267 }
00268 }
00269
00270 if (wi->type & WWB_MASK) {
00271
00272 switch (wi->type) {
00273 default: NOT_REACHED();
00274 case WWT_PANEL | WWB_PUSHBUTTON:
00275 case WWT_IMGBTN | WWB_PUSHBUTTON:
00276 case WWT_TEXTBTN | WWB_PUSHBUTTON:
00277 w->HandleButtonClick(widget);
00278 break;
00279 }
00280 } else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
00281 ScrollbarClickHandler(w, wi, x, y);
00282 } else if (wi->type == WWT_EDITBOX && !focused_widget_changed) {
00283
00284 QueryStringBaseWindow *qs = dynamic_cast<QueryStringBaseWindow*>(w);
00285 if (qs != NULL) {
00286 const int widget_index = wi - w->widget;
00287 qs->OnOpenOSKWindow(widget_index);
00288 }
00289 }
00290
00291
00292
00293 if (HideDropDownMenu(w) == widget) return;
00294
00295 if (w->desc_flags & WDF_STD_BTN) {
00296 if (widget == 0) {
00297 delete w;
00298 return;
00299 }
00300
00301 if (widget == 1) {
00302 StartWindowDrag(w);
00303 return;
00304 }
00305 }
00306
00307 if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
00308 StartWindowSizing(w);
00309 w->InvalidateWidget(widget);
00310 return;
00311 }
00312
00313 if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) {
00314 w->flags4 ^= WF_STICKY;
00315 w->InvalidateWidget(widget);
00316 return;
00317 }
00318 }
00319
00320 Point pt = { x, y };
00321
00322 if (double_click) {
00323 w->OnDoubleClick(pt, widget);
00324 } else {
00325 w->OnClick(pt, widget);
00326 }
00327 }
00328
00335 static void DispatchRightClickEvent(Window *w, int x, int y)
00336 {
00337 int widget = 0;
00338
00339
00340 if (w->desc_flags & WDF_STD_TOOLTIPS) {
00341 widget = GetWidgetFromPos(w, x, y);
00342 if (widget < 0) return;
00343
00344 if (w->widget[widget].tooltips != 0) {
00345 GuiShowTooltips(w->widget[widget].tooltips);
00346 return;
00347 }
00348 }
00349
00350 Point pt = { x, y };
00351 w->OnRightClick(pt, widget);
00352 }
00353
00361 static void DispatchMouseWheelEvent(Window *w, int widget, int wheel)
00362 {
00363 if (widget < 0) return;
00364
00365 const Widget *wi1 = &w->widget[widget];
00366 const Widget *wi2 = &w->widget[widget + 1];
00367
00368
00369
00370
00371
00372 Scrollbar *sb;
00373 if ((sb = &w->vscroll, wi1->type == WWT_SCROLLBAR) || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR) ||
00374 (sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) {
00375
00376 if (sb->count > sb->cap) {
00377 int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap);
00378 if (pos != sb->pos) {
00379 sb->pos = pos;
00380 w->SetDirty();
00381 }
00382 }
00383 }
00384 }
00385
00398 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00399 {
00400 const Window *v;
00401 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00402 if (right > v->left &&
00403 bottom > v->top &&
00404 left < v->left + v->width &&
00405 top < v->top + v->height) {
00406
00407 int x;
00408
00409 if (left < (x = v->left)) {
00410 DrawOverlappedWindow(w, left, top, x, bottom);
00411 DrawOverlappedWindow(w, x, top, right, bottom);
00412 return;
00413 }
00414
00415 if (right > (x = v->left + v->width)) {
00416 DrawOverlappedWindow(w, left, top, x, bottom);
00417 DrawOverlappedWindow(w, x, top, right, bottom);
00418 return;
00419 }
00420
00421 if (top < (x = v->top)) {
00422 DrawOverlappedWindow(w, left, top, right, x);
00423 DrawOverlappedWindow(w, left, x, right, bottom);
00424 return;
00425 }
00426
00427 if (bottom > (x = v->top + v->height)) {
00428 DrawOverlappedWindow(w, left, top, right, x);
00429 DrawOverlappedWindow(w, left, x, right, bottom);
00430 return;
00431 }
00432
00433 return;
00434 }
00435 }
00436
00437
00438 DrawPixelInfo *dp = _cur_dpi;
00439 dp->width = right - left;
00440 dp->height = bottom - top;
00441 dp->left = left - w->left;
00442 dp->top = top - w->top;
00443 dp->pitch = _screen.pitch;
00444 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00445 dp->zoom = ZOOM_LVL_NORMAL;
00446 w->OnPaint();
00447 }
00448
00457 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00458 {
00459 Window *w;
00460 DrawPixelInfo bk;
00461 _cur_dpi = &bk;
00462
00463 FOR_ALL_WINDOWS_FROM_BACK(w) {
00464 if (right > w->left &&
00465 bottom > w->top &&
00466 left < w->left + w->width &&
00467 top < w->top + w->height) {
00468
00469 DrawOverlappedWindow(w, left, top, right, bottom);
00470 }
00471 }
00472 }
00473
00478 void Window::SetDirty() const
00479 {
00480 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00481 }
00482
00488 void SetWindowDirty(const Window *w)
00489 {
00490 if (w != NULL) w->SetDirty();
00491 }
00492
00496 static Window *FindChildWindow(const Window *w)
00497 {
00498 Window *v;
00499 FOR_ALL_WINDOWS_FROM_BACK(v) {
00500 if (v->parent == w) return v;
00501 }
00502
00503 return NULL;
00504 }
00505
00509 void Window::DeleteChildWindows() const
00510 {
00511 Window *child = FindChildWindow(this);
00512 while (child != NULL) {
00513 delete child;
00514 child = FindChildWindow(this);
00515 }
00516 }
00517
00521 Window::~Window()
00522 {
00523 if (_thd.place_mode != VHM_NONE &&
00524 _thd.window_class == this->window_class &&
00525 _thd.window_number == this->window_number) {
00526 ResetObjectToPlace();
00527 }
00528
00529
00530 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00531
00532
00533 if (_focused_window == this) _focused_window = NULL;
00534
00535 this->DeleteChildWindows();
00536
00537 if (this->viewport != NULL) DeleteWindowViewport(this);
00538
00539 this->SetDirty();
00540
00541 free(this->widget);
00542
00543 this->window_class = WC_INVALID;
00544 }
00545
00552 Window *FindWindowById(WindowClass cls, WindowNumber number)
00553 {
00554 Window *w;
00555 FOR_ALL_WINDOWS_FROM_BACK(w) {
00556 if (w->window_class == cls && w->window_number == number) return w;
00557 }
00558
00559 return NULL;
00560 }
00561
00568 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00569 {
00570 Window *w = FindWindowById(cls, number);
00571 if (force || w == NULL ||
00572 (w->desc_flags & WDF_STICKY_BUTTON) == 0 ||
00573 (w->flags4 & WF_STICKY) == 0) {
00574 delete w;
00575 }
00576 }
00577
00582 void DeleteWindowByClass(WindowClass cls)
00583 {
00584 Window *w;
00585
00586 restart_search:
00587
00588
00589
00590 FOR_ALL_WINDOWS_FROM_BACK(w) {
00591 if (w->window_class == cls) {
00592 delete w;
00593 goto restart_search;
00594 }
00595 }
00596 }
00597
00602 void DeleteCompanyWindows(CompanyID id)
00603 {
00604 Window *w;
00605
00606 restart_search:
00607
00608
00609
00610 FOR_ALL_WINDOWS_FROM_BACK(w) {
00611 if (w->owner == id) {
00612 delete w;
00613 goto restart_search;
00614 }
00615 }
00616
00617
00618 DeleteWindowById(WC_BUY_COMPANY, id);
00619 }
00620
00626 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00627 {
00628 Window *w;
00629 FOR_ALL_WINDOWS_FROM_BACK(w) {
00630 if (w->owner != old_owner) continue;
00631
00632 switch (w->window_class) {
00633 case WC_COMPANY_COLOUR:
00634 case WC_FINANCES:
00635 case WC_STATION_LIST:
00636 case WC_TRAINS_LIST:
00637 case WC_ROADVEH_LIST:
00638 case WC_SHIPS_LIST:
00639 case WC_AIRCRAFT_LIST:
00640 case WC_BUY_COMPANY:
00641 case WC_COMPANY:
00642 continue;
00643
00644 default:
00645 w->owner = new_owner;
00646 break;
00647 }
00648 }
00649 }
00650
00651 static void BringWindowToFront(Window *w);
00652
00658 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00659 {
00660 Window *w = FindWindowById(cls, number);
00661
00662 if (w != NULL) {
00663 w->flags4 |= WF_WHITE_BORDER_MASK;
00664 BringWindowToFront(w);
00665 w->SetDirty();
00666 }
00667
00668 return w;
00669 }
00670
00671 static inline bool IsVitalWindow(const Window *w)
00672 {
00673 switch (w->window_class) {
00674 case WC_MAIN_TOOLBAR:
00675 case WC_STATUS_BAR:
00676 case WC_NEWS_WINDOW:
00677 case WC_SEND_NETWORK_MSG:
00678 return true;
00679
00680 default:
00681 return false;
00682 }
00683 }
00684
00693 static void BringWindowToFront(Window *w)
00694 {
00695 Window *v = _z_front_window;
00696
00697
00698 for (; v != NULL && v != w && IsVitalWindow(v); v = v->z_back) { }
00699
00700 if (v == NULL || w == v) return;
00701
00702
00703 assert(w != _z_front_window);
00704
00705 if (w->z_back == NULL) {
00706 _z_back_window = w->z_front;
00707 } else {
00708 w->z_back->z_front = w->z_front;
00709 }
00710 w->z_front->z_back = w->z_back;
00711
00712 w->z_front = v->z_front;
00713 w->z_back = v;
00714
00715 if (v->z_front == NULL) {
00716 _z_front_window = w;
00717 } else {
00718 v->z_front->z_back = w;
00719 }
00720 v->z_front = w;
00721
00722 w->SetDirty();
00723 }
00724
00735 static void AssignWidgetToWindow(Window *w, const Widget *widget)
00736 {
00737 if (widget != NULL) {
00738 uint index = 1;
00739
00740 for (const Widget *wi = widget; wi->type != WWT_LAST; wi++) index++;
00741
00742 w->widget = MallocT<Widget>(index);
00743 memcpy(w->widget, widget, sizeof(*w->widget) * index);
00744 w->widget_count = index - 1;
00745 } else {
00746 w->widget = NULL;
00747 w->widget_count = 0;
00748 }
00749 }
00750
00765 void Window::Initialize(int x, int y, int min_width, int min_height,
00766 WindowClass cls, const Widget *widget, int window_number)
00767 {
00768
00769 this->window_class = cls;
00770 this->flags4 = WF_WHITE_BORDER_MASK;
00771 this->owner = INVALID_OWNER;
00772 this->left = x;
00773 this->top = y;
00774 this->width = min_width;
00775 this->height = min_height;
00776 AssignWidgetToWindow(this, widget);
00777 this->focused_widget = 0;
00778 this->resize.width = min_width;
00779 this->resize.height = min_height;
00780 this->resize.step_width = 1;
00781 this->resize.step_height = 1;
00782 this->window_number = window_number;
00783
00784
00785
00786
00787 if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->HasWidgetOfType(WWT_EDITBOX))) SetFocusedWindow(this);
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797 Window *w = _z_front_window;
00798 if (w != NULL && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) {
00799 if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) w = w->z_back;
00800 if (FindWindowById(WC_STATUS_BAR, 0) != NULL) w = w->z_back;
00801 if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) w = w->z_back;
00802 if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) w = w->z_back;
00803
00804 if (w == NULL) {
00805 _z_back_window->z_front = this;
00806 this->z_back = _z_back_window;
00807 _z_back_window = this;
00808 } else {
00809 if (w->z_front == NULL) {
00810 _z_front_window = this;
00811 } else {
00812 this->z_front = w->z_front;
00813 w->z_front->z_back = this;
00814 }
00815
00816 this->z_back = w;
00817 w->z_front = this;
00818 }
00819 } else {
00820 this->z_back = _z_front_window;
00821 if (_z_front_window != NULL) {
00822 _z_front_window->z_front = this;
00823 } else {
00824 _z_back_window = this;
00825 }
00826 _z_front_window = this;
00827 }
00828 }
00829
00840 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
00841 {
00842
00843
00844
00845
00846
00847 if (this->width != def_width || this->height != def_height) {
00848
00849 int free_height = _screen.height;
00850 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
00851 if (wt != NULL) free_height -= wt->height;
00852 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
00853 if (wt != NULL) free_height -= wt->height;
00854
00855 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
00856 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
00857
00858
00859
00860
00861 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
00862 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
00863
00864 ResizeWindow(this, enlarge_x, enlarge_y);
00865
00866 Point size;
00867 Point diff;
00868 size.x = this->width;
00869 size.y = this->height;
00870 diff.x = enlarge_x;
00871 diff.y = enlarge_y;
00872 this->OnResize(size, diff);
00873 }
00874
00875 int nx = this->left;
00876 int ny = this->top;
00877
00878 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
00879
00880 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
00881 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
00882 nx = max(nx, 0);
00883
00884 if (this->viewport != NULL) {
00885 this->viewport->left += nx - this->left;
00886 this->viewport->top += ny - this->top;
00887 }
00888 this->left = nx;
00889 this->top = ny;
00890
00891 this->SetDirty();
00892 }
00893
00898 void Window::FindWindowPlacementAndResize(const WindowDesc *desc)
00899 {
00900 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
00901 }
00902
00916 Window::Window(int x, int y, int width, int height, WindowClass cls, const Widget *widget)
00917 {
00918 this->Initialize(x, y, width, height, cls, widget, 0);
00919 }
00920
00932 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
00933 {
00934 int right = width + left;
00935 int bottom = height + top;
00936
00937 if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height) return false;
00938
00939
00940 const Window *w;
00941 FOR_ALL_WINDOWS_FROM_BACK(w) {
00942 if (w->window_class == WC_MAIN_WINDOW) continue;
00943
00944 if (right > w->left &&
00945 w->left + w->width > left &&
00946 bottom > w->top &&
00947 w->top + w->height > top) {
00948 return false;
00949 }
00950 }
00951
00952 pos.x = left;
00953 pos.y = top;
00954 return true;
00955 }
00956
00968 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
00969 {
00970
00971
00972
00973 if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
00974
00975 if (top < 22 || top > _screen.height - (height>>2)) return false;
00976
00977
00978 const Window *w;
00979 FOR_ALL_WINDOWS_FROM_BACK(w) {
00980 if (w->window_class == WC_MAIN_WINDOW) continue;
00981
00982 if (left + width > w->left &&
00983 w->left + w->width > left &&
00984 top + height > w->top &&
00985 w->top + w->height > top) {
00986 return false;
00987 }
00988 }
00989
00990 pos.x = left;
00991 pos.y = top;
00992 return true;
00993 }
00994
01001 static Point GetAutoPlacePosition(int width, int height)
01002 {
01003 Point pt;
01004
01005
01006 if (IsGoodAutoPlace1(0, 24, width, height, pt)) return pt;
01007
01008
01009
01010
01011
01012 const Window *w;
01013 FOR_ALL_WINDOWS_FROM_BACK(w) {
01014 if (w->window_class == WC_MAIN_WINDOW) continue;
01015
01016 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01017 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01018 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01019 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01020 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01021 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01022 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01023 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01024 }
01025
01026
01027
01028
01029
01030 FOR_ALL_WINDOWS_FROM_BACK(w) {
01031 if (w->window_class == WC_MAIN_WINDOW) continue;
01032
01033 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01034 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01035 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01036 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01037 }
01038
01039
01040
01041
01042 int left = 0, top = 24;
01043
01044 restart:
01045 FOR_ALL_WINDOWS_FROM_BACK(w) {
01046 if (w->left == left && w->top == top) {
01047 left += 5;
01048 top += 5;
01049 goto restart;
01050 }
01051 }
01052
01053 pt.x = left;
01054 pt.y = top;
01055 return pt;
01056 }
01057
01073 static Point LocalGetWindowPlacement(const WindowDesc *desc, int window_number)
01074 {
01075 Point pt;
01076 Window *w;
01077
01078 if (desc->parent_cls != 0 &&
01079 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01080 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01081
01082 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01083 if (pt.x > _screen.width + 10 - desc->default_width) {
01084 pt.x = (_screen.width + 10 - desc->default_width) - 20;
01085 }
01086 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 36 : 10);
01087 } else {
01088 switch (desc->left) {
01089 case WDP_ALIGN_TBR:
01090 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01091 pt.x = (w->left + w->width) - desc->default_width;
01092 break;
01093
01094 case WDP_ALIGN_TBL:
01095 pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left;
01096 break;
01097
01098 case WDP_AUTO:
01099 return GetAutoPlacePosition(desc->default_width, desc->default_height);
01100
01101 case WDP_CENTER:
01102 pt.x = (_screen.width - desc->default_width) / 2;
01103 break;
01104
01105 default:
01106 pt.x = desc->left;
01107 if (pt.x < 0) pt.x += _screen.width;
01108 }
01109
01110 switch (desc->top) {
01111 case WDP_CENTER:
01112 pt.y = (_screen.height - desc->default_height) / 2;
01113 break;
01114
01115
01116
01117 case WDP_AUTO:
01118 NOT_REACHED();
01119 assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO);
01120
01121
01122 default:
01123 pt.y = desc->top;
01124 if (pt.y < 0) pt.y += _screen.height;
01125 break;
01126 }
01127 }
01128
01129 return pt;
01130 }
01131
01140 Window::Window(const WindowDesc *desc, WindowNumber window_number)
01141 {
01142 Point pt = LocalGetWindowPlacement(desc, window_number);
01143 this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->cls, desc->widgets, window_number);
01144 this->desc_flags = desc->flags;
01145 }
01146
01152 Window *FindWindowFromPt(int x, int y)
01153 {
01154 Window *w;
01155 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01156 if (IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01157 return w;
01158 }
01159 }
01160
01161 return NULL;
01162 }
01163
01167 void InitWindowSystem()
01168 {
01169 IConsoleClose();
01170
01171 _z_back_window = NULL;
01172 _z_front_window = NULL;
01173 _focused_window = NULL;
01174 _mouseover_last_w = NULL;
01175 _scrolling_viewport = 0;
01176 }
01177
01181 void UnInitWindowSystem()
01182 {
01183 Window *w;
01184 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01185
01186 for (w = _z_front_window; w != NULL; ) {
01187 Window *to_del = w;
01188 w = w->z_back;
01189 free(to_del);
01190 }
01191
01192 _z_front_window = NULL;
01193 _z_back_window = NULL;
01194 }
01195
01199 void ResetWindowSystem()
01200 {
01201 UnInitWindowSystem();
01202 InitWindowSystem();
01203 _thd.pos.x = 0;
01204 _thd.pos.y = 0;
01205 _thd.new_pos.x = 0;
01206 _thd.new_pos.y = 0;
01207 }
01208
01209 static void DecreaseWindowCounters()
01210 {
01211 Window *w;
01212 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01213
01214 if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
01215 w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
01216 w->SetDirty();
01217 }
01218 w->OnMouseLoop();
01219 }
01220
01221 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01222 if (w->flags4 & WF_TIMEOUT_MASK && !(--w->flags4 & WF_TIMEOUT_MASK)) {
01223 w->OnTimeout();
01224 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons();
01225 }
01226 }
01227 }
01228
01229 Window *GetCallbackWnd()
01230 {
01231 return FindWindowById(_thd.window_class, _thd.window_number);
01232 }
01233
01234 static void HandlePlacePresize()
01235 {
01236 if (_special_mouse_mode != WSM_PRESIZE) return;
01237
01238 Window *w = GetCallbackWnd();
01239 if (w == NULL) return;
01240
01241 Point pt = GetTileBelowCursor();
01242 if (pt.x == -1) {
01243 _thd.selend.x = -1;
01244 return;
01245 }
01246
01247 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01248 }
01249
01250 static bool HandleDragDrop()
01251 {
01252 if (_special_mouse_mode != WSM_DRAGDROP) return true;
01253 if (_left_button_down) return false;
01254
01255 Window *w = GetCallbackWnd();
01256
01257 if (w != NULL) {
01258
01259 Point pt;
01260 pt.x = _cursor.pos.x - w->left;
01261 pt.y = _cursor.pos.y - w->top;
01262 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01263 }
01264
01265 ResetObjectToPlace();
01266
01267 return false;
01268 }
01269
01270 static bool HandleMouseOver()
01271 {
01272 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01273
01274
01275 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01276
01277 Point pt = { -1, -1 };
01278 _mouseover_last_w->OnMouseOver(pt, 0);
01279 }
01280
01281
01282 _mouseover_last_w = w;
01283
01284 if (w != NULL) {
01285
01286 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01287 int widget = 0;
01288 if (w->widget != NULL) {
01289 widget = GetWidgetFromPos(w, pt.x, pt.y);
01290 }
01291 w->OnMouseOver(pt, widget);
01292 }
01293
01294
01295 return true;
01296 }
01297
01307 void ResizeWindow(Window *w, int x, int y)
01308 {
01309 bool resize_height = false;
01310 bool resize_width = false;
01311
01312 if (x == 0 && y == 0) return;
01313
01314 w->SetDirty();
01315 for (Widget *wi = w->widget; wi->type != WWT_LAST; wi++) {
01316
01317 byte rsizeflag = GB(wi->display_flags, 0, 4);
01318
01319 if (rsizeflag == RESIZE_NONE) continue;
01320
01321
01322 if (rsizeflag & RESIZE_LEFT) {
01323 wi->left += x;
01324 resize_width = true;
01325 }
01326
01327 if (rsizeflag & RESIZE_RIGHT) {
01328 wi->right += x;
01329 resize_width = true;
01330 }
01331
01332 if (rsizeflag & RESIZE_TOP) {
01333 wi->top += y;
01334 resize_height = true;
01335 }
01336
01337 if (rsizeflag & RESIZE_BOTTOM) {
01338 wi->bottom += y;
01339 resize_height = true;
01340 }
01341 }
01342
01343
01344 if (resize_width) w->width += x;
01345 if (resize_height) w->height += y;
01346
01347 w->SetDirty();
01348 }
01349
01350 static bool _dragging_window;
01351
01352 static bool HandleWindowDragging()
01353 {
01354
01355 if (!_dragging_window) return true;
01356
01357
01358 Window *w;
01359 FOR_ALL_WINDOWS_FROM_BACK(w) {
01360 if (w->flags4 & WF_DRAGGING) {
01361 const Widget *t = &w->widget[1];
01362
01363
01364 if (!_left_button_down) {
01365 w->flags4 &= ~WF_DRAGGING;
01366 break;
01367 }
01368
01369 w->SetDirty();
01370
01371 int x = _cursor.pos.x + _drag_delta.x;
01372 int y = _cursor.pos.y + _drag_delta.y;
01373 int nx = x;
01374 int ny = y;
01375
01376 if (_settings_client.gui.window_snap_radius != 0) {
01377 const Window *v;
01378
01379 int hsnap = _settings_client.gui.window_snap_radius;
01380 int vsnap = _settings_client.gui.window_snap_radius;
01381 int delta;
01382
01383 FOR_ALL_WINDOWS_FROM_BACK(v) {
01384 if (v == w) continue;
01385
01386 if (y + w->height > v->top && y < v->top + v->height) {
01387
01388 delta = abs(v->left + v->width - x);
01389 if (delta <= hsnap) {
01390 nx = v->left + v->width;
01391 hsnap = delta;
01392 }
01393
01394
01395 delta = abs(v->left - x - w->width);
01396 if (delta <= hsnap) {
01397 nx = v->left - w->width;
01398 hsnap = delta;
01399 }
01400 }
01401
01402 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01403
01404 delta = abs(v->left - x);
01405 if (delta <= hsnap) {
01406 nx = v->left;
01407 hsnap = delta;
01408 }
01409
01410
01411 delta = abs(v->left + v->width - x - w->width);
01412 if (delta <= hsnap) {
01413 nx = v->left + v->width - w->width;
01414 hsnap = delta;
01415 }
01416 }
01417
01418 if (x + w->width > v->left && x < v->left + v->width) {
01419
01420 delta = abs(v->top + v->height - y);
01421 if (delta <= vsnap) {
01422 ny = v->top + v->height;
01423 vsnap = delta;
01424 }
01425
01426
01427 delta = abs(v->top - y - w->height);
01428 if (delta <= vsnap) {
01429 ny = v->top - w->height;
01430 vsnap = delta;
01431 }
01432 }
01433
01434 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01435
01436 delta = abs(v->top - y);
01437 if (delta <= vsnap) {
01438 ny = v->top;
01439 vsnap = delta;
01440 }
01441
01442
01443 delta = abs(v->top + v->height - y - w->height);
01444 if (delta <= vsnap) {
01445 ny = v->top + v->height - w->height;
01446 vsnap = delta;
01447 }
01448 }
01449 }
01450 }
01451
01452
01453
01454 nx = Clamp(nx, 13 - t->right, _screen.width - 13 - t->left);
01455 ny = Clamp(ny, 0, _screen.height - 13);
01456
01457
01458 Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0);
01459 if (v != NULL) {
01460 int v_bottom = v->top + v->height;
01461 int v_right = v->left + v->width;
01462 if (ny + t->top >= v->top && ny + t->top < v_bottom) {
01463 if ((v->left < 13 && nx + t->left < v->left) ||
01464 (v_right > _screen.width - 13 && nx + t->right > v_right)) {
01465 ny = v_bottom;
01466 } else {
01467 if (nx + t->left > v->left - 13 &&
01468 nx + t->right < v_right + 13) {
01469 if (w->top >= v_bottom) {
01470 ny = v_bottom;
01471 } else if (w->left < nx) {
01472 nx = v->left - 13 - t->left;
01473 } else {
01474 nx = v_right + 13 - t->right;
01475 }
01476 }
01477 }
01478 }
01479 }
01480
01481 if (w->viewport != NULL) {
01482 w->viewport->left += nx - w->left;
01483 w->viewport->top += ny - w->top;
01484 }
01485 w->left = nx;
01486 w->top = ny;
01487
01488 w->SetDirty();
01489 return false;
01490 } else if (w->flags4 & WF_SIZING) {
01491 int x, y;
01492
01493
01494 if (!_left_button_down) {
01495 w->flags4 &= ~WF_SIZING;
01496 w->SetDirty();
01497 break;
01498 }
01499
01500 x = _cursor.pos.x - _drag_delta.x;
01501 y = _cursor.pos.y - _drag_delta.y;
01502
01503
01504
01505
01506 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
01507
01508 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
01509
01510
01511 if ((int)w->width + x < (int)w->resize.width)
01512 x = w->resize.width - w->width;
01513 if ((int)w->height + y < (int)w->resize.height)
01514 y = w->resize.height - w->height;
01515
01516
01517 if (x == 0 && y == 0) return false;
01518
01519
01520
01521 _drag_delta.x += x;
01522 _drag_delta.y += y;
01523
01524
01525 ResizeWindow(w, x, y);
01526
01527 Point size;
01528 Point diff;
01529 size.x = x + w->width;
01530 size.y = y + w->height;
01531 diff.x = x;
01532 diff.y = y;
01533 w->OnResize(size, diff);
01534 return false;
01535 }
01536 }
01537
01538 _dragging_window = false;
01539 return false;
01540 }
01541
01546 static void StartWindowDrag(Window *w)
01547 {
01548 w->flags4 |= WF_DRAGGING;
01549 _dragging_window = true;
01550
01551 _drag_delta.x = w->left - _cursor.pos.x;
01552 _drag_delta.y = w->top - _cursor.pos.y;
01553
01554 BringWindowToFront(w);
01555 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01556 }
01557
01562 static void StartWindowSizing(Window *w)
01563 {
01564 w->flags4 |= WF_SIZING;
01565 _dragging_window = true;
01566
01567 _drag_delta.x = _cursor.pos.x;
01568 _drag_delta.y = _cursor.pos.y;
01569
01570 BringWindowToFront(w);
01571 DeleteWindowById(WC_DROPDOWN_MENU, 0);
01572 }
01573
01574
01575 static bool HandleScrollbarScrolling()
01576 {
01577 Window *w;
01578
01579
01580 if (!_scrolling_scrollbar) return true;
01581
01582
01583 FOR_ALL_WINDOWS_FROM_BACK(w) {
01584 if (w->flags4 & WF_SCROLL_MIDDLE) {
01585
01586 if (!_left_button_down) {
01587 w->flags4 &= ~WF_SCROLL_MIDDLE;
01588 w->SetDirty();
01589 break;
01590 }
01591
01592 int i;
01593 Scrollbar *sb;
01594
01595 if (w->flags4 & WF_HSCROLL) {
01596 sb = &w->hscroll;
01597 i = _cursor.pos.x - _cursorpos_drag_start.x;
01598 } else if (w->flags4 & WF_SCROLL2){
01599 sb = &w->vscroll2;
01600 i = _cursor.pos.y - _cursorpos_drag_start.y;
01601 } else {
01602 sb = &w->vscroll;
01603 i = _cursor.pos.y - _cursorpos_drag_start.y;
01604 }
01605
01606
01607 int pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap));
01608 if (pos != sb->pos) {
01609 sb->pos = pos;
01610 w->SetDirty();
01611 }
01612 return false;
01613 }
01614 }
01615
01616 _scrolling_scrollbar = false;
01617 return false;
01618 }
01619
01620 static bool HandleViewportScroll()
01621 {
01622 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
01623
01624 if (!_scrolling_viewport) return true;
01625
01626 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01627
01628 if (!(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) || w == NULL) {
01629 _cursor.fix_at = false;
01630 _scrolling_viewport = false;
01631 return true;
01632 }
01633
01634 if (w == FindWindowById(WC_MAIN_WINDOW, 0) && w->viewport->follow_vehicle != INVALID_VEHICLE) {
01635
01636 const Vehicle *veh = GetVehicle(w->viewport->follow_vehicle);
01637 ScrollMainWindowTo(veh->x_pos, veh->y_pos, true);
01638 return true;
01639 }
01640
01641 Point delta;
01642 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
01643 delta.x = -_cursor.delta.x;
01644 delta.y = -_cursor.delta.y;
01645 } else {
01646 delta.x = _cursor.delta.x;
01647 delta.y = _cursor.delta.y;
01648 }
01649
01650 if (scrollwheel_scrolling) {
01651
01652 delta.x = _cursor.h_wheel;
01653 delta.y = _cursor.v_wheel;
01654 _cursor.v_wheel = 0;
01655 _cursor.h_wheel = 0;
01656 }
01657
01658
01659 w->OnScroll(delta);
01660
01661 _cursor.delta.x = 0;
01662 _cursor.delta.y = 0;
01663 return false;
01664 }
01665
01674 static bool MaybeBringWindowToFront(Window *w)
01675 {
01676 bool bring_to_front = false;
01677
01678 if (w->window_class == WC_MAIN_WINDOW ||
01679 IsVitalWindow(w) ||
01680 w->window_class == WC_TOOLTIPS ||
01681 w->window_class == WC_DROPDOWN_MENU) {
01682 return true;
01683 }
01684
01685 Window *u;
01686 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
01687
01688 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
01689 u->flags4 |= WF_WHITE_BORDER_MASK;
01690 u->SetDirty();
01691 return false;
01692 }
01693
01694 if (u->window_class == WC_MAIN_WINDOW ||
01695 IsVitalWindow(u) ||
01696 u->window_class == WC_TOOLTIPS ||
01697 u->window_class == WC_DROPDOWN_MENU) {
01698 continue;
01699 }
01700
01701
01702 if (w->left + w->width <= u->left ||
01703 u->left + u->width <= w->left ||
01704 w->top + w->height <= u->top ||
01705 u->top + u->height <= w->top) {
01706 continue;
01707 }
01708
01709 bring_to_front = true;
01710 }
01711
01712 if (bring_to_front) BringWindowToFront(w);
01713 return true;
01714 }
01715
01719 void HandleKeypress(uint32 raw_key)
01720 {
01721
01722
01723
01724
01725
01726
01727
01728
01729
01730 if (!IsGeneratingWorld()) _current_company = _local_company;
01731
01732
01733 uint16 key = GB(raw_key, 0, 16);
01734 uint16 keycode = GB(raw_key, 16, 16);
01735
01736
01737
01738
01739
01740
01741
01742
01743 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
01744
01745
01746
01747
01748 if (key == 0 && keycode == 0) return;
01749
01750
01751 if (EditBoxInGlobalFocus()) {
01752
01753 _focused_window->OnKeyPress(key, keycode);
01754 return;
01755 }
01756
01757
01758 Window *w;
01759 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01760 if (w->OnKeyPress(key, keycode) == Window::ES_HANDLED) return;
01761 }
01762
01763 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01764
01765 if (w != NULL) w->OnKeyPress(key, keycode);
01766 }
01767
01771 void HandleCtrlChanged()
01772 {
01773
01774 Window *w;
01775 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01776 if (w->OnCTRLStateChange() == Window::ES_HANDLED) return;
01777 }
01778 }
01779
01786 static int _input_events_this_tick = 0;
01787
01792 static void HandleAutoscroll()
01793 {
01794 if (_settings_client.gui.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
01795 int x = _cursor.pos.x;
01796 int y = _cursor.pos.y;
01797 Window *w = FindWindowFromPt(x, y);
01798 if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
01799 ViewPort *vp = IsPtInWindowViewport(w, x, y);
01800 if (vp != NULL) {
01801 x -= vp->left;
01802 y -= vp->top;
01803
01804
01805 #define scrollspeed 3
01806 if (x - 15 < 0) {
01807 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
01808 } else if (15 - (vp->width - x) > 0) {
01809 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
01810 }
01811 if (y - 15 < 0) {
01812 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
01813 } else if (15 - (vp->height - y) > 0) {
01814 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
01815 }
01816 #undef scrollspeed
01817 }
01818 }
01819 }
01820
01821 enum MouseClick {
01822 MC_NONE = 0,
01823 MC_LEFT,
01824 MC_RIGHT,
01825 MC_DOUBLE_LEFT,
01826
01827 MAX_OFFSET_DOUBLE_CLICK = 5,
01828 TIME_BETWEEN_DOUBLE_CLICK = 500,
01829 };
01830
01831 extern bool VpHandlePlaceSizingDrag();
01832
01833 static void ScrollMainViewport(int x, int y)
01834 {
01835 if (_game_mode != GM_MENU) {
01836 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
01837 assert(w);
01838
01839 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
01840 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
01841 }
01842 }
01843
01853 static const int8 scrollamt[16][2] = {
01854 { 0, 0},
01855 {-2, 0},
01856 { 0, -2},
01857 {-2, -1},
01858 { 2, 0},
01859 { 0, 0},
01860 { 2, -1},
01861 { 0, -2},
01862 { 0 ,2},
01863 {-2 ,1},
01864 { 0, 0},
01865 {-2, 0},
01866 { 2, 1},
01867 { 0, 2},
01868 { 2, 0},
01869 { 0, 0},
01870 };
01871
01872 static void HandleKeyScrolling()
01873 {
01874
01875
01876
01877
01878 if (_dirkeys && !EditBoxInGlobalFocus()) {
01879 int factor = _shift_pressed ? 50 : 10;
01880 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
01881 }
01882 }
01883
01884 void MouseLoop(MouseClick click, int mousewheel)
01885 {
01886 DecreaseWindowCounters();
01887 HandlePlacePresize();
01888 UpdateTileSelection();
01889
01890 if (!VpHandlePlaceSizingDrag()) return;
01891 if (!HandleDragDrop()) return;
01892 if (!HandleWindowDragging()) return;
01893 if (!HandleScrollbarScrolling()) return;
01894 if (!HandleViewportScroll()) return;
01895 if (!HandleMouseOver()) return;
01896
01897 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
01898 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
01899
01900 int x = _cursor.pos.x;
01901 int y = _cursor.pos.y;
01902 Window *w = FindWindowFromPt(x, y);
01903 if (w == NULL) return;
01904
01905 if (!MaybeBringWindowToFront(w)) return;
01906 ViewPort *vp = IsPtInWindowViewport(w, x, y);
01907
01908
01909 if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return;
01910
01911 if (mousewheel != 0) {
01912 if (_settings_client.gui.scrollwheel_scrolling == 0) {
01913
01914 w->OnMouseWheel(mousewheel);
01915 }
01916
01917
01918 if (vp == NULL) DispatchMouseWheelEvent(w, GetWidgetFromPos(w, x - w->left, y - w->top), mousewheel);
01919 }
01920
01921 if (vp != NULL) {
01922 if (scrollwheel_scrolling) click = MC_RIGHT;
01923 switch (click) {
01924 case MC_DOUBLE_LEFT:
01925 case MC_LEFT:
01926 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
01927 if (_thd.place_mode != VHM_NONE &&
01928
01929 _cursor.sprite != SPR_CURSOR_QUERY &&
01930 _cursor.sprite != SPR_CURSOR_SIGN &&
01931 _pause_game != 0 &&
01932 !_cheats.build_in_pause.value) {
01933 return;
01934 }
01935
01936 if (_thd.place_mode == VHM_NONE) {
01937 if (!HandleViewportClicked(vp, x, y) &&
01938 !(w->flags4 & WF_DISABLE_VP_SCROLL) &&
01939 _settings_client.gui.left_mouse_btn_scrolling) {
01940 _scrolling_viewport = true;
01941 _cursor.fix_at = false;
01942 }
01943 } else {
01944 PlaceObject();
01945 }
01946 break;
01947
01948 case MC_RIGHT:
01949 if (!(w->flags4 & WF_DISABLE_VP_SCROLL)) {
01950 _scrolling_viewport = true;
01951 _cursor.fix_at = true;
01952 }
01953 break;
01954
01955 default:
01956 break;
01957 }
01958 } else {
01959 switch (click) {
01960 case MC_DOUBLE_LEFT:
01961 DispatchLeftClickEvent(w, x - w->left, y - w->top, true);
01962 if (_mouseover_last_w == NULL) break;
01963
01964 case MC_LEFT:
01965 DispatchLeftClickEvent(w, x - w->left, y - w->top, false);
01966 break;
01967
01968 default:
01969 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
01970
01971
01972
01973 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
01974 }
01975 }
01976 }
01977
01981 void HandleMouseEvents()
01982 {
01983 static int double_click_time = 0;
01984 static int double_click_x = 0;
01985 static int double_click_y = 0;
01986
01987
01988
01989
01990
01991
01992
01993
01994
01995
01996 if (!IsGeneratingWorld()) _current_company = _local_company;
01997
01998
01999 MouseClick click = MC_NONE;
02000 if (_left_button_down && !_left_button_clicked) {
02001 click = MC_LEFT;
02002 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02003 double_click_x != 0 && abs(_cursor.pos.x - double_click_x) < MAX_OFFSET_DOUBLE_CLICK &&
02004 double_click_y != 0 && abs(_cursor.pos.y - double_click_y) < MAX_OFFSET_DOUBLE_CLICK) {
02005 click = MC_DOUBLE_LEFT;
02006 }
02007 double_click_time = _realtime_tick;
02008 double_click_x = _cursor.pos.x;
02009 double_click_y = _cursor.pos.y;
02010 _left_button_clicked = true;
02011 _input_events_this_tick++;
02012 } else if (_right_button_clicked) {
02013 _right_button_clicked = false;
02014 click = MC_RIGHT;
02015 _input_events_this_tick++;
02016 }
02017
02018 int mousewheel = 0;
02019 if (_cursor.wheel) {
02020 mousewheel = _cursor.wheel;
02021 _cursor.wheel = 0;
02022 _input_events_this_tick++;
02023 }
02024
02025 MouseLoop(click, mousewheel);
02026 }
02027
02031 static void CheckSoftLimit()
02032 {
02033 if (_settings_client.gui.window_soft_limit == 0) return;
02034
02035 for (;;) {
02036 uint deletable_count = 0;
02037 Window *w, *last_deletable = NULL;
02038 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02039 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags4 & WF_STICKY)) continue;
02040
02041 last_deletable = w;
02042 deletable_count++;
02043 }
02044
02045
02046 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02047
02048 assert(last_deletable != NULL);
02049 delete last_deletable;
02050 }
02051 }
02052
02056 void InputLoop()
02057 {
02058 CheckSoftLimit();
02059 HandleKeyScrolling();
02060
02061
02062 for (Window *v = _z_front_window; v != NULL; ) {
02063 Window *w = v;
02064 v = v->z_back;
02065
02066 if (w->window_class != WC_INVALID) continue;
02067
02068
02069
02070
02071
02072 if (w->z_front == NULL) {
02073 _z_front_window = w->z_back;
02074 } else {
02075 w->z_front->z_back = w->z_back;
02076 }
02077 if (w->z_back == NULL) {
02078 _z_back_window = w->z_front;
02079 } else {
02080 w->z_back->z_front = w->z_front;
02081 }
02082 free(w);
02083 }
02084
02085 if (_input_events_this_tick != 0) {
02086
02087 _input_events_this_tick = 0;
02088
02089 return;
02090 }
02091
02092
02093 HandleMouseEvents();
02094 HandleAutoscroll();
02095 }
02096
02100 void UpdateWindows()
02101 {
02102 Window *w;
02103 static int we4_timer = 0;
02104 int t = we4_timer + 1;
02105
02106 if (t >= 100) {
02107 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02108 w->OnHundredthTick();
02109 }
02110 t = 0;
02111 }
02112 we4_timer = t;
02113
02114 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02115 if (w->flags4 & WF_WHITE_BORDER_MASK) {
02116 w->flags4 -= WF_WHITE_BORDER_ONE;
02117
02118 if (!(w->flags4 & WF_WHITE_BORDER_MASK)) w->SetDirty();
02119 }
02120 }
02121
02122 DrawDirtyBlocks();
02123
02124 FOR_ALL_WINDOWS_FROM_BACK(w) {
02125 if (w->viewport != NULL) UpdateViewportPosition(w);
02126 }
02127 NetworkDrawChatMessage();
02128
02129 DrawMouseCursor();
02130 }
02131
02137 void InvalidateWindow(WindowClass cls, WindowNumber number)
02138 {
02139 const Window *w;
02140 FOR_ALL_WINDOWS_FROM_BACK(w) {
02141 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02142 }
02143 }
02144
02151 void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index)
02152 {
02153 const Window *w;
02154 FOR_ALL_WINDOWS_FROM_BACK(w) {
02155 if (w->window_class == cls && w->window_number == number) {
02156 w->InvalidateWidget(widget_index);
02157 }
02158 }
02159 }
02160
02165 void InvalidateWindowClasses(WindowClass cls)
02166 {
02167 Window *w;
02168 FOR_ALL_WINDOWS_FROM_BACK(w) {
02169 if (w->window_class == cls) w->SetDirty();
02170 }
02171 }
02172
02177 void InvalidateThisWindowData(Window *w, int data)
02178 {
02179 w->OnInvalidateData(data);
02180 w->SetDirty();
02181 }
02182
02188 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data)
02189 {
02190 Window *w;
02191 FOR_ALL_WINDOWS_FROM_BACK(w) {
02192 if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w, data);
02193 }
02194 }
02195
02200 void InvalidateWindowClassesData(WindowClass cls, int data)
02201 {
02202 Window *w;
02203
02204 FOR_ALL_WINDOWS_FROM_BACK(w) {
02205 if (w->window_class == cls) InvalidateThisWindowData(w, data);
02206 }
02207 }
02208
02212 void CallWindowTickEvent()
02213 {
02214 if (_scroller_click_timeout > 3) {
02215 _scroller_click_timeout -= 3;
02216 } else {
02217 _scroller_click_timeout = 0;
02218 }
02219
02220 Window *w;
02221 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02222 w->OnTick();
02223 }
02224 }
02225
02232 void DeleteNonVitalWindows()
02233 {
02234 Window *w;
02235
02236 restart_search:
02237
02238
02239
02240 FOR_ALL_WINDOWS_FROM_BACK(w) {
02241 if (w->window_class != WC_MAIN_WINDOW &&
02242 w->window_class != WC_SELECT_GAME &&
02243 w->window_class != WC_MAIN_TOOLBAR &&
02244 w->window_class != WC_STATUS_BAR &&
02245 w->window_class != WC_TOOLBAR_MENU &&
02246 w->window_class != WC_TOOLTIPS &&
02247 (w->flags4 & WF_STICKY) == 0) {
02248
02249 delete w;
02250 goto restart_search;
02251 }
02252 }
02253 }
02254
02260 void DeleteAllNonVitalWindows()
02261 {
02262 Window *w;
02263
02264
02265 DeleteNonVitalWindows();
02266
02267 restart_search:
02268
02269
02270
02271 FOR_ALL_WINDOWS_FROM_BACK(w) {
02272 if (w->flags4 & WF_STICKY) {
02273 delete w;
02274 goto restart_search;
02275 }
02276 }
02277 }
02278
02283 void DeleteConstructionWindows()
02284 {
02285 Window *w;
02286
02287 restart_search:
02288
02289
02290
02291 FOR_ALL_WINDOWS_FROM_BACK(w) {
02292 if (w->desc_flags & WDF_CONSTRUCTION) {
02293 delete w;
02294 goto restart_search;
02295 }
02296 }
02297
02298 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02299 }
02300
02302 void HideVitalWindows()
02303 {
02304 DeleteWindowById(WC_TOOLBAR_MENU, 0);
02305 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02306 DeleteWindowById(WC_STATUS_BAR, 0);
02307 }
02308
02314 int PositionMainToolbar(Window *w)
02315 {
02316 DEBUG(misc, 5, "Repositioning Main Toolbar...");
02317
02318 if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
02319 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02320 }
02321
02322 switch (_settings_client.gui.toolbar_pos) {
02323 case 1: w->left = (_screen.width - w->width) / 2; break;
02324 case 2: w->left = _screen.width - w->width; break;
02325 default: w->left = 0;
02326 }
02327 SetDirtyBlocks(0, 0, _screen.width, w->height);
02328 return w->left;
02329 }
02330
02338 void SetVScrollCount(Window *w, int num)
02339 {
02340 w->vscroll.count = num;
02341 num -= w->vscroll.cap;
02342 if (num < 0) num = 0;
02343 if (num < w->vscroll.pos) w->vscroll.pos = num;
02344 }
02345
02353 void SetVScroll2Count(Window *w, int num)
02354 {
02355 w->vscroll2.count = num;
02356 num -= w->vscroll2.cap;
02357 if (num < 0) num = 0;
02358 if (num < w->vscroll2.pos) w->vscroll2.pos = num;
02359 }
02360
02368 void SetHScrollCount(Window *w, int num)
02369 {
02370 w->hscroll.count = num;
02371 num -= w->hscroll.cap;
02372 if (num < 0) num = 0;
02373 if (num < w->hscroll.pos) w->hscroll.pos = num;
02374 }
02375
02381 void RelocateAllWindows(int neww, int newh)
02382 {
02383 Window *w;
02384
02385 FOR_ALL_WINDOWS_FROM_BACK(w) {
02386 int left, top;
02387
02388 if (w->window_class == WC_MAIN_WINDOW) {
02389 ViewPort *vp = w->viewport;
02390 vp->width = w->width = neww;
02391 vp->height = w->height = newh;
02392 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
02393 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
02394 continue;
02395 }
02396
02397
02398
02399 switch (w->window_class) {
02400 case WC_MAIN_TOOLBAR:
02401 if (neww - w->width != 0) {
02402 ResizeWindow(w, min(neww, 640) - w->width, 0);
02403
02404 Point size;
02405 Point delta;
02406 size.x = w->width;
02407 size.y = w->height;
02408 delta.x = neww - w->width;
02409 delta.y = 0;
02410 w->OnResize(size, delta);
02411 }
02412
02413 top = w->top;
02414 left = PositionMainToolbar(w);
02415 break;
02416
02417 case WC_SELECT_GAME:
02418 case WC_GAME_OPTIONS:
02419 case WC_NETWORK_WINDOW:
02420 top = (newh - w->height) >> 1;
02421 left = (neww - w->width) >> 1;
02422 break;
02423
02424 case WC_NEWS_WINDOW:
02425 top = newh - w->height;
02426 left = (neww - w->width) >> 1;
02427 break;
02428
02429 case WC_STATUS_BAR:
02430 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
02431 top = newh - w->height;
02432 left = (neww - w->width) >> 1;
02433 break;
02434
02435 case WC_SEND_NETWORK_MSG:
02436 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
02437 top = (newh - 26);
02438 left = (neww - w->width) >> 1;
02439 break;
02440
02441 case WC_CONSOLE:
02442 IConsoleResize(w);
02443 continue;
02444
02445 default: {
02446 left = w->left;
02447 if (left + (w->width >> 1) >= neww) left = neww - w->width;
02448 if (left < 0) left = 0;
02449
02450 top = w->top;
02451 if (top + (w->height >> 1) >= newh) top = newh - w->height;
02452
02453 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
02454 if (wt != NULL) {
02455 if (top < wt->height && wt->left < (w->left + w->width) && (wt->left + wt->width) > w->left) top = wt->height;
02456 if (top >= newh) top = newh - 1;
02457 } else {
02458 if (top < 0) top = 0;
02459 }
02460 } break;
02461 }
02462
02463 if (w->viewport != NULL) {
02464 w->viewport->left += left - w->left;
02465 w->viewport->top += top - w->top;
02466 }
02467
02468 w->left = left;
02469 w->top = top;
02470 }
02471 }
02472
02477 PickerWindowBase::~PickerWindowBase()
02478 {
02479 this->window_class = WC_INVALID;
02480 ResetObjectToPlace();
02481 }