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) _video_driver->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) _video_driver->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) _video_driver->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 w->OnFocus();
00623 }
00624
00625 if (nw == NULL) return;
00626
00627
00628 if (nw->IsDisabled()) return;
00629
00630 int widget_index = nw->index;
00631
00632
00633
00634
00635 if (widget_type != WWT_CAPTION && w->window_class != WC_OSK) {
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645 focused_widget_changed |= w->SetFocusedWidget(widget_index);
00646 }
00647
00648
00649
00650 if (HideDropDownMenu(w) == widget_index && widget_index >= 0) return;
00651
00652 if ((widget_type & ~WWB_PUSHBUTTON) < WWT_LAST && (widget_type & WWB_PUSHBUTTON)) w->HandleButtonClick(widget_index);
00653
00654 Point pt = { x, y };
00655
00656 switch (widget_type) {
00657 case NWID_VSCROLLBAR:
00658 case NWID_HSCROLLBAR:
00659 ScrollbarClickHandler(w, nw, x, y);
00660 break;
00661
00662 case WWT_EDITBOX: {
00663 QueryString *query = w->GetQueryString(widget_index);
00664 if (query != NULL) query->ClickEditBox(w, pt, widget_index, click_count, focused_widget_changed);
00665 break;
00666 }
00667
00668 case WWT_CLOSEBOX:
00669 delete w;
00670 return;
00671
00672 case WWT_CAPTION:
00673 StartWindowDrag(w);
00674 return;
00675
00676 case WWT_RESIZEBOX:
00677
00678
00679 StartWindowSizing(w, (int)nw->pos_x < (w->width / 2));
00680 nw->SetDirty(w);
00681 return;
00682
00683 case WWT_DEFSIZEBOX: {
00684 if (_ctrl_pressed) {
00685 w->window_desc->pref_width = w->width;
00686 w->window_desc->pref_height = w->height;
00687 } else {
00688 int16 def_width = max<int16>(min(w->window_desc->GetDefaultWidth(), _screen.width), w->nested_root->smallest_x);
00689 int16 def_height = max<int16>(min(w->window_desc->GetDefaultHeight(), _screen.height - 50), w->nested_root->smallest_y);
00690
00691 int dx = (w->resize.step_width == 0) ? 0 : def_width - w->width;
00692 int dy = (w->resize.step_height == 0) ? 0 : def_height - w->height;
00693
00694
00695 if (w->resize.step_width > 1) dx -= dx % (int)w->resize.step_width;
00696 if (w->resize.step_height > 1) dy -= dy % (int)w->resize.step_height;
00697 ResizeWindow(w, dx, dy, false);
00698 }
00699
00700 nw->SetLowered(true);
00701 nw->SetDirty(w);
00702 w->SetTimeout();
00703 break;
00704 }
00705
00706 case WWT_DEBUGBOX:
00707 w->ShowNewGRFInspectWindow();
00708 break;
00709
00710 case WWT_SHADEBOX:
00711 nw->SetDirty(w);
00712 w->SetShaded(!w->IsShaded());
00713 return;
00714
00715 case WWT_STICKYBOX:
00716 w->flags ^= WF_STICKY;
00717 nw->SetDirty(w);
00718 if (_ctrl_pressed) w->window_desc->pref_sticky = (w->flags & WF_STICKY) != 0;
00719 return;
00720
00721 default:
00722 break;
00723 }
00724
00725
00726 if (widget_index < 0) return;
00727
00728
00729 if (w->IsWidgetHighlighted(widget_index)) {
00730 w->SetWidgetHighlight(widget_index, TC_INVALID);
00731 Game::NewEvent(new ScriptEventWindowWidgetClick((ScriptWindow::WindowClass)w->window_class, w->window_number, widget_index));
00732 }
00733
00734 w->OnClick(pt, widget_index, click_count);
00735 }
00736
00743 static void DispatchRightClickEvent(Window *w, int x, int y)
00744 {
00745 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00746 if (wid == NULL) return;
00747
00748
00749 if (wid->index >= 0) {
00750 Point pt = { x, y };
00751 if (w->OnRightClick(pt, wid->index)) return;
00752 }
00753
00754 if (_settings_client.gui.hover_delay == 0 && wid->tool_tip != 0) GuiShowTooltips(w, wid->tool_tip, 0, NULL, TCC_RIGHT_CLICK);
00755 }
00756
00763 static void DispatchHoverEvent(Window *w, int x, int y)
00764 {
00765 NWidgetCore *wid = w->nested_root->GetWidgetFromPos(x, y);
00766
00767
00768 if (wid == NULL) return;
00769
00770
00771 if (wid->tool_tip != 0) {
00772 GuiShowTooltips(w, wid->tool_tip);
00773 return;
00774 }
00775
00776
00777 if (wid->index < 0) return;
00778
00779 Point pt = { x, y };
00780 w->OnHover(pt, wid->index);
00781 }
00782
00790 static void DispatchMouseWheelEvent(Window *w, NWidgetCore *nwid, int wheel)
00791 {
00792 if (nwid == NULL) return;
00793
00794
00795 if (nwid->type == WWT_CAPTION || nwid->type == WWT_SHADEBOX) {
00796 w->SetShaded(wheel < 0);
00797 return;
00798 }
00799
00800
00801 if (nwid->type == NWID_VSCROLLBAR) {
00802 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar *>(nwid);
00803 if (sb->GetCount() > sb->GetCapacity()) {
00804 sb->UpdatePosition(wheel);
00805 w->SetDirty();
00806 }
00807 return;
00808 }
00809
00810
00811 Scrollbar *sb = (nwid->scrollbar_index >= 0 ? w->GetScrollbar(nwid->scrollbar_index) : NULL);
00812 if (sb != NULL && sb->GetCount() > sb->GetCapacity()) {
00813 sb->UpdatePosition(wheel);
00814 w->SetDirty();
00815 }
00816 }
00817
00823 static bool MayBeShown(const Window *w)
00824 {
00825
00826 if (!HasModalProgress()) return true;
00827
00828 switch (w->window_class) {
00829 case WC_MAIN_WINDOW:
00830 case WC_MODAL_PROGRESS:
00831 case WC_CONFIRM_POPUP_QUERY:
00832 return true;
00833
00834 default:
00835 return false;
00836 }
00837 }
00838
00851 static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
00852 {
00853 const Window *v;
00854 FOR_ALL_WINDOWS_FROM_BACK_FROM(v, w->z_front) {
00855 if (MayBeShown(v) &&
00856 right > v->left &&
00857 bottom > v->top &&
00858 left < v->left + v->width &&
00859 top < v->top + v->height) {
00860
00861 int x;
00862
00863 if (left < (x = v->left)) {
00864 DrawOverlappedWindow(w, left, top, x, bottom);
00865 DrawOverlappedWindow(w, x, top, right, bottom);
00866 return;
00867 }
00868
00869 if (right > (x = v->left + v->width)) {
00870 DrawOverlappedWindow(w, left, top, x, bottom);
00871 DrawOverlappedWindow(w, x, top, right, bottom);
00872 return;
00873 }
00874
00875 if (top < (x = v->top)) {
00876 DrawOverlappedWindow(w, left, top, right, x);
00877 DrawOverlappedWindow(w, left, x, right, bottom);
00878 return;
00879 }
00880
00881 if (bottom > (x = v->top + v->height)) {
00882 DrawOverlappedWindow(w, left, top, right, x);
00883 DrawOverlappedWindow(w, left, x, right, bottom);
00884 return;
00885 }
00886
00887 return;
00888 }
00889 }
00890
00891
00892 DrawPixelInfo *dp = _cur_dpi;
00893 dp->width = right - left;
00894 dp->height = bottom - top;
00895 dp->left = left - w->left;
00896 dp->top = top - w->top;
00897 dp->pitch = _screen.pitch;
00898 dp->dst_ptr = BlitterFactory::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
00899 dp->zoom = ZOOM_LVL_NORMAL;
00900 w->OnPaint();
00901 }
00902
00911 void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
00912 {
00913 Window *w;
00914 DrawPixelInfo bk;
00915 _cur_dpi = &bk;
00916
00917 FOR_ALL_WINDOWS_FROM_BACK(w) {
00918 if (MayBeShown(w) &&
00919 right > w->left &&
00920 bottom > w->top &&
00921 left < w->left + w->width &&
00922 top < w->top + w->height) {
00923
00924 DrawOverlappedWindow(w, left, top, right, bottom);
00925 }
00926 }
00927 }
00928
00933 void Window::SetDirty() const
00934 {
00935 SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height);
00936 }
00937
00944 void Window::ReInit(int rx, int ry)
00945 {
00946 this->SetDirty();
00947
00948
00949 int window_width = this->width;
00950 int window_height = this->height;
00951
00952 this->OnInit();
00953
00954 this->nested_root->SetupSmallestSize(this, false);
00955 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
00956 this->width = this->nested_root->smallest_x;
00957 this->height = this->nested_root->smallest_y;
00958 this->resize.step_width = this->nested_root->resize_x;
00959 this->resize.step_height = this->nested_root->resize_y;
00960
00961
00962 window_width = max(window_width + rx, this->width);
00963 window_height = max(window_height + ry, this->height);
00964 int dx = (this->resize.step_width == 0) ? 0 : window_width - this->width;
00965 int dy = (this->resize.step_height == 0) ? 0 : window_height - this->height;
00966
00967
00968 if (this->resize.step_width > 1) dx -= dx % (int)this->resize.step_width;
00969 if (this->resize.step_height > 1) dy -= dy % (int)this->resize.step_height;
00970
00971 ResizeWindow(this, dx, dy);
00972
00973 }
00974
00980 void Window::SetShaded(bool make_shaded)
00981 {
00982 if (this->shade_select == NULL) return;
00983
00984 int desired = make_shaded ? SZSP_HORIZONTAL : 0;
00985 if (this->shade_select->shown_plane != desired) {
00986 if (make_shaded) {
00987 if (this->nested_focus != NULL) this->UnfocusFocusedWidget();
00988 this->unshaded_size.width = this->width;
00989 this->unshaded_size.height = this->height;
00990 this->shade_select->SetDisplayedPlane(desired);
00991 this->ReInit(0, -this->height);
00992 } else {
00993 this->shade_select->SetDisplayedPlane(desired);
00994 int dx = ((int)this->unshaded_size.width > this->width) ? (int)this->unshaded_size.width - this->width : 0;
00995 int dy = ((int)this->unshaded_size.height > this->height) ? (int)this->unshaded_size.height - this->height : 0;
00996 this->ReInit(dx, dy);
00997 }
00998 }
00999 }
01000
01007 static Window *FindChildWindow(const Window *w, WindowClass wc)
01008 {
01009 Window *v;
01010 FOR_ALL_WINDOWS_FROM_BACK(v) {
01011 if ((wc == WC_INVALID || wc == v->window_class) && v->parent == w) return v;
01012 }
01013
01014 return NULL;
01015 }
01016
01021 void Window::DeleteChildWindows(WindowClass wc) const
01022 {
01023 Window *child = FindChildWindow(this, wc);
01024 while (child != NULL) {
01025 delete child;
01026 child = FindChildWindow(this, wc);
01027 }
01028 }
01029
01033 Window::~Window()
01034 {
01035 if (_thd.window_class == this->window_class &&
01036 _thd.window_number == this->window_number) {
01037 ResetObjectToPlace();
01038 }
01039
01040
01041 if (_mouseover_last_w == this) _mouseover_last_w = NULL;
01042
01043
01044 if (_last_scroll_window == this) _last_scroll_window = NULL;
01045
01046
01047 if (_focused_window == this) {
01048 this->OnFocusLost();
01049 _focused_window = NULL;
01050 }
01051
01052 this->DeleteChildWindows();
01053
01054 if (this->viewport != NULL) DeleteWindowViewport(this);
01055
01056 this->SetDirty();
01057
01058 free(this->nested_array);
01059 delete this->nested_root;
01060
01061 this->window_class = WC_INVALID;
01062 }
01063
01070 Window *FindWindowById(WindowClass cls, WindowNumber number)
01071 {
01072 Window *w;
01073 FOR_ALL_WINDOWS_FROM_BACK(w) {
01074 if (w->window_class == cls && w->window_number == number) return w;
01075 }
01076
01077 return NULL;
01078 }
01079
01086 Window *FindWindowByClass(WindowClass cls)
01087 {
01088 Window *w;
01089 FOR_ALL_WINDOWS_FROM_BACK(w) {
01090 if (w->window_class == cls) return w;
01091 }
01092
01093 return NULL;
01094 }
01095
01102 void DeleteWindowById(WindowClass cls, WindowNumber number, bool force)
01103 {
01104 Window *w = FindWindowById(cls, number);
01105 if (force || w == NULL ||
01106 (w->flags & WF_STICKY) == 0) {
01107 delete w;
01108 }
01109 }
01110
01115 void DeleteWindowByClass(WindowClass cls)
01116 {
01117 Window *w;
01118
01119 restart_search:
01120
01121
01122
01123 FOR_ALL_WINDOWS_FROM_BACK(w) {
01124 if (w->window_class == cls) {
01125 delete w;
01126 goto restart_search;
01127 }
01128 }
01129 }
01130
01137 void DeleteCompanyWindows(CompanyID id)
01138 {
01139 Window *w;
01140
01141 restart_search:
01142
01143
01144
01145 FOR_ALL_WINDOWS_FROM_BACK(w) {
01146 if (w->owner == id) {
01147 delete w;
01148 goto restart_search;
01149 }
01150 }
01151
01152
01153 DeleteWindowById(WC_BUY_COMPANY, id);
01154 }
01155
01163 void ChangeWindowOwner(Owner old_owner, Owner new_owner)
01164 {
01165 Window *w;
01166 FOR_ALL_WINDOWS_FROM_BACK(w) {
01167 if (w->owner != old_owner) continue;
01168
01169 switch (w->window_class) {
01170 case WC_COMPANY_COLOUR:
01171 case WC_FINANCES:
01172 case WC_STATION_LIST:
01173 case WC_TRAINS_LIST:
01174 case WC_ROADVEH_LIST:
01175 case WC_SHIPS_LIST:
01176 case WC_AIRCRAFT_LIST:
01177 case WC_BUY_COMPANY:
01178 case WC_COMPANY:
01179 case WC_COMPANY_INFRASTRUCTURE:
01180 continue;
01181
01182 default:
01183 w->owner = new_owner;
01184 break;
01185 }
01186 }
01187 }
01188
01189 static void BringWindowToFront(Window *w);
01190
01198 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
01199 {
01200 Window *w = FindWindowById(cls, number);
01201
01202 if (w != NULL) {
01203 if (w->IsShaded()) w->SetShaded(false);
01204
01205 w->SetWhiteBorder();
01206 BringWindowToFront(w);
01207 w->SetDirty();
01208 }
01209
01210 return w;
01211 }
01212
01213 static inline bool IsVitalWindow(const Window *w)
01214 {
01215 switch (w->window_class) {
01216 case WC_MAIN_TOOLBAR:
01217 case WC_STATUS_BAR:
01218 case WC_NEWS_WINDOW:
01219 case WC_SEND_NETWORK_MSG:
01220 return true;
01221
01222 default:
01223 return false;
01224 }
01225 }
01226
01235 static uint GetWindowZPriority(const Window *w)
01236 {
01237 assert(w->window_class != WC_INVALID);
01238
01239 uint z_priority = 0;
01240
01241 switch (w->window_class) {
01242 case WC_ENDSCREEN:
01243 ++z_priority;
01244
01245 case WC_HIGHSCORE:
01246 ++z_priority;
01247
01248 case WC_TOOLTIPS:
01249 ++z_priority;
01250
01251 case WC_DROPDOWN_MENU:
01252 ++z_priority;
01253
01254 case WC_MAIN_TOOLBAR:
01255 case WC_STATUS_BAR:
01256 ++z_priority;
01257
01258 case WC_OSK:
01259 ++z_priority;
01260
01261 case WC_QUERY_STRING:
01262 case WC_SEND_NETWORK_MSG:
01263 ++z_priority;
01264
01265 case WC_ERRMSG:
01266 case WC_CONFIRM_POPUP_QUERY:
01267 case WC_MODAL_PROGRESS:
01268 case WC_NETWORK_STATUS_WINDOW:
01269 ++z_priority;
01270
01271 case WC_GENERATE_LANDSCAPE:
01272 case WC_SAVELOAD:
01273 case WC_GAME_OPTIONS:
01274 case WC_CUSTOM_CURRENCY:
01275 case WC_NETWORK_WINDOW:
01276 case WC_GRF_PARAMETERS:
01277 case WC_AI_LIST:
01278 case WC_AI_SETTINGS:
01279 case WC_TEXTFILE:
01280 ++z_priority;
01281
01282 case WC_CONSOLE:
01283 ++z_priority;
01284
01285 case WC_NEWS_WINDOW:
01286 ++z_priority;
01287
01288 default:
01289 ++z_priority;
01290
01291 case WC_MAIN_WINDOW:
01292 return z_priority;
01293 }
01294 }
01295
01300 static void AddWindowToZOrdering(Window *w)
01301 {
01302 assert(w->z_front == NULL && w->z_back == NULL);
01303
01304 if (_z_front_window == NULL) {
01305
01306 _z_front_window = _z_back_window = w;
01307 w->z_front = w->z_back = NULL;
01308 } else {
01309
01310 Window *v = _z_front_window;
01311 uint last_z_priority = UINT_MAX;
01312 while (v != NULL && (v->window_class == WC_INVALID || GetWindowZPriority(v) > GetWindowZPriority(w))) {
01313 if (v->window_class != WC_INVALID) {
01314
01315 assert(last_z_priority >= GetWindowZPriority(v));
01316 last_z_priority = GetWindowZPriority(v);
01317 }
01318
01319 v = v->z_back;
01320 }
01321
01322 if (v == NULL) {
01323
01324 w->z_front = _z_back_window;
01325 w->z_back = NULL;
01326 _z_back_window->z_back = w;
01327 _z_back_window = w;
01328 } else if (v == _z_front_window) {
01329
01330 w->z_front = NULL;
01331 w->z_back = _z_front_window;
01332 _z_front_window->z_front = w;
01333 _z_front_window = w;
01334 } else {
01335
01336 w->z_front = v->z_front;
01337 w->z_back = v;
01338 v->z_front->z_back = w;
01339 v->z_front = w;
01340 }
01341 }
01342 }
01343
01344
01349 static void RemoveWindowFromZOrdering(Window *w)
01350 {
01351 if (w->z_front == NULL) {
01352 assert(_z_front_window == w);
01353 _z_front_window = w->z_back;
01354 } else {
01355 w->z_front->z_back = w->z_back;
01356 }
01357
01358 if (w->z_back == NULL) {
01359 assert(_z_back_window == w);
01360 _z_back_window = w->z_front;
01361 } else {
01362 w->z_back->z_front = w->z_front;
01363 }
01364
01365 w->z_front = w->z_back = NULL;
01366 }
01367
01373 static void BringWindowToFront(Window *w)
01374 {
01375 RemoveWindowFromZOrdering(w);
01376 AddWindowToZOrdering(w);
01377
01378 w->SetDirty();
01379 }
01380
01389 void Window::InitializeData(WindowNumber window_number)
01390 {
01391
01392 this->window_class = this->window_desc->cls;
01393 this->SetWhiteBorder();
01394 if (this->window_desc->default_pos == WDP_CENTER) this->flags |= WF_CENTERED;
01395 this->owner = INVALID_OWNER;
01396 this->nested_focus = NULL;
01397 this->window_number = window_number;
01398
01399 this->OnInit();
01400
01401 if (this->nested_array == NULL) {
01402 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01403 this->nested_root->SetupSmallestSize(this, true);
01404 } else {
01405 this->nested_root->SetupSmallestSize(this, false);
01406 }
01407
01408 this->nested_root->AssignSizePosition(ST_SMALLEST, 0, 0, this->nested_root->smallest_x, this->nested_root->smallest_y, _current_text_dir == TD_RTL);
01409
01410
01411
01412 this->resize.step_width = this->nested_root->resize_x;
01413 this->resize.step_height = this->nested_root->resize_y;
01414
01415
01416
01417
01418 if (!EditBoxInGlobalFocus() || this->nested_root->GetWidgetOfType(WWT_EDITBOX) != NULL) SetFocusedWindow(this);
01419
01420
01421 AddWindowToZOrdering(this);
01422 }
01423
01431 void Window::InitializePositionSize(int x, int y, int sm_width, int sm_height)
01432 {
01433 this->left = x;
01434 this->top = y;
01435 this->width = sm_width;
01436 this->height = sm_height;
01437 }
01438
01449 void Window::FindWindowPlacementAndResize(int def_width, int def_height)
01450 {
01451 def_width = max(def_width, this->width);
01452 def_height = max(def_height, this->height);
01453
01454
01455
01456
01457
01458 if (this->width != def_width || this->height != def_height) {
01459
01460 int free_height = _screen.height;
01461 const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
01462 if (wt != NULL) free_height -= wt->height;
01463 wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01464 if (wt != NULL) free_height -= wt->height;
01465
01466 int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0);
01467 int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0);
01468
01469
01470
01471
01472 if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
01473 if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
01474
01475 ResizeWindow(this, enlarge_x, enlarge_y);
01476
01477 } else {
01478
01479 this->OnResize();
01480 }
01481
01482 int nx = this->left;
01483 int ny = this->top;
01484
01485 if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
01486
01487 const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
01488 ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
01489 nx = max(nx, 0);
01490
01491 if (this->viewport != NULL) {
01492 this->viewport->left += nx - this->left;
01493 this->viewport->top += ny - this->top;
01494 }
01495 this->left = nx;
01496 this->top = ny;
01497
01498 this->SetDirty();
01499 }
01500
01512 static bool IsGoodAutoPlace1(int left, int top, int width, int height, Point &pos)
01513 {
01514 int right = width + left;
01515 int bottom = height + top;
01516
01517 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01518 if (left < 0 || (main_toolbar != NULL && top < main_toolbar->height) || right > _screen.width || bottom > _screen.height) return false;
01519
01520
01521 const Window *w;
01522 FOR_ALL_WINDOWS_FROM_BACK(w) {
01523 if (w->window_class == WC_MAIN_WINDOW) continue;
01524
01525 if (right > w->left &&
01526 w->left + w->width > left &&
01527 bottom > w->top &&
01528 w->top + w->height > top) {
01529 return false;
01530 }
01531 }
01532
01533 pos.x = left;
01534 pos.y = top;
01535 return true;
01536 }
01537
01549 static bool IsGoodAutoPlace2(int left, int top, int width, int height, Point &pos)
01550 {
01551
01552
01553
01554 if (left < -(width >> 2) || left > _screen.width - (width >> 1)) return false;
01555
01556 if (top < 22 || top > _screen.height - (height >> 2)) return false;
01557
01558
01559 const Window *w;
01560 FOR_ALL_WINDOWS_FROM_BACK(w) {
01561 if (w->window_class == WC_MAIN_WINDOW) continue;
01562
01563 if (left + width > w->left &&
01564 w->left + w->width > left &&
01565 top + height > w->top &&
01566 w->top + w->height > top) {
01567 return false;
01568 }
01569 }
01570
01571 pos.x = left;
01572 pos.y = top;
01573 return true;
01574 }
01575
01582 static Point GetAutoPlacePosition(int width, int height)
01583 {
01584 Point pt;
01585
01586
01587 const Window *main_toolbar = FindWindowByClass(WC_MAIN_TOOLBAR);
01588 if (IsGoodAutoPlace1(0, main_toolbar != NULL ? main_toolbar->height + 2 : 2, width, height, pt)) return pt;
01589
01590
01591
01592
01593
01594 const Window *w;
01595 FOR_ALL_WINDOWS_FROM_BACK(w) {
01596 if (w->window_class == WC_MAIN_WINDOW) continue;
01597
01598 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01599 if (IsGoodAutoPlace1(w->left - width - 2, w->top, width, height, pt)) return pt;
01600 if (IsGoodAutoPlace1(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01601 if (IsGoodAutoPlace1(w->left, w->top - height - 2, width, height, pt)) return pt;
01602 if (IsGoodAutoPlace1(w->left + w->width + 2, w->top + w->height - height, width, height, pt)) return pt;
01603 if (IsGoodAutoPlace1(w->left - width - 2, w->top + w->height - height, width, height, pt)) return pt;
01604 if (IsGoodAutoPlace1(w->left + w->width - width, w->top + w->height + 2, width, height, pt)) return pt;
01605 if (IsGoodAutoPlace1(w->left + w->width - width, w->top - height - 2, width, height, pt)) return pt;
01606 }
01607
01608
01609
01610
01611
01612 FOR_ALL_WINDOWS_FROM_BACK(w) {
01613 if (w->window_class == WC_MAIN_WINDOW) continue;
01614
01615 if (IsGoodAutoPlace2(w->left + w->width + 2, w->top, width, height, pt)) return pt;
01616 if (IsGoodAutoPlace2(w->left - width - 2, w->top, width, height, pt)) return pt;
01617 if (IsGoodAutoPlace2(w->left, w->top + w->height + 2, width, height, pt)) return pt;
01618 if (IsGoodAutoPlace2(w->left, w->top - height - 2, width, height, pt)) return pt;
01619 }
01620
01621
01622
01623
01624 int left = 0, top = 24;
01625
01626 restart:
01627 FOR_ALL_WINDOWS_FROM_BACK(w) {
01628 if (w->left == left && w->top == top) {
01629 left += 5;
01630 top += 5;
01631 goto restart;
01632 }
01633 }
01634
01635 pt.x = left;
01636 pt.y = top;
01637 return pt;
01638 }
01639
01646 Point GetToolbarAlignedWindowPosition(int window_width)
01647 {
01648 const Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
01649 assert(w != NULL);
01650 Point pt = { _current_text_dir == TD_RTL ? w->left : (w->left + w->width) - window_width, w->top + w->height };
01651 return pt;
01652 }
01653
01671 static Point LocalGetWindowPlacement(const WindowDesc *desc, int16 sm_width, int16 sm_height, int window_number)
01672 {
01673 Point pt;
01674 const Window *w;
01675
01676 int16 default_width = max(desc->GetDefaultWidth(), sm_width);
01677 int16 default_height = max(desc->GetDefaultHeight(), sm_height);
01678
01679 if (desc->parent_cls != 0 &&
01680 (w = FindWindowById(desc->parent_cls, window_number)) != NULL &&
01681 w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) {
01682
01683 pt.x = w->left + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 0 : 10);
01684 if (pt.x > _screen.width + 10 - default_width) {
01685 pt.x = (_screen.width + 10 - default_width) - 20;
01686 }
01687 pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? w->height : 10);
01688 return pt;
01689 }
01690
01691 switch (desc->default_pos) {
01692 case WDP_ALIGN_TOOLBAR:
01693 return GetToolbarAlignedWindowPosition(default_width);
01694
01695 case WDP_AUTO:
01696 return GetAutoPlacePosition(default_width, default_height);
01697
01698 case WDP_CENTER:
01699 pt.x = (_screen.width - default_width) / 2;
01700 pt.y = (_screen.height - default_height) / 2;
01701 break;
01702
01703 case WDP_MANUAL:
01704 pt.x = 0;
01705 pt.y = 0;
01706 break;
01707
01708 default:
01709 NOT_REACHED();
01710 }
01711
01712 return pt;
01713 }
01714
01715 Point Window::OnInitialPosition(int16 sm_width, int16 sm_height, int window_number)
01716 {
01717 return LocalGetWindowPlacement(this->window_desc, sm_width, sm_height, window_number);
01718 }
01719
01727 void Window::CreateNestedTree(bool fill_nested)
01728 {
01729 int biggest_index = -1;
01730 this->nested_root = MakeWindowNWidgetTree(this->window_desc->nwid_parts, this->window_desc->nwid_length, &biggest_index, &this->shade_select);
01731 this->nested_array_size = (uint)(biggest_index + 1);
01732
01733 if (fill_nested) {
01734 this->nested_array = CallocT<NWidgetBase *>(this->nested_array_size);
01735 this->nested_root->FillNestedArray(this->nested_array, this->nested_array_size);
01736 }
01737 }
01738
01743 void Window::FinishInitNested(WindowNumber window_number)
01744 {
01745 this->InitializeData(window_number);
01746 this->ApplyDefaults();
01747 Point pt = this->OnInitialPosition(this->nested_root->smallest_x, this->nested_root->smallest_y, window_number);
01748 this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y);
01749 this->FindWindowPlacementAndResize(this->window_desc->GetDefaultWidth(), this->window_desc->GetDefaultHeight());
01750 }
01751
01756 void Window::InitNested(WindowNumber window_number)
01757 {
01758 this->CreateNestedTree(false);
01759 this->FinishInitNested(window_number);
01760 }
01761
01766 Window::Window(WindowDesc *desc) : window_desc(desc), scrolling_scrollbar(-1)
01767 {
01768 }
01769
01777 Window *FindWindowFromPt(int x, int y)
01778 {
01779 Window *w;
01780 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01781 if (MayBeShown(w) && IsInsideBS(x, w->left, w->width) && IsInsideBS(y, w->top, w->height)) {
01782 return w;
01783 }
01784 }
01785
01786 return NULL;
01787 }
01788
01792 void InitWindowSystem()
01793 {
01794 IConsoleClose();
01795
01796 _z_back_window = NULL;
01797 _z_front_window = NULL;
01798 _focused_window = NULL;
01799 _mouseover_last_w = NULL;
01800 _last_scroll_window = NULL;
01801 _scrolling_viewport = false;
01802 _mouse_hovering = false;
01803
01804 NWidgetLeaf::InvalidateDimensionCache();
01805 NWidgetScrollbar::InvalidateDimensionCache();
01806
01807 ShowFirstError();
01808 }
01809
01813 void UnInitWindowSystem()
01814 {
01815 UnshowCriticalError();
01816
01817 Window *w;
01818 FOR_ALL_WINDOWS_FROM_FRONT(w) delete w;
01819
01820 for (w = _z_front_window; w != NULL; ) {
01821 Window *to_del = w;
01822 w = w->z_back;
01823 free(to_del);
01824 }
01825
01826 _z_front_window = NULL;
01827 _z_back_window = NULL;
01828 }
01829
01833 void ResetWindowSystem()
01834 {
01835 UnInitWindowSystem();
01836 InitWindowSystem();
01837 _thd.Reset();
01838 }
01839
01840 static void DecreaseWindowCounters()
01841 {
01842 Window *w;
01843 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01844 if (_scroller_click_timeout == 0) {
01845
01846 for (uint i = 0; i < w->nested_array_size; i++) {
01847 NWidgetBase *nwid = w->nested_array[i];
01848 if (nwid != NULL && (nwid->type == NWID_HSCROLLBAR || nwid->type == NWID_VSCROLLBAR)) {
01849 NWidgetScrollbar *sb = static_cast<NWidgetScrollbar*>(nwid);
01850 if (sb->disp_flags & (ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN)) {
01851 sb->disp_flags &= ~(ND_SCROLLBAR_UP | ND_SCROLLBAR_DOWN);
01852 w->scrolling_scrollbar = -1;
01853 sb->SetDirty(w);
01854 }
01855 }
01856 }
01857 }
01858
01859
01860 for (SmallMap<int, QueryString*>::Pair *it = w->querystrings.Begin(); it != w->querystrings.End(); ++it) {
01861 it->second->HandleEditBox(w, it->first);
01862 }
01863
01864 w->OnMouseLoop();
01865 }
01866
01867 FOR_ALL_WINDOWS_FROM_FRONT(w) {
01868 if ((w->flags & WF_TIMEOUT) && --w->timeout_timer == 0) {
01869 CLRBITS(w->flags, WF_TIMEOUT);
01870
01871 w->OnTimeout();
01872 w->RaiseButtons(true);
01873 }
01874 }
01875 }
01876
01877 static void HandlePlacePresize()
01878 {
01879 if (_special_mouse_mode != WSM_PRESIZE) return;
01880
01881 Window *w = _thd.GetCallbackWnd();
01882 if (w == NULL) return;
01883
01884 Point pt = GetTileBelowCursor();
01885 if (pt.x == -1) {
01886 _thd.selend.x = -1;
01887 return;
01888 }
01889
01890 w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
01891 }
01892
01897 static EventState HandleMouseDragDrop()
01898 {
01899 if (_special_mouse_mode != WSM_DRAGDROP) return ES_NOT_HANDLED;
01900
01901 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
01902
01903 Window *w = _thd.GetCallbackWnd();
01904 if (w != NULL) {
01905
01906 Point pt;
01907 pt.x = _cursor.pos.x - w->left;
01908 pt.y = _cursor.pos.y - w->top;
01909 if (_left_button_down) {
01910 w->OnMouseDrag(pt, GetWidgetFromPos(w, pt.x, pt.y));
01911 } else {
01912 w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
01913 }
01914 }
01915
01916 if (!_left_button_down) ResetObjectToPlace();
01917 return ES_HANDLED;
01918 }
01919
01921 static void HandleMouseOver()
01922 {
01923 Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
01924
01925
01926 if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
01927
01928 Point pt = { -1, -1 };
01929 _mouseover_last_w->OnMouseOver(pt, 0);
01930 }
01931
01932
01933 _mouseover_last_w = w;
01934
01935 if (w != NULL) {
01936
01937 Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
01938 const NWidgetCore *widget = w->nested_root->GetWidgetFromPos(pt.x, pt.y);
01939 if (widget != NULL) w->OnMouseOver(pt, widget->index);
01940 }
01941 }
01942
01944 static const int MIN_VISIBLE_TITLE_BAR = 13;
01945
01947 enum PreventHideDirection {
01948 PHD_UP,
01949 PHD_DOWN,
01950 };
01951
01962 static void PreventHiding(int *nx, int *ny, const Rect &rect, const Window *v, int px, PreventHideDirection dir)
01963 {
01964 if (v == NULL) return;
01965
01966 int v_bottom = v->top + v->height;
01967 int v_right = v->left + v->width;
01968 int safe_y = (dir == PHD_UP) ? (v->top - MIN_VISIBLE_TITLE_BAR - rect.top) : (v_bottom + MIN_VISIBLE_TITLE_BAR - rect.bottom);
01969
01970 if (*ny + rect.top <= v->top - MIN_VISIBLE_TITLE_BAR) return;
01971 if (*ny + rect.bottom >= v_bottom + MIN_VISIBLE_TITLE_BAR) return;
01972
01973
01974 if (*nx + rect.left + MIN_VISIBLE_TITLE_BAR < v->left) {
01975 if (v->left < MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01976 return;
01977 }
01978 if (*nx + rect.right - MIN_VISIBLE_TITLE_BAR > v_right) {
01979 if (v_right > _screen.width - MIN_VISIBLE_TITLE_BAR) *ny = safe_y;
01980 return;
01981 }
01982
01983
01984 if (px + rect.left < v->left && v->left >= MIN_VISIBLE_TITLE_BAR) {
01985 *nx = v->left - MIN_VISIBLE_TITLE_BAR - rect.left;
01986 } else if (px + rect.right > v_right && v_right <= _screen.width - MIN_VISIBLE_TITLE_BAR) {
01987 *nx = v_right + MIN_VISIBLE_TITLE_BAR - rect.right;
01988 } else {
01989 *ny = safe_y;
01990 }
01991 }
01992
02000 static void EnsureVisibleCaption(Window *w, int nx, int ny)
02001 {
02002
02003 Rect caption_rect;
02004 const NWidgetBase *caption = w->nested_root->GetWidgetOfType(WWT_CAPTION);
02005 if (caption != NULL) {
02006 caption_rect.left = caption->pos_x;
02007 caption_rect.right = caption->pos_x + caption->current_x;
02008 caption_rect.top = caption->pos_y;
02009 caption_rect.bottom = caption->pos_y + caption->current_y;
02010
02011
02012 nx = Clamp(nx, MIN_VISIBLE_TITLE_BAR - caption_rect.right, _screen.width - MIN_VISIBLE_TITLE_BAR - caption_rect.left);
02013 ny = Clamp(ny, 0, _screen.height - MIN_VISIBLE_TITLE_BAR);
02014
02015
02016 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_MAIN_TOOLBAR, 0), w->left, PHD_DOWN);
02017 PreventHiding(&nx, &ny, caption_rect, FindWindowById(WC_STATUS_BAR, 0), w->left, PHD_UP);
02018 }
02019
02020 if (w->viewport != NULL) {
02021 w->viewport->left += nx - w->left;
02022 w->viewport->top += ny - w->top;
02023 }
02024
02025 w->left = nx;
02026 w->top = ny;
02027 }
02028
02039 void ResizeWindow(Window *w, int delta_x, int delta_y, bool clamp_to_screen)
02040 {
02041 if (delta_x != 0 || delta_y != 0) {
02042 if (clamp_to_screen) {
02043
02044
02045 int new_right = w->left + w->width + delta_x;
02046 int new_bottom = w->top + w->height + delta_y;
02047 if (new_right >= (int)_cur_resolution.width) delta_x -= Ceil(new_right - _cur_resolution.width, max(1U, w->nested_root->resize_x));
02048 if (new_bottom >= (int)_cur_resolution.height) delta_y -= Ceil(new_bottom - _cur_resolution.height, max(1U, w->nested_root->resize_y));
02049 }
02050
02051 w->SetDirty();
02052
02053 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);
02054 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);
02055 assert(w->nested_root->resize_x == 0 || new_xinc % w->nested_root->resize_x == 0);
02056 assert(w->nested_root->resize_y == 0 || new_yinc % w->nested_root->resize_y == 0);
02057
02058 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);
02059 w->width = w->nested_root->current_x;
02060 w->height = w->nested_root->current_y;
02061 }
02062
02063 EnsureVisibleCaption(w, w->left, w->top);
02064
02065
02066 w->OnResize();
02067 w->SetDirty();
02068 }
02069
02075 int GetMainViewTop()
02076 {
02077 Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02078 return (w == NULL) ? 0 : w->top + w->height;
02079 }
02080
02086 int GetMainViewBottom()
02087 {
02088 Window *w = FindWindowById(WC_STATUS_BAR, 0);
02089 return (w == NULL) ? _screen.height : w->top;
02090 }
02091
02092 static bool _dragging_window;
02093
02098 static EventState HandleWindowDragging()
02099 {
02100
02101 if (!_dragging_window) return ES_NOT_HANDLED;
02102
02103
02104 if (_left_button_down && _cursor.delta.x == 0 && _cursor.delta.y == 0) return ES_HANDLED;
02105
02106
02107 Window *w;
02108 FOR_ALL_WINDOWS_FROM_BACK(w) {
02109 if (w->flags & WF_DRAGGING) {
02110
02111 if (!_left_button_down) {
02112 w->flags &= ~WF_DRAGGING;
02113 break;
02114 }
02115
02116 w->SetDirty();
02117
02118 int x = _cursor.pos.x + _drag_delta.x;
02119 int y = _cursor.pos.y + _drag_delta.y;
02120 int nx = x;
02121 int ny = y;
02122
02123 if (_settings_client.gui.window_snap_radius != 0) {
02124 const Window *v;
02125
02126 int hsnap = _settings_client.gui.window_snap_radius;
02127 int vsnap = _settings_client.gui.window_snap_radius;
02128 int delta;
02129
02130 FOR_ALL_WINDOWS_FROM_BACK(v) {
02131 if (v == w) continue;
02132
02133 if (y + w->height > v->top && y < v->top + v->height) {
02134
02135 delta = abs(v->left + v->width - x);
02136 if (delta <= hsnap) {
02137 nx = v->left + v->width;
02138 hsnap = delta;
02139 }
02140
02141
02142 delta = abs(v->left - x - w->width);
02143 if (delta <= hsnap) {
02144 nx = v->left - w->width;
02145 hsnap = delta;
02146 }
02147 }
02148
02149 if (w->top + w->height >= v->top && w->top <= v->top + v->height) {
02150
02151 delta = abs(v->left - x);
02152 if (delta <= hsnap) {
02153 nx = v->left;
02154 hsnap = delta;
02155 }
02156
02157
02158 delta = abs(v->left + v->width - x - w->width);
02159 if (delta <= hsnap) {
02160 nx = v->left + v->width - w->width;
02161 hsnap = delta;
02162 }
02163 }
02164
02165 if (x + w->width > v->left && x < v->left + v->width) {
02166
02167 delta = abs(v->top + v->height - y);
02168 if (delta <= vsnap) {
02169 ny = v->top + v->height;
02170 vsnap = delta;
02171 }
02172
02173
02174 delta = abs(v->top - y - w->height);
02175 if (delta <= vsnap) {
02176 ny = v->top - w->height;
02177 vsnap = delta;
02178 }
02179 }
02180
02181 if (w->left + w->width >= v->left && w->left <= v->left + v->width) {
02182
02183 delta = abs(v->top - y);
02184 if (delta <= vsnap) {
02185 ny = v->top;
02186 vsnap = delta;
02187 }
02188
02189
02190 delta = abs(v->top + v->height - y - w->height);
02191 if (delta <= vsnap) {
02192 ny = v->top + v->height - w->height;
02193 vsnap = delta;
02194 }
02195 }
02196 }
02197 }
02198
02199 EnsureVisibleCaption(w, nx, ny);
02200
02201 w->SetDirty();
02202 return ES_HANDLED;
02203 } else if (w->flags & WF_SIZING) {
02204
02205 if (!_left_button_down) {
02206 w->flags &= ~WF_SIZING;
02207 w->SetDirty();
02208 break;
02209 }
02210
02211
02212
02213
02214 int x, y = _cursor.pos.y - _drag_delta.y;
02215 if (w->flags & WF_SIZING_LEFT) {
02216 x = _drag_delta.x - _cursor.pos.x;
02217 } else {
02218 x = _cursor.pos.x - _drag_delta.x;
02219 }
02220
02221
02222 if (w->resize.step_width == 0) x = 0;
02223 if (w->resize.step_height == 0) y = 0;
02224
02225
02226 if (w->top + w->height + y > _screen.height) {
02227 y = _screen.height - w->height - w->top;
02228 }
02229
02230
02231
02232
02233 if (w->resize.step_width > 1) x -= x % (int)w->resize.step_width;
02234 if (w->resize.step_height > 1) y -= y % (int)w->resize.step_height;
02235
02236
02237 if ((int)w->width + x < (int)w->nested_root->smallest_x) {
02238 x = w->nested_root->smallest_x - w->width;
02239 }
02240 if ((int)w->height + y < (int)w->nested_root->smallest_y) {
02241 y = w->nested_root->smallest_y - w->height;
02242 }
02243
02244
02245 if (x == 0 && y == 0) return ES_HANDLED;
02246
02247
02248 _drag_delta.y += y;
02249 if ((w->flags & WF_SIZING_LEFT) && x != 0) {
02250 _drag_delta.x -= x;
02251 w->SetDirty();
02252 w->left -= x;
02253
02254 } else {
02255 _drag_delta.x += x;
02256 }
02257
02258
02259 ResizeWindow(w, x, y);
02260 return ES_HANDLED;
02261 }
02262 }
02263
02264 _dragging_window = false;
02265 return ES_HANDLED;
02266 }
02267
02272 static void StartWindowDrag(Window *w)
02273 {
02274 w->flags |= WF_DRAGGING;
02275 w->flags &= ~WF_CENTERED;
02276 _dragging_window = true;
02277
02278 _drag_delta.x = w->left - _cursor.pos.x;
02279 _drag_delta.y = w->top - _cursor.pos.y;
02280
02281 BringWindowToFront(w);
02282 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02283 }
02284
02290 static void StartWindowSizing(Window *w, bool to_left)
02291 {
02292 w->flags |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT;
02293 w->flags &= ~WF_CENTERED;
02294 _dragging_window = true;
02295
02296 _drag_delta.x = _cursor.pos.x;
02297 _drag_delta.y = _cursor.pos.y;
02298
02299 BringWindowToFront(w);
02300 DeleteWindowById(WC_DROPDOWN_MENU, 0);
02301 }
02302
02307 static EventState HandleScrollbarScrolling()
02308 {
02309 Window *w;
02310 FOR_ALL_WINDOWS_FROM_BACK(w) {
02311 if (w->scrolling_scrollbar >= 0) {
02312
02313 if (!_left_button_down) {
02314 w->scrolling_scrollbar = -1;
02315 w->SetDirty();
02316 return ES_HANDLED;
02317 }
02318
02319 int i;
02320 NWidgetScrollbar *sb = w->GetWidget<NWidgetScrollbar>(w->scrolling_scrollbar);
02321 bool rtl = false;
02322
02323 if (sb->type == NWID_HSCROLLBAR) {
02324 i = _cursor.pos.x - _cursorpos_drag_start.x;
02325 rtl = _current_text_dir == TD_RTL;
02326 } else {
02327 i = _cursor.pos.y - _cursorpos_drag_start.y;
02328 }
02329
02330 if (sb->disp_flags & ND_SCROLLBAR_BTN) {
02331 if (_scroller_click_timeout == 1) {
02332 _scroller_click_timeout = 3;
02333 sb->UpdatePosition(rtl == HasBit(sb->disp_flags, NDB_SCROLLBAR_UP) ? 1 : -1);
02334 w->SetDirty();
02335 }
02336 return ES_HANDLED;
02337 }
02338
02339
02340 int pos = min(max(0, i + _scrollbar_start_pos) * sb->GetCount() / _scrollbar_size, max(0, sb->GetCount() - sb->GetCapacity()));
02341 if (rtl) pos = max(0, sb->GetCount() - sb->GetCapacity() - pos);
02342 if (pos != sb->GetPosition()) {
02343 sb->SetPosition(pos);
02344 w->SetDirty();
02345 }
02346 return ES_HANDLED;
02347 }
02348 }
02349
02350 return ES_NOT_HANDLED;
02351 }
02352
02357 static EventState HandleViewportScroll()
02358 {
02359 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02360
02361 if (!_scrolling_viewport) return ES_NOT_HANDLED;
02362
02363
02364
02365
02366 if (_last_scroll_window == NULL) _last_scroll_window = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
02367
02368 if (_last_scroll_window == NULL || !(_right_button_down || scrollwheel_scrolling || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down))) {
02369 _cursor.fix_at = false;
02370 _scrolling_viewport = false;
02371 _last_scroll_window = NULL;
02372 return ES_NOT_HANDLED;
02373 }
02374
02375 if (_last_scroll_window == FindWindowById(WC_MAIN_WINDOW, 0) && _last_scroll_window->viewport->follow_vehicle != INVALID_VEHICLE) {
02376
02377 const Vehicle *veh = Vehicle::Get(_last_scroll_window->viewport->follow_vehicle);
02378 ScrollMainWindowTo(veh->x_pos, veh->y_pos, veh->z_pos, true);
02379 return ES_NOT_HANDLED;
02380 }
02381
02382 Point delta;
02383 if (_settings_client.gui.reverse_scroll || (_settings_client.gui.left_mouse_btn_scrolling && _left_button_down)) {
02384 delta.x = -_cursor.delta.x;
02385 delta.y = -_cursor.delta.y;
02386 } else {
02387 delta.x = _cursor.delta.x;
02388 delta.y = _cursor.delta.y;
02389 }
02390
02391 if (scrollwheel_scrolling) {
02392
02393 delta.x = _cursor.h_wheel;
02394 delta.y = _cursor.v_wheel;
02395 _cursor.v_wheel = 0;
02396 _cursor.h_wheel = 0;
02397 }
02398
02399
02400 if (delta.x != 0 || delta.y != 0) _last_scroll_window->OnScroll(delta);
02401
02402 _cursor.delta.x = 0;
02403 _cursor.delta.y = 0;
02404 return ES_HANDLED;
02405 }
02406
02417 static bool MaybeBringWindowToFront(Window *w)
02418 {
02419 bool bring_to_front = false;
02420
02421 if (w->window_class == WC_MAIN_WINDOW ||
02422 IsVitalWindow(w) ||
02423 w->window_class == WC_TOOLTIPS ||
02424 w->window_class == WC_DROPDOWN_MENU) {
02425 return true;
02426 }
02427
02428
02429 int w_width = w->width;
02430 int w_height = w->height;
02431 if (w->IsShaded()) {
02432 w_width = w->unshaded_size.width;
02433 w_height = w->unshaded_size.height;
02434 }
02435
02436 Window *u;
02437 FOR_ALL_WINDOWS_FROM_BACK_FROM(u, w->z_front) {
02438
02439 if (u->parent == w && (u->window_desc->flags & WDF_MODAL)) {
02440 u->SetWhiteBorder();
02441 u->SetDirty();
02442 return false;
02443 }
02444
02445 if (u->window_class == WC_MAIN_WINDOW ||
02446 IsVitalWindow(u) ||
02447 u->window_class == WC_TOOLTIPS ||
02448 u->window_class == WC_DROPDOWN_MENU) {
02449 continue;
02450 }
02451
02452
02453 if (w->left + w_width <= u->left ||
02454 u->left + u->width <= w->left ||
02455 w->top + w_height <= u->top ||
02456 u->top + u->height <= w->top) {
02457 continue;
02458 }
02459
02460 bring_to_front = true;
02461 }
02462
02463 if (bring_to_front) BringWindowToFront(w);
02464 return true;
02465 }
02466
02475 EventState Window::HandleEditBoxKey(int wid, WChar key, uint16 keycode)
02476 {
02477 QueryString *query = this->GetQueryString(wid);
02478 if (query == NULL) return ES_NOT_HANDLED;
02479
02480 int action = QueryString::ACTION_NOTHING;
02481
02482 switch (query->text.HandleKeyPress(key, keycode)) {
02483 case HKPR_EDITING:
02484 this->SetWidgetDirty(wid);
02485 this->OnEditboxChanged(wid);
02486 break;
02487
02488 case HKPR_CURSOR:
02489 this->SetWidgetDirty(wid);
02490
02491 if (this->window_class == WC_OSK) this->InvalidateData();
02492 break;
02493
02494 case HKPR_CONFIRM:
02495 if (this->window_class == WC_OSK) {
02496 this->OnClick(Point(), WID_OSK_OK, 1);
02497 } else if (query->ok_button >= 0) {
02498 this->OnClick(Point(), query->ok_button, 1);
02499 } else {
02500 action = query->ok_button;
02501 }
02502 break;
02503
02504 case HKPR_CANCEL:
02505 if (this->window_class == WC_OSK) {
02506 this->OnClick(Point(), WID_OSK_CANCEL, 1);
02507 } else if (query->cancel_button >= 0) {
02508 this->OnClick(Point(), query->cancel_button, 1);
02509 } else {
02510 action = query->cancel_button;
02511 }
02512 break;
02513
02514 case HKPR_NOT_HANDLED:
02515 return ES_NOT_HANDLED;
02516
02517 default: break;
02518 }
02519
02520 switch (action) {
02521 case QueryString::ACTION_DESELECT:
02522 this->UnfocusFocusedWidget();
02523 break;
02524
02525 case QueryString::ACTION_CLEAR:
02526 if (query->text.bytes <= 1) {
02527
02528 this->UnfocusFocusedWidget();
02529 } else {
02530 query->text.DeleteAll();
02531 this->SetWidgetDirty(wid);
02532 this->OnEditboxChanged(wid);
02533 }
02534 break;
02535
02536 default:
02537 break;
02538 }
02539
02540 return ES_HANDLED;
02541 }
02542
02548 void HandleKeypress(uint keycode, WChar key)
02549 {
02550
02551
02552 assert(HasModalProgress() || IsLocalCompany());
02553
02554
02555
02556
02557
02558
02559
02560
02561 if (key >= 0xE000 && key <= 0xF8FF) key = 0;
02562
02563
02564
02565
02566 if (key == 0 && keycode == 0) return;
02567
02568
02569 if (EditBoxInGlobalFocus()) {
02570
02571 if (_focused_window->window_class == WC_CONSOLE) {
02572 if (_focused_window->OnKeyPress(key, keycode) == ES_HANDLED) return;
02573 } else {
02574 if (_focused_window->HandleEditBoxKey(_focused_window->nested_focus->index, key, keycode) == ES_HANDLED) return;
02575 }
02576 }
02577
02578
02579 Window *w;
02580 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02581 if (w->window_class == WC_MAIN_TOOLBAR) continue;
02582 if (w->window_desc->hotkeys != NULL) {
02583 int hotkey = w->window_desc->hotkeys->CheckMatch(keycode);
02584 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
02585 }
02586 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02587 }
02588
02589 w = FindWindowById(WC_MAIN_TOOLBAR, 0);
02590
02591 if (w != NULL) {
02592 if (w->window_desc->hotkeys != NULL) {
02593 int hotkey = w->window_desc->hotkeys->CheckMatch(keycode);
02594 if (hotkey >= 0 && w->OnHotkey(hotkey) == ES_HANDLED) return;
02595 }
02596 if (w->OnKeyPress(key, keycode) == ES_HANDLED) return;
02597 }
02598
02599 HandleGlobalHotkeys(key, keycode);
02600 }
02601
02605 void HandleCtrlChanged()
02606 {
02607
02608 Window *w;
02609 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02610 if (w->OnCTRLStateChange() == ES_HANDLED) return;
02611 }
02612 }
02613
02619 void Window::InsertTextString(int wid, const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
02620 {
02621 QueryString *query = this->GetQueryString(wid);
02622 if (query == NULL) return;
02623
02624 if (query->text.InsertString(str, marked, caret, insert_location, replacement_end) || marked) {
02625 this->SetWidgetDirty(wid);
02626 this->OnEditboxChanged(wid);
02627 }
02628 }
02629
02636 void HandleTextInput(const char *str, bool marked, const char *caret, const char *insert_location, const char *replacement_end)
02637 {
02638 if (!EditBoxInGlobalFocus()) return;
02639
02640 _focused_window->InsertTextString(_focused_window->window_class == WC_CONSOLE ? 0 : _focused_window->nested_focus->index, str, marked, caret, insert_location, replacement_end);
02641 }
02642
02649 static int _input_events_this_tick = 0;
02650
02655 static void HandleAutoscroll()
02656 {
02657 if (_game_mode == GM_MENU || HasModalProgress()) return;
02658 if (_settings_client.gui.auto_scrolling == VA_DISABLED) return;
02659 if (_settings_client.gui.auto_scrolling == VA_MAIN_VIEWPORT_FULLSCREEN && !_fullscreen) return;
02660
02661 int x = _cursor.pos.x;
02662 int y = _cursor.pos.y;
02663 Window *w = FindWindowFromPt(x, y);
02664 if (w == NULL || w->flags & WF_DISABLE_VP_SCROLL) return;
02665 if (_settings_client.gui.auto_scrolling != VA_EVERY_VIEWPORT && w->window_class != WC_MAIN_WINDOW) return;
02666
02667 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02668 if (vp == NULL) return;
02669
02670 x -= vp->left;
02671 y -= vp->top;
02672
02673
02674 #define scrollspeed 3
02675 if (x - 15 < 0) {
02676 w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
02677 } else if (15 - (vp->width - x) > 0) {
02678 w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
02679 }
02680 if (y - 15 < 0) {
02681 w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
02682 } else if (15 - (vp->height - y) > 0) {
02683 w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
02684 }
02685 #undef scrollspeed
02686 }
02687
02688 enum MouseClick {
02689 MC_NONE = 0,
02690 MC_LEFT,
02691 MC_RIGHT,
02692 MC_DOUBLE_LEFT,
02693 MC_HOVER,
02694
02695 MAX_OFFSET_DOUBLE_CLICK = 5,
02696 TIME_BETWEEN_DOUBLE_CLICK = 500,
02697 MAX_OFFSET_HOVER = 5,
02698 };
02699 extern EventState VpHandlePlaceSizingDrag();
02700
02701 static void ScrollMainViewport(int x, int y)
02702 {
02703 if (_game_mode != GM_MENU) {
02704 Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02705 assert(w);
02706
02707 w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
02708 w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
02709 }
02710 }
02711
02721 static const int8 scrollamt[16][2] = {
02722 { 0, 0},
02723 {-2, 0},
02724 { 0, -2},
02725 {-2, -1},
02726 { 2, 0},
02727 { 0, 0},
02728 { 2, -1},
02729 { 0, -2},
02730 { 0, 2},
02731 {-2, 1},
02732 { 0, 0},
02733 {-2, 0},
02734 { 2, 1},
02735 { 0, 2},
02736 { 2, 0},
02737 { 0, 0},
02738 };
02739
02740 static void HandleKeyScrolling()
02741 {
02742
02743
02744
02745
02746 if (_dirkeys && !EditBoxInGlobalFocus()) {
02747 int factor = _shift_pressed ? 50 : 10;
02748 ScrollMainViewport(scrollamt[_dirkeys][0] * factor, scrollamt[_dirkeys][1] * factor);
02749 }
02750 }
02751
02752 static void MouseLoop(MouseClick click, int mousewheel)
02753 {
02754
02755
02756 assert(HasModalProgress() || IsLocalCompany());
02757
02758 HandlePlacePresize();
02759 UpdateTileSelection();
02760
02761 if (VpHandlePlaceSizingDrag() == ES_HANDLED) return;
02762 if (HandleMouseDragDrop() == ES_HANDLED) return;
02763 if (HandleWindowDragging() == ES_HANDLED) return;
02764 if (HandleScrollbarScrolling() == ES_HANDLED) return;
02765 if (HandleViewportScroll() == ES_HANDLED) return;
02766
02767 HandleMouseOver();
02768
02769 bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
02770 if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return;
02771
02772 int x = _cursor.pos.x;
02773 int y = _cursor.pos.y;
02774 Window *w = FindWindowFromPt(x, y);
02775 if (w == NULL) return;
02776
02777 if (click != MC_HOVER && !MaybeBringWindowToFront(w)) return;
02778 ViewPort *vp = IsPtInWindowViewport(w, x, y);
02779
02780
02781 if (vp != NULL && (_game_mode == GM_MENU || HasModalProgress())) return;
02782
02783 if (mousewheel != 0) {
02784
02785 w->OnMouseWheel(mousewheel);
02786
02787
02788 if (vp == NULL) DispatchMouseWheelEvent(w, w->nested_root->GetWidgetFromPos(x - w->left, y - w->top), mousewheel);
02789 }
02790
02791 if (vp != NULL) {
02792 if (scrollwheel_scrolling) click = MC_RIGHT;
02793 switch (click) {
02794 case MC_DOUBLE_LEFT:
02795 case MC_LEFT:
02796 DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
02797 if (!HandleViewportClicked(vp, x, y) &&
02798 !(w->flags & WF_DISABLE_VP_SCROLL) &&
02799 _settings_client.gui.left_mouse_btn_scrolling) {
02800 _scrolling_viewport = true;
02801 _cursor.fix_at = false;
02802 }
02803 break;
02804
02805 case MC_RIGHT:
02806 if (!(w->flags & WF_DISABLE_VP_SCROLL)) {
02807 _scrolling_viewport = true;
02808 _cursor.fix_at = true;
02809
02810
02811 _cursor.h_wheel = 0;
02812 _cursor.v_wheel = 0;
02813 }
02814 break;
02815
02816 default:
02817 break;
02818 }
02819 } else {
02820 switch (click) {
02821 case MC_LEFT:
02822 case MC_DOUBLE_LEFT:
02823 DispatchLeftClickEvent(w, x - w->left, y - w->top, click == MC_DOUBLE_LEFT ? 2 : 1);
02824 break;
02825
02826 default:
02827 if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
02828
02829
02830
02831
02832 case MC_RIGHT: DispatchRightClickEvent(w, x - w->left, y - w->top); break;
02833
02834 case MC_HOVER: DispatchHoverEvent(w, x - w->left, y - w->top); break;
02835 }
02836 }
02837 }
02838
02842 void HandleMouseEvents()
02843 {
02844
02845
02846 assert(HasModalProgress() || IsLocalCompany());
02847
02848 static int double_click_time = 0;
02849 static Point double_click_pos = {0, 0};
02850
02851
02852 MouseClick click = MC_NONE;
02853 if (_left_button_down && !_left_button_clicked) {
02854 click = MC_LEFT;
02855 if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK &&
02856 double_click_pos.x != 0 && abs(_cursor.pos.x - double_click_pos.x) < MAX_OFFSET_DOUBLE_CLICK &&
02857 double_click_pos.y != 0 && abs(_cursor.pos.y - double_click_pos.y) < MAX_OFFSET_DOUBLE_CLICK) {
02858 click = MC_DOUBLE_LEFT;
02859 }
02860 double_click_time = _realtime_tick;
02861 double_click_pos = _cursor.pos;
02862 _left_button_clicked = true;
02863 _input_events_this_tick++;
02864 } else if (_right_button_clicked) {
02865 _right_button_clicked = false;
02866 click = MC_RIGHT;
02867 _input_events_this_tick++;
02868 }
02869
02870 int mousewheel = 0;
02871 if (_cursor.wheel) {
02872 mousewheel = _cursor.wheel;
02873 _cursor.wheel = 0;
02874 _input_events_this_tick++;
02875 }
02876
02877 static uint32 hover_time = 0;
02878 static Point hover_pos = {0, 0};
02879
02880 if (_settings_client.gui.hover_delay > 0) {
02881 if (!_cursor.in_window || click != MC_NONE || mousewheel != 0 || _left_button_down || _right_button_down ||
02882 hover_pos.x == 0 || abs(_cursor.pos.x - hover_pos.x) >= MAX_OFFSET_HOVER ||
02883 hover_pos.y == 0 || abs(_cursor.pos.y - hover_pos.y) >= MAX_OFFSET_HOVER) {
02884 hover_pos = _cursor.pos;
02885 hover_time = _realtime_tick;
02886 _mouse_hovering = false;
02887 } else {
02888 if (hover_time != 0 && _realtime_tick > hover_time + _settings_client.gui.hover_delay * 1000) {
02889 click = MC_HOVER;
02890 _input_events_this_tick++;
02891 _mouse_hovering = true;
02892 }
02893 }
02894 }
02895
02896
02897 if (_newgrf_debug_sprite_picker.mode == SPM_REDRAW && _newgrf_debug_sprite_picker.click_time != _realtime_tick) {
02898
02899 _newgrf_debug_sprite_picker.mode = SPM_NONE;
02900 InvalidateWindowData(WC_SPRITE_ALIGNER, 0, 1);
02901 }
02902
02903 if (click == MC_LEFT && _newgrf_debug_sprite_picker.mode == SPM_WAIT_CLICK) {
02904
02905 Blitter *blitter = BlitterFactory::GetCurrentBlitter();
02906 _newgrf_debug_sprite_picker.clicked_pixel = blitter->MoveTo(_screen.dst_ptr, _cursor.pos.x, _cursor.pos.y);
02907 _newgrf_debug_sprite_picker.click_time = _realtime_tick;
02908 _newgrf_debug_sprite_picker.sprites.Clear();
02909 _newgrf_debug_sprite_picker.mode = SPM_REDRAW;
02910 MarkWholeScreenDirty();
02911 } else {
02912 MouseLoop(click, mousewheel);
02913 }
02914
02915
02916
02917 _cursor.delta.x = 0;
02918 _cursor.delta.y = 0;
02919 }
02920
02924 static void CheckSoftLimit()
02925 {
02926 if (_settings_client.gui.window_soft_limit == 0) return;
02927
02928 for (;;) {
02929 uint deletable_count = 0;
02930 Window *w, *last_deletable = NULL;
02931 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02932 if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || (w->flags & WF_STICKY)) continue;
02933
02934 last_deletable = w;
02935 deletable_count++;
02936 }
02937
02938
02939 if (deletable_count <= _settings_client.gui.window_soft_limit) break;
02940
02941 assert(last_deletable != NULL);
02942 delete last_deletable;
02943 }
02944 }
02945
02949 void InputLoop()
02950 {
02951
02952
02953 assert(HasModalProgress() || IsLocalCompany());
02954
02955 CheckSoftLimit();
02956 HandleKeyScrolling();
02957
02958
02959 for (Window *v = _z_front_window; v != NULL; ) {
02960 Window *w = v;
02961 v = v->z_back;
02962
02963 if (w->window_class != WC_INVALID) continue;
02964
02965 RemoveWindowFromZOrdering(w);
02966 free(w);
02967 }
02968
02969 if (_scroller_click_timeout != 0) _scroller_click_timeout--;
02970 DecreaseWindowCounters();
02971
02972 if (_input_events_this_tick != 0) {
02973
02974 _input_events_this_tick = 0;
02975
02976 return;
02977 }
02978
02979
02980 HandleMouseEvents();
02981 HandleAutoscroll();
02982 }
02983
02987 void UpdateWindows()
02988 {
02989 Window *w;
02990
02991 static int highlight_timer = 1;
02992 if (--highlight_timer == 0) {
02993 highlight_timer = 15;
02994 _window_highlight_colour = !_window_highlight_colour;
02995 }
02996
02997 FOR_ALL_WINDOWS_FROM_FRONT(w) {
02998 w->ProcessScheduledInvalidations();
02999 w->ProcessHighlightedInvalidations();
03000 }
03001
03002 static int we4_timer = 0;
03003 int t = we4_timer + 1;
03004
03005 if (t >= 100) {
03006 FOR_ALL_WINDOWS_FROM_FRONT(w) {
03007 w->OnHundredthTick();
03008 }
03009 t = 0;
03010 }
03011 we4_timer = t;
03012
03013 FOR_ALL_WINDOWS_FROM_FRONT(w) {
03014 if ((w->flags & WF_WHITE_BORDER) && --w->white_border_timer == 0) {
03015 CLRBITS(w->flags, WF_WHITE_BORDER);
03016 w->SetDirty();
03017 }
03018 }
03019
03020 DrawDirtyBlocks();
03021
03022 FOR_ALL_WINDOWS_FROM_BACK(w) {
03023
03024 if (w->viewport != NULL && !w->IsShaded()) UpdateViewportPosition(w);
03025 }
03026 NetworkDrawChatMessage();
03027
03028 DrawMouseCursor();
03029 }
03030
03036 void SetWindowDirty(WindowClass cls, WindowNumber number)
03037 {
03038 const Window *w;
03039 FOR_ALL_WINDOWS_FROM_BACK(w) {
03040 if (w->window_class == cls && w->window_number == number) w->SetDirty();
03041 }
03042 }
03043
03050 void SetWindowWidgetDirty(WindowClass cls, WindowNumber number, byte widget_index)
03051 {
03052 const Window *w;
03053 FOR_ALL_WINDOWS_FROM_BACK(w) {
03054 if (w->window_class == cls && w->window_number == number) {
03055 w->SetWidgetDirty(widget_index);
03056 }
03057 }
03058 }
03059
03064 void SetWindowClassesDirty(WindowClass cls)
03065 {
03066 Window *w;
03067 FOR_ALL_WINDOWS_FROM_BACK(w) {
03068 if (w->window_class == cls) w->SetDirty();
03069 }
03070 }
03071
03077 void Window::InvalidateData(int data, bool gui_scope)
03078 {
03079 this->SetDirty();
03080 if (!gui_scope) {
03081
03082 *this->scheduled_invalidation_data.Append() = data;
03083 }
03084 this->OnInvalidateData(data, gui_scope);
03085 }
03086
03090 void Window::ProcessScheduledInvalidations()
03091 {
03092 for (int *data = this->scheduled_invalidation_data.Begin(); this->window_class != WC_INVALID && data != this->scheduled_invalidation_data.End(); data++) {
03093 this->OnInvalidateData(*data, true);
03094 }
03095 this->scheduled_invalidation_data.Clear();
03096 }
03097
03101 void Window::ProcessHighlightedInvalidations()
03102 {
03103 if ((this->flags & WF_HIGHLIGHTED) == 0) return;
03104
03105 for (uint i = 0; i < this->nested_array_size; i++) {
03106 if (this->IsWidgetHighlighted(i)) this->SetWidgetDirty(i);
03107 }
03108 }
03109
03136 void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
03137 {
03138 Window *w;
03139 FOR_ALL_WINDOWS_FROM_BACK(w) {
03140 if (w->window_class == cls && w->window_number == number) {
03141 w->InvalidateData(data, gui_scope);
03142 }
03143 }
03144 }
03145
03154 void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
03155 {
03156 Window *w;
03157
03158 FOR_ALL_WINDOWS_FROM_BACK(w) {
03159 if (w->window_class == cls) {
03160 w->InvalidateData(data, gui_scope);
03161 }
03162 }
03163 }
03164
03168 void CallWindowTickEvent()
03169 {
03170 Window *w;
03171 FOR_ALL_WINDOWS_FROM_FRONT(w) {
03172 w->OnTick();
03173 }
03174 }
03175
03182 void DeleteNonVitalWindows()
03183 {
03184 Window *w;
03185
03186 restart_search:
03187
03188
03189
03190 FOR_ALL_WINDOWS_FROM_BACK(w) {
03191 if (w->window_class != WC_MAIN_WINDOW &&
03192 w->window_class != WC_SELECT_GAME &&
03193 w->window_class != WC_MAIN_TOOLBAR &&
03194 w->window_class != WC_STATUS_BAR &&
03195 w->window_class != WC_TOOLTIPS &&
03196 (w->flags & WF_STICKY) == 0) {
03197
03198 delete w;
03199 goto restart_search;
03200 }
03201 }
03202 }
03203
03211 void DeleteAllNonVitalWindows()
03212 {
03213 Window *w;
03214
03215
03216 DeleteNonVitalWindows();
03217
03218 restart_search:
03219
03220
03221
03222 FOR_ALL_WINDOWS_FROM_BACK(w) {
03223 if (w->flags & WF_STICKY) {
03224 delete w;
03225 goto restart_search;
03226 }
03227 }
03228 }
03229
03234 void DeleteConstructionWindows()
03235 {
03236 Window *w;
03237
03238 restart_search:
03239
03240
03241
03242 FOR_ALL_WINDOWS_FROM_BACK(w) {
03243 if (w->window_desc->flags & WDF_CONSTRUCTION) {
03244 delete w;
03245 goto restart_search;
03246 }
03247 }
03248
03249 FOR_ALL_WINDOWS_FROM_BACK(w) w->SetDirty();
03250 }
03251
03253 void HideVitalWindows()
03254 {
03255 DeleteWindowById(WC_MAIN_TOOLBAR, 0);
03256 DeleteWindowById(WC_STATUS_BAR, 0);
03257 }
03258
03260 void ReInitAllWindows()
03261 {
03262 NWidgetLeaf::InvalidateDimensionCache();
03263 NWidgetScrollbar::InvalidateDimensionCache();
03264
03265 Window *w;
03266 FOR_ALL_WINDOWS_FROM_BACK(w) {
03267 w->ReInit();
03268 }
03269 #ifdef ENABLE_NETWORK
03270 void NetworkReInitChatBoxSize();
03271 NetworkReInitChatBoxSize();
03272 #endif
03273
03274
03275 RelocateAllWindows(_cur_resolution.width, _cur_resolution.height);
03276 MarkWholeScreenDirty();
03277 }
03278
03286 static int PositionWindow(Window *w, WindowClass clss, int setting)
03287 {
03288 if (w == NULL || w->window_class != clss) {
03289 w = FindWindowById(clss, 0);
03290 }
03291 if (w == NULL) return 0;
03292
03293 int old_left = w->left;
03294 switch (setting) {
03295 case 1: w->left = (_screen.width - w->width) / 2; break;
03296 case 2: w->left = _screen.width - w->width; break;
03297 default: w->left = 0; break;
03298 }
03299 if (w->viewport != NULL) w->viewport->left += w->left - old_left;
03300 SetDirtyBlocks(0, w->top, _screen.width, w->top + w->height);
03301 return w->left;
03302 }
03303
03309 int PositionMainToolbar(Window *w)
03310 {
03311 DEBUG(misc, 5, "Repositioning Main Toolbar...");
03312 return PositionWindow(w, WC_MAIN_TOOLBAR, _settings_client.gui.toolbar_pos);
03313 }
03314
03320 int PositionStatusbar(Window *w)
03321 {
03322 DEBUG(misc, 5, "Repositioning statusbar...");
03323 return PositionWindow(w, WC_STATUS_BAR, _settings_client.gui.statusbar_pos);
03324 }
03325
03331 int PositionNewsMessage(Window *w)
03332 {
03333 DEBUG(misc, 5, "Repositioning news message...");
03334 return PositionWindow(w, WC_NEWS_WINDOW, _settings_client.gui.statusbar_pos);
03335 }
03336
03342 int PositionNetworkChatWindow(Window *w)
03343 {
03344 DEBUG(misc, 5, "Repositioning network chat window...");
03345 return PositionWindow(w, WC_SEND_NETWORK_MSG, _settings_client.gui.statusbar_pos);
03346 }
03347
03348
03354 void ChangeVehicleViewports(VehicleID from_index, VehicleID to_index)
03355 {
03356 Window *w;
03357 FOR_ALL_WINDOWS_FROM_BACK(w) {
03358 if (w->viewport != NULL && w->viewport->follow_vehicle == from_index) {
03359 w->viewport->follow_vehicle = to_index;
03360 w->SetDirty();
03361 }
03362 }
03363 }
03364
03365
03371 void RelocateAllWindows(int neww, int newh)
03372 {
03373 Window *w;
03374
03375 FOR_ALL_WINDOWS_FROM_BACK(w) {
03376 int left, top;
03377
03378
03379 switch (w->window_class) {
03380 case WC_MAIN_WINDOW:
03381 case WC_BOOTSTRAP:
03382 ResizeWindow(w, neww, newh);
03383 continue;
03384
03385 case WC_MAIN_TOOLBAR:
03386 ResizeWindow(w, min(neww, w->window_desc->default_width) - w->width, 0, false);
03387
03388 top = w->top;
03389 left = PositionMainToolbar(w);
03390 break;
03391
03392 case WC_NEWS_WINDOW:
03393 top = newh - w->height;
03394 left = PositionNewsMessage(w);
03395 break;
03396
03397 case WC_STATUS_BAR:
03398 ResizeWindow(w, min(neww, w->window_desc->default_width) - w->width, 0, false);
03399
03400 top = newh - w->height;
03401 left = PositionStatusbar(w);
03402 break;
03403
03404 case WC_SEND_NETWORK_MSG:
03405 ResizeWindow(w, Clamp(neww, 320, 640) - w->width, 0, false);
03406 top = newh - w->height - FindWindowById(WC_STATUS_BAR, 0)->height;
03407 left = PositionNetworkChatWindow(w);
03408 break;
03409
03410 case WC_CONSOLE:
03411 IConsoleResize(w);
03412 continue;
03413
03414 default: {
03415 if (w->flags & WF_CENTERED) {
03416 top = (newh - w->height) >> 1;
03417 left = (neww - w->width) >> 1;
03418 break;
03419 }
03420
03421 left = w->left;
03422 if (left + (w->width >> 1) >= neww) left = neww - w->width;
03423 if (left < 0) left = 0;
03424
03425 top = w->top;
03426 if (top + (w->height >> 1) >= newh) top = newh - w->height;
03427 break;
03428 }
03429 }
03430
03431 EnsureVisibleCaption(w, left, top);
03432 }
03433 }
03434
03440 PickerWindowBase::~PickerWindowBase()
03441 {
03442 this->window_class = WC_INVALID;
03443 ResetObjectToPlace();
03444 }