00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include <stdarg.h>
00014 #include "company_func.h"
00015 #include "gfx_func.h"
00016 #include "console_func.h"
00017 #include "console_gui.h"
00018 #include "viewport_func.h"
00019 #include "genworld.h"
00020 #include "progress.h"
00021 #include "blitter/factory.hpp"
00022 #include "zoom_func.h"
00023 #include "vehicle_base.h"
00024 #include "window_func.h"
00025 #include "tilehighlight_func.h"
00026 #include "network/network.h"
00027 #include "querystring_gui.h"
00028 #include "widgets/dropdown_func.h"
00029 #include "strings_func.h"
00030 #include "settings_type.h"
00031 #include "newgrf_debug.h"
00032 #include "hotkeys.h"
00033 #include "toolbar_gui.h"
00034 #include "statusbar_gui.h"
00035 #include "error.h"
00036 #include "game/game.hpp"
00037
00038
00039 static Point _drag_delta;
00040 static Window *_mouseover_last_w = NULL;
00041 static Window *_last_scroll_window = NULL;
00042
00044 Window *_z_front_window = NULL;
00046 Window *_z_back_window = NULL;
00047
00049 bool _window_highlight_colour = false;
00050
00051
00052
00053
00054
00055
00056 Window *_focused_window;
00057
00058 Point _cursorpos_drag_start;
00059
00060 int _scrollbar_start_pos;
00061 int _scrollbar_size;
00062 byte _scroller_click_timeout = 0;
00063
00064 bool _scrolling_viewport;
00065 bool _mouse_hovering;
00066
00067 SpecialMouseMode _special_mouse_mode;
00068
00070 WindowDesc::WindowDesc(WindowPosition def_pos, int16 def_width, int16 def_height,
00071 WindowClass window_class, WindowClass parent_class, uint32 flags,
00072 const NWidgetPart *nwid_parts, int16 nwid_length) :
00073 default_pos(def_pos),
00074 default_width(def_width),
00075 default_height(def_height),
00076 cls(window_class),
00077 parent_cls(parent_class),
00078 flags(flags),
00079 nwid_parts(nwid_parts),
00080 nwid_length(nwid_length)
00081 {
00082 }
00083
00084 WindowDesc::~WindowDesc()
00085 {
00086 }
00087
00097 int Window::GetRowFromWidget(int clickpos, int widget, int padding, int line_height) const
00098 {
00099 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
00100 if (line_height < 0) line_height = wid->resize_y;
00101 if (clickpos < (int)wid->pos_y + padding) return INT_MAX;
00102 return (clickpos - (int)wid->pos_y - padding) / line_height;
00103 }
00104
00108 void Window::DisableAllWidgetHighlight()
00109 {
00110 for (uint i = 0; i < this->nested_array_size; i++) {
00111 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(i);
00112 if (nwid == NULL) continue;
00113
00114 if (nwid->IsHighlighted()) {
00115 nwid->SetHighlighted(TC_INVALID);
00116 this->SetWidgetDirty(i);
00117 }
00118 }
00119
00120 CLRBITS(this->flags, WF_HIGHLIGHTED);
00121 }
00122
00128 void Window::SetWidgetHighlight(byte widget_index, TextColour highlighted_colour)
00129 {
00130 assert(widget_index < this->nested_array_size);
00131
00132 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
00133 if (nwid == NULL) return;
00134
00135 nwid->SetHighlighted(highlighted_colour);
00136 this->SetWidgetDirty(widget_index);
00137
00138 if (highlighted_colour != TC_INVALID) {
00139
00140 this->flags |= WF_HIGHLIGHTED;
00141 } else {
00142
00143 bool valid = false;
00144 for (uint i = 0; i < this->nested_array_size; i++) {
00145 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(i);
00146 if (nwid == NULL) continue;
00147 if (!nwid->IsHighlighted()) continue;
00148
00149 valid = true;
00150 }
00151
00152 if (!valid) CLRBITS(this->flags, WF_HIGHLIGHTED);
00153 }
00154 }
00155
00161 bool Window::IsWidgetHighlighted(byte widget_index) const
00162 {
00163 assert(widget_index < this->nested_array_size);
00164
00165 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
00166 if (nwid == NULL) return false;
00167
00168 return nwid->IsHighlighted();
00169 }
00170
00176 const Scrollbar *Window::GetScrollbar(uint widnum) const
00177 {
00178 return this->GetWidget<NWidgetScrollbar>(widnum);
00179 }
00180
00186 Scrollbar *Window::GetScrollbar(uint widnum)
00187 {
00188 return this->GetWidget<NWidgetScrollbar>(widnum);
00189 }
00190
00191
00196 void SetFocusedWindow(Window *w)
00197 {
00198 if (_focused_window == w) return;
00199
00200
00201 if (_focused_window != NULL) {
00202 if (_focused_window->nested_focus != NULL) _focused_window->nested_focus->SetDirty(_focused_window);
00203 }
00204
00205
00206 Window *old_focused = _focused_window;
00207 _focused_window = w;
00208
00209
00210 if (old_focused != NULL) old_focused->OnFocusLost();
00211 if (_focused_window != NULL) _focused_window->OnFocus();
00212 }
00213
00219 static bool EditBoxInGlobalFocus()
00220 {
00221 if (_focused_window == NULL) return false;
00222
00223
00224 if (_focused_window->window_class == WC_CONSOLE) return true;
00225
00226 return _focused_window->nested_focus != NULL && _focused_window->nested_focus->type == WWT_EDITBOX;
00227 }
00228
00232 void Window::UnfocusFocusedWidget()
00233 {
00234 if (this->nested_focus != NULL) {
00235
00236 this->nested_focus->SetDirty(this);
00237 this->nested_focus = NULL;
00238 }
00239 }
00240
00246 bool Window::SetFocusedWidget(byte widget_index)
00247 {
00248
00249 if (widget_index >= this->nested_array_size) return false;
00250
00251 assert(this->nested_array[widget_index] != NULL);
00252 if (this->nested_focus != NULL) {
00253 if (this->GetWidget<NWidgetCore>(widget_index) == this->nested_focus) return false;
00254
00255
00256 this->nested_focus->SetDirty(this);
00257 }
00258 this->nested_focus = this->GetWidget<NWidgetCore>(widget_index);
00259 return true;
00260 }
00261
00269 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
00270 {
00271 va_list wdg_list;
00272
00273 va_start(wdg_list, widgets);
00274
00275 while (widgets != WIDGET_LIST_END) {
00276 SetWidgetDisabledState(widgets, disab_stat);
00277 widgets = va_arg(wdg_list, int);
00278 }
00279
00280 va_end(wdg_list);
00281 }
00282
00288 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
00289 {
00290 va_list wdg_list;
00291
00292 va_start(wdg_list, widgets);
00293
00294 while (widgets != WIDGET_LIST_END) {
00295 SetWidgetLoweredState(widgets, lowered_stat);
00296 widgets = va_arg(wdg_list, int);
00297 }
00298
00299 va_end(wdg_list);
00300 }
00301
00306 void Window::RaiseButtons(bool autoraise)
00307 {
00308 for (uint i = 0; i < this->nested_array_size; i++) {
00309 if (this->nested_array[i] != NULL && (this->nested_array[i]->type & ~WWB_PUSHBUTTON) < WWT_LAST &&
00310 (!autoraise || (this->nested_array[i]->type & WWB_PUSHBUTTON)) && this->IsWidgetLowered(i)) {
00311 this->RaiseWidget(i);
00312 this->SetWidgetDirty(i);
00313 }
00314 }
00315 }
00316
00321 void Window::SetWidgetDirty(byte widget_index) const
00322 {
00323
00324 if (this->nested_array == NULL) return;
00325
00326 this->nested_array[widget_index]->SetDirty(this);
00327 }
00328
00334 void Window::HandleButtonClick(byte widget)
00335 {
00336 this->LowerWidget(widget);
00337 this->SetTimeout();
00338 this->SetWidgetDirty(widget);
00339 }
00340
00341 static void StartWindowDrag(Window *w);
00342 static void StartWindowSizing(Window *w, bool to_left);
00343
00351 static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
00352 {
00353 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
00354 WidgetType widget_type = (nw != NULL) ? nw->type : WWT_EMPTY;
00355
00356 bool focused_widget_changed = false;
00357
00358 if (_focused_window != w &&
00359 (w->desc_flags & WDF_NO_FOCUS) == 0 &&
00360 widget_type != WWT_CLOSEBOX) {
00361 focused_widget_changed = true;
00362 if (_focused_window != NULL) {
00363 _focused_window->OnFocusLost();
00364
00365
00366 if (w->window_class != WC_OSK) DeleteWindowById(WC_OSK, 0);
00367 }
00368 SetFocusedWindow(w);
00369 w->OnFocus();
00370 }
00371
00372 if (nw == NULL) return;
00373
00374
00375 if (nw->IsDisabled()) return;
00376
00377 int widget_index = nw->index;
00378
00379
00380
00381 if (widget_type != WWT_CAPTION) {
00382
00383 if (w->nested_focus != NULL && w->nested_focus->type == WWT_EDITBOX && w->nested_focus != nw && w->window_class != WC_OSK) {
00384 DeleteWindowById(WC_OSK, 0);
00385 }
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396 focused_widget_changed |= w->SetFocusedWidget(widget_index);
00397 }
00398
00399
00400
00401 if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
00402
00403 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
00404
00405 switch (widget_type) {
00406 case NWID_VSCROLLBAR:
00407 case NWID_HSCROLLBAR:
00408 ScrollbarClickHandler(w, nw, x, y);
00409 break;
00410
00411 case WWT_EDITBOX:
00412 if (!focused_widget_changed) {
00413
00414 QueryStringBaseWindow *qs = dynamic_cast<QueryStringBaseWindow *>(w);
00415 if (qs != NULL) {
00416 qs->OnOpenOSKWindow(widget_index);
00417 }
00418 }
00419 break;
00420
00421 case WWT_CLOSEBOX:
00422 delete w;
00423 return;
00424
00425 case WWT_CAPTION:
00426 StartWindowDrag(w);
00427 return;
00428
00429 case WWT_RESIZEBOX:
00430
00431
00432 StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
00433 nw->SetDirty(w);
00434 return;
00435
00436 case WWT_DEBUGBOX:
00437 w->ShowNewGRFInspectWindow();
00438 break;
00439
00440 case WWT_SHADEBOX:
00441 nw->SetDirty(w);
00442 w->SetShaded(!w->IsShaded());
00443 return;
00444
00445 case WWT_STICKYBOX:
00446 w->flags ^= WF_STICKY;
00447 nw->SetDirty(w);
00448 return;
00449
00450 default:
00451 break;
00452 }
00453
00454
00455 if (widget_index < 0) return;
00456
00457
00458 if (w->IsWidgetHighlighted(widget_index)) {
00459 w->SetWidgetHighlight(widget_index, TC_INVALID);
00460 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
00461 }
00462
00463 Point pt = { x, y };
00464 w->OnClick(pt, widget_index, click_count);
00465 }
00466
00473 static void DispatchRightClickEvent(Window *w, int x, int y)
00474 {
00475 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00476 if (wid == NULL) return;
00477
00478
00479 if (wid->index >= 0) {
00480 Point pt = { x, y };
00481 if (w->OnRightClick(pt, wid->index)) return;
00482 }
00483
00484 if (_settings_client.gui.hover_delay == 0 && wid->tool_tip != 0) GuiShowTooltips(w, wid->tool_tip, 0, NULL, TCC_RIGHT_CLICK);
00485 }
00486
00493 static void DispatchHoverEvent(Window *w, int x, int y)
00494 {
00495 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00496
00497
00498 if (wid == NULL) return;
00499
00500
00501 if (wid->tool_tip != 0) {
00502 GuiShowTooltips(w, wid->tool_tip);
00503 return;
00504 }
00505
00506
00507 if (wid->index < 0) return;
00508
00509 Point pt = { x, y };
00510 w->OnHover(pt, wid->index);
00511 }
00512
00520 static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
00521 {
00522 if (nwid == NULL) return;
00523
00524
00525 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
00526 w->SetShaded(wheel < 0);
00527 return;
00528 }
00529
00530
00531 if (nwid->type == NWID_VSCROLLBAR) {
00532 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
00533 if (sb->GetCount() > sb->GetCapacity()) {
00534 sb->UpdatePosition(wheel);
00535 w->SetDirty();
00536 }
00537 return;
00538 }
00539
00540
00541 Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : NULL);
00542 if (sb != NULL && sb->GetCount() > sb->GetCapacity()) {
00543 sb->UpdatePosition(wheel);
00544 w->SetDirty();
00545 }
00546 }
00547
00553 static bool MayBeShown(const Window *w)
00554 {
00555
00556 if (!HasModalProgress()) return true;
00557
00558 switch (w->window_class) {
00559 case WC_MAIN_WINDOW:
00560 case WC_MODAL_PROGRESS:
00561 case WC_QUERY_STRING:
00562 return true;
00563
00564 default:
00565 return false;
00566 }
00567 }
00568
00581 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00582 {
00583 const Window *v;
00584 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00585 if (MayBeShown(v) &&
00586 right > v->left &&
00587 bottom > v->top &&
00588 left < v->left + v->width &&
00589 top < v->top + v->height) {
00590
00591 int x;
00592
00593 if (left < (x = v->left)) {
00594 DrawOverlappedWindow(w, left, top, x, bottom);
00595 DrawOverlappedWindow(w, x, top, right, bottom);
00596 return;
00597 }
00598
00599 if (right > (x = v->left + v->width)) {
00600 DrawOverlappedWindow(w, left, top, x, bottom);
00601 DrawOverlappedWindow(w, x, top, right, bottom);
00602 return;
00603 }
00604
00605 if (top < (x = v->top)) {
00606 DrawOverlappedWindow(w, left, top, right, x);
00607 DrawOverlappedWindow(w, left, x, right, bottom);
00608 return;
00609 }
00610
00611 if (bottom > (x = v->top + v->height)) {
00612 DrawOverlappedWindow(w, left, top, right, x);
00613 DrawOverlappedWindow(w, left, x, right, bottom);
00614 return;
00615 }
00616
00617 return;
00618 }
00619 }
00620
00621
00622 DrawPixelInfo *dp = _cur_dpi;
00623 dp->width = right - left;
00624 dp->height = bottom - top;
00625 dp->left = left - w->left;
00626 dp->top = top - w->top;
00627 dp->pitch = _screen.pitch;
00628 dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00629 dp->zoom = ZOOM_LVL_NORMAL;
00630 w->OnPaint();
00631 }
00632
00641 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00642 {
00643 Window *w;
00644 DrawPixelInfo bk;
00645 _cur_dpi = &bk;
00646
00647 FOR_ALL_WINDOWS_FROM_BACK(w) {
00648 if (MayBeShown(w) &&
00649 right > w->left &&
00650 bottom > w->top &&
00651 left < w->left + w->width &&
00652 top < w->top + w->height) {
00653
00654 DrawOverlappedWindow(w, left, top, right, bottom);
00655 }
00656 }
00657 }
00658
00663 void Window::SetDirty() const
00664 {
00665 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00666 }
00667
00674 void Window::ReInit(int rx, int ry)
00675 {
00676 this->SetDirty();
00677
00678
00679 int window_width = this->width;
00680 int window_height = this->height;
00681
00682 this->OnInit();
00683
00684 this->nested_root->SetupSmallestSize(this, false);
00685 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
00686 this->width = this->nested_root->smallest_x;
00687 this->height = this->nested_root->smallest_y;
00688 this->resize.step_width = this->nested_root->resize_x;
00689 this->resize.step_height = this->nested_root->resize_y;
00690
00691
00692 window_width = max(window_width + rx, this->width);
00693 window_height = max(window_height + ry, this->height);
00694 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
00695 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
00696
00697
00698 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
00699 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
00700
00701 ResizeWindow(this, dx, dy);
00702
00703 }
00704
00710 void Window::SetShaded(bool make_shaded)
00711 {
00712 if (this->shade_select == NULL) return;
00713
00714 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
00715 if (this->shade_select->shown_plane != desired) {
00716 if (make_shaded) {
00717 this->unshaded_size.width = this->width;
00718 this->unshaded_size.height = this->height;
00719 this->shade_select->SetDisplayedPlane(desired);
00720 this->ReInit(0, -this->height);
00721 } else {
00722 this->shade_select->SetDisplayedPlane(desired);
00723 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
00724 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
00725 this->ReInit(dx, dy);
00726 }
00727 }
00728 }
00729
00736 static Window *FindChildWindow(const Window *w, WindowClass wc)
00737 {
00738 Window *v;
00739 FOR_ALL_WINDOWS_FROM_BACK(v) {
00740 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
00741 }
00742
00743 return NULL;
00744 }
00745
00750 void Window::DeleteChildWindows(WindowClass wc) const
00751 {
00752 Window *child = FindChildWindow(this, wc);
00753 while (child != NULL) {
00754 delete child;
00755 child = FindChildWindow(this, wc);
00756 }
00757 }
00758
00762 Window::~Window()
00763 {
00764 if (_thd.window_class == this->window_class &&
00765 _thd.window_number == this->window_number) {
00766 ResetObjectToPlace();
00767 }
00768
00769
00770 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
00771
00772
00773 if (_last_scroll_window == this) _last_scroll_window = NULL;
00774
00775
00776 if (_focused_window == this) _focused_window = NULL;
00777
00778 this->DeleteChildWindows();
00779
00780 if (this->viewport != NULL) DeleteWindowViewport(this);
00781
00782 this->SetDirty();
00783
00784 free(this->nested_array);
00785 delete this->nested_root;
00786
00787 this->window_class = WC_INVALID;
00788 }
00789
00796 Window *FindWindowById(WindowClass cls, WindowNumber number)
00797 {
00798 Window *w;
00799 FOR_ALL_WINDOWS_FROM_BACK(w) {
00800 if (w->window_class == cls && w->window_number == number) return w;
00801 }
00802
00803 return NULL;
00804 }
00805
00812 Window *FindWindowByClass(WindowClass cls)
00813 {
00814 Window *w;
00815 FOR_ALL_WINDOWS_FROM_BACK(w) {
00816 if (w->window_class == cls) return w;
00817 }
00818
00819 return NULL;
00820 }
00821
00828 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
00829 {
00830 Window *w = FindWindowById(cls, number);
00831 if (force || w == NULL ||
00832 (w->flags & WF_STICKY) == 0) {
00833 delete w;
00834 }
00835 }
00836
00841 void DeleteWindowByClass(WindowClass cls)
00842 {
00843 Window *w;
00844
00845 restart_search:
00846
00847
00848
00849 FOR_ALL_WINDOWS_FROM_BACK(w) {
00850 if (w->window_class == cls) {
00851 delete w;
00852 goto restart_search;
00853 }
00854 }
00855 }
00856
00863 void DeleteCompanyWindows(CompanyID id)
00864 {
00865 Window *w;
00866
00867 restart_search:
00868
00869
00870
00871 FOR_ALL_WINDOWS_FROM_BACK(w) {
00872 if (w->owner == id) {
00873 delete w;
00874 goto restart_search;
00875 }
00876 }
00877
00878
00879 DeleteWindowById(WC_BUY_COMPANY, id);
00880 }
00881
00889 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
00890 {
00891 Window *w;
00892 FOR_ALL_WINDOWS_FROM_BACK(w) {
00893 if (w->owner != old_owner) continue;
00894
00895 switch (w->window_class) {
00896 case WC_COMPANY_COLOUR:
00897 case WC_FINANCES:
00898 case WC_STATION_LIST:
00899 case WC_TRAINS_LIST:
00900 case WC_ROADVEH_LIST:
00901 case WC_SHIPS_LIST:
00902 case WC_AIRCRAFT_LIST:
00903 case WC_BUY_COMPANY:
00904 case WC_COMPANY:
00905 case WC_COMPANY_INFRASTRUCTURE:
00906 continue;
00907
00908 default:
00909 w->owner = new_owner;
00910 break;
00911 }
00912 }
00913 }
00914
00915 static void BringWindowToFront(Window *w);
00916
00924 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
00925 {
00926 Window *w = FindWindowById(cls, number);
00927
00928 if (w != NULL) {
00929 if (w->IsShaded()) w->SetShaded(false);
00930
00931 w->SetWhiteBorder();
00932 BringWindowToFront(w);
00933 w->SetDirty();
00934 }
00935
00936 return w;
00937 }
00938
00939 static inline bool IsVitalWindow(const Window *w)
00940 {
00941 switch (w->window_class) {
00942 case WC_MAIN_TOOLBAR:
00943 case WC_STATUS_BAR:
00944 case WC_NEWS_WINDOW:
00945 case WC_SEND_NETWORK_MSG:
00946 return true;
00947
00948 default:
00949 return false;
00950 }
00951 }
00952
00961 static uint GetWindowZPriority(const Window *w)
00962 {
00963 assert(w->window_class != WC_INVALID);
00964
00965 uint z_priority = 0;
00966
00967 switch (w->window_class) {
00968 case WC_ENDSCREEN:
00969 ++z_priority;
00970
00971 case WC_HIGHSCORE:
00972 ++z_priority;
00973
00974 case WC_TOOLTIPS:
00975 ++z_priority;
00976
00977 case WC_DROPDOWN_MENU:
00978 ++z_priority;
00979
00980 case WC_MAIN_TOOLBAR:
00981 case WC_STATUS_BAR:
00982 ++z_priority;
00983
00984 case WC_OSK:
00985 ++z_priority;
00986
00987 case WC_QUERY_STRING:
00988 ++z_priority;
00989
00990 case WC_ERRMSG:
00991 case WC_CONFIRM_POPUP_QUERY:
00992 case WC_MODAL_PROGRESS:
00993 case WC_NETWORK_STATUS_WINDOW:
00994 ++z_priority;
00995
00996 case WC_GENERATE_LANDSCAPE:
00997 case WC_SAVELOAD:
00998 case WC_GAME_OPTIONS:
00999 case WC_CUSTOM_CURRENCY:
01000 case WC_NETWORK_WINDOW:
01001 case WC_GRF_PARAMETERS:
01002 case WC_NEWGRF_TEXTFILE:
01003 case WC_AI_LIST:
01004 case WC_AI_SETTINGS:
01005 ++z_priority;
01006
01007 case WC_CONSOLE:
01008 ++z_priority;
01009
01010 case WC_SEND_NETWORK_MSG:
01011 case WC_NEWS_WINDOW:
01012 ++z_priority;
01013
01014 default:
01015 ++z_priority;
01016
01017 case WC_MAIN_WINDOW:
01018 return z_priority;
01019 }
01020 }
01021
01026 static void AddWindowToZOrdering(Window *w)
01027 {
01028 assert(w->z_front == NULL && w->z_back == NULL);
01029
01030 if (_z_front_window == NULL) {
01031
01032 _z_front_window = _z_back_window = w;
01033 w->z_front = w->z_back = NULL;
01034 } else {
01035
01036 Window *v = _z_front_window;
01037 uint last_z_priority = UINT_MAX;
01038 while (v != NULL && (v->window_class == WC_INVALID || GetWindowZPriority(v) > GetWindowZPriority(w))) {
01039 if (v->window_class != WC_INVALID) {
01040
01041 assert(last_z_priority >= GetWindowZPriority(v));
01042 last_z_priority = GetWindowZPriority(v);
01043 }
01044
01045 v = v->z_back;
01046 }
01047
01048 if (v == NULL) {
01049
01050 w->z_front = _z_back_window;
01051 w->z_back = NULL;
01052 _z_back_window->z_back = w;
01053 _z_back_window = w;
01054 } else if (v == _z_front_window) {
01055
01056 w->z_front = NULL;
01057 w->z_back = _z_front_window;
01058 _z_front_window->z_front = w;
01059 _z_front_window = w;
01060 } else {
01061
01062 w->z_front = v->z_front;
01063 w->z_back = v;
01064 v->z_front->z_back = w;
01065 v->z_front = w;
01066 }
01067 }
01068 }
01069
01070
01075 static void RemoveWindowFromZOrdering(Window *w)
01076 {
01077 if (w->z_front == NULL) {
01078 assert(_z_front_window == w);
01079 _z_front_window = w->z_back;
01080 } else {
01081 w->z_front->z_back = w->z_back;
01082 }
01083
01084 if (w->z_back == NULL) {
01085 assert(_z_back_window == w);
01086 _z_back_window = w->z_front;
01087 } else {
01088 w->z_back->z_front = w->z_front;
01089 }
01090
01091 w->z_front = w->z_back = NULL;
01092 }
01093
01099 static void BringWindowToFront(Window *w)
01100 {
01101 RemoveWindowFromZOrdering(w);
01102 AddWindowToZOrdering(w);
01103
01104 w->SetDirty();
01105 }
01106
01115 void Window::InitializeData(const WindowDesc *desc, WindowNumber window_number)
01116 {
01117
01118 this->window_class = desc->cls;
01119 this->SetWhiteBorder();
01120 if (desc->default_pos == WDP_CENTER) this->flags |= WF_CENTERED;
01121 this->owner = INVALID_OWNER;
01122 this->nested_focus = NULL;
01123 this->window_number = window_number;
01124 this->desc_flags = desc->flags;
01125
01126 this->OnInit();
01127
01128 if (this->nested_array == NULL) {
01129 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01130 this->nested_root->SetupSmallestSize(this, true);
01131 } else {
01132 this->nested_root->SetupSmallestSize(this, false);
01133 }
01134
01135 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
01136
01137
01138
01139 this->resize.step_width = this->nested_root->resize_x;
01140 this->resize.step_height = this->nested_root->resize_y;
01141
01142
01143
01144
01145 if (this->window_class != WC_OSK && (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL)) SetFocusedWindow(this);
01146
01147
01148 AddWindowToZOrdering(this);
01149 }
01150
01158 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
01159 {
01160 this->left = x;
01161 this->top = y;
01162 this->width = sm_width;
01163 this->height = sm_height;
01164 }
01165
01176 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
01177 {
01178 def_width = max(def_width, this->width);
01179 def_height = max(def_height, this->height);
01180
01181
01182
01183
01184
01185 if (this->width != def_width || this->height != def_height) {
01186
01187 int free_height = _screen.height;
01188 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
01189 if (wt != NULL) free_height -= wt->height;
01190 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01191 if (wt != NULL) free_height -= wt->height;
01192
01193 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
01194 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
01195
01196
01197
01198
01199 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
01200 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
01201
01202 ResizeWindow(this, enlarge_x, enlarge_y);
01203
01204 } else {
01205
01206 this->OnResize();
01207 }
01208
01209 int nx = this->left;
01210 int ny = this->top;
01211
01212 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
01213
01214 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01215 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
01216 nx = max(nx, 0);
01217
01218 if (this->viewport != NULL) {
01219 this->viewport->left += nx - this->left;
01220 this->viewport->top += ny - this->top;
01221 }
01222 this->left = nx;
01223 this->top = ny;
01224
01225 this->SetDirty();
01226 }
01227
01239 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
01240 {
01241 int right = width + left;
01242 int bottom = height + top;
01243
01244 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01245 if (left < 0 || (main_toolbar != NULL && top < main_toolbar->height) || right > _screen.width || bottom > _screen.height) return false;
01246
01247
01248 const Window *w;
01249 FOR_ALL_WINDOWS_FROM_BACK(w) {
01250 if (w->window_class == WC_MAIN_WINDOW) continue;
01251
01252 if (right > w->left &&
01253 w->left + w->width > left &&
01254 bottom > w->top &&
01255 w->top + w->height > top) {
01256 return false;
01257 }
01258 }
01259
01260 pos.x = left;
01261 pos.y = top;
01262 return true;
01263 }
01264
01276 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
01277 {
01278
01279
01280
01281 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
01282
01283 if (top < 22 || top > _screen.height - (height >> 2)) return false;
01284
01285
01286 const Window *w;
01287 FOR_ALL_WINDOWS_FROM_BACK(w) {
01288 if (w->window_class == WC_MAIN_WINDOW) continue;
01289
01290 if (left + width > w->left &&
01291 w->left + w->width > left &&
01292 top + height > w->top &&
01293 w->top + w->height > top) {
01294 return false;
01295 }
01296 }
01297
01298 pos.x = left;
01299 pos.y = top;
01300 return true;
01301 }
01302
01309 static Point GetAutoPlacePosition(int width, int height)
01310 {
01311 Point pt;
01312
01313
01314 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01315 if (IsGoodAutoPlace1(0, main_toolbar != NULL ? main_toolbar->height + 2 : 2, width, height, pt)) return pt;
01316
01317
01318
01319
01320
01321 const Window *w;
01322 FOR_ALL_WINDOWS_FROM_BACK(w) {
01323 if (w->window_class == WC_MAIN_WINDOW) continue;
01324
01325 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01326 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01327 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01328 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01329 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01330 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01331 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01332 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01333 }
01334
01335
01336
01337
01338
01339 FOR_ALL_WINDOWS_FROM_BACK(w) {
01340 if (w->window_class == WC_MAIN_WINDOW) continue;
01341
01342 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01343 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01344 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01345 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01346 }
01347
01348
01349
01350
01351 int left = 0, top = 24;
01352
01353 restart:
01354 FOR_ALL_WINDOWS_FROM_BACK(w) {
01355 if (w->left == left && w->top == top) {
01356 left += 5;
01357 top += 5;
01358 goto restart;
01359 }
01360 }
01361
01362 pt.x = left;
01363 pt.y = top;
01364 return pt;
01365 }
01366
01373 Point GetToolbarAlignedWindowPosition(int window_width)
01374 {
01375 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01376 assert(w != NULL);
01377 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
01378 return pt;
01379 }
01380
01398 static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01399 {
01400 Point pt;
01401 const Window *w;
01402
01403 int16 default_width = max(desc->default_width, sm_width);
01404 int16 default_height = max(desc->default_height, sm_height);
01405
01406 if (desc->parent_cls != 0 &&
01407 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01408 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01409
01410 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01411 if (pt.x > _screen.width + 10 - default_width) {
01412 pt.x = (_screen.width + 10 - default_width) - 20;
01413 }
01414 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? w->height : 10);
01415 return pt;
01416 }
01417
01418 switch (desc->default_pos) {
01419 case WDP_ALIGN_TOOLBAR:
01420 return GetToolbarAlignedWindowPosition(default_width);
01421
01422 case WDP_AUTO:
01423 return GetAutoPlacePosition(default_width, default_height);
01424
01425 case WDP_CENTER:
01426 pt.x = (_screen.width - default_width) / 2;
01427 pt.y = (_screen.height - default_height) / 2;
01428 break;
01429
01430 case WDP_MANUAL:
01431 pt.x = 0;
01432 pt.y = 0;
01433 break;
01434
01435 default:
01436 NOT_REACHED();
01437 }
01438
01439 return pt;
01440 }
01441
01442 Point Window::OnInitialPosition(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01443 {
01444 return LocalGetWindowPlacement(desc, sm_width, sm_height, window_number);
01445 }
01446
01455 void Window::CreateNestedTree(const WindowDesc *desc, bool fill_nested)
01456 {
01457 int biggest_index = -1;
01458 this->nested_root = MakeWindowNWidgetTree(desc->nwid_parts, desc->nwid_length, &biggest_index, &this->shade_select);
01459 this->nested_array_size = (uint)(biggest_index + 1);
01460
01461 if (fill_nested) {
01462 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01463 this->nested_root->FillNestedArray(this->nested_array, this->nested_array_size);
01464 }
01465 }
01466
01472 void Window::FinishInitNested(const WindowDesc *desc, WindowNumber window_number)
01473 {
01474 this->InitializeData(desc, window_number);
01475 Point pt = this->OnInitialPosition(desc, this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
01476 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
01477 this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
01478 }
01479
01485 void Window::InitNested(const WindowDesc *desc, WindowNumber window_number)
01486 {
01487 this->CreateNestedTree(desc, false);
01488 this->FinishInitNested(desc, window_number);
01489 }
01490
01492 Window::Window() : scrolling_scrollbar(-1)
01493 {
01494 }
01495
01503 Window *FindWindowFromPt(int x, int y)
01504 {
01505 Window *w;
01506 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01507 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01508 return w;
01509 }
01510 }
01511
01512 return NULL;
01513 }
01514
01518 void InitWindowSystem()
01519 {
01520 IConsoleClose();
01521
01522 _z_back_window = NULL;
01523 _z_front_window = NULL;
01524 _focused_window = NULL;
01525 _mouseover_last_w = NULL;
01526 _last_scroll_window = NULL;
01527 _scrolling_viewport = false;
01528 _mouse_hovering = false;
01529
01530 NWidgetLeaf::InvalidateDimensionCache();
01531 NWidgetScrollbar::InvalidateDimensionCache();
01532
01533 ShowFirstError();
01534 }
01535
01539 void UnInitWindowSystem()
01540 {
01541 UnshowCriticalError();
01542
01543 Window *w;
01544 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01545
01546 for (w = _z_front_window; w != NULL; ) {
01547 Window *to_del = w;
01548 w = w->z_back;
01549 free(to_del);
01550 }
01551
01552 _z_front_window = NULL;
01553 _z_back_window = NULL;
01554 }
01555
01559 void ResetWindowSystem()
01560 {
01561 UnInitWindowSystem();
01562 InitWindowSystem();
01563 _thd.Reset();
01564 }
01565
01566 static void DecreaseWindowCounters()
01567 {
01568 Window *w;
01569 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01570 if (_scroller_click_timeout == 0) {
01571
01572 for (uint i = 0; i < w->nested_array_size; i++) {
01573 NWidgetBase *nwid = w->nested_array[i];
01574 if (nwid != NULL && (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR)) {
01575 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
01576 if (sb->disp_flags & (ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN)) {
01577 sb->disp_flags &= ~(ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN);
01578 w->scrolling_scrollbar = -1;
01579 sb->SetDirty(w);
01580 }
01581 }
01582 }
01583 }
01584 w->OnMouseLoop();
01585 }
01586
01587 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01588 if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) {
01589 CLRBITS(w->flags, WF_TIMEOUT);
01590
01591 w->OnTimeout();
01592 if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons(true);
01593 }
01594 }
01595 }
01596
01597 static void HandlePlacePresize()
01598 {
01599 if (_special_mouse_mode != WSM_PRESIZE) return;
01600
01601 Window *w = _thd.GetCallbackWnd();
01602 if (w == NULL) return;
01603
01604 Point pt = GetTileBelowCursor();
01605 if (pt.x == -1) {
01606 _thd.selend.x = -1;
01607 return;
01608 }
01609
01610 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01611 }
01612
01617 static EventState HandleMouseDragDrop()
01618 {
01619 if (_special_mouse_mode != WSM_DRAGDROP) return ES_NOT_HANDLED;
01620
01621 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01622
01623 Window *w = _thd.GetCallbackWnd();
01624 if (w != NULL) {
01625
01626 Point pt;
01627 pt.x = _cursor.pos.x - w->left;
01628 pt.y = _cursor.pos.y - w->top;
01629 if (_left_button_down) {
01630 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
01631 } else {
01632 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01633 }
01634 }
01635
01636 if (!_left_button_down) ResetObjectToPlace();
01637 return ES_HANDLED;
01638 }
01639
01641 static void HandleMouseOver()
01642 {
01643 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01644
01645
01646 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01647
01648 Point pt = { -1, -1 };
01649 _mouseover_last_w->OnMouseOver(pt, 0);
01650 }
01651
01652
01653 _mouseover_last_w = w;
01654
01655 if (w != NULL) {
01656
01657 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01658 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
01659 if (widget != NULL) w->OnMouseOver(pt, widget->index);
01660 }
01661 }
01662
01664 static const int MIN_VISIBLE_TITLE_BAR = 13;
01665
01667 enum PreventHideDirection {
01668 PHD_UP,
01669 PHD_DOWN,
01670 };
01671
01682 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
01683 {
01684 if (v == NULL) return;
01685
01686 int v_bottom = v->top + v->height;
01687 int v_right = v->left + v->width;
01688 int safe_y = (dir == PHD_UP) ? (v->top - MIN_VISIBLE_TITLE_BAR - rect.top) : (v_bottom + MIN_VISIBLE_TITLE_BAR - rect.bottom);
01689
01690 if (*ny + rect.top <= v->top - MIN_VISIBLE_TITLE_BAR) return;
01691 if (*ny + rect.bottom >= v_bottom + MIN_VISIBLE_TITLE_BAR) return;
01692
01693
01694 if (*nx + rect.left + MIN_VISIBLE_TITLE_BAR < v->left) {
01695 if (v->left < MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01696 return;
01697 }
01698 if (*nx + rect.right - MIN_VISIBLE_TITLE_BAR > v_right) {
01699 if (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01700 return;
01701 }
01702
01703
01704 if (px + rect.left < v->left && v->left >= MIN_VISIBLE_TITLE_BAR) {
01705 *nx = v->left - MIN_VISIBLE_TITLE_BAR - rect.left;
01706 } else if (px + rect.right > v_right && v_right <= _screen.width - MIN_VISIBLE_TITLE_BAR) {
01707 *nx = v_right + MIN_VISIBLE_TITLE_BAR - rect.right;
01708 } else {
01709 *ny = safe_y;
01710 }
01711 }
01712
01720 static void EnsureVisibleCaption(Window *w, int nx, int ny)
01721 {
01722
01723 Rect caption_rect;
01724 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
01725 if (caption != NULL) {
01726 caption_rect.left = caption->pos_x;
01727 caption_rect.right = caption->pos_x + caption->current_x;
01728 caption_rect.top = caption->pos_y;
01729 caption_rect.bottom = caption->pos_y + caption->current_y;
01730
01731
01732 nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
01733 ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
01734
01735
01736 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
01737 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
01738 }
01739
01740 if (w->viewport != NULL) {
01741 w->viewport->left += nx - w->left;
01742 w->viewport->top += ny - w->top;
01743 }
01744
01745 w->left = nx;
01746 w->top = ny;
01747 }
01748
01758 void ResizeWindow(Window *w, int delta_x, int delta_y)
01759 {
01760 if (delta_x != 0 || delta_y != 0) {
01761
01762
01763 int new_right = w->left + w->width + delta_x;
01764 int new_bottom = w->top + w->height + delta_y;
01765 if (new_right >= (int)_cur_resolution.width) delta_x -= Ceil(new_right - _cur_resolution.width, max(1U, w->nested_root->resize_x));
01766 if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, max(1U, w->nested_root->resize_y));
01767
01768 w->SetDirty();
01769
01770 uint new_xinc = max(0, (w->nested_root->resize_x == 0) ? 0 : (int)(w->nested_root->current_x - w->nested_root->smallest_x) + delta_x);
01771 uint new_yinc = max(0, (w->nested_root->resize_y == 0) ? 0 : (int)(w->nested_root->current_y - w->nested_root->smallest_y) + delta_y);
01772 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
01773 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
01774
01775 w->nested_root->AssignSizePosition(ST_RESIZE, 0, 0, w->nested_root->smallest_x + new_xinc, w->nested_root->smallest_y + new_yinc, _current_text_dir == TD_RTL);
01776 w->width = w->nested_root->current_x;
01777 w->height = w->nested_root->current_y;
01778 }
01779
01780 EnsureVisibleCaption(w, w->left, w->top);
01781
01782
01783 w->OnResize();
01784 w->SetDirty();
01785 }
01786
01792 int GetMainViewTop()
01793 {
01794 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01795 return (w == NULL) ? 0 : w->top + w->height;
01796 }
01797
01803 int GetMainViewBottom()
01804 {
01805 Window *w = FindWindowById(WC_STATUS_BAR, 0);
01806 return (w == NULL) ? _screen.height : w->top;
01807 }
01808
01809 static bool _dragging_window;
01810
01815 static EventState HandleWindowDragging()
01816 {
01817
01818 if (!_dragging_window) return ES_NOT_HANDLED;
01819
01820
01821 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01822
01823
01824 Window *w;
01825 FOR_ALL_WINDOWS_FROM_BACK(w) {
01826 if (w->flags & WF_DRAGGING) {
01827
01828 if (!_left_button_down) {
01829 w->flags &= ~WF_DRAGGING;
01830 break;
01831 }
01832
01833 w->SetDirty();
01834
01835 int x = _cursor.pos.x + _drag_delta.x;
01836 int y = _cursor.pos.y + _drag_delta.y;
01837 int nx = x;
01838 int ny = y;
01839
01840 if (_settings_client.gui.window_snap_radius != 0) {
01841 const Window *v;
01842
01843 int hsnap = _settings_client.gui.window_snap_radius;
01844 int vsnap = _settings_client.gui.window_snap_radius;
01845 int delta;
01846
01847 FOR_ALL_WINDOWS_FROM_BACK(v) {
01848 if (v == w) continue;
01849
01850 if (y + w->height > v->top && y < v->top + v->height) {
01851
01852 delta = abs(v->left + v->width - x);
01853 if (delta <= hsnap) {
01854 nx = v->left + v->width;
01855 hsnap = delta;
01856 }
01857
01858
01859 delta = abs(v->left - x - w->width);
01860 if (delta <= hsnap) {
01861 nx = v->left - w->width;
01862 hsnap = delta;
01863 }
01864 }
01865
01866 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
01867
01868 delta = abs(v->left - x);
01869 if (delta <= hsnap) {
01870 nx = v->left;
01871 hsnap = delta;
01872 }
01873
01874
01875 delta = abs(v->left + v->width - x - w->width);
01876 if (delta <= hsnap) {
01877 nx = v->left + v->width - w->width;
01878 hsnap = delta;
01879 }
01880 }
01881
01882 if (x + w->width > v->left && x < v->left + v->width) {
01883
01884 delta = abs(v->top + v->height - y);
01885 if (delta <= vsnap) {
01886 ny = v->top + v->height;
01887 vsnap = delta;
01888 }
01889
01890
01891 delta = abs(v->top - y - w->height);
01892 if (delta <= vsnap) {
01893 ny = v->top - w->height;
01894 vsnap = delta;
01895 }
01896 }
01897
01898 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
01899
01900 delta = abs(v->top - y);
01901 if (delta <= vsnap) {
01902 ny = v->top;
01903 vsnap = delta;
01904 }
01905
01906
01907 delta = abs(v->top + v->height - y - w->height);
01908 if (delta <= vsnap) {
01909 ny = v->top + v->height - w->height;
01910 vsnap = delta;
01911 }
01912 }
01913 }
01914 }
01915
01916 EnsureVisibleCaption(w, nx, ny);
01917
01918 w->SetDirty();
01919 return ES_HANDLED;
01920 } else if (w->flags & WF_SIZING) {
01921
01922 if (!_left_button_down) {
01923 w->flags &= ~WF_SIZING;
01924 w->SetDirty();
01925 break;
01926 }
01927
01928
01929
01930
01931 int x, y = _cursor.pos.y - _drag_delta.y;
01932 if (w->flags & WF_SIZING_LEFT) {
01933 x = _drag_delta.x - _cursor.pos.x;
01934 } else {
01935 x = _cursor.pos.x - _drag_delta.x;
01936 }
01937
01938
01939 if (w->resize.step_width == 0) x = 0;
01940 if (w->resize.step_height == 0) y = 0;
01941
01942
01943 if (w->top + w->height + y > _screen.height) {
01944 y = _screen.height - w->height - w->top;
01945 }
01946
01947
01948
01949
01950 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
01951 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
01952
01953
01954 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
01955 x = w->nested_root->smallest_x - w->width;
01956 }
01957 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
01958 y = w->nested_root->smallest_y - w->height;
01959 }
01960
01961
01962 if (x == 0 && y == 0) return ES_HANDLED;
01963
01964
01965 _drag_delta.y += y;
01966 if ((w->flags & WF_SIZING_LEFT) && x != 0) {
01967 _drag_delta.x -= x;
01968 w->SetDirty();
01969 w->left -= x;
01970
01971 } else {
01972 _drag_delta.x += x;
01973 }
01974
01975
01976 ResizeWindow(w, x, y);
01977 return ES_HANDLED;
01978 }
01979 }
01980
01981 _dragging_window = false;
01982 return ES_HANDLED;
01983 }
01984
01989 static void StartWindowDrag(Window *w)
01990 {
01991 w->flags |= WF_DRAGGING;
01992 w->flags &= ~WF_CENTERED;
01993 _dragging_window = true;
01994
01995 _drag_delta.x = w->left - _cursor.pos.x;
01996 _drag_delta.y = w->top - _cursor.pos.y;
01997
01998 BringWindowToFront(w);
01999 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02000 }
02001
02007 static void StartWindowSizing(Window *w, bool to_left)
02008 {
02009 w->flags |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
02010 w->flags &= ~WF_CENTERED;
02011 _dragging_window = true;
02012
02013 _drag_delta.x = _cursor.pos.x;
02014 _drag_delta.y = _cursor.pos.y;
02015
02016 BringWindowToFront(w);
02017 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02018 }
02019
02024 static EventState HandleScrollbarScrolling()
02025 {
02026 Window *w;
02027 FOR_ALL_WINDOWS_FROM_BACK(w) {
02028 if (w->scrolling_scrollbar >= 0) {
02029
02030 if (!_left_button_down) {
02031 w->scrolling_scrollbar = -1;
02032 w->SetDirty();
02033 return ES_HANDLED;
02034 }
02035
02036 int i;
02037 NWidgetScrollbar *sb = w->GetWidget<NWidgetScrollbar>(w->scrolling_scrollbar);
02038 bool rtl = false;
02039
02040 if (sb->type == NWID_HSCROLLBAR) {
02041 i = _cursor.pos.x - _cursorpos_drag_start.x;
02042 rtl = _current_text_dir == TD_RTL;
02043 } else {
02044 i = _cursor.pos.y - _cursorpos_drag_start.y;
02045 }
02046
02047 if (sb->disp_flags & ND_SCROLLBAR_BTN) {
02048 if (_scroller_click_timeout == 1) {
02049 _scroller_click_timeout = 3;
02050 sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1);
02051 w->SetDirty();
02052 }
02053 return ES_HANDLED;
02054 }
02055
02056
02057 int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity()));
02058 if (rtl) pos = max(0, sb->GetCount() - sb->GetCapacity() - pos);
02059 if (pos != sb->GetPosition()) {
02060 sb->SetPosition(pos);
02061 w->SetDirty();
02062 }
02063 return ES_HANDLED;
02064 }
02065 }
02066
02067 return ES_NOT_HANDLED;
02068 }
02069
02074 static EventState HandleViewportScroll()
02075 {
02076 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02077
02078 if (!_scrolling_viewport) return ES_NOT_HANDLED;
02079
02080
02081
02082
02083 if (_last_scroll_window == NULL) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
02084
02085 if (_last_scroll_window == NULL || !(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down))) {
02086 _cursor.fix_at = false;
02087 _scrolling_viewport = false;
02088 _last_scroll_window = NULL;
02089 return ES_NOT_HANDLED;
02090 }
02091
02092 if (_last_scroll_window == FindWindowById(WC_MAIN_WINDOW, 0) && _last_scroll_window->viewport->follow_vehicle != INVALID_VEHICLE) {
02093
02094 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
02095 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
02096 return ES_NOT_HANDLED;
02097 }
02098
02099 Point delta;
02100 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
02101 delta.x = -_cursor.delta.x;
02102 delta.y = -_cursor.delta.y;
02103 } else {
02104 delta.x = _cursor.delta.x;
02105 delta.y = _cursor.delta.y;
02106 }
02107
02108 if (scrollwheel_scrolling) {
02109
02110 delta.x = _cursor.h_wheel;
02111 delta.y = _cursor.v_wheel;
02112 _cursor.v_wheel = 0;
02113 _cursor.h_wheel = 0;
02114 }
02115
02116
02117 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
02118
02119 _cursor.delta.x = 0;
02120 _cursor.delta.y = 0;
02121 return ES_HANDLED;
02122 }
02123
02134 static bool MaybeBringWindowToFront(Window *w)
02135 {
02136 bool bring_to_front = false;
02137
02138 if (w->window_class == WC_MAIN_WINDOW ||
02139 IsVitalWindow(w) ||
02140 w->window_class == WC_TOOLTIPS ||
02141 w->window_class == WC_DROPDOWN_MENU) {
02142 return true;
02143 }
02144
02145
02146 int w_width = w->width;
02147 int w_height = w->height;
02148 if (w->IsShaded()) {
02149 w_width = w->unshaded_size.width;
02150 w_height = w->unshaded_size.height;
02151 }
02152
02153 Window *u;
02154 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
02155
02156 if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
02157 u->SetWhiteBorder();
02158 u->SetDirty();
02159 return false;
02160 }
02161
02162 if (u->window_class == WC_MAIN_WINDOW ||
02163 IsVitalWindow(u) ||
02164 u->window_class == WC_TOOLTIPS ||
02165 u->window_class == WC_DROPDOWN_MENU) {
02166 continue;
02167 }
02168
02169
02170 if (w->left + w_width <= u->left ||
02171 u->left + u->width <= w->left ||
02172 w->top + w_height <= u->top ||
02173 u->top + u->height <= w->top) {
02174 continue;
02175 }
02176
02177 bring_to_front = true;
02178 }
02179
02180 if (bring_to_front) BringWindowToFront(w);
02181 return true;
02182 }
02183
02188 void HandleKeypress(uint32 raw_key)
02189 {
02190
02191
02192 assert(HasModalProgress() || IsLocalCompany());
02193
02194
02195 uint16 key = GB(raw_key, 0, 16);
02196 uint16 keycode = GB(raw_key, 16, 16);
02197
02198
02199
02200
02201
02202
02203
02204
02205 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
02206
02207
02208
02209
02210 if (key == 0 && keycode == 0) return;
02211
02212
02213 if (EditBoxInGlobalFocus()) {
02214
02215 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
02216 }
02217
02218
02219 Window *w;
02220 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02221 if (w->window_class == WC_MAIN_TOOLBAR) continue;
02222 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02223 }
02224
02225 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02226
02227 if (w != NULL && w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02228
02229 HandleGlobalHotkeys(key, keycode);
02230 }
02231
02235 void HandleCtrlChanged()
02236 {
02237
02238 Window *w;
02239 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02240 if (w->OnCTRLStateChange() == ES_HANDLED) return;
02241 }
02242 }
02243
02250 static int _input_events_this_tick = 0;
02251
02256 static void HandleAutoscroll()
02257 {
02258 if (_settings_client.gui.autoscroll && _game_mode != GM_MENU && !HasModalProgress()) {
02259 int x = _cursor.pos.x;
02260 int y = _cursor.pos.y;
02261 Window *w = FindWindowFromPt(x, y);
02262 if (w == NULL || w->flags & WF_DISABLE_VP_SCROLL) return;
02263 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02264 if (vp != NULL) {
02265 x -= vp->left;
02266 y -= vp->top;
02267
02268
02269 #define scrollspeed 3
02270 if (x - 15 < 0) {
02271 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
02272 } else if (15 - (vp->width - x) > 0) {
02273 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
02274 }
02275 if (y - 15 < 0) {
02276 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
02277 } else if (15 - (vp->height - y) > 0) {
02278 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
02279 }
02280 #undef scrollspeed
02281 }
02282 }
02283 }
02284
02285 enum MouseClick {
02286 MC_NONE = 0,
02287 MC_LEFT,
02288 MC_RIGHT,
02289 MC_DOUBLE_LEFT,
02290 MC_HOVER,
02291
02292 MAX_OFFSET_DOUBLE_CLICK = 5,
02293 TIME_BETWEEN_DOUBLE_CLICK = 500,
02294 MAX_OFFSET_HOVER = 5,
02295 };
02296 extern EventState VpHandlePlaceSizingDrag();
02297
02298 static void ScrollMainViewport(int x, int y)
02299 {
02300 if (_game_mode != GM_MENU) {
02301 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02302 assert(w);
02303
02304 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
02305 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
02306 }
02307 }
02308
02318 static const int8 scrollamt[16][2] = {
02319 { 0, 0},
02320 {-2, 0},
02321 { 0, -2},
02322 {-2, -1},
02323 { 2, 0},
02324 { 0, 0},
02325 { 2, -1},
02326 { 0, -2},
02327 { 0, 2},
02328 {-2, 1},
02329 { 0, 0},
02330 {-2, 0},
02331 { 2, 1},
02332 { 0, 2},
02333 { 2, 0},
02334 { 0, 0},
02335 };
02336
02337 static void HandleKeyScrolling()
02338 {
02339
02340
02341
02342
02343 if (_dirkeys && !EditBoxInGlobalFocus()) {
02344 int factor = _shift_pressed ? 50 : 10;
02345 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
02346 }
02347 }
02348
02349 static void MouseLoop(MouseClick click, int mousewheel)
02350 {
02351
02352
02353 assert(HasModalProgress() || IsLocalCompany());
02354
02355 HandlePlacePresize();
02356 UpdateTileSelection();
02357
02358 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
02359 if (HandleMouseDragDrop() == ES_HANDLED) return;
02360 if (HandleWindowDragging() == ES_HANDLED) return;
02361 if (HandleScrollbarScrolling() == ES_HANDLED) return;
02362 if (HandleViewportScroll() == ES_HANDLED) return;
02363
02364 HandleMouseOver();
02365
02366 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02367 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
02368
02369 int x = _cursor.pos.x;
02370 int y = _cursor.pos.y;
02371 Window *w = FindWindowFromPt(x, y);
02372 if (w == NULL) return;
02373
02374 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
02375 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02376
02377
02378 if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
02379
02380 if (mousewheel != 0) {
02381
02382 w->OnMouseWheel(mousewheel);
02383
02384
02385 if (vp == NULL) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
02386 }
02387
02388 if (vp != NULL) {
02389 if (scrollwheel_scrolling) click = MC_RIGHT;
02390 switch (click) {
02391 case MC_DOUBLE_LEFT:
02392 case MC_LEFT:
02393 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
02394 if (!HandleViewportClicked(vp, x, y) &&
02395 !(w->flags & WF_DISABLE_VP_SCROLL) &&
02396 _settings_client.gui.left_mouse_btn_scrolling) {
02397 _scrolling_viewport = true;
02398 _cursor.fix_at = false;
02399 }
02400 break;
02401
02402 case MC_RIGHT:
02403 if (!(w->flags & WF_DISABLE_VP_SCROLL)) {
02404 _scrolling_viewport = true;
02405 _cursor.fix_at = true;
02406
02407
02408 _cursor.h_wheel = 0;
02409 _cursor.v_wheel = 0;
02410 }
02411 break;
02412
02413 default:
02414 break;
02415 }
02416 } else {
02417 switch (click) {
02418 case MC_LEFT:
02419 case MC_DOUBLE_LEFT:
02420 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
02421 break;
02422
02423 default:
02424 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
02425
02426
02427
02428
02429 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
02430
02431 case MC_HOVER: DispatchHoverEvent(w, x - w->left, y - w->top); break;
02432 }
02433 }
02434 }
02435
02439 void HandleMouseEvents()
02440 {
02441
02442
02443 assert(HasModalProgress() || IsLocalCompany());
02444
02445 static int double_click_time = 0;
02446 static Point double_click_pos = {0, 0};
02447
02448
02449 MouseClick click = MC_NONE;
02450 if (_left_button_down && !_left_button_clicked) {
02451 click = MC_LEFT;
02452 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02453 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
02454 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
02455 click = MC_DOUBLE_LEFT;
02456 }
02457 double_click_time = _realtime_tick;
02458 double_click_pos = _cursor.pos;
02459 _left_button_clicked = true;
02460 _input_events_this_tick++;
02461 } else if (_right_button_clicked) {
02462 _right_button_clicked = false;
02463 click = MC_RIGHT;
02464 _input_events_this_tick++;
02465 }
02466
02467 int mousewheel = 0;
02468 if (_cursor.wheel) {
02469 mousewheel = _cursor.wheel;
02470 _cursor.wheel = 0;
02471 _input_events_this_tick++;
02472 }
02473
02474 static uint32 hover_time = 0;
02475 static Point hover_pos = {0, 0};
02476
02477 if (_settings_client.gui.hover_delay > 0) {
02478 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
02479 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
02480 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
02481 hover_pos = _cursor.pos;
02482 hover_time = _realtime_tick;
02483 _mouse_hovering = false;
02484 } else {
02485 if (hover_time != 0 && _realtime_tick > hover_time + _settings_client.gui.hover_delay * 1000) {
02486 click = MC_HOVER;
02487 _input_events_this_tick++;
02488 _mouse_hovering = true;
02489 }
02490 }
02491 }
02492
02493
02494 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _newgrf_debug_sprite_picker.click_time != _realtime_tick) {
02495
02496 _newgrf_debug_sprite_picker.mode = SPM_NONE;
02497 InvalidateWindowData(WC_SPRITE_ALIGNER, 0, 1);
02498 }
02499
02500 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
02501
02502 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
02503 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
02504 _newgrf_debug_sprite_picker.click_time = _realtime_tick;
02505 _newgrf_debug_sprite_picker.sprites.Clear();
02506 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
02507 MarkWholeScreenDirty();
02508 } else {
02509 MouseLoop(click, mousewheel);
02510 }
02511
02512
02513
02514 _cursor.delta.x = 0;
02515 _cursor.delta.y = 0;
02516 }
02517
02521 static void CheckSoftLimit()
02522 {
02523 if (_settings_client.gui.window_soft_limit == 0) return;
02524
02525 for (;;) {
02526 uint deletable_count = 0;
02527 Window *w, *last_deletable = NULL;
02528 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02529 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue;
02530
02531 last_deletable = w;
02532 deletable_count++;
02533 }
02534
02535
02536 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02537
02538 assert(last_deletable != NULL);
02539 delete last_deletable;
02540 }
02541 }
02542
02546 void InputLoop()
02547 {
02548
02549
02550 assert(HasModalProgress() || IsLocalCompany());
02551
02552 CheckSoftLimit();
02553 HandleKeyScrolling();
02554
02555
02556 for (Window *v = _z_front_window; v != NULL; ) {
02557 Window *w = v;
02558 v = v->z_back;
02559
02560 if (w->window_class != WC_INVALID) continue;
02561
02562 RemoveWindowFromZOrdering(w);
02563 free(w);
02564 }
02565
02566 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
02567 DecreaseWindowCounters();
02568
02569 if (_input_events_this_tick != 0) {
02570
02571 _input_events_this_tick = 0;
02572
02573 return;
02574 }
02575
02576
02577 HandleMouseEvents();
02578 HandleAutoscroll();
02579 }
02580
02584 void UpdateWindows()
02585 {
02586 Window *w;
02587
02588 static int highlight_timer = 1;
02589 if (--highlight_timer == 0) {
02590 highlight_timer = 15;
02591 _window_highlight_colour = !_window_highlight_colour;
02592 }
02593
02594 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02595 w->ProcessScheduledInvalidations();
02596 w->ProcessHighlightedInvalidations();
02597 }
02598
02599 static int we4_timer = 0;
02600 int t = we4_timer + 1;
02601
02602 if (t >= 100) {
02603 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02604 w->OnHundredthTick();
02605 }
02606 t = 0;
02607 }
02608 we4_timer = t;
02609
02610 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02611 if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
02612 CLRBITS(w->flags, WF_WHITE_BORDER);
02613 w->SetDirty();
02614 }
02615 }
02616
02617 DrawDirtyBlocks();
02618
02619 FOR_ALL_WINDOWS_FROM_BACK(w) {
02620
02621 if (w->viewport != NULL && !w->IsShaded()) UpdateViewportPosition(w);
02622 }
02623 NetworkDrawChatMessage();
02624
02625 DrawMouseCursor();
02626 }
02627
02633 void SetWindowDirty(WindowClass cls, WindowNumber number)
02634 {
02635 const Window *w;
02636 FOR_ALL_WINDOWS_FROM_BACK(w) {
02637 if (w->window_class == cls && w->window_number == number) w->SetDirty();
02638 }
02639 }
02640
02647 void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
02648 {
02649 const Window *w;
02650 FOR_ALL_WINDOWS_FROM_BACK(w) {
02651 if (w->window_class == cls && w->window_number == number) {
02652 w->SetWidgetDirty(widget_index);
02653 }
02654 }
02655 }
02656
02661 void SetWindowClassesDirty(WindowClass cls)
02662 {
02663 Window *w;
02664 FOR_ALL_WINDOWS_FROM_BACK(w) {
02665 if (w->window_class == cls) w->SetDirty();
02666 }
02667 }
02668
02674 void Window::InvalidateData(int data, bool gui_scope)
02675 {
02676 this->SetDirty();
02677 if (!gui_scope) {
02678
02679 *this->scheduled_invalidation_data.Append() = data;
02680 }
02681 this->OnInvalidateData(data, gui_scope);
02682 }
02683
02687 void Window::ProcessScheduledInvalidations()
02688 {
02689 for (int *data = this->scheduled_invalidation_data.Begin(); this->window_class != WC_INVALID && data != this->scheduled_invalidation_data.End(); data++) {
02690 this->OnInvalidateData(*data, true);
02691 }
02692 this->scheduled_invalidation_data.Clear();
02693 }
02694
02698 void Window::ProcessHighlightedInvalidations()
02699 {
02700 if ((this->flags & WF_HIGHLIGHTED) == 0) return;
02701
02702 for (uint i = 0; i < this->nested_array_size; i++) {
02703 if (this->IsWidgetHighlighted(i)) this->SetWidgetDirty(i);
02704 }
02705 }
02706
02733 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
02734 {
02735 Window *w;
02736 FOR_ALL_WINDOWS_FROM_BACK(w) {
02737 if (w->window_class == cls && w->window_number == number) {
02738 w->InvalidateData(data, gui_scope);
02739 }
02740 }
02741 }
02742
02751 void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
02752 {
02753 Window *w;
02754
02755 FOR_ALL_WINDOWS_FROM_BACK(w) {
02756 if (w->window_class == cls) {
02757 w->InvalidateData(data, gui_scope);
02758 }
02759 }
02760 }
02761
02765 void CallWindowTickEvent()
02766 {
02767 Window *w;
02768 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02769 w->OnTick();
02770 }
02771 }
02772
02779 void DeleteNonVitalWindows()
02780 {
02781 Window *w;
02782
02783 restart_search:
02784
02785
02786
02787 FOR_ALL_WINDOWS_FROM_BACK(w) {
02788 if (w->window_class != WC_MAIN_WINDOW &&
02789 w->window_class != WC_SELECT_GAME &&
02790 w->window_class != WC_MAIN_TOOLBAR &&
02791 w->window_class != WC_STATUS_BAR &&
02792 w->window_class != WC_TOOLTIPS &&
02793 (w->flags & WF_STICKY) == 0) {
02794
02795 delete w;
02796 goto restart_search;
02797 }
02798 }
02799 }
02800
02808 void DeleteAllNonVitalWindows()
02809 {
02810 Window *w;
02811
02812
02813 DeleteNonVitalWindows();
02814
02815 restart_search:
02816
02817
02818
02819 FOR_ALL_WINDOWS_FROM_BACK(w) {
02820 if (w->flags & WF_STICKY) {
02821 delete w;
02822 goto restart_search;
02823 }
02824 }
02825 }
02826
02831 void DeleteConstructionWindows()
02832 {
02833 Window *w;
02834
02835 restart_search:
02836
02837
02838
02839 FOR_ALL_WINDOWS_FROM_BACK(w) {
02840 if (w->desc_flags & WDF_CONSTRUCTION) {
02841 delete w;
02842 goto restart_search;
02843 }
02844 }
02845
02846 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
02847 }
02848
02850 void HideVitalWindows()
02851 {
02852 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
02853 DeleteWindowById(WC_STATUS_BAR, 0);
02854 }
02855
02857 void ReInitAllWindows()
02858 {
02859 NWidgetLeaf::InvalidateDimensionCache();
02860 NWidgetScrollbar::InvalidateDimensionCache();
02861
02862 Window *w;
02863 FOR_ALL_WINDOWS_FROM_BACK(w) {
02864 w->ReInit();
02865 }
02866 #ifdef ENABLE_NETWORK
02867 void NetworkReInitChatBoxSize();
02868 NetworkReInitChatBoxSize();
02869 #endif
02870
02871
02872 RelocateAllWindows(_cur_resolution.width, _cur_resolution.height);
02873 MarkWholeScreenDirty();
02874 }
02875
02883 static int PositionWindow(Window *w, WindowClass clss, int setting)
02884 {
02885 if (w == NULL || w->window_class != clss) {
02886 w = FindWindowById(clss, 0);
02887 }
02888 if (w == NULL) return 0;
02889
02890 int old_left = w->left;
02891 switch (setting) {
02892 case 1: w->left = (_screen.width - w->width) / 2; break;
02893 case 2: w->left = _screen.width - w->width; break;
02894 default: w->left = 0; break;
02895 }
02896 if (w->viewport != NULL) w->viewport->left += w->left - old_left;
02897 SetDirtyBlocks(0, w->top, _screen.width, w->top + w->height);
02898 return w->left;
02899 }
02900
02906 int PositionMainToolbar(Window *w)
02907 {
02908 DEBUG(misc, 5, "Repositioning Main Toolbar...");
02909 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
02910 }
02911
02917 int PositionStatusbar(Window *w)
02918 {
02919 DEBUG(misc, 5, "Repositioning statusbar...");
02920 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
02921 }
02922
02928 int PositionNewsMessage(Window *w)
02929 {
02930 DEBUG(misc, 5, "Repositioning news message...");
02931 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
02932 }
02933
02939 int PositionNetworkChatWindow(Window *w)
02940 {
02941 DEBUG(misc, 5, "Repositioning network chat window...");
02942 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
02943 }
02944
02945
02951 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
02952 {
02953 Window *w;
02954 FOR_ALL_WINDOWS_FROM_BACK(w) {
02955 if (w->viewport != NULL && w->viewport->follow_vehicle == from_index) {
02956 w->viewport->follow_vehicle = to_index;
02957 w->SetDirty();
02958 }
02959 }
02960 }
02961
02962
02968 void RelocateAllWindows(int neww, int newh)
02969 {
02970 Window *w;
02971
02972 FOR_ALL_WINDOWS_FROM_BACK(w) {
02973 int left, top;
02974
02975 if (w->window_class == WC_MAIN_WINDOW) {
02976 ViewPort *vp = w->viewport;
02977 vp->width = w->width = neww;
02978 vp->height = w->height = newh;
02979 vp->virtual_width = ScaleByZoom(neww, vp->zoom);
02980 vp->virtual_height = ScaleByZoom(newh, vp->zoom);
02981 continue;
02982 }
02983
02984
02985
02986 switch (w->window_class) {
02987 case WC_BOOTSTRAP:
02988 ResizeWindow(w, neww, newh);
02989 continue;
02990
02991 case WC_MAIN_TOOLBAR:
02992 ResizeWindow(w, min(neww, *_preferred_toolbar_size) - w->width, 0);
02993
02994 top = w->top;
02995 left = PositionMainToolbar(w);
02996 break;
02997
02998 case WC_NEWS_WINDOW:
02999 top = newh - w->height;
03000 left = PositionNewsMessage(w);
03001 break;
03002
03003 case WC_STATUS_BAR:
03004 ResizeWindow(w, min(neww, *_preferred_statusbar_size) - w->width, 0);
03005
03006 top = newh - w->height;
03007 left = PositionStatusbar(w);
03008 break;
03009
03010 case WC_SEND_NETWORK_MSG:
03011 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0);
03012 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
03013 left = PositionNetworkChatWindow(w);
03014 break;
03015
03016 case WC_CONSOLE:
03017 IConsoleResize(w);
03018 continue;
03019
03020 default: {
03021 if (w->flags & WF_CENTERED) {
03022 top = (newh - w->height) >> 1;
03023 left = (neww - w->width) >> 1;
03024 break;
03025 }
03026
03027 left = w->left;
03028 if (left + (w->width >> 1) >= neww) left = neww - w->width;
03029 if (left < 0) left = 0;
03030
03031 top = w->top;
03032 if (top + (w->height >> 1) >= newh) top = newh - w->height;
03033 break;
03034 }
03035 }
03036
03037 EnsureVisibleCaption(w, left, top);
03038 }
03039 }
03040
03046 PickerWindowBase::~PickerWindowBase()
03047 {
03048 this->window_class = WC_INVALID;
03049 ResetObjectToPlace();
03050 }