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 "progress.h"
00020 #include "blitter/factory.hpp"
00021 #include "zoom_func.h"
00022 #include "vehicle_base.h"
00023 #include "window_func.h"
00024 #include "tilehighlight_func.h"
00025 #include "network/network.h"
00026 #include "querystring_gui.h"
00027 #include "widgets/dropdown_func.h"
00028 #include "strings_func.h"
00029 #include "settings_type.h"
00030 #include "settings_func.h"
00031 #include "ini_type.h"
00032 #include "newgrf_debug.h"
00033 #include "hotkeys.h"
00034 #include "toolbar_gui.h"
00035 #include "statusbar_gui.h"
00036 #include "error.h"
00037 #include "game/game.hpp"
00038 #include "video/video_driver.hpp"
00039
00041 enum ViewportAutoscrolling {
00042 VA_DISABLED,
00043 VA_MAIN_VIEWPORT_FULLSCREEN,
00044 VA_MAIN_VIEWPORT,
00045 VA_EVERY_VIEWPORT,
00046 };
00047
00048 static Point _drag_delta;
00049 static Window *_mouseover_last_w = NULL;
00050 static Window *_last_scroll_window = NULL;
00051
00053 Window *_z_front_window = NULL;
00055 Window *_z_back_window = NULL;
00056
00058 bool _window_highlight_colour = false;
00059
00060
00061
00062
00063
00064
00065 Window *_focused_window;
00066
00067 Point _cursorpos_drag_start;
00068
00069 int _scrollbar_start_pos;
00070 int _scrollbar_size;
00071 byte _scroller_click_timeout = 0;
00072
00073 bool _scrolling_viewport;
00074 bool _mouse_hovering;
00075
00076 SpecialMouseMode _special_mouse_mode;
00077
00082 static SmallVector<WindowDesc*, 16> *_window_descs = NULL;
00083
00085 char *_windows_file;
00086
00088 WindowDesc::WindowDesc(WindowPosition def_pos, const char *ini_key, int16 def_width, int16 def_height,
00089 WindowClass window_class, WindowClass parent_class, uint32 flags,
00090 const NWidgetPart *nwid_parts, int16 nwid_length, HotkeyList *hotkeys) :
00091 default_pos(def_pos),
00092 default_width(def_width),
00093 default_height(def_height),
00094 cls(window_class),
00095 parent_cls(parent_class),
00096 ini_key(ini_key),
00097 flags(flags),
00098 nwid_parts(nwid_parts),
00099 nwid_length(nwid_length),
00100 hotkeys(hotkeys),
00101 pref_sticky(false),
00102 pref_width(0),
00103 pref_height(0)
00104 {
00105 if (_window_descs == NULL) _window_descs = new SmallVector<WindowDesc*, 16>();
00106 *_window_descs->Append() = this;
00107 }
00108
00109 WindowDesc::~WindowDesc()
00110 {
00111 _window_descs->Erase(_window_descs->Find(this));
00112 }
00113
00117 void WindowDesc::LoadFromConfig()
00118 {
00119 IniFile *ini = new IniFile();
00120 ini->LoadFromDisk(_windows_file, BASE_DIR);
00121 for (WindowDesc **it = _window_descs->Begin(); it != _window_descs->End(); ++it) {
00122 if ((*it)->ini_key == NULL) continue;
00123 IniLoadWindowSettings(ini, (*it)->ini_key, *it);
00124 }
00125 delete ini;
00126 }
00127
00131 static int CDECL DescSorter(WindowDesc * const *a, WindowDesc * const *b)
00132 {
00133 if ((*a)->ini_key != NULL && (*b)->ini_key != NULL) return strcmp((*a)->ini_key, (*b)->ini_key);
00134 return ((*b)->ini_key != NULL ? 1 : 0) - ((*a)->ini_key != NULL ? 1 : 0);
00135 }
00136
00140 void WindowDesc::SaveToConfig()
00141 {
00142
00143 QSortT(_window_descs->Begin(), _window_descs->Length(), DescSorter);
00144
00145 IniFile *ini = new IniFile();
00146 ini->LoadFromDisk(_windows_file, BASE_DIR);
00147 for (WindowDesc **it = _window_descs->Begin(); it != _window_descs->End(); ++it) {
00148 if ((*it)->ini_key == NULL) continue;
00149 IniSaveWindowSettings(ini, (*it)->ini_key, *it);
00150 }
00151 ini->SaveToDisk(_windows_file);
00152 delete ini;
00153 }
00154
00158 void Window::ApplyDefaults()
00159 {
00160 if (this->nested_root != NULL && this->nested_root->GetWidgetOfType(WWT_STICKYBOX) != NULL) {
00161 if (this->window_desc->pref_sticky) this->flags |= WF_STICKY;
00162 } else {
00163
00164 this->window_desc->pref_sticky = false;
00165 }
00166 }
00167
00177 int Window::GetRowFromWidget(int clickpos, int widget, int padding, int line_height) const
00178 {
00179 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(widget);
00180 if (line_height < 0) line_height = wid->resize_y;
00181 if (clickpos < (int)wid->pos_y + padding) return INT_MAX;
00182 return (clickpos - (int)wid->pos_y - padding) / line_height;
00183 }
00184
00188 void Window::DisableAllWidgetHighlight()
00189 {
00190 for (uint i = 0; i < this->nested_array_size; i++) {
00191 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(i);
00192 if (nwid == NULL) continue;
00193
00194 if (nwid->IsHighlighted()) {
00195 nwid->SetHighlighted(TC_INVALID);
00196 this->SetWidgetDirty(i);
00197 }
00198 }
00199
00200 CLRBITS(this->flags, WF_HIGHLIGHTED);
00201 }
00202
00208 void Window::SetWidgetHighlight(byte widget_index, TextColour highlighted_colour)
00209 {
00210 assert(widget_index < this->nested_array_size);
00211
00212 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
00213 if (nwid == NULL) return;
00214
00215 nwid->SetHighlighted(highlighted_colour);
00216 this->SetWidgetDirty(widget_index);
00217
00218 if (highlighted_colour != TC_INVALID) {
00219
00220 this->flags |= WF_HIGHLIGHTED;
00221 } else {
00222
00223 bool valid = false;
00224 for (uint i = 0; i < this->nested_array_size; i++) {
00225 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(i);
00226 if (nwid == NULL) continue;
00227 if (!nwid->IsHighlighted()) continue;
00228
00229 valid = true;
00230 }
00231
00232 if (!valid) CLRBITS(this->flags, WF_HIGHLIGHTED);
00233 }
00234 }
00235
00241 bool Window::IsWidgetHighlighted(byte widget_index) const
00242 {
00243 assert(widget_index < this->nested_array_size);
00244
00245 const NWidgetBase *nwid = this->GetWidget<NWidgetBase>(widget_index);
00246 if (nwid == NULL) return false;
00247
00248 return nwid->IsHighlighted();
00249 }
00250
00258 void Window::OnDropdownClose(Point pt, int widget, int index, bool instant_close)
00259 {
00260 if (widget < 0) return;
00261
00262 if (instant_close) {
00263
00264
00265 if (GetWidgetFromPos(this, pt.x, pt.y) == widget) {
00266 this->OnDropdownSelect(widget, index);
00267 }
00268 }
00269
00270
00271 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(widget);
00272 if ((nwi2->type & WWT_MASK) == NWID_BUTTON_DROPDOWN) {
00273 nwi2->disp_flags &= ~ND_DROPDOWN_ACTIVE;
00274 } else {
00275 this->RaiseWidget(widget);
00276 }
00277 this->SetWidgetDirty(widget);
00278 }
00279
00285 const Scrollbar *Window::GetScrollbar(uint widnum) const
00286 {
00287 return this->GetWidget<NWidgetScrollbar>(widnum);
00288 }
00289
00295 Scrollbar *Window::GetScrollbar(uint widnum)
00296 {
00297 return this->GetWidget<NWidgetScrollbar>(widnum);
00298 }
00299
00305 const QueryString *Window::GetQueryString(uint widnum) const
00306 {
00307 const SmallMap<int, QueryString*>::Pair *query = this->querystrings.Find(widnum);
00308 return query != this->querystrings.End() ? query->second : NULL;
00309 }
00310
00316 QueryString *Window::GetQueryString(uint widnum)
00317 {
00318 SmallMap<int, QueryString*>::Pair *query = this->querystrings.Find(widnum);
00319 return query != this->querystrings.End() ? query->second : NULL;
00320 }
00321
00326 const char *Window::GetFocusedText() const
00327 {
00328 if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
00329 return this->GetQueryString(this->nested_focus->index)->GetText();
00330 }
00331
00332 return NULL;
00333 }
00334
00339 const char *Window::GetCaret() const
00340 {
00341 if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
00342 return this->GetQueryString(this->nested_focus->index)->GetCaret();
00343 }
00344
00345 return NULL;
00346 }
00347
00353 const char *Window::GetMarkedText(size_t *length) const
00354 {
00355 if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
00356 return this->GetQueryString(this->nested_focus->index)->GetMarkedText(length);
00357 }
00358
00359 return NULL;
00360 }
00361
00366 Point Window::GetCaretPosition() const
00367 {
00368 if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
00369 return this->GetQueryString(this->nested_focus->index)->GetCaretPosition(this, this->nested_focus->index);
00370 }
00371
00372 Point pt = {0, 0};
00373 return pt;
00374 }
00375
00382 Rect Window::GetTextBoundingRect(const char *from, const char *to) const
00383 {
00384 if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
00385 return this->GetQueryString(this->nested_focus->index)->GetBoundingRect(this, this->nested_focus->index, from, to);
00386 }
00387
00388 Rect r = {0, 0, 0, 0};
00389 return r;
00390 }
00391
00397 const char *Window::GetTextCharacterAtPosition(const Point &pt) const
00398 {
00399 if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) {
00400 return this->GetQueryString(this->nested_focus->index)->GetCharAtPosition(this, this->nested_focus->index, pt);
00401 }
00402
00403 return NULL;
00404 }
00405
00410 void SetFocusedWindow(Window *w)
00411 {
00412 if (_focused_window == w) return;
00413
00414
00415 if (_focused_window != NULL) {
00416 if (_focused_window->nested_focus != NULL) _focused_window->nested_focus->SetDirty(_focused_window);
00417 }
00418
00419
00420 Window *old_focused = _focused_window;
00421 _focused_window = w;
00422
00423
00424 if (old_focused != NULL) old_focused->OnFocusLost();
00425 if (_focused_window != NULL) _focused_window->OnFocus();
00426 }
00427
00433 bool EditBoxInGlobalFocus()
00434 {
00435 if (_focused_window == NULL) return false;
00436
00437
00438 if (_focused_window->window_class == WC_CONSOLE) return true;
00439
00440 return _focused_window->nested_focus != NULL && _focused_window->nested_focus->type == WWT_EDITBOX;
00441 }
00442
00446 void Window::UnfocusFocusedWidget()
00447 {
00448 if (this->nested_focus != NULL) {
00449 if (this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus();
00450
00451
00452 this->nested_focus->SetDirty(this);
00453 this->nested_focus = NULL;
00454 }
00455 }
00456
00462 bool Window::SetFocusedWidget(int widget_index)
00463 {
00464
00465 if ((uint)widget_index >= this->nested_array_size) return false;
00466
00467 assert(this->nested_array[widget_index] != NULL);
00468 if (this->nested_focus != NULL) {
00469 if (this->GetWidget<NWidgetCore>(widget_index) == this->nested_focus) return false;
00470
00471
00472 this->nested_focus->SetDirty(this);
00473 if (this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus();
00474 }
00475 this->nested_focus = this->GetWidget<NWidgetCore>(widget_index);
00476 return true;
00477 }
00478
00482 void Window::OnFocusLost()
00483 {
00484 if (this->nested_focus != NULL && this->nested_focus->type == WWT_EDITBOX) VideoDriver::GetInstance()->EditBoxLostFocus();
00485 }
00486
00494 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
00495 {
00496 va_list wdg_list;
00497
00498 va_start(wdg_list, widgets);
00499
00500 while (widgets != WIDGET_LIST_END) {
00501 SetWidgetDisabledState(widgets, disab_stat);
00502 widgets = va_arg(wdg_list, int);
00503 }
00504
00505 va_end(wdg_list);
00506 }
00507
00513 void CDECL Window::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
00514 {
00515 va_list wdg_list;
00516
00517 va_start(wdg_list, widgets);
00518
00519 while (widgets != WIDGET_LIST_END) {
00520 SetWidgetLoweredState(widgets, lowered_stat);
00521 widgets = va_arg(wdg_list, int);
00522 }
00523
00524 va_end(wdg_list);
00525 }
00526
00531 void Window::RaiseButtons(bool autoraise)
00532 {
00533 for (uint i = 0; i < this->nested_array_size; i++) {
00534 if (this->nested_array[i] == NULL) continue;
00535 WidgetType type = this->nested_array[i]->type;
00536 if (((type & ~WWB_PUSHBUTTON) < WWT_LAST || type == NWID_PUSHBUTTON_DROPDOWN) &&
00537 (!autoraise || (type & WWB_PUSHBUTTON) || type == WWT_EDITBOX) && this->IsWidgetLowered(i)) {
00538 this->RaiseWidget(i);
00539 this->SetWidgetDirty(i);
00540 }
00541 }
00542
00543
00544 NWidgetCore *wid = this->nested_root != NULL ? (NWidgetCore*)this->nested_root->GetWidgetOfType(WWT_DEFSIZEBOX) : NULL;
00545 if (wid != NULL) {
00546 wid->SetLowered(false);
00547 wid->SetDirty(this);
00548 }
00549 }
00550
00555 void Window::SetWidgetDirty(byte widget_index) const
00556 {
00557
00558 if (this->nested_array == NULL) return;
00559
00560 this->nested_array[widget_index]->SetDirty(this);
00561 }
00562
00568 EventState Window::OnHotkey(int hotkey)
00569 {
00570 if (hotkey < 0) return ES_NOT_HANDLED;
00571
00572 NWidgetCore *nw = this->GetWidget<NWidgetCore>(hotkey);
00573 if (nw == NULL || nw->IsDisabled()) return ES_NOT_HANDLED;
00574
00575 if (nw->type == WWT_EDITBOX) {
00576 if (this->IsShaded()) return ES_NOT_HANDLED;
00577
00578
00579 this->SetFocusedWidget(hotkey);
00580 SetFocusedWindow(this);
00581 } else {
00582
00583 this->OnClick(Point(), hotkey, 1);
00584 }
00585 return ES_HANDLED;
00586 }
00587
00593 void Window::HandleButtonClick(byte widget)
00594 {
00595 this->LowerWidget(widget);
00596 this->SetTimeout();
00597 this->SetWidgetDirty(widget);
00598 }
00599
00600 static void StartWindowDrag(Window *w);
00601 static void StartWindowSizing(Window *w, bool to_left);
00602
00610 static void DispatchLeftClickEvent(Window *w, int x, int y, int click_count)
00611 {
00612 NWidgetCore *nw = w->nested_root->GetWidgetFromPos(x, y);
00613 WidgetType widget_type = (nw != NULL) ? nw->type : WWT_EMPTY;
00614
00615 bool focused_widget_changed = false;
00616
00617 if (_focused_window != w &&
00618 (w->window_desc->flags & WDF_NO_FOCUS) == 0 &&
00619 widget_type != WWT_CLOSEBOX) {
00620 focused_widget_changed = true;
00621 SetFocusedWindow(w);
00622 }
00623
00624 if (nw == NULL) return;
00625
00626
00627 if (nw->IsDisabled()) return;
00628
00629 int widget_index = nw->index;
00630
00631
00632
00633
00634 if (widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644 focused_widget_changed |= w->SetFocusedWidget(widget_index);
00645 }
00646
00647
00648
00649 if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
00650
00651 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
00652
00653 Point pt = { x, y };
00654
00655 switch (widget_type) {
00656 case NWID_VSCROLLBAR:
00657 case NWID_HSCROLLBAR:
00658 ScrollbarClickHandler(w, nw, x, y);
00659 break;
00660
00661 case WWT_EDITBOX: {
00662 QueryString *query = w->GetQueryString(widget_index);
00663 if (query != NULL) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
00664 break;
00665 }
00666
00667 case WWT_CLOSEBOX:
00668 delete w;
00669 return;
00670
00671 case WWT_CAPTION:
00672 StartWindowDrag(w);
00673 return;
00674
00675 case WWT_RESIZEBOX:
00676
00677
00678 StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
00679 nw->SetDirty(w);
00680 return;
00681
00682 case WWT_DEFSIZEBOX: {
00683 if (_ctrl_pressed) {
00684 w->window_desc->pref_width = w->width;
00685 w->window_desc->pref_height = w->height;
00686 } else {
00687 int16 def_width = max<int16>(min(w->window_desc->GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
00688 int16 def_height = max<int16>(min(w->window_desc->GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
00689
00690 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
00691 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
00692
00693
00694 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
00695 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
00696 ResizeWindow(w, dx, dy, false);
00697 }
00698
00699 nw->SetLowered(true);
00700 nw->SetDirty(w);
00701 w->SetTimeout();
00702 break;
00703 }
00704
00705 case WWT_DEBUGBOX:
00706 w->ShowNewGRFInspectWindow();
00707 break;
00708
00709 case WWT_SHADEBOX:
00710 nw->SetDirty(w);
00711 w->SetShaded(!w->IsShaded());
00712 return;
00713
00714 case WWT_STICKYBOX:
00715 w->flags ^= WF_STICKY;
00716 nw->SetDirty(w);
00717 if (_ctrl_pressed) w->window_desc->pref_sticky = (w->flags & WF_STICKY) != 0;
00718 return;
00719
00720 default:
00721 break;
00722 }
00723
00724
00725 if (widget_index < 0) return;
00726
00727
00728 if (w->IsWidgetHighlighted(widget_index)) {
00729 w->SetWidgetHighlight(widget_index, TC_INVALID);
00730 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
00731 }
00732
00733 w->OnClick(pt, widget_index, click_count);
00734 }
00735
00742 static void DispatchRightClickEvent(Window *w, int x, int y)
00743 {
00744 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00745 if (wid == NULL) return;
00746
00747
00748 if (wid->index >= 0) {
00749 Point pt = { x, y };
00750 if (w->OnRightClick(pt, wid->index)) return;
00751 }
00752
00753 if (_settings_client.gui.hover_delay == 0 && wid->tool_tip != 0) GuiShowTooltips(w, wid->tool_tip, 0, NULL, TCC_RIGHT_CLICK);
00754 }
00755
00762 static void DispatchHoverEvent(Window *w, int x, int y)
00763 {
00764 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00765
00766
00767 if (wid == NULL) return;
00768
00769
00770 if (wid->tool_tip != 0) {
00771 GuiShowTooltips(w, wid->tool_tip);
00772 return;
00773 }
00774
00775
00776 if (wid->index < 0) return;
00777
00778 Point pt = { x, y };
00779 w->OnHover(pt, wid->index);
00780 }
00781
00789 static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
00790 {
00791 if (nwid == NULL) return;
00792
00793
00794 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
00795 w->SetShaded(wheel < 0);
00796 return;
00797 }
00798
00799
00800 if (nwid->type == NWID_VSCROLLBAR) {
00801 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
00802 if (sb->GetCount() > sb->GetCapacity()) {
00803 sb->UpdatePosition(wheel);
00804 w->SetDirty();
00805 }
00806 return;
00807 }
00808
00809
00810 Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : NULL);
00811 if (sb != NULL && sb->GetCount() > sb->GetCapacity()) {
00812 sb->UpdatePosition(wheel);
00813 w->SetDirty();
00814 }
00815 }
00816
00822 static bool MayBeShown(const Window *w)
00823 {
00824
00825 if (!HasModalProgress()) return true;
00826
00827 switch (w->window_class) {
00828 case WC_MAIN_WINDOW:
00829 case WC_MODAL_PROGRESS:
00830 case WC_CONFIRM_POPUP_QUERY:
00831 return true;
00832
00833 default:
00834 return false;
00835 }
00836 }
00837
00850 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00851 {
00852 const Window *v;
00853 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00854 if (MayBeShown(v) &&
00855 right > v->left &&
00856 bottom > v->top &&
00857 left < v->left + v->width &&
00858 top < v->top + v->height) {
00859
00860 int x;
00861
00862 if (left < (x = v->left)) {
00863 DrawOverlappedWindow(w, left, top, x, bottom);
00864 DrawOverlappedWindow(w, x, top, right, bottom);
00865 return;
00866 }
00867
00868 if (right > (x = v->left + v->width)) {
00869 DrawOverlappedWindow(w, left, top, x, bottom);
00870 DrawOverlappedWindow(w, x, top, right, bottom);
00871 return;
00872 }
00873
00874 if (top < (x = v->top)) {
00875 DrawOverlappedWindow(w, left, top, right, x);
00876 DrawOverlappedWindow(w, left, x, right, bottom);
00877 return;
00878 }
00879
00880 if (bottom > (x = v->top + v->height)) {
00881 DrawOverlappedWindow(w, left, top, right, x);
00882 DrawOverlappedWindow(w, left, x, right, bottom);
00883 return;
00884 }
00885
00886 return;
00887 }
00888 }
00889
00890
00891 DrawPixelInfo *dp = _cur_dpi;
00892 dp->width = right - left;
00893 dp->height = bottom - top;
00894 dp->left = left - w->left;
00895 dp->top = top - w->top;
00896 dp->pitch = _screen.pitch;
00897 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00898 dp->zoom = ZOOM_LVL_NORMAL;
00899 w->OnPaint();
00900 }
00901
00910 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00911 {
00912 Window *w;
00913 DrawPixelInfo bk;
00914 _cur_dpi = &bk;
00915
00916 FOR_ALL_WINDOWS_FROM_BACK(w) {
00917 if (MayBeShown(w) &&
00918 right > w->left &&
00919 bottom > w->top &&
00920 left < w->left + w->width &&
00921 top < w->top + w->height) {
00922
00923 DrawOverlappedWindow(w, left, top, right, bottom);
00924 }
00925 }
00926 }
00927
00932 void Window::SetDirty() const
00933 {
00934 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00935 }
00936
00943 void Window::ReInit(int rx, int ry)
00944 {
00945 this->SetDirty();
00946
00947
00948 int window_width = this->width;
00949 int window_height = this->height;
00950
00951 this->OnInit();
00952
00953 this->nested_root->SetupSmallestSize(this, false);
00954 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
00955 this->width = this->nested_root->smallest_x;
00956 this->height = this->nested_root->smallest_y;
00957 this->resize.step_width = this->nested_root->resize_x;
00958 this->resize.step_height = this->nested_root->resize_y;
00959
00960
00961 window_width = max(window_width + rx, this->width);
00962 window_height = max(window_height + ry, this->height);
00963 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
00964 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
00965
00966
00967 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
00968 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
00969
00970 ResizeWindow(this, dx, dy);
00971
00972 }
00973
00979 void Window::SetShaded(bool make_shaded)
00980 {
00981 if (this->shade_select == NULL) return;
00982
00983 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
00984 if (this->shade_select->shown_plane != desired) {
00985 if (make_shaded) {
00986 if (this->nested_focus != NULL) this->UnfocusFocusedWidget();
00987 this->unshaded_size.width = this->width;
00988 this->unshaded_size.height = this->height;
00989 this->shade_select->SetDisplayedPlane(desired);
00990 this->ReInit(0, -this->height);
00991 } else {
00992 this->shade_select->SetDisplayedPlane(desired);
00993 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
00994 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
00995 this->ReInit(dx, dy);
00996 }
00997 }
00998 }
00999
01006 static Window *FindChildWindow(const Window *w, WindowClass wc)
01007 {
01008 Window *v;
01009 FOR_ALL_WINDOWS_FROM_BACK(v) {
01010 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
01011 }
01012
01013 return NULL;
01014 }
01015
01020 void Window::DeleteChildWindows(WindowClass wc) const
01021 {
01022 Window *child = FindChildWindow(this, wc);
01023 while (child != NULL) {
01024 delete child;
01025 child = FindChildWindow(this, wc);
01026 }
01027 }
01028
01032 Window::~Window()
01033 {
01034 if (_thd.window_class == this->window_class &&
01035 _thd.window_number == this->window_number) {
01036 ResetObjectToPlace();
01037 }
01038
01039
01040 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
01041
01042
01043 if (_last_scroll_window == this) _last_scroll_window = NULL;
01044
01045
01046 if (_focused_window == this) {
01047 this->OnFocusLost();
01048 _focused_window = NULL;
01049 }
01050
01051 this->DeleteChildWindows();
01052
01053 if (this->viewport != NULL) DeleteWindowViewport(this);
01054
01055 this->SetDirty();
01056
01057 free(this->nested_array);
01058 delete this->nested_root;
01059
01060 this->window_class = WC_INVALID;
01061 }
01062
01069 Window *FindWindowById(WindowClass cls, WindowNumber number)
01070 {
01071 Window *w;
01072 FOR_ALL_WINDOWS_FROM_BACK(w) {
01073 if (w->window_class == cls && w->window_number == number) return w;
01074 }
01075
01076 return NULL;
01077 }
01078
01085 Window *FindWindowByClass(WindowClass cls)
01086 {
01087 Window *w;
01088 FOR_ALL_WINDOWS_FROM_BACK(w) {
01089 if (w->window_class == cls) return w;
01090 }
01091
01092 return NULL;
01093 }
01094
01101 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
01102 {
01103 Window *w = FindWindowById(cls, number);
01104 if (force || w == NULL ||
01105 (w->flags & WF_STICKY) == 0) {
01106 delete w;
01107 }
01108 }
01109
01114 void DeleteWindowByClass(WindowClass cls)
01115 {
01116 Window *w;
01117
01118 restart_search:
01119
01120
01121
01122 FOR_ALL_WINDOWS_FROM_BACK(w) {
01123 if (w->window_class == cls) {
01124 delete w;
01125 goto restart_search;
01126 }
01127 }
01128 }
01129
01136 void DeleteCompanyWindows(CompanyID id)
01137 {
01138 Window *w;
01139
01140 restart_search:
01141
01142
01143
01144 FOR_ALL_WINDOWS_FROM_BACK(w) {
01145 if (w->owner == id) {
01146 delete w;
01147 goto restart_search;
01148 }
01149 }
01150
01151
01152 DeleteWindowById(WC_BUY_COMPANY, id);
01153 }
01154
01162 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
01163 {
01164 Window *w;
01165 FOR_ALL_WINDOWS_FROM_BACK(w) {
01166 if (w->owner != old_owner) continue;
01167
01168 switch (w->window_class) {
01169 case WC_COMPANY_COLOUR:
01170 case WC_FINANCES:
01171 case WC_STATION_LIST:
01172 case WC_TRAINS_LIST:
01173 case WC_ROADVEH_LIST:
01174 case WC_SHIPS_LIST:
01175 case WC_AIRCRAFT_LIST:
01176 case WC_BUY_COMPANY:
01177 case WC_COMPANY:
01178 case WC_COMPANY_INFRASTRUCTURE:
01179 continue;
01180
01181 default:
01182 w->owner = new_owner;
01183 break;
01184 }
01185 }
01186 }
01187
01188 static void BringWindowToFront(Window *w);
01189
01197 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
01198 {
01199 Window *w = FindWindowById(cls, number);
01200
01201 if (w != NULL) {
01202 if (w->IsShaded()) w->SetShaded(false);
01203
01204 w->SetWhiteBorder();
01205 BringWindowToFront(w);
01206 w->SetDirty();
01207 }
01208
01209 return w;
01210 }
01211
01212 static inline bool IsVitalWindow(const Window *w)
01213 {
01214 switch (w->window_class) {
01215 case WC_MAIN_TOOLBAR:
01216 case WC_STATUS_BAR:
01217 case WC_NEWS_WINDOW:
01218 case WC_SEND_NETWORK_MSG:
01219 return true;
01220
01221 default:
01222 return false;
01223 }
01224 }
01225
01234 static uint GetWindowZPriority(const Window *w)
01235 {
01236 assert(w->window_class != WC_INVALID);
01237
01238 uint z_priority = 0;
01239
01240 switch (w->window_class) {
01241 case WC_ENDSCREEN:
01242 ++z_priority;
01243
01244 case WC_HIGHSCORE:
01245 ++z_priority;
01246
01247 case WC_TOOLTIPS:
01248 ++z_priority;
01249
01250 case WC_DROPDOWN_MENU:
01251 ++z_priority;
01252
01253 case WC_MAIN_TOOLBAR:
01254 case WC_STATUS_BAR:
01255 ++z_priority;
01256
01257 case WC_OSK:
01258 ++z_priority;
01259
01260 case WC_QUERY_STRING:
01261 case WC_SEND_NETWORK_MSG:
01262 ++z_priority;
01263
01264 case WC_ERRMSG:
01265 case WC_CONFIRM_POPUP_QUERY:
01266 case WC_MODAL_PROGRESS:
01267 case WC_NETWORK_STATUS_WINDOW:
01268 ++z_priority;
01269
01270 case WC_GENERATE_LANDSCAPE:
01271 case WC_SAVELOAD:
01272 case WC_GAME_OPTIONS:
01273 case WC_CUSTOM_CURRENCY:
01274 case WC_NETWORK_WINDOW:
01275 case WC_GRF_PARAMETERS:
01276 case WC_AI_LIST:
01277 case WC_AI_SETTINGS:
01278 case WC_TEXTFILE:
01279 ++z_priority;
01280
01281 case WC_CONSOLE:
01282 ++z_priority;
01283
01284 case WC_NEWS_WINDOW:
01285 ++z_priority;
01286
01287 default:
01288 ++z_priority;
01289
01290 case WC_MAIN_WINDOW:
01291 return z_priority;
01292 }
01293 }
01294
01299 static void AddWindowToZOrdering(Window *w)
01300 {
01301 assert(w->z_front == NULL && w->z_back == NULL);
01302
01303 if (_z_front_window == NULL) {
01304
01305 _z_front_window = _z_back_window = w;
01306 w->z_front = w->z_back = NULL;
01307 } else {
01308
01309 Window *v = _z_front_window;
01310 uint last_z_priority = UINT_MAX;
01311 while (v != NULL && (v->window_class == WC_INVALID || GetWindowZPriority(v) > GetWindowZPriority(w))) {
01312 if (v->window_class != WC_INVALID) {
01313
01314 assert(last_z_priority >= GetWindowZPriority(v));
01315 last_z_priority = GetWindowZPriority(v);
01316 }
01317
01318 v = v->z_back;
01319 }
01320
01321 if (v == NULL) {
01322
01323 w->z_front = _z_back_window;
01324 w->z_back = NULL;
01325 _z_back_window->z_back = w;
01326 _z_back_window = w;
01327 } else if (v == _z_front_window) {
01328
01329 w->z_front = NULL;
01330 w->z_back = _z_front_window;
01331 _z_front_window->z_front = w;
01332 _z_front_window = w;
01333 } else {
01334
01335 w->z_front = v->z_front;
01336 w->z_back = v;
01337 v->z_front->z_back = w;
01338 v->z_front = w;
01339 }
01340 }
01341 }
01342
01343
01348 static void RemoveWindowFromZOrdering(Window *w)
01349 {
01350 if (w->z_front == NULL) {
01351 assert(_z_front_window == w);
01352 _z_front_window = w->z_back;
01353 } else {
01354 w->z_front->z_back = w->z_back;
01355 }
01356
01357 if (w->z_back == NULL) {
01358 assert(_z_back_window == w);
01359 _z_back_window = w->z_front;
01360 } else {
01361 w->z_back->z_front = w->z_front;
01362 }
01363
01364 w->z_front = w->z_back = NULL;
01365 }
01366
01372 static void BringWindowToFront(Window *w)
01373 {
01374 RemoveWindowFromZOrdering(w);
01375 AddWindowToZOrdering(w);
01376
01377 w->SetDirty();
01378 }
01379
01388 void Window::InitializeData(WindowNumber window_number)
01389 {
01390
01391 this->window_class = this->window_desc->cls;
01392 this->SetWhiteBorder();
01393 if (this->window_desc->default_pos == WDP_CENTER) this->flags |= WF_CENTERED;
01394 this->owner = INVALID_OWNER;
01395 this->nested_focus = NULL;
01396 this->window_number = window_number;
01397
01398 this->OnInit();
01399
01400 if (this->nested_array == NULL) {
01401 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01402 this->nested_root->SetupSmallestSize(this, true);
01403 } else {
01404 this->nested_root->SetupSmallestSize(this, false);
01405 }
01406
01407 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
01408
01409
01410
01411 this->resize.step_width = this->nested_root->resize_x;
01412 this->resize.step_height = this->nested_root->resize_y;
01413
01414
01415
01416
01417 if (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL) SetFocusedWindow(this);
01418
01419
01420 AddWindowToZOrdering(this);
01421 }
01422
01430 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
01431 {
01432 this->left = x;
01433 this->top = y;
01434 this->width = sm_width;
01435 this->height = sm_height;
01436 }
01437
01448 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
01449 {
01450 def_width = max(def_width, this->width);
01451 def_height = max(def_height, this->height);
01452
01453
01454
01455
01456
01457 if (this->width != def_width || this->height != def_height) {
01458
01459 int free_height = _screen.height;
01460 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
01461 if (wt != NULL) free_height -= wt->height;
01462 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01463 if (wt != NULL) free_height -= wt->height;
01464
01465 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
01466 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
01467
01468
01469
01470
01471 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
01472 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
01473
01474 ResizeWindow(this, enlarge_x, enlarge_y);
01475
01476 } else {
01477
01478 this->OnResize();
01479 }
01480
01481 int nx = this->left;
01482 int ny = this->top;
01483
01484 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
01485
01486 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01487 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
01488 nx = max(nx, 0);
01489
01490 if (this->viewport != NULL) {
01491 this->viewport->left += nx - this->left;
01492 this->viewport->top += ny - this->top;
01493 }
01494 this->left = nx;
01495 this->top = ny;
01496
01497 this->SetDirty();
01498 }
01499
01511 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
01512 {
01513 int right = width + left;
01514 int bottom = height + top;
01515
01516 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01517 if (left < 0 || (main_toolbar != NULL && top < main_toolbar->height) || right > _screen.width || bottom > _screen.height) return false;
01518
01519
01520 const Window *w;
01521 FOR_ALL_WINDOWS_FROM_BACK(w) {
01522 if (w->window_class == WC_MAIN_WINDOW) continue;
01523
01524 if (right > w->left &&
01525 w->left + w->width > left &&
01526 bottom > w->top &&
01527 w->top + w->height > top) {
01528 return false;
01529 }
01530 }
01531
01532 pos.x = left;
01533 pos.y = top;
01534 return true;
01535 }
01536
01548 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
01549 {
01550
01551
01552
01553 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
01554
01555 if (top < 22 || top > _screen.height - (height >> 2)) return false;
01556
01557
01558 const Window *w;
01559 FOR_ALL_WINDOWS_FROM_BACK(w) {
01560 if (w->window_class == WC_MAIN_WINDOW) continue;
01561
01562 if (left + width > w->left &&
01563 w->left + w->width > left &&
01564 top + height > w->top &&
01565 w->top + w->height > top) {
01566 return false;
01567 }
01568 }
01569
01570 pos.x = left;
01571 pos.y = top;
01572 return true;
01573 }
01574
01581 static Point GetAutoPlacePosition(int width, int height)
01582 {
01583 Point pt;
01584
01585
01586 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01587 if (IsGoodAutoPlace1(0, main_toolbar != NULL ? main_toolbar->height + 2 : 2, width, height, pt)) return pt;
01588
01589
01590
01591
01592
01593 const Window *w;
01594 FOR_ALL_WINDOWS_FROM_BACK(w) {
01595 if (w->window_class == WC_MAIN_WINDOW) continue;
01596
01597 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01598 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01599 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01600 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01601 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01602 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01603 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01604 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01605 }
01606
01607
01608
01609
01610
01611 FOR_ALL_WINDOWS_FROM_BACK(w) {
01612 if (w->window_class == WC_MAIN_WINDOW) continue;
01613
01614 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01615 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01616 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01617 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01618 }
01619
01620
01621
01622
01623 int left = 0, top = 24;
01624
01625 restart:
01626 FOR_ALL_WINDOWS_FROM_BACK(w) {
01627 if (w->left == left && w->top == top) {
01628 left += 5;
01629 top += 5;
01630 goto restart;
01631 }
01632 }
01633
01634 pt.x = left;
01635 pt.y = top;
01636 return pt;
01637 }
01638
01645 Point GetToolbarAlignedWindowPosition(int window_width)
01646 {
01647 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01648 assert(w != NULL);
01649 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
01650 return pt;
01651 }
01652
01670 static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01671 {
01672 Point pt;
01673 const Window *w;
01674
01675 int16 default_width = max(desc->GetDefaultWidth(), sm_width);
01676 int16 default_height = max(desc->GetDefaultHeight(), sm_height);
01677
01678 if (desc->parent_cls != 0 &&
01679 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01680 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01681
01682 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01683 if (pt.x > _screen.width + 10 - default_width) {
01684 pt.x = (_screen.width + 10 - default_width) - 20;
01685 }
01686 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? w->height : 10);
01687 return pt;
01688 }
01689
01690 switch (desc->default_pos) {
01691 case WDP_ALIGN_TOOLBAR:
01692 return GetToolbarAlignedWindowPosition(default_width);
01693
01694 case WDP_AUTO:
01695 return GetAutoPlacePosition(default_width, default_height);
01696
01697 case WDP_CENTER:
01698 pt.x = (_screen.width - default_width) / 2;
01699 pt.y = (_screen.height - default_height) / 2;
01700 break;
01701
01702 case WDP_MANUAL:
01703 pt.x = 0;
01704 pt.y = 0;
01705 break;
01706
01707 default:
01708 NOT_REACHED();
01709 }
01710
01711 return pt;
01712 }
01713
01714 Point Window::OnInitialPosition(int16 sm_width, int16 sm_height, int window_number)
01715 {
01716 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
01717 }
01718
01726 void Window::CreateNestedTree(bool fill_nested)
01727 {
01728 int biggest_index = -1;
01729 this->nested_root = MakeWindowNWidgetTree(this->window_desc->nwid_parts, this->window_desc->nwid_length, &biggest_index, &this->shade_select);
01730 this->nested_array_size = (uint)(biggest_index + 1);
01731
01732 if (fill_nested) {
01733 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01734 this->nested_root->FillNestedArray(this->nested_array, this->nested_array_size);
01735 }
01736 }
01737
01742 void Window::FinishInitNested(WindowNumber window_number)
01743 {
01744 this->InitializeData(window_number);
01745 this->ApplyDefaults();
01746 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
01747 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
01748 this->FindWindowPlacementAndResize(this->window_desc->GetDefaultWidth(), this->window_desc->GetDefaultHeight());
01749 }
01750
01755 void Window::InitNested(WindowNumber window_number)
01756 {
01757 this->CreateNestedTree(false);
01758 this->FinishInitNested(window_number);
01759 }
01760
01765 Window::Window(WindowDesc *desc) : window_desc(desc), scrolling_scrollbar(-1)
01766 {
01767 }
01768
01776 Window *FindWindowFromPt(int x, int y)
01777 {
01778 Window *w;
01779 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01780 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01781 return w;
01782 }
01783 }
01784
01785 return NULL;
01786 }
01787
01791 void InitWindowSystem()
01792 {
01793 IConsoleClose();
01794
01795 _z_back_window = NULL;
01796 _z_front_window = NULL;
01797 _focused_window = NULL;
01798 _mouseover_last_w = NULL;
01799 _last_scroll_window = NULL;
01800 _scrolling_viewport = false;
01801 _mouse_hovering = false;
01802
01803 NWidgetLeaf::InvalidateDimensionCache();
01804 NWidgetScrollbar::InvalidateDimensionCache();
01805
01806 ShowFirstError();
01807 }
01808
01812 void UnInitWindowSystem()
01813 {
01814 UnshowCriticalError();
01815
01816 Window *w;
01817 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01818
01819 for (w = _z_front_window; w != NULL; ) {
01820 Window *to_del = w;
01821 w = w->z_back;
01822 free(to_del);
01823 }
01824
01825 _z_front_window = NULL;
01826 _z_back_window = NULL;
01827 }
01828
01832 void ResetWindowSystem()
01833 {
01834 UnInitWindowSystem();
01835 InitWindowSystem();
01836 _thd.Reset();
01837 }
01838
01839 static void DecreaseWindowCounters()
01840 {
01841 Window *w;
01842 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01843 if (_scroller_click_timeout == 0) {
01844
01845 for (uint i = 0; i < w->nested_array_size; i++) {
01846 NWidgetBase *nwid = w->nested_array[i];
01847 if (nwid != NULL && (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR)) {
01848 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
01849 if (sb->disp_flags & (ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN)) {
01850 sb->disp_flags &= ~(ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN);
01851 w->scrolling_scrollbar = -1;
01852 sb->SetDirty(w);
01853 }
01854 }
01855 }
01856 }
01857
01858
01859 for (SmallMap<int, QueryString*>::Pair *it = w->querystrings.Begin(); it != w->querystrings.End(); ++it) {
01860 it->second->HandleEditBox(w, it->first);
01861 }
01862
01863 w->OnMouseLoop();
01864 }
01865
01866 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01867 if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) {
01868 CLRBITS(w->flags, WF_TIMEOUT);
01869
01870 w->OnTimeout();
01871 w->RaiseButtons(true);
01872 }
01873 }
01874 }
01875
01876 static void HandlePlacePresize()
01877 {
01878 if (_special_mouse_mode != WSM_PRESIZE) return;
01879
01880 Window *w = _thd.GetCallbackWnd();
01881 if (w == NULL) return;
01882
01883 Point pt = GetTileBelowCursor();
01884 if (pt.x == -1) {
01885 _thd.selend.x = -1;
01886 return;
01887 }
01888
01889 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01890 }
01891
01896 static EventState HandleMouseDragDrop()
01897 {
01898 if (_special_mouse_mode != WSM_DRAGDROP) return ES_NOT_HANDLED;
01899
01900 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01901
01902 Window *w = _thd.GetCallbackWnd();
01903 if (w != NULL) {
01904
01905 Point pt;
01906 pt.x = _cursor.pos.x - w->left;
01907 pt.y = _cursor.pos.y - w->top;
01908 if (_left_button_down) {
01909 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
01910 } else {
01911 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01912 }
01913 }
01914
01915 if (!_left_button_down) ResetObjectToPlace();
01916 return ES_HANDLED;
01917 }
01918
01920 static void HandleMouseOver()
01921 {
01922 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01923
01924
01925 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01926
01927 Point pt = { -1, -1 };
01928 _mouseover_last_w->OnMouseOver(pt, 0);
01929 }
01930
01931
01932 _mouseover_last_w = w;
01933
01934 if (w != NULL) {
01935
01936 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01937 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
01938 if (widget != NULL) w->OnMouseOver(pt, widget->index);
01939 }
01940 }
01941
01943 static const int MIN_VISIBLE_TITLE_BAR = 13;
01944
01946 enum PreventHideDirection {
01947 PHD_UP,
01948 PHD_DOWN,
01949 };
01950
01961 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
01962 {
01963 if (v == NULL) return;
01964
01965 int v_bottom = v->top + v->height;
01966 int v_right = v->left + v->width;
01967 int safe_y = (dir == PHD_UP) ? (v->top - MIN_VISIBLE_TITLE_BAR - rect.top) : (v_bottom + MIN_VISIBLE_TITLE_BAR - rect.bottom);
01968
01969 if (*ny + rect.top <= v->top - MIN_VISIBLE_TITLE_BAR) return;
01970 if (*ny + rect.bottom >= v_bottom + MIN_VISIBLE_TITLE_BAR) return;
01971
01972
01973 if (*nx + rect.left + MIN_VISIBLE_TITLE_BAR < v->left) {
01974 if (v->left < MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01975 return;
01976 }
01977 if (*nx + rect.right - MIN_VISIBLE_TITLE_BAR > v_right) {
01978 if (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01979 return;
01980 }
01981
01982
01983 if (px + rect.left < v->left && v->left >= MIN_VISIBLE_TITLE_BAR) {
01984 *nx = v->left - MIN_VISIBLE_TITLE_BAR - rect.left;
01985 } else if (px + rect.right > v_right && v_right <= _screen.width - MIN_VISIBLE_TITLE_BAR) {
01986 *nx = v_right + MIN_VISIBLE_TITLE_BAR - rect.right;
01987 } else {
01988 *ny = safe_y;
01989 }
01990 }
01991
01999 static void EnsureVisibleCaption(Window *w, int nx, int ny)
02000 {
02001
02002 Rect caption_rect;
02003 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
02004 if (caption != NULL) {
02005 caption_rect.left = caption->pos_x;
02006 caption_rect.right = caption->pos_x + caption->current_x;
02007 caption_rect.top = caption->pos_y;
02008 caption_rect.bottom = caption->pos_y + caption->current_y;
02009
02010
02011 nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
02012 ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
02013
02014
02015 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
02016 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
02017 }
02018
02019 if (w->viewport != NULL) {
02020 w->viewport->left += nx - w->left;
02021 w->viewport->top += ny - w->top;
02022 }
02023
02024 w->left = nx;
02025 w->top = ny;
02026 }
02027
02038 void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
02039 {
02040 if (delta_x != 0 || delta_y != 0) {
02041 if (clamp_to_screen) {
02042
02043
02044 int new_right = w->left + w->width + delta_x;
02045 int new_bottom = w->top + w->height + delta_y;
02046 if (new_right >= (int)_cur_resolution.width) delta_x -= Ceil(new_right - _cur_resolution.width, max(1U, w->nested_root->resize_x));
02047 if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, max(1U, w->nested_root->resize_y));
02048 }
02049
02050 w->SetDirty();
02051
02052 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);
02053 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);
02054 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
02055 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
02056
02057 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);
02058 w->width = w->nested_root->current_x;
02059 w->height = w->nested_root->current_y;
02060 }
02061
02062 EnsureVisibleCaption(w, w->left, w->top);
02063
02064
02065 w->OnResize();
02066 w->SetDirty();
02067 }
02068
02074 int GetMainViewTop()
02075 {
02076 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02077 return (w == NULL) ? 0 : w->top + w->height;
02078 }
02079
02085 int GetMainViewBottom()
02086 {
02087 Window *w = FindWindowById(WC_STATUS_BAR, 0);
02088 return (w == NULL) ? _screen.height : w->top;
02089 }
02090
02091 static bool _dragging_window;
02092
02097 static EventState HandleWindowDragging()
02098 {
02099
02100 if (!_dragging_window) return ES_NOT_HANDLED;
02101
02102
02103 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
02104
02105
02106 Window *w;
02107 FOR_ALL_WINDOWS_FROM_BACK(w) {
02108 if (w->flags & WF_DRAGGING) {
02109
02110 if (!_left_button_down) {
02111 w->flags &= ~WF_DRAGGING;
02112 break;
02113 }
02114
02115 w->SetDirty();
02116
02117 int x = _cursor.pos.x + _drag_delta.x;
02118 int y = _cursor.pos.y + _drag_delta.y;
02119 int nx = x;
02120 int ny = y;
02121
02122 if (_settings_client.gui.window_snap_radius != 0) {
02123 const Window *v;
02124
02125 int hsnap = _settings_client.gui.window_snap_radius;
02126 int vsnap = _settings_client.gui.window_snap_radius;
02127 int delta;
02128
02129 FOR_ALL_WINDOWS_FROM_BACK(v) {
02130 if (v == w) continue;
02131
02132 if (y + w->height > v->top && y < v->top + v->height) {
02133
02134 delta = abs(v->left + v->width - x);
02135 if (delta <= hsnap) {
02136 nx = v->left + v->width;
02137 hsnap = delta;
02138 }
02139
02140
02141 delta = abs(v->left - x - w->width);
02142 if (delta <= hsnap) {
02143 nx = v->left - w->width;
02144 hsnap = delta;
02145 }
02146 }
02147
02148 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
02149
02150 delta = abs(v->left - x);
02151 if (delta <= hsnap) {
02152 nx = v->left;
02153 hsnap = delta;
02154 }
02155
02156
02157 delta = abs(v->left + v->width - x - w->width);
02158 if (delta <= hsnap) {
02159 nx = v->left + v->width - w->width;
02160 hsnap = delta;
02161 }
02162 }
02163
02164 if (x + w->width > v->left && x < v->left + v->width) {
02165
02166 delta = abs(v->top + v->height - y);
02167 if (delta <= vsnap) {
02168 ny = v->top + v->height;
02169 vsnap = delta;
02170 }
02171
02172
02173 delta = abs(v->top - y - w->height);
02174 if (delta <= vsnap) {
02175 ny = v->top - w->height;
02176 vsnap = delta;
02177 }
02178 }
02179
02180 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
02181
02182 delta = abs(v->top - y);
02183 if (delta <= vsnap) {
02184 ny = v->top;
02185 vsnap = delta;
02186 }
02187
02188
02189 delta = abs(v->top + v->height - y - w->height);
02190 if (delta <= vsnap) {
02191 ny = v->top + v->height - w->height;
02192 vsnap = delta;
02193 }
02194 }
02195 }
02196 }
02197
02198 EnsureVisibleCaption(w, nx, ny);
02199
02200 w->SetDirty();
02201 return ES_HANDLED;
02202 } else if (w->flags & WF_SIZING) {
02203
02204 if (!_left_button_down) {
02205 w->flags &= ~WF_SIZING;
02206 w->SetDirty();
02207 break;
02208 }
02209
02210
02211
02212
02213 int x, y = _cursor.pos.y - _drag_delta.y;
02214 if (w->flags & WF_SIZING_LEFT) {
02215 x = _drag_delta.x - _cursor.pos.x;
02216 } else {
02217 x = _cursor.pos.x - _drag_delta.x;
02218 }
02219
02220
02221 if (w->resize.step_width == 0) x = 0;
02222 if (w->resize.step_height == 0) y = 0;
02223
02224
02225 if (w->top + w->height + y > _screen.height) {
02226 y = _screen.height - w->height - w->top;
02227 }
02228
02229
02230
02231
02232 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
02233 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
02234
02235
02236 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
02237 x = w->nested_root->smallest_x - w->width;
02238 }
02239 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
02240 y = w->nested_root->smallest_y - w->height;
02241 }
02242
02243
02244 if (x == 0 && y == 0) return ES_HANDLED;
02245
02246
02247 _drag_delta.y += y;
02248 if ((w->flags & WF_SIZING_LEFT) && x != 0) {
02249 _drag_delta.x -= x;
02250 w->SetDirty();
02251 w->left -= x;
02252
02253 } else {
02254 _drag_delta.x += x;
02255 }
02256
02257
02258 ResizeWindow(w, x, y);
02259 return ES_HANDLED;
02260 }
02261 }
02262
02263 _dragging_window = false;
02264 return ES_HANDLED;
02265 }
02266
02271 static void StartWindowDrag(Window *w)
02272 {
02273 w->flags |= WF_DRAGGING;
02274 w->flags &= ~WF_CENTERED;
02275 _dragging_window = true;
02276
02277 _drag_delta.x = w->left - _cursor.pos.x;
02278 _drag_delta.y = w->top - _cursor.pos.y;
02279
02280 BringWindowToFront(w);
02281 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02282 }
02283
02289 static void StartWindowSizing(Window *w, bool to_left)
02290 {
02291 w->flags |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
02292 w->flags &= ~WF_CENTERED;
02293 _dragging_window = true;
02294
02295 _drag_delta.x = _cursor.pos.x;
02296 _drag_delta.y = _cursor.pos.y;
02297
02298 BringWindowToFront(w);
02299 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02300 }
02301
02306 static EventState HandleScrollbarScrolling()
02307 {
02308 Window *w;
02309 FOR_ALL_WINDOWS_FROM_BACK(w) {
02310 if (w->scrolling_scrollbar >= 0) {
02311
02312 if (!_left_button_down) {
02313 w->scrolling_scrollbar = -1;
02314 w->SetDirty();
02315 return ES_HANDLED;
02316 }
02317
02318 int i;
02319 NWidgetScrollbar *sb = w->GetWidget<NWidgetScrollbar>(w->scrolling_scrollbar);
02320 bool rtl = false;
02321
02322 if (sb->type == NWID_HSCROLLBAR) {
02323 i = _cursor.pos.x - _cursorpos_drag_start.x;
02324 rtl = _current_text_dir == TD_RTL;
02325 } else {
02326 i = _cursor.pos.y - _cursorpos_drag_start.y;
02327 }
02328
02329 if (sb->disp_flags & ND_SCROLLBAR_BTN) {
02330 if (_scroller_click_timeout == 1) {
02331 _scroller_click_timeout = 3;
02332 sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1);
02333 w->SetDirty();
02334 }
02335 return ES_HANDLED;
02336 }
02337
02338
02339 int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity()));
02340 if (rtl) pos = max(0, sb->GetCount() - sb->GetCapacity() - pos);
02341 if (pos != sb->GetPosition()) {
02342 sb->SetPosition(pos);
02343 w->SetDirty();
02344 }
02345 return ES_HANDLED;
02346 }
02347 }
02348
02349 return ES_NOT_HANDLED;
02350 }
02351
02356 static EventState HandleViewportScroll()
02357 {
02358 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02359
02360 if (!_scrolling_viewport) return ES_NOT_HANDLED;
02361
02362
02363
02364
02365 if (_last_scroll_window == NULL) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
02366
02367 if (_last_scroll_window == NULL || !(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down))) {
02368 _cursor.fix_at = false;
02369 _scrolling_viewport = false;
02370 _last_scroll_window = NULL;
02371 return ES_NOT_HANDLED;
02372 }
02373
02374 if (_last_scroll_window == FindWindowById(WC_MAIN_WINDOW, 0) && _last_scroll_window->viewport->follow_vehicle != INVALID_VEHICLE) {
02375
02376 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
02377 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
02378 return ES_NOT_HANDLED;
02379 }
02380
02381 Point delta;
02382 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
02383 delta.x = -_cursor.delta.x;
02384 delta.y = -_cursor.delta.y;
02385 } else {
02386 delta.x = _cursor.delta.x;
02387 delta.y = _cursor.delta.y;
02388 }
02389
02390 if (scrollwheel_scrolling) {
02391
02392 delta.x = _cursor.h_wheel;
02393 delta.y = _cursor.v_wheel;
02394 _cursor.v_wheel = 0;
02395 _cursor.h_wheel = 0;
02396 }
02397
02398
02399 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
02400
02401 _cursor.delta.x = 0;
02402 _cursor.delta.y = 0;
02403 return ES_HANDLED;
02404 }
02405
02416 static bool MaybeBringWindowToFront(Window *w)
02417 {
02418 bool bring_to_front = false;
02419
02420 if (w->window_class == WC_MAIN_WINDOW ||
02421 IsVitalWindow(w) ||
02422 w->window_class == WC_TOOLTIPS ||
02423 w->window_class == WC_DROPDOWN_MENU) {
02424 return true;
02425 }
02426
02427
02428 int w_width = w->width;
02429 int w_height = w->height;
02430 if (w->IsShaded()) {
02431 w_width = w->unshaded_size.width;
02432 w_height = w->unshaded_size.height;
02433 }
02434
02435 Window *u;
02436 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
02437
02438 if (u->parent == w && (u->window_desc->flags & WDF_MODAL)) {
02439 u->SetWhiteBorder();
02440 u->SetDirty();
02441 return false;
02442 }
02443
02444 if (u->window_class == WC_MAIN_WINDOW ||
02445 IsVitalWindow(u) ||
02446 u->window_class == WC_TOOLTIPS ||
02447 u->window_class == WC_DROPDOWN_MENU) {
02448 continue;
02449 }
02450
02451
02452 if (w->left + w_width <= u->left ||
02453 u->left + u->width <= w->left ||
02454 w->top + w_height <= u->top ||
02455 u->top + u->height <= w->top) {
02456 continue;
02457 }
02458
02459 bring_to_front = true;
02460 }
02461
02462 if (bring_to_front) BringWindowToFront(w);
02463 return true;
02464 }
02465
02474 EventState Window::HandleEditBoxKey(int wid, WChar key, uint16 keycode)
02475 {
02476 QueryString *query = this->GetQueryString(wid);
02477 if (query == NULL) return ES_NOT_HANDLED;
02478
02479 int action = QueryString::ACTION_NOTHING;
02480
02481 switch (query->text.HandleKeyPress(key, keycode)) {
02482 case HKPR_EDITING:
02483 this->SetWidgetDirty(wid);
02484 this->OnEditboxChanged(wid);
02485 break;
02486
02487 case HKPR_CURSOR:
02488 this->SetWidgetDirty(wid);
02489
02490 if (this->window_class == WC_OSK) this->InvalidateData();
02491 break;
02492
02493 case HKPR_CONFIRM:
02494 if (this->window_class == WC_OSK) {
02495 this->OnClick(Point(), WID_OSK_OK, 1);
02496 } else if (query->ok_button >= 0) {
02497 this->OnClick(Point(), query->ok_button, 1);
02498 } else {
02499 action = query->ok_button;
02500 }
02501 break;
02502
02503 case HKPR_CANCEL:
02504 if (this->window_class == WC_OSK) {
02505 this->OnClick(Point(), WID_OSK_CANCEL, 1);
02506 } else if (query->cancel_button >= 0) {
02507 this->OnClick(Point(), query->cancel_button, 1);
02508 } else {
02509 action = query->cancel_button;
02510 }
02511 break;
02512
02513 case HKPR_NOT_HANDLED:
02514 return ES_NOT_HANDLED;
02515
02516 default: break;
02517 }
02518
02519 switch (action) {
02520 case QueryString::ACTION_DESELECT:
02521 this->UnfocusFocusedWidget();
02522 break;
02523
02524 case QueryString::ACTION_CLEAR:
02525 if (query->text.bytes <= 1) {
02526
02527 this->UnfocusFocusedWidget();
02528 } else {
02529 query->text.DeleteAll();
02530 this->SetWidgetDirty(wid);
02531 this->OnEditboxChanged(wid);
02532 }
02533 break;
02534
02535 default:
02536 break;
02537 }
02538
02539 return ES_HANDLED;
02540 }
02541
02547 void HandleKeypress(uint keycode, WChar key)
02548 {
02549
02550
02551 assert(HasModalProgress() || IsLocalCompany());
02552
02553
02554
02555
02556
02557
02558
02559
02560 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
02561
02562
02563
02564
02565 if (key == 0 && keycode == 0) return;
02566
02567
02568 if (EditBoxInGlobalFocus()) {
02569
02570 if (_focused_window->window_class == WC_CONSOLE) {
02571 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
02572 } else {
02573 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->index, key, keycode) == ES_HANDLED) return;
02574 }
02575 }
02576
02577
02578 Window *w;
02579 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02580 if (w->window_class == WC_MAIN_TOOLBAR) continue;
02581 if (w->window_desc->hotkeys != NULL) {
02582 int hotkey = w->window_desc->hotkeys->CheckMatch(keycode);
02583 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
02584 }
02585 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02586 }
02587
02588 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02589
02590 if (w != NULL) {
02591 if (w->window_desc->hotkeys != NULL) {
02592 int hotkey = w->window_desc->hotkeys->CheckMatch(keycode);
02593 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
02594 }
02595 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02596 }
02597
02598 HandleGlobalHotkeys(key, keycode);
02599 }
02600
02604 void HandleCtrlChanged()
02605 {
02606
02607 Window *w;
02608 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02609 if (w->OnCTRLStateChange() == ES_HANDLED) return;
02610 }
02611 }
02612
02618 void Window::InsertTextString(int wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
02619 {
02620 QueryString *query = this->GetQueryString(wid);
02621 if (query == NULL) return;
02622
02623 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
02624 this->SetWidgetDirty(wid);
02625 this->OnEditboxChanged(wid);
02626 }
02627 }
02628
02635 void HandleTextInput(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
02636 {
02637 if (!EditBoxInGlobalFocus()) return;
02638
02639 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->index, str, marked, caret, insert_location, replacement_end);
02640 }
02641
02648 static int _input_events_this_tick = 0;
02649
02654 static void HandleAutoscroll()
02655 {
02656 if (_game_mode == GM_MENU || HasModalProgress()) return;
02657 if (_settings_client.gui.auto_scrolling == VA_DISABLED) return;
02658 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
02659
02660 int x = _cursor.pos.x;
02661 int y = _cursor.pos.y;
02662 Window *w = FindWindowFromPt(x, y);
02663 if (w == NULL || w->flags & WF_DISABLE_VP_SCROLL) return;
02664 if (_settings_client.gui.auto_scrolling != VA_EVERY_VIEWPORT && w->window_class != WC_MAIN_WINDOW) return;
02665
02666 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02667 if (vp == NULL) return;
02668
02669 x -= vp->left;
02670 y -= vp->top;
02671
02672
02673 #define scrollspeed 3
02674 if (x - 15 < 0) {
02675 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
02676 } else if (15 - (vp->width - x) > 0) {
02677 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
02678 }
02679 if (y - 15 < 0) {
02680 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
02681 } else if (15 - (vp->height - y) > 0) {
02682 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
02683 }
02684 #undef scrollspeed
02685 }
02686
02687 enum MouseClick {
02688 MC_NONE = 0,
02689 MC_LEFT,
02690 MC_RIGHT,
02691 MC_DOUBLE_LEFT,
02692 MC_HOVER,
02693
02694 MAX_OFFSET_DOUBLE_CLICK = 5,
02695 TIME_BETWEEN_DOUBLE_CLICK = 500,
02696 MAX_OFFSET_HOVER = 5,
02697 };
02698 extern EventState VpHandlePlaceSizingDrag();
02699
02700 static void ScrollMainViewport(int x, int y)
02701 {
02702 if (_game_mode != GM_MENU) {
02703 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02704 assert(w);
02705
02706 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
02707 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
02708 }
02709 }
02710
02720 static const int8 scrollamt[16][2] = {
02721 { 0, 0},
02722 {-2, 0},
02723 { 0, -2},
02724 {-2, -1},
02725 { 2, 0},
02726 { 0, 0},
02727 { 2, -1},
02728 { 0, -2},
02729 { 0, 2},
02730 {-2, 1},
02731 { 0, 0},
02732 {-2, 0},
02733 { 2, 1},
02734 { 0, 2},
02735 { 2, 0},
02736 { 0, 0},
02737 };
02738
02739 static void HandleKeyScrolling()
02740 {
02741
02742
02743
02744
02745 if (_dirkeys && !EditBoxInGlobalFocus()) {
02746 int factor = _shift_pressed ? 50 : 10;
02747 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
02748 }
02749 }
02750
02751 static void MouseLoop(MouseClick click, int mousewheel)
02752 {
02753
02754
02755 assert(HasModalProgress() || IsLocalCompany());
02756
02757 HandlePlacePresize();
02758 UpdateTileSelection();
02759
02760 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
02761 if (HandleMouseDragDrop() == ES_HANDLED) return;
02762 if (HandleWindowDragging() == ES_HANDLED) return;
02763 if (HandleScrollbarScrolling() == ES_HANDLED) return;
02764 if (HandleViewportScroll() == ES_HANDLED) return;
02765
02766 HandleMouseOver();
02767
02768 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02769 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
02770
02771 int x = _cursor.pos.x;
02772 int y = _cursor.pos.y;
02773 Window *w = FindWindowFromPt(x, y);
02774 if (w == NULL) return;
02775
02776 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
02777 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02778
02779
02780 if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
02781
02782 if (mousewheel != 0) {
02783
02784 w->OnMouseWheel(mousewheel);
02785
02786
02787 if (vp == NULL) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
02788 }
02789
02790 if (vp != NULL) {
02791 if (scrollwheel_scrolling) click = MC_RIGHT;
02792 switch (click) {
02793 case MC_DOUBLE_LEFT:
02794 case MC_LEFT:
02795 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
02796 if (!HandleViewportClicked(vp, x, y) &&
02797 !(w->flags & WF_DISABLE_VP_SCROLL) &&
02798 _settings_client.gui.left_mouse_btn_scrolling) {
02799 _scrolling_viewport = true;
02800 _cursor.fix_at = false;
02801 }
02802 break;
02803
02804 case MC_RIGHT:
02805 if (!(w->flags & WF_DISABLE_VP_SCROLL)) {
02806 _scrolling_viewport = true;
02807 _cursor.fix_at = true;
02808
02809
02810 _cursor.h_wheel = 0;
02811 _cursor.v_wheel = 0;
02812 }
02813 break;
02814
02815 default:
02816 break;
02817 }
02818 } else {
02819 switch (click) {
02820 case MC_LEFT:
02821 case MC_DOUBLE_LEFT:
02822 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
02823 break;
02824
02825 default:
02826 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
02827
02828
02829
02830
02831 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
02832
02833 case MC_HOVER: DispatchHoverEvent(w, x - w->left, y - w->top); break;
02834 }
02835 }
02836 }
02837
02841 void HandleMouseEvents()
02842 {
02843
02844
02845 assert(HasModalProgress() || IsLocalCompany());
02846
02847 static int double_click_time = 0;
02848 static Point double_click_pos = {0, 0};
02849
02850
02851 MouseClick click = MC_NONE;
02852 if (_left_button_down && !_left_button_clicked) {
02853 click = MC_LEFT;
02854 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02855 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
02856 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
02857 click = MC_DOUBLE_LEFT;
02858 }
02859 double_click_time = _realtime_tick;
02860 double_click_pos = _cursor.pos;
02861 _left_button_clicked = true;
02862 _input_events_this_tick++;
02863 } else if (_right_button_clicked) {
02864 _right_button_clicked = false;
02865 click = MC_RIGHT;
02866 _input_events_this_tick++;
02867 }
02868
02869 int mousewheel = 0;
02870 if (_cursor.wheel) {
02871 mousewheel = _cursor.wheel;
02872 _cursor.wheel = 0;
02873 _input_events_this_tick++;
02874 }
02875
02876 static uint32 hover_time = 0;
02877 static Point hover_pos = {0, 0};
02878
02879 if (_settings_client.gui.hover_delay > 0) {
02880 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
02881 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
02882 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
02883 hover_pos = _cursor.pos;
02884 hover_time = _realtime_tick;
02885 _mouse_hovering = false;
02886 } else {
02887 if (hover_time != 0 && _realtime_tick > hover_time + _settings_client.gui.hover_delay * 1000) {
02888 click = MC_HOVER;
02889 _input_events_this_tick++;
02890 _mouse_hovering = true;
02891 }
02892 }
02893 }
02894
02895
02896 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _newgrf_debug_sprite_picker.click_time != _realtime_tick) {
02897
02898 _newgrf_debug_sprite_picker.mode = SPM_NONE;
02899 InvalidateWindowData(WC_SPRITE_ALIGNER, 0, 1);
02900 }
02901
02902 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
02903
02904 Blitter *blitter = BlitterFactory::GetCurrentBlitter();
02905 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
02906 _newgrf_debug_sprite_picker.click_time = _realtime_tick;
02907 _newgrf_debug_sprite_picker.sprites.Clear();
02908 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
02909 MarkWholeScreenDirty();
02910 } else {
02911 MouseLoop(click, mousewheel);
02912 }
02913
02914
02915
02916 _cursor.delta.x = 0;
02917 _cursor.delta.y = 0;
02918 }
02919
02923 static void CheckSoftLimit()
02924 {
02925 if (_settings_client.gui.window_soft_limit == 0) return;
02926
02927 for (;;) {
02928 uint deletable_count = 0;
02929 Window *w, *last_deletable = NULL;
02930 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02931 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue;
02932
02933 last_deletable = w;
02934 deletable_count++;
02935 }
02936
02937
02938 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02939
02940 assert(last_deletable != NULL);
02941 delete last_deletable;
02942 }
02943 }
02944
02948 void InputLoop()
02949 {
02950
02951
02952 assert(HasModalProgress() || IsLocalCompany());
02953
02954 CheckSoftLimit();
02955 HandleKeyScrolling();
02956
02957
02958 for (Window *v = _z_front_window; v != NULL; ) {
02959 Window *w = v;
02960 v = v->z_back;
02961
02962 if (w->window_class != WC_INVALID) continue;
02963
02964 RemoveWindowFromZOrdering(w);
02965 free(w);
02966 }
02967
02968 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
02969 DecreaseWindowCounters();
02970
02971 if (_input_events_this_tick != 0) {
02972
02973 _input_events_this_tick = 0;
02974
02975 return;
02976 }
02977
02978
02979 HandleMouseEvents();
02980 HandleAutoscroll();
02981 }
02982
02986 void UpdateWindows()
02987 {
02988 Window *w;
02989
02990 static int highlight_timer = 1;
02991 if (--highlight_timer == 0) {
02992 highlight_timer = 15;
02993 _window_highlight_colour = !_window_highlight_colour;
02994 }
02995
02996 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02997 w->ProcessScheduledInvalidations();
02998 w->ProcessHighlightedInvalidations();
02999 }
03000
03001 static int we4_timer = 0;
03002 int t = we4_timer + 1;
03003
03004 if (t >= 100) {
03005 FOR_ALL_WINDOWS_FROM_FRONT(w) {
03006 w->OnHundredthTick();
03007 }
03008 t = 0;
03009 }
03010 we4_timer = t;
03011
03012 FOR_ALL_WINDOWS_FROM_FRONT(w) {
03013 if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
03014 CLRBITS(w->flags, WF_WHITE_BORDER);
03015 w->SetDirty();
03016 }
03017 }
03018
03019 DrawDirtyBlocks();
03020
03021 FOR_ALL_WINDOWS_FROM_BACK(w) {
03022
03023 if (w->viewport != NULL && !w->IsShaded()) UpdateViewportPosition(w);
03024 }
03025 NetworkDrawChatMessage();
03026
03027 DrawMouseCursor();
03028 }
03029
03035 void SetWindowDirty(WindowClass cls, WindowNumber number)
03036 {
03037 const Window *w;
03038 FOR_ALL_WINDOWS_FROM_BACK(w) {
03039 if (w->window_class == cls && w->window_number == number) w->SetDirty();
03040 }
03041 }
03042
03049 void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
03050 {
03051 const Window *w;
03052 FOR_ALL_WINDOWS_FROM_BACK(w) {
03053 if (w->window_class == cls && w->window_number == number) {
03054 w->SetWidgetDirty(widget_index);
03055 }
03056 }
03057 }
03058
03063 void SetWindowClassesDirty(WindowClass cls)
03064 {
03065 Window *w;
03066 FOR_ALL_WINDOWS_FROM_BACK(w) {
03067 if (w->window_class == cls) w->SetDirty();
03068 }
03069 }
03070
03076 void Window::InvalidateData(int data, bool gui_scope)
03077 {
03078 this->SetDirty();
03079 if (!gui_scope) {
03080
03081 *this->scheduled_invalidation_data.Append() = data;
03082 }
03083 this->OnInvalidateData(data, gui_scope);
03084 }
03085
03089 void Window::ProcessScheduledInvalidations()
03090 {
03091 for (int *data = this->scheduled_invalidation_data.Begin(); this->window_class != WC_INVALID && data != this->scheduled_invalidation_data.End(); data++) {
03092 this->OnInvalidateData(*data, true);
03093 }
03094 this->scheduled_invalidation_data.Clear();
03095 }
03096
03100 void Window::ProcessHighlightedInvalidations()
03101 {
03102 if ((this->flags & WF_HIGHLIGHTED) == 0) return;
03103
03104 for (uint i = 0; i < this->nested_array_size; i++) {
03105 if (this->IsWidgetHighlighted(i)) this->SetWidgetDirty(i);
03106 }
03107 }
03108
03135 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
03136 {
03137 Window *w;
03138 FOR_ALL_WINDOWS_FROM_BACK(w) {
03139 if (w->window_class == cls && w->window_number == number) {
03140 w->InvalidateData(data, gui_scope);
03141 }
03142 }
03143 }
03144
03153 void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
03154 {
03155 Window *w;
03156
03157 FOR_ALL_WINDOWS_FROM_BACK(w) {
03158 if (w->window_class == cls) {
03159 w->InvalidateData(data, gui_scope);
03160 }
03161 }
03162 }
03163
03167 void CallWindowTickEvent()
03168 {
03169 Window *w;
03170 FOR_ALL_WINDOWS_FROM_FRONT(w) {
03171 w->OnTick();
03172 }
03173 }
03174
03181 void DeleteNonVitalWindows()
03182 {
03183 Window *w;
03184
03185 restart_search:
03186
03187
03188
03189 FOR_ALL_WINDOWS_FROM_BACK(w) {
03190 if (w->window_class != WC_MAIN_WINDOW &&
03191 w->window_class != WC_SELECT_GAME &&
03192 w->window_class != WC_MAIN_TOOLBAR &&
03193 w->window_class != WC_STATUS_BAR &&
03194 w->window_class != WC_TOOLTIPS &&
03195 (w->flags & WF_STICKY) == 0) {
03196
03197 delete w;
03198 goto restart_search;
03199 }
03200 }
03201 }
03202
03210 void DeleteAllNonVitalWindows()
03211 {
03212 Window *w;
03213
03214
03215 DeleteNonVitalWindows();
03216
03217 restart_search:
03218
03219
03220
03221 FOR_ALL_WINDOWS_FROM_BACK(w) {
03222 if (w->flags & WF_STICKY) {
03223 delete w;
03224 goto restart_search;
03225 }
03226 }
03227 }
03228
03233 void DeleteConstructionWindows()
03234 {
03235 Window *w;
03236
03237 restart_search:
03238
03239
03240
03241 FOR_ALL_WINDOWS_FROM_BACK(w) {
03242 if (w->window_desc->flags & WDF_CONSTRUCTION) {
03243 delete w;
03244 goto restart_search;
03245 }
03246 }
03247
03248 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
03249 }
03250
03252 void HideVitalWindows()
03253 {
03254 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
03255 DeleteWindowById(WC_STATUS_BAR, 0);
03256 }
03257
03259 void ReInitAllWindows()
03260 {
03261 NWidgetLeaf::InvalidateDimensionCache();
03262 NWidgetScrollbar::InvalidateDimensionCache();
03263
03264 Window *w;
03265 FOR_ALL_WINDOWS_FROM_BACK(w) {
03266 w->ReInit();
03267 }
03268 #ifdef ENABLE_NETWORK
03269 void NetworkReInitChatBoxSize();
03270 NetworkReInitChatBoxSize();
03271 #endif
03272
03273
03274 RelocateAllWindows(_cur_resolution.width, _cur_resolution.height);
03275 MarkWholeScreenDirty();
03276 }
03277
03285 static int PositionWindow(Window *w, WindowClass clss, int setting)
03286 {
03287 if (w == NULL || w->window_class != clss) {
03288 w = FindWindowById(clss, 0);
03289 }
03290 if (w == NULL) return 0;
03291
03292 int old_left = w->left;
03293 switch (setting) {
03294 case 1: w->left = (_screen.width - w->width) / 2; break;
03295 case 2: w->left = _screen.width - w->width; break;
03296 default: w->left = 0; break;
03297 }
03298 if (w->viewport != NULL) w->viewport->left += w->left - old_left;
03299 SetDirtyBlocks(0, w->top, _screen.width, w->top + w->height);
03300 return w->left;
03301 }
03302
03308 int PositionMainToolbar(Window *w)
03309 {
03310 DEBUG(misc, 5, "Repositioning Main Toolbar...");
03311 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
03312 }
03313
03319 int PositionStatusbar(Window *w)
03320 {
03321 DEBUG(misc, 5, "Repositioning statusbar...");
03322 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
03323 }
03324
03330 int PositionNewsMessage(Window *w)
03331 {
03332 DEBUG(misc, 5, "Repositioning news message...");
03333 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
03334 }
03335
03341 int PositionNetworkChatWindow(Window *w)
03342 {
03343 DEBUG(misc, 5, "Repositioning network chat window...");
03344 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
03345 }
03346
03347
03353 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
03354 {
03355 Window *w;
03356 FOR_ALL_WINDOWS_FROM_BACK(w) {
03357 if (w->viewport != NULL && w->viewport->follow_vehicle == from_index) {
03358 w->viewport->follow_vehicle = to_index;
03359 w->SetDirty();
03360 }
03361 }
03362 }
03363
03364
03370 void RelocateAllWindows(int neww, int newh)
03371 {
03372 Window *w;
03373
03374 FOR_ALL_WINDOWS_FROM_BACK(w) {
03375 int left, top;
03376
03377
03378 switch (w->window_class) {
03379 case WC_MAIN_WINDOW:
03380 case WC_BOOTSTRAP:
03381 ResizeWindow(w, neww, newh);
03382 continue;
03383
03384 case WC_MAIN_TOOLBAR:
03385 ResizeWindow(w, min(neww, w->window_desc->default_width) - w->width, 0, false);
03386
03387 top = w->top;
03388 left = PositionMainToolbar(w);
03389 break;
03390
03391 case WC_NEWS_WINDOW:
03392 top = newh - w->height;
03393 left = PositionNewsMessage(w);
03394 break;
03395
03396 case WC_STATUS_BAR:
03397 ResizeWindow(w, min(neww, w->window_desc->default_width) - w->width, 0, false);
03398
03399 top = newh - w->height;
03400 left = PositionStatusbar(w);
03401 break;
03402
03403 case WC_SEND_NETWORK_MSG:
03404 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0, false);
03405 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
03406 left = PositionNetworkChatWindow(w);
03407 break;
03408
03409 case WC_CONSOLE:
03410 IConsoleResize(w);
03411 continue;
03412
03413 default: {
03414 if (w->flags & WF_CENTERED) {
03415 top = (newh - w->height) >> 1;
03416 left = (neww - w->width) >> 1;
03417 break;
03418 }
03419
03420 left = w->left;
03421 if (left + (w->width >> 1) >= neww) left = neww - w->width;
03422 if (left < 0) left = 0;
03423
03424 top = w->top;
03425 if (top + (w->height >> 1) >= newh) top = newh - w->height;
03426 break;
03427 }
03428 }
03429
03430 EnsureVisibleCaption(w, left, top);
03431 }
03432 }
03433
03439 PickerWindowBase::~PickerWindowBase()
03440 {
03441 this->window_class = WC_INVALID;
03442 ResetObjectToPlace();
03443 }