diff -r ff900a23e102 -r 68a692eacf22 src/window.cpp --- a/src/window.cpp Fri Apr 25 02:15:34 2008 +0000 +++ b/src/window.cpp Mon May 26 20:45:25 2008 +0000 @@ -1,6 +1,6 @@ /* $Id$ */ -/** @file window.cpp windowing system, widgets and events */ +/** @file window.cpp Windowing system, widgets and events */ #include "stdafx.h" #include @@ -20,6 +20,8 @@ #include "vehicle_base.h" #include "settings_type.h" #include "cheat_func.h" +#include "window_func.h" +#include "tilehighlight_func.h" #include "table/sprites.h" @@ -42,11 +44,173 @@ bool _scrolling_scrollbar; bool _scrolling_viewport; -bool _popup_menu_active; byte _special_mouse_mode; +/** + * Call the window event handler for handling event \a e. + * This is a temporary helper functions that will be removed + * once all windows that still rely on WindowEvent and + * WindowEventCodes have been rewritten to use the 'OnXXX' + * event handlers. + * @param e Window event to handle + */ +void Window::HandleWindowEvent(WindowEvent *e) +{ + if (wndproc != NULL) wndproc(this, e); +} + +void Window::OnPaint() +{ + WindowEvent e; + e.event = WE_PAINT; + this->HandleWindowEvent(&e); +} + +bool Window::OnKeyPress(uint16 key, uint16 keycode) +{ + WindowEvent e; + e.event = WE_KEYPRESS; + e.we.keypress.key = key; + e.we.keypress.keycode = keycode; + e.we.keypress.cont = true; + this->HandleWindowEvent(&e); + + return e.we.keypress.cont; +} + +bool Window::OnCTRLStateChange() +{ + WindowEvent e; + e.event = WE_CTRL_CHANGED; + e.we.ctrl.cont = true; + this->HandleWindowEvent(&e); + + return e.we.ctrl.cont; +} + +void Window::OnClick(Point pt, int widget) +{ + WindowEvent e; + e.event = WE_CLICK; + e.we.click.pt = pt; + e.we.click.widget = widget; + this->HandleWindowEvent(&e); +} + +void Window::OnMouseLoop() +{ + WindowEvent e; + e.event = WE_MOUSELOOP; + this->HandleWindowEvent(&e); +} + +void Window::OnTick() +{ + WindowEvent e; + e.event = WE_TICK; + this->HandleWindowEvent(&e); +} + +void Window::OnHundredthTick() +{ + WindowEvent e; + e.event = WE_100_TICKS; + this->HandleWindowEvent(&e); +} + +void Window::OnTimeout() +{ + WindowEvent e; + e.event = WE_TIMEOUT; + this->HandleWindowEvent(&e); +} + +void Window::OnResize(Point new_size, Point delta) +{ + WindowEvent e; + e.event = WE_RESIZE; + e.we.sizing.size = new_size; + e.we.sizing.diff = delta; + this->HandleWindowEvent(&e); +} + +void Window::OnDropdownSelect(int widget, int index) +{ + WindowEvent e; + e.event = WE_DROPDOWN_SELECT; + e.we.dropdown.button = widget; + e.we.dropdown.index = index; + this->HandleWindowEvent(&e); +} + +void Window::OnQueryTextFinished(char *str) +{ + WindowEvent e; + e.event = WE_ON_EDIT_TEXT; + e.we.edittext.str = str; + this->HandleWindowEvent(&e); +} + +void Window::OnInvalidateData(int data) +{ + WindowEvent e; + e.event = WE_INVALIDATE_DATA; + e.we.invalidate.data = data; + this->HandleWindowEvent(&e); +} + +void Window::OnPlaceObject(Point pt, TileIndex tile) +{ + WindowEvent e; + e.event = WE_PLACE_OBJ; + e.we.place.pt = pt; + e.we.place.tile = tile; + this->HandleWindowEvent(&e); +} + +void Window::OnPlaceObjectAbort() +{ + WindowEvent e; + e.event = WE_ABORT_PLACE_OBJ; + this->HandleWindowEvent(&e); +} + + +void Window::OnPlaceDrag(ViewportPlaceMethod select_method, byte select_proc, Point pt) +{ + WindowEvent e; + e.event = WE_PLACE_DRAG; + e.we.place.select_method = select_method; + e.we.place.select_proc = select_proc; + e.we.place.pt = pt; + this->HandleWindowEvent(&e); +} + +void Window::OnPlaceMouseUp(ViewportPlaceMethod select_method, byte select_proc, Point pt, TileIndex start_tile, TileIndex end_tile) +{ + WindowEvent e; + e.event = WE_PLACE_MOUSEUP; + e.we.place.select_method = select_method; + e.we.place.select_proc = select_proc; + e.we.place.pt = pt; + e.we.place.tile = end_tile; + e.we.place.starttile = start_tile; + this->HandleWindowEvent(&e); +} + +void Window::OnPlacePresize(Point pt, TileIndex tile) +{ + WindowEvent e; + e.event = WE_PLACE_PRESIZE; + e.we.place.pt = pt; + e.we.place.tile = tile; + this->HandleWindowEvent(&e); +} + + + void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...) { va_list wdg_list; @@ -91,9 +255,7 @@ void Window::RaiseButtons() { - uint i; - - for (i = 0; i < this->widget_count; i++) { + for (uint i = 0; i < this->widget_count; i++) { if (this->IsWidgetLowered(i)) { this->RaiseWidget(i); this->InvalidateWidget(i); @@ -130,21 +292,15 @@ */ static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click) { - WindowEvent e; - const Widget *wi; - - e.we.click.pt.x = x; - e.we.click.pt.y = y; - e.event = double_click ? WE_DOUBLE_CLICK : WE_CLICK; - + int widget = 0; if (w->desc_flags & WDF_DEF_WIDGET) { - e.we.click.widget = GetWidgetFromPos(w, x, y); - if (e.we.click.widget < 0) return; // exit if clicked outside of widgets + widget = GetWidgetFromPos(w, x, y); + if (widget < 0) return; // exit if clicked outside of widgets /* don't allow any interaction if the button has been disabled */ - if (w->IsWidgetDisabled(e.we.click.widget)) return; + if (w->IsWidgetDisabled(widget)) return; - wi = &w->widget[e.we.click.widget]; + const Widget *wi = &w->widget[widget]; if (wi->type & WWB_MASK) { /* special widget handling for buttons*/ @@ -152,20 +308,20 @@ case WWT_PANEL | WWB_PUSHBUTTON: /* WWT_PUSHBTN */ case WWT_IMGBTN | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */ case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */ - w->HandleButtonClick(e.we.click.widget); + w->HandleButtonClick(widget); break; } } else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) { - ScrollbarClickHandler(w, wi, e.we.click.pt.x, e.we.click.pt.y); + ScrollbarClickHandler(w, wi, x, y); } if (w->desc_flags & WDF_STD_BTN) { - if (e.we.click.widget == 0) { /* 'X' */ - DeleteWindow(w); + if (widget == 0) { /* 'X' */ + delete w; return; } - if (e.we.click.widget == 1) { /* 'Title bar' */ + if (widget == 1) { /* 'Title bar' */ StartWindowDrag(w); return; } @@ -173,18 +329,24 @@ if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) { StartWindowSizing(w); - w->InvalidateWidget(e.we.click.widget); + w->InvalidateWidget(widget); return; } if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) { w->flags4 ^= WF_STICKY; - w->InvalidateWidget(e.we.click.widget); + w->InvalidateWidget(widget); return; } } - w->wndproc(w, &e); + Point pt = { x, y }; + + if (double_click) { + w->OnDoubleClick(pt, widget); + } else { + w->OnClick(pt, widget); + } } /** @@ -195,24 +357,21 @@ */ static void DispatchRightClickEvent(Window *w, int x, int y) { - WindowEvent e; + int widget = 0; /* default tooltips handler? */ if (w->desc_flags & WDF_STD_TOOLTIPS) { - e.we.click.widget = GetWidgetFromPos(w, x, y); - if (e.we.click.widget < 0) - return; // exit if clicked outside of widgets + widget = GetWidgetFromPos(w, x, y); + if (widget < 0) return; // exit if clicked outside of widgets - if (w->widget[e.we.click.widget].tooltips != 0) { - GuiShowTooltips(w->widget[e.we.click.widget].tooltips); + if (w->widget[widget].tooltips != 0) { + GuiShowTooltips(w->widget[widget].tooltips); return; } } - e.event = WE_RCLICK; - e.we.click.pt.x = x; - e.we.click.pt.y = y; - w->wndproc(w, &e); + Point pt = { x, y }; + w->OnRightClick(pt, widget); } /** @@ -224,18 +383,16 @@ */ static void DispatchMouseWheelEvent(Window *w, int widget, int wheel) { - const Widget *wi1, *wi2; - Scrollbar *sb; - if (widget < 0) return; - wi1 = &w->widget[widget]; - wi2 = &w->widget[widget + 1]; + const Widget *wi1 = &w->widget[widget]; + const Widget *wi2 = &w->widget[widget + 1]; /* The listbox can only scroll if scrolling was done on the scrollbar itself, * or on the listbox (and the next item is (must be) the scrollbar) * XXX - should be rewritten as a widget-dependent scroller but that's * not happening until someone rewrites the whole widget-code */ + Scrollbar *sb; if ((sb = &w->vscroll, wi1->type == WWT_SCROLLBAR) || (sb = &w->vscroll2, wi1->type == WWT_SCROLL2BAR) || (sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->vscroll, wi2->type == WWT_SCROLLBAR) ) { @@ -243,7 +400,7 @@ int pos = Clamp(sb->pos + wheel, 0, sb->count - sb->cap); if (pos != sb->pos) { sb->pos = pos; - SetWindowDirty(w); + w->SetDirty(); } } } @@ -312,7 +469,7 @@ dp->pitch = _screen.pitch; dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top); dp->zoom = ZOOM_LVL_NORMAL; - CallWindowEventNP(*wz, WE_PAINT); + (*wz)->OnPaint(); } /** @@ -342,17 +499,13 @@ } /** - * Dispatch an event to a possibly non-existing window. - * If the window pointer w is \c NULL, the event is not dispatched - * @param w Window to dispatch the event to, may be \c NULL - * @param event Event to dispatch + * Mark entire window as dirty (in need of re-paint) + * @param w Window to redraw + * @ingroup dirty */ -void CallWindowEventNP(Window *w, int event) +void Window::SetDirty() const { - WindowEvent e; - - e.event = event; - w->wndproc(w, &e); + SetDirtyBlocks(this->left, this->top, this->left + this->width, this->top + this->height); } /** @@ -362,8 +515,7 @@ */ void SetWindowDirty(const Window *w) { - if (w == NULL) return; - SetDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height); + if (w != NULL) w->SetDirty(); } /** Find the Window whose parent pointer points to this window @@ -401,42 +553,41 @@ /** * Remove window and all its child windows from the window stack. - * @param w Window to delete */ -void DeleteWindow(Window *w) +Window::~Window() { - if (w == NULL) return; - - /* Delete any children a window might have in a head-recursive manner */ - Window *v = FindChildWindow(w); - if (v != NULL) DeleteWindow(v); - if (_thd.place_mode != VHM_NONE && - _thd.window_class == w->window_class && - _thd.window_number == w->window_number) { + _thd.window_class == this->window_class && + _thd.window_number == this->window_number) { ResetObjectToPlace(); } - CallWindowEventNP(w, WE_DESTROY); - if (w->viewport != NULL) DeleteWindowViewport(w); - - SetWindowDirty(w); - free(w->widget); - w->widget = NULL; - w->widget_count = 0; - w->parent = NULL; - /* Prevent Mouseover() from resetting mouse-over coordinates on a non-existing window */ - if (_mouseover_last_w == w) _mouseover_last_w = NULL; + if (_mouseover_last_w == this) _mouseover_last_w = NULL; /* Find the window in the z-array, and effectively remove it - * by moving all windows after it one to the left */ - Window **wz = FindWindowZPosition(w); + * by moving all windows after it one to the left. This must be + * done before removing the child so we cannot cause recursion + * between the deletion of the parent and the child. */ + Window **wz = FindWindowZPosition(this); if (wz == NULL) return; memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz); _last_z_window--; - delete w; + /* Delete all children a window might have in a head-recursive manner */ + Window *child = FindChildWindow(this); + while (child != NULL) { + delete child; + child = FindChildWindow(this); + } + + WindowEvent e; + e.event = WE_DESTROY; + this->HandleWindowEvent(&e); + if (this->viewport != NULL) DeleteWindowViewport(this); + + this->SetDirty(); + free(this->widget); } /** @@ -464,7 +615,7 @@ */ void DeleteWindowById(WindowClass cls, WindowNumber number) { - DeleteWindow(FindWindowById(cls, number)); + delete FindWindowById(cls, number); } /** @@ -482,7 +633,7 @@ FOR_ALL_WINDOWS(wz) { Window *w = *wz; if (w->window_class == cls) { - DeleteWindow(w); + delete w; goto restart_search; } } @@ -503,7 +654,7 @@ FOR_ALL_WINDOWS(wz) { Window *w = *wz; if (w->caption_color == id) { - DeleteWindow(w); + delete w; goto restart_search; } } @@ -524,18 +675,24 @@ FOR_ALL_WINDOWS(wz) { Window *w = *wz; - if (w->caption_color != old_player) continue; - if (w->window_class == WC_PLAYER_COLOR) continue; - if (w->window_class == WC_FINANCES) continue; - if (w->window_class == WC_STATION_LIST) continue; - if (w->window_class == WC_TRAINS_LIST) continue; - if (w->window_class == WC_ROADVEH_LIST) continue; - if (w->window_class == WC_SHIPS_LIST) continue; - if (w->window_class == WC_AIRCRAFT_LIST) continue; - if (w->window_class == WC_BUY_COMPANY) continue; - if (w->window_class == WC_COMPANY) continue; + if (w->caption_color != old_player) continue; - w->caption_color = new_player; + switch (w->window_class) { + case WC_PLAYER_COLOR: + case WC_FINANCES: + case WC_STATION_LIST: + case WC_TRAINS_LIST: + case WC_ROADVEH_LIST: + case WC_SHIPS_LIST: + case WC_AIRCRAFT_LIST: + case WC_BUY_COMPANY: + case WC_COMPANY: + continue; + + default: + w->caption_color = new_player; + break; + } } } @@ -553,7 +710,7 @@ if (w != NULL) { w->flags4 |= WF_WHITE_BORDER_MASK; BringWindowToFront(w); - SetWindowDirty(w); + w->SetDirty(); } return w; @@ -561,8 +718,16 @@ static inline bool IsVitalWindow(const Window *w) { - WindowClass wc = w->window_class; - return (wc == WC_MAIN_TOOLBAR || wc == WC_STATUS_BAR || wc == WC_NEWS_WINDOW || wc == WC_SEND_NETWORK_MSG); + switch (w->window_class) { + case WC_MAIN_TOOLBAR: + case WC_STATUS_BAR: + case WC_NEWS_WINDOW: + case WC_SEND_NETWORK_MSG: + return true; + + default: + return false; + } } /** On clicking on a window, make it the frontmost window of all. However @@ -575,7 +740,6 @@ */ static void BringWindowToFront(const Window *w) { - Window *tempz; Window **wz = FindWindowZPosition(w); Window **vz = _last_z_window; @@ -587,11 +751,11 @@ if (wz == vz) return; // window is already in the right position assert(wz < vz); - tempz = *wz; + Window *tempz = *wz; memmove(wz, wz + 1, (byte*)vz - (byte*)wz); *vz = tempz; - SetWindowDirty(w); + w->SetDirty(); } /** We have run out of windows, so find a suitable candidate for replacement. @@ -632,25 +796,24 @@ NOT_REACHED(); } -bool IsWindowOfPrototype(const Window *w, const Widget *widget) +/** + * Assign widgets to a new window by initialising its widget pointers, and by + * copying the widget array \a widget to \c w->widget to allow for resizable + * windows. + * @param w Window on which to attach the widget array + * @param widget pointer of widget array to fill the window with + * + * @post \c w->widget points to allocated memory and contains the copied widget array except for the terminating widget, + * \c w->widget_count contains number of widgets in the allocated memory. + */ +static void AssignWidgetToWindow(Window *w, const Widget *widget) { - return (w->original_widget == widget); -} - -/** Copies 'widget' to 'w->widget' to allow for resizable windows - * @param w Window on which to attach the widget array - * @param widget pointer of widget array to fill the window with */ -void AssignWidgetToWindow(Window *w, const Widget *widget) -{ - w->original_widget = widget; - if (widget != NULL) { uint index = 1; - const Widget *wi; - for (wi = widget; wi->type != WWT_LAST; wi++) index++; + for (const Widget *wi = widget; wi->type != WWT_LAST; wi++) index++; - w->widget = ReallocT(w->widget, index); + w->widget = MallocT(index); memcpy(w->widget, widget, sizeof(*w->widget) * index); w->widget_count = index - 1; } else { @@ -659,89 +822,87 @@ } } -/** Open a new window. - * This function is called from AllocateWindow() or AllocateWindowDesc() +/** + * Initializes a new Window. + * This function is called the constructors. * See descriptions for those functions for usage - * See AllocateWindow() for description of arguments. * Only addition here is window_number, which is the window_number being assigned to the new window * @param x offset in pixels from the left of the screen * @param y offset in pixels from the top of the screen * @param min_width minimum width in pixels of the window * @param min_height minimum height in pixels of the window - * @param def_width default width in pixels of the window - * @param def_height default height in pixels of the window * @param *proc see WindowProc function to call when any messages/updates happen to the window * @param cls see WindowClass class of the window, used for identification and grouping * @param *widget see Widget pointer to the window layout and various elements * @param window_number number being assigned to the new window - * @param data the data to be given during the WE_CREATE message - * @return Window pointer of the newly created window */ -static Window *LocalAllocateWindow(int x, int y, int min_width, int min_height, int def_width, int def_height, - WindowProc *proc, WindowClass cls, const Widget *widget, int window_number, void *data) + * @return Window pointer of the newly created window + */ +void Window::Initialize(int x, int y, int min_width, int min_height, + WindowProc *proc, WindowClass cls, const Widget *widget, int window_number) { - Window *w; - /* We have run out of windows, close one and use that as the place for our new one */ if (_last_z_window == endof(_z_windows)) { - w = FindDeletableWindow(); + Window *w = FindDeletableWindow(); if (w == NULL) w = ForceFindDeletableWindow(); - DeleteWindow(w); + delete w; } - w = new Window; - /* Set up window properties */ - w->window_class = cls; - w->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border - w->caption_color = 0xFF; - w->left = x; - w->top = y; - w->width = min_width; - w->height = min_height; - w->wndproc = proc; - AssignWidgetToWindow(w, widget); - w->resize.width = min_width; - w->resize.height = min_height; - w->resize.step_width = 1; - w->resize.step_height = 1; - w->window_number = window_number; - - { - Window **wz = _last_z_window; + this->window_class = cls; + this->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border + this->caption_color = 0xFF; + this->left = x; + this->top = y; + this->width = min_width; + this->height = min_height; + this->wndproc = proc; + AssignWidgetToWindow(this, widget); + this->resize.width = min_width; + this->resize.height = min_height; + this->resize.step_width = 1; + this->resize.step_height = 1; + this->window_number = window_number; - /* Hacky way of specifying always-on-top windows. These windows are - * always above other windows because they are moved below them. - * status-bar is above news-window because it has been created earlier. - * Also, as the chat-window is excluded from this, it will always be - * the last window, thus always on top. - * XXX - Yes, ugly, probably needs something like w->always_on_top flag - * to implement correctly, but even then you need some kind of distinction - * between on-top of chat/news and status windows, because these conflict */ - if (wz != _z_windows && w->window_class != WC_SEND_NETWORK_MSG && w->window_class != WC_HIGHSCORE && w->window_class != WC_ENDSCREEN) { - if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) wz--; - if (FindWindowById(WC_STATUS_BAR, 0) != NULL) wz--; - if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) wz--; - if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--; + /* Hacky way of specifying always-on-top windows. These windows are + * always above other windows because they are moved below them. + * status-bar is above news-window because it has been created earlier. + * Also, as the chat-window is excluded from this, it will always be + * the last window, thus always on top. + * XXX - Yes, ugly, probably needs something like w->always_on_top flag + * to implement correctly, but even then you need some kind of distinction + * between on-top of chat/news and status windows, because these conflict */ + Window **wz = _last_z_window; + if (wz != _z_windows && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) { + if (FindWindowById(WC_MAIN_TOOLBAR, 0) != NULL) wz--; + if (FindWindowById(WC_STATUS_BAR, 0) != NULL) wz--; + if (FindWindowById(WC_NEWS_WINDOW, 0) != NULL) wz--; + if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--; - assert(wz >= _z_windows); - if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz); - } + assert(wz >= _z_windows); + if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz); + } - *wz = w; - _last_z_window++; - } + *wz = this; + _last_z_window++; WindowEvent e; e.event = WE_CREATE; - e.we.create.data = data; - w->wndproc(w, &e); + this->HandleWindowEvent(&e); +} +/** + * Find a nice spot for this window and resize it towards the default size. + * @param def_width default width in pixels of the window + * @param def_height default height in pixels of the window + */ +void Window::FindWindowPlacementAndResize(int def_width, int def_height) +{ /* Try to make windows smaller when our window is too small. * w->(width|height) is normally the same as min_(width|height), * but this way the GUIs can be made a little more dynamic; * one can use the same spec for multiple windows and those * can then determine the real minimum size of the window. */ - if (w->width != def_width || w->height != def_height) { + if (this->width != def_width || this->height != def_height) { /* Think about the overlapping toolbars when determining the minimum window size */ int free_height = _screen.height; const Window *wt = FindWindowById(WC_STATUS_BAR, 0); @@ -749,45 +910,48 @@ wt = FindWindowById(WC_MAIN_TOOLBAR, 0); if (wt != NULL) free_height -= wt->height; - int enlarge_x = max(min(def_width - w->width, _screen.width - w->width), 0); - int enlarge_y = max(min(def_height - w->height, free_height - w->height), 0); + int enlarge_x = max(min(def_width - this->width, _screen.width - this->width), 0); + int enlarge_y = max(min(def_height - this->height, free_height - this->height), 0); /* X and Y has to go by step.. calculate it. * The cast to int is necessary else x/y are implicitly casted to * unsigned int, which won't work. */ - if (w->resize.step_width > 1) enlarge_x -= enlarge_x % (int)w->resize.step_width; - if (w->resize.step_height > 1) enlarge_y -= enlarge_y % (int)w->resize.step_height; - - ResizeWindow(w, enlarge_x, enlarge_y); + if (this->resize.step_width > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width; + if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height; - WindowEvent e; - e.event = WE_RESIZE; - e.we.sizing.size.x = w->width; - e.we.sizing.size.y = w->height; - e.we.sizing.diff.x = enlarge_x; - e.we.sizing.diff.y = enlarge_y; - w->wndproc(w, &e); + ResizeWindow(this, enlarge_x, enlarge_y); + + Point size; + Point diff; + size.x = this->width; + size.y = this->height; + diff.x = enlarge_x; + diff.y = enlarge_y; + this->OnResize(size, diff); } - int nx = w->left; - int ny = w->top; + int nx = this->left; + int ny = this->top; - if (nx + w->width > _screen.width) nx -= (nx + w->width - _screen.width); + if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width); const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0); - ny = max(ny, (wt == NULL || w == wt || y == 0) ? 0 : wt->height); + ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height); nx = max(nx, 0); - if (w->viewport != NULL) { - w->viewport->left += nx - w->left; - w->viewport->top += ny - w->top; + if (this->viewport != NULL) { + this->viewport->left += nx - this->left; + this->viewport->top += ny - this->top; } - w->left = nx; - w->top = ny; + this->left = nx; + this->top = ny; - SetWindowDirty(w); + this->SetDirty(); +} - return w; +void Window::FindWindowPlacementAndResize(const WindowDesc *desc) +{ + this->FindWindowPlacementAndResize(desc->default_width, desc->default_height); } /** @@ -804,10 +968,11 @@ * @param *widget see Widget pointer to the window layout and various elements * @return Window pointer of the newly created window */ -Window *AllocateWindow(int x, int y, int width, int height, - WindowProc *proc, WindowClass cls, const Widget *widget, void *data) +Window::Window(int x, int y, int width, int height, WindowProc *proc, WindowClass cls, const Widget *widget) { - return LocalAllocateWindow(x, y, width, height, width, height, proc, cls, widget, 0, data); + this->Initialize(x, y, width, height, proc, cls, widget, 0); + + if (proc != NULL) this->FindWindowPlacementAndResize(width, height); } @@ -818,8 +983,7 @@ int right = width + left; int bottom = height + top; - if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height) - return false; + if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height) return false; /* Make sure it is not obscured by any window. */ FOR_ALL_WINDOWS(wz) { @@ -916,25 +1080,25 @@ } /** - * Set the x and y coordinates of a new window. + * Compute the position of the top-left corner of a new window that is opened. + * + * By default position a child window at an offset of 10/10 of its parent. + * With the exception of WC_BUILD_TOOLBAR (build railway/roads/ship docks/airports) + * and WC_SCEN_LAND_GEN (landscaping). Whose child window has an offset of 0/36 of + * its parent. So it's exactly under the parent toolbar and no buttons will be covered. + * However if it falls too extremely outside window positions, reposition + * it to an automatic place. * * @param *desc The pointer to the WindowDesc to be created * @param window_number the window number of the new window - * @param data arbitrary data that is send with the WE_CREATE message * - * @return see Window pointer of the newly created window + * @return Coordinate of the top-left corner of the new window */ -static Window *LocalAllocateWindowDesc(const WindowDesc *desc, int window_number, void *data) +static Point LocalGetWindowPlacement(const WindowDesc *desc, int window_number) { Point pt; Window *w; - /* By default position a child window at an offset of 10/10 of its parent. - * With the exception of WC_BUILD_TOOLBAR (build railway/roads/ship docks/airports) - * and WC_SCEN_LAND_GEN (landscaping). Whose child window has an offset of 0/36 of - * its parent. So it's exactly under the parent toolbar and no buttons will be covered. - * However if it falls too extremely outside window positions, reposition - * it to an automatic place */ if (desc->parent_cls != 0 /* WC_MAIN_WINDOW */ && (w = FindWindowById(desc->parent_cls, window_number)) != NULL && w->left < _screen.width - 20 && w->left > -60 && w->top < _screen.height - 20) { @@ -946,34 +1110,39 @@ pt.y = w->top + ((desc->parent_cls == WC_BUILD_TOOLBAR || desc->parent_cls == WC_SCEN_LAND_GEN) ? 36 : 10); } else { switch (desc->left) { - case WDP_ALIGN_TBR: { /* Align the right side with the top toolbar */ + case WDP_ALIGN_TBR: // Align the right side with the top toolbar w = FindWindowById(WC_MAIN_TOOLBAR, 0); pt.x = (w->left + w->width) - desc->default_width; - } break; - case WDP_ALIGN_TBL: /* Align the left side with the top toolbar */ + break; + + case WDP_ALIGN_TBL: // Align the left side with the top toolbar pt.x = FindWindowById(WC_MAIN_TOOLBAR, 0)->left; break; - case WDP_AUTO: /* Find a good automatic position for the window */ - pt = GetAutoPlacePosition(desc->default_width, desc->default_height); - goto allocate_window; - case WDP_CENTER: /* Centre the window horizontally */ + + case WDP_AUTO: // Find a good automatic position for the window + return GetAutoPlacePosition(desc->default_width, desc->default_height); + + case WDP_CENTER: // Centre the window horizontally pt.x = (_screen.width - desc->default_width) / 2; break; + default: pt.x = desc->left; if (pt.x < 0) pt.x += _screen.width; // negative is from right of the screen } switch (desc->top) { - case WDP_CENTER: /* Centre the window vertically */ + case WDP_CENTER: // Centre the window vertically pt.y = (_screen.height - desc->default_height) / 2; break; + /* WDP_AUTO sets the position at once and is controlled by desc->left. * Both left and top must be set to WDP_AUTO */ case WDP_AUTO: NOT_REACHED(); assert(desc->left == WDP_AUTO && desc->top != WDP_AUTO); /* fallthrough */ + default: pt.y = desc->top; if (pt.y < 0) pt.y += _screen.height; // negative is from bottom of the screen @@ -981,37 +1150,24 @@ } } -allocate_window: - w = LocalAllocateWindow(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->default_width, desc->default_height, desc->proc, desc->cls, desc->widgets, window_number, data); - w->desc_flags = desc->flags; - return w; + return pt; } /** - * Open a new window. - * @param *desc The pointer to the WindowDesc to be created - * @param data arbitrary data that is send with the WE_CREATE message + * Set the positions of a new window from a WindowDesc and open it. + * + * @param *desc The pointer to the WindowDesc to be created + * @param window_number the window number of the new window + * * @return Window pointer of the newly created window */ -Window *AllocateWindowDesc(const WindowDesc *desc, void *data) +Window::Window(const WindowDesc *desc, WindowNumber window_number) { - return LocalAllocateWindowDesc(desc, 0, data); -} + Point pt = LocalGetWindowPlacement(desc, window_number); + this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->proc, desc->cls, desc->widgets, window_number); + this->desc_flags = desc->flags; -/** - * Open a new window. - * @param *desc The pointer to the WindowDesc to be created - * @param window_number the window number of the new window - * @param data arbitrary data that is send with the WE_CREATE message - * @return see Window pointer of the newly created window - */ -Window *AllocateWindowDescFront(const WindowDesc *desc, int window_number, void *data) -{ - Window *w; - - if (BringWindowToFrontById(desc->cls, window_number)) return NULL; - w = LocalAllocateWindowDesc(desc, window_number, data); - return w; + if (desc->proc != NULL) this->FindWindowPlacementAndResize(desc->default_width, desc->default_height); } /** Do a search for a window at specific coordinates. For this we start @@ -1048,20 +1204,7 @@ */ void UnInitWindowSystem() { - Window **wz; - -restart_search: - /* Delete all windows, reset z-array. - * When we find the window to delete, we need to restart the search - * as deleting this window could cascade in deleting (many) others - * anywhere in the z-array. We call DeleteWindow() so that it can properly - * release own alloc'd memory, which otherwise could result in memleaks */ - FOR_ALL_WINDOWS(wz) { - DeleteWindow(*wz); - goto restart_search; - } - - assert(_last_z_window == _z_windows); + while (_last_z_window != _z_windows) delete _z_windows[0]; } /** @@ -1079,24 +1222,23 @@ static void DecreaseWindowCounters() { - Window *w; Window* const *wz; for (wz = _last_z_window; wz != _z_windows;) { - w = *--wz; + Window *w = *--wz; /* Unclick scrollbar buttons if they are pressed. */ if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) { w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP); - SetWindowDirty(w); + w->SetDirty(); } - CallWindowEventNP(w, WE_MOUSELOOP); + w->OnMouseLoop(); } for (wz = _last_z_window; wz != _z_windows;) { - w = *--wz; + Window *w = *--wz; - if (w->flags4&WF_TIMEOUT_MASK && !(--w->flags4&WF_TIMEOUT_MASK)) { - CallWindowEventNP(w, WE_TIMEOUT); + if (w->flags4 & WF_TIMEOUT_MASK && !(--w->flags4 & WF_TIMEOUT_MASK)) { + w->OnTimeout(); if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons(); } } @@ -1109,42 +1251,33 @@ static void HandlePlacePresize() { - Window *w; - WindowEvent e; - if (_special_mouse_mode != WSM_PRESIZE) return; - w = GetCallbackWnd(); + Window *w = GetCallbackWnd(); if (w == NULL) return; - e.we.place.pt = GetTileBelowCursor(); - if (e.we.place.pt.x == -1) { + Point pt = GetTileBelowCursor(); + if (pt.x == -1) { _thd.selend.x = -1; return; } - e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y); - e.event = WE_PLACE_PRESIZE; - w->wndproc(w, &e); + + w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y)); } static bool HandleDragDrop() { - Window *w; - WindowEvent e; - if (_special_mouse_mode != WSM_DRAGDROP) return true; - if (_left_button_down) return false; - w = GetCallbackWnd(); + Window *w = GetCallbackWnd(); if (w != NULL) { /* send an event in client coordinates. */ - e.event = WE_DRAGDROP; - e.we.dragdrop.pt.x = _cursor.pos.x - w->left; - e.we.dragdrop.pt.y = _cursor.pos.y - w->top; - e.we.dragdrop.widget = GetWidgetFromPos(w, e.we.dragdrop.pt.x, e.we.dragdrop.pt.y); - w->wndproc(w, &e); + Point pt; + pt.x = _cursor.pos.x - w->left; + pt.y = _cursor.pos.y - w->top; + w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y)); } ResetObjectToPlace(); @@ -1152,46 +1285,15 @@ return false; } -static bool HandlePopupMenu() -{ - Window *w; - WindowEvent e; - - if (!_popup_menu_active) return true; - - w = FindWindowById(WC_TOOLBAR_MENU, 0); - if (w == NULL) { - _popup_menu_active = false; - return false; - } - - if (_left_button_down) { - e.event = WE_POPUPMENU_OVER; - e.we.popupmenu.pt = _cursor.pos; - } else { - _popup_menu_active = false; - e.event = WE_POPUPMENU_SELECT; - e.we.popupmenu.pt = _cursor.pos; - } - - w->wndproc(w, &e); - - return false; -} - static bool HandleMouseOver() { - WindowEvent e; - Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); /* We changed window, put a MOUSEOVER event to the last window */ if (_mouseover_last_w != NULL && _mouseover_last_w != w) { /* Reset mouse-over coordinates of previous window */ - e.event = WE_MOUSEOVER; - e.we.mouseover.pt.x = -1; - e.we.mouseover.pt.y = -1; - if (_mouseover_last_w->wndproc != NULL) _mouseover_last_w->wndproc(_mouseover_last_w, &e); + Point pt = { -1, -1 }; + _mouseover_last_w->OnMouseOver(pt, 0); } /* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */ @@ -1199,13 +1301,12 @@ if (w != NULL) { /* send an event in client coordinates. */ - e.event = WE_MOUSEOVER; - e.we.mouseover.pt.x = _cursor.pos.x - w->left; - e.we.mouseover.pt.y = _cursor.pos.y - w->top; + Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top }; + int widget = 0; if (w->widget != NULL) { - e.we.mouseover.widget = GetWidgetFromPos(w, e.we.mouseover.pt.x, e.we.mouseover.pt.y); + widget = GetWidgetFromPos(w, pt.x, pt.y); } - w->wndproc(w, &e); + w->OnMouseOver(pt, widget); } /* Mouseover never stops execution */ @@ -1223,14 +1324,13 @@ */ void ResizeWindow(Window *w, int x, int y) { - Widget *wi; bool resize_height = false; bool resize_width = false; if (x == 0 && y == 0) return; - SetWindowDirty(w); - for (wi = w->widget; wi->type != WWT_LAST; wi++) { + w->SetDirty(); + for (Widget *wi = w->widget; wi->type != WWT_LAST; wi++) { /* Isolate the resizing flags */ byte rsizeflag = GB(wi->display_flags, 0, 4); @@ -1262,7 +1362,7 @@ if (resize_width) w->width += x; if (resize_height) w->height += y; - SetWindowDirty(w); + w->SetDirty(); } static bool _dragging_window; @@ -1279,11 +1379,6 @@ if (w->flags4 & WF_DRAGGING) { const Widget *t = &w->widget[1]; // the title bar ... ugh - const Window *v; - int x; - int y; - int nx; - int ny; /* Stop the dragging if the left mouse button was released */ if (!_left_button_down) { @@ -1291,12 +1386,12 @@ break; } - SetWindowDirty(w); + w->SetDirty(); - x = _cursor.pos.x + _drag_delta.x; - y = _cursor.pos.y + _drag_delta.y; - nx = x; - ny = y; + int x = _cursor.pos.x + _drag_delta.x; + int y = _cursor.pos.y + _drag_delta.y; + int nx = x; + int ny = y; if (_patches.window_snap_radius != 0) { Window* const *vz; @@ -1382,7 +1477,7 @@ ny = Clamp(ny, 0, _screen.height - 13); /* Make sure the title bar isn't hidden by behind the main tool bar */ - v = FindWindowById(WC_MAIN_TOOLBAR, 0); + Window *v = FindWindowById(WC_MAIN_TOOLBAR, 0); if (v != NULL) { int v_bottom = v->top + v->height; int v_right = v->left + v->width; @@ -1412,16 +1507,15 @@ w->left = nx; w->top = ny; - SetWindowDirty(w); + w->SetDirty(); return false; } else if (w->flags4 & WF_SIZING) { - WindowEvent e; int x, y; /* Stop the sizing if the left mouse button was released */ if (!_left_button_down) { w->flags4 &= ~WF_SIZING; - SetWindowDirty(w); + w->SetDirty(); break; } @@ -1452,12 +1546,13 @@ /* ResizeWindow sets both pre- and after-size to dirty for redrawal */ ResizeWindow(w, x, y); - e.event = WE_RESIZE; - e.we.sizing.size.x = x + w->width; - e.we.sizing.size.y = y + w->height; - e.we.sizing.diff.x = x; - e.we.sizing.diff.y = y; - w->wndproc(w, &e); + Point size; + Point diff; + size.x = x + w->width; + size.y = y + w->height; + diff.x = x; + diff.y = y; + w->OnResize(size, diff); return false; } } @@ -1502,9 +1597,6 @@ static bool HandleScrollbarScrolling() { Window* const *wz; - int i; - int pos; - Scrollbar *sb; /* Get out quickly if no item is being scrolled */ if (!_scrolling_scrollbar) return true; @@ -1517,10 +1609,13 @@ /* Abort if no button is clicked any more. */ if (!_left_button_down) { w->flags4 &= ~WF_SCROLL_MIDDLE; - SetWindowDirty(w); + w->SetDirty(); break; } + int i; + Scrollbar *sb; + if (w->flags4 & WF_HSCROLL) { sb = &w->hscroll; i = _cursor.pos.x - _cursorpos_drag_start.x; @@ -1533,10 +1628,10 @@ } /* Find the item we want to move to and make sure it's inside bounds. */ - pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap)); + int pos = min(max(0, i + _scrollbar_start_pos) * sb->count / _scrollbar_size, max(0, sb->count - sb->cap)); if (pos != sb->pos) { sb->pos = pos; - SetWindowDirty(w); + w->SetDirty(); } return false; } @@ -1548,14 +1643,11 @@ static bool HandleViewportScroll() { - WindowEvent e; - Window *w; - bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); if (!_scrolling_viewport) return true; - w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); + Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); if (!(_right_button_down || scrollwheel_scrolling) || w == NULL) { _cursor.fix_at = false; @@ -1563,32 +1655,32 @@ return true; } - if (WP(w, vp_d).follow_vehicle != INVALID_VEHICLE && w == FindWindowById(WC_MAIN_WINDOW, 0)) { + if (w == FindWindowById(WC_MAIN_WINDOW, 0) && w->viewport->follow_vehicle != INVALID_VEHICLE) { /* If the main window is following a vehicle, then first let go of it! */ - const Vehicle *veh = GetVehicle(WP(w, vp_d).follow_vehicle); + const Vehicle *veh = GetVehicle(w->viewport->follow_vehicle); ScrollMainWindowTo(veh->x_pos, veh->y_pos, true); /* This also resets follow_vehicle */ return true; } + Point delta; if (_patches.reverse_scroll) { - e.we.scroll.delta.x = -_cursor.delta.x; - e.we.scroll.delta.y = -_cursor.delta.y; + delta.x = -_cursor.delta.x; + delta.y = -_cursor.delta.y; } else { - e.we.scroll.delta.x = _cursor.delta.x; - e.we.scroll.delta.y = _cursor.delta.y; + delta.x = _cursor.delta.x; + delta.y = _cursor.delta.y; } if (scrollwheel_scrolling) { /* We are using scrollwheels for scrolling */ - e.we.scroll.delta.x = _cursor.h_wheel; - e.we.scroll.delta.y = _cursor.v_wheel; + delta.x = _cursor.h_wheel; + delta.y = _cursor.v_wheel; _cursor.v_wheel = 0; _cursor.h_wheel = 0; } /* Create a scroll-event and send it to the window */ - e.event = WE_SCROLL; - w->wndproc(w, &e); + w->OnScroll(delta); _cursor.delta.x = 0; _cursor.delta.y = 0; @@ -1606,7 +1698,6 @@ static bool MaybeBringWindowToFront(const Window *w) { bool bring_to_front = false; - Window * const *wz; if (w->window_class == WC_MAIN_WINDOW || IsVitalWindow(w) || @@ -1615,14 +1706,14 @@ return true; } - wz = FindWindowZPosition(w); + Window * const *wz = FindWindowZPosition(w); for (Window * const *uz = wz; ++uz != _last_z_window;) { Window *u = *uz; /* A modal child will prevent the activation of the parent window */ if (u->parent == w && (u->desc_flags & WDF_MODAL)) { u->flags4 |= WF_WHITE_BORDER_MASK; - SetWindowDirty(u); + u->SetDirty(); return false; } @@ -1648,59 +1739,11 @@ return true; } -/** Send a message from one window to another. The receiving window is found by - * @param w Window pointer pointing to the other window - * @param msg Specifies the message to be sent - * @param wparam Specifies additional message-specific information - * @param lparam Specifies additional message-specific information - */ -static void SendWindowMessageW(Window *w, uint msg, uint wparam, uint lparam) -{ - WindowEvent e; - - e.event = WE_MESSAGE; - e.we.message.msg = msg; - e.we.message.wparam = wparam; - e.we.message.lparam = lparam; - - w->wndproc(w, &e); -} - -/** Send a message from one window to another. The receiving window is found by - * @param wnd_class see WindowClass class AND - * @param wnd_num see WindowNumber number, mostly 0 - * @param msg Specifies the message to be sent - * @param wparam Specifies additional message-specific information - * @param lparam Specifies additional message-specific information +/** Handle keyboard input. + * @param raw_key Lower 8 bits contain the ASCII character, the higher 16 bits the keycode */ -void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, int msg, int wparam, int lparam) +void HandleKeypress(uint32 raw_key) { - Window *w = FindWindowById(wnd_class, wnd_num); - if (w != NULL) SendWindowMessageW(w, msg, wparam, lparam); -} - -/** Send a message from one window to another. The message will be sent - * to ALL windows of the windowclass specified in the first parameter - * @param wnd_class see WindowClass class - * @param msg Specifies the message to be sent - * @param wparam Specifies additional message-specific information - * @param lparam Specifies additional message-specific information - */ -void SendWindowMessageClass(WindowClass wnd_class, int msg, int wparam, int lparam) -{ - Window* const *wz; - - FOR_ALL_WINDOWS(wz) { - if ((*wz)->window_class == wnd_class) SendWindowMessageW(*wz, msg, wparam, lparam); - } -} - -/** Handle keyboard input. - * @param key Lower 8 bits contain the ASCII character, the higher 16 bits the keycode - */ -void HandleKeypress(uint32 key) -{ - WindowEvent e; /* Stores if a window with a textfield for typing is open * If this is the case, keypress events are only passed to windows with text fields and * to thein this main toolbar. */ @@ -1718,10 +1761,8 @@ if (!IsGeneratingWorld()) _current_player = _local_player; /* Setup event */ - e.event = WE_KEYPRESS; - e.we.keypress.key = GB(key, 0, 16); - e.we.keypress.keycode = GB(key, 16, 16); - e.we.keypress.cont = true; + uint16 key = GB(raw_key, 0, 16); + uint16 keycode = GB(raw_key, 16, 16); /* * The Unicode standard defines an area called the private use area. Code points in this @@ -1730,12 +1771,12 @@ * on a system running OS X. We don't want these keys to show up in text fields and such, * and thus we have to clear the unicode character when we encounter such a key. */ - if (e.we.keypress.key >= 0xE000 && e.we.keypress.key <= 0xF8FF) e.we.keypress.key = 0; + if (key >= 0xE000 && key <= 0xF8FF) key = 0; /* * If both key and keycode is zero, we don't bother to process the event. */ - if (e.we.keypress.key == 0 && e.we.keypress.keycode == 0) return; + if (key == 0 && keycode == 0) return; /* check if we have a query string window open before allowing hotkeys */ if (FindWindowById(WC_QUERY_STRING, 0) != NULL || @@ -1761,15 +1802,13 @@ w->window_class != WC_COMPANY_PASSWORD_WINDOW) { continue; } - w->wndproc(w, &e); - if (!e.we.keypress.cont) break; + ; + if (!w->OnKeyPress(key, keycode)) return; } - if (e.we.keypress.cont) { - Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); - /* When there is no toolbar w is null, check for that */ - if (w != NULL) w->wndproc(w, &e); - } + Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0); + /* When there is no toolbar w is null, check for that */ + if (w != NULL) w->OnKeyPress(key, keycode); } /** @@ -1777,16 +1816,10 @@ */ void HandleCtrlChanged() { - WindowEvent e; - - e.event = WE_CTRL_CHANGED; - e.we.ctrl.cont = true; - /* Call the event, start with the uppermost window. */ for (Window* const *wz = _last_z_window; wz != _z_windows;) { Window *w = *--wz; - w->wndproc(w, &e); - if (!e.we.ctrl.cont) break; + if (!w->OnCTRLStateChange()) break; } } @@ -1804,11 +1837,6 @@ */ static void HandleAutoscroll() { - Window *w; - ViewPort *vp; - int x = _cursor.pos.x; - int y = _cursor.pos.y; - if (_input_events_this_tick != 0) { /* HandleAutoscroll is called only once per GameLoop() - so we can clear the counter here */ _input_events_this_tick = 0; @@ -1817,23 +1845,26 @@ } if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) { - w = FindWindowFromPt(x, y); + int x = _cursor.pos.x; + int y = _cursor.pos.y; + Window *w = FindWindowFromPt(x, y); if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return; - vp = IsPtInWindowViewport(w, x, y); + ViewPort *vp = IsPtInWindowViewport(w, x, y); if (vp != NULL) { x -= vp->left; y -= vp->top; + /* here allows scrolling in both x and y axis */ #define scrollspeed 3 if (x - 15 < 0) { - WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom); + w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom); } else if (15 - (vp->width - x) > 0) { - WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom); + w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom); } if (y - 15 < 0) { - WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom); + w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom); } else if (15 - (vp->height - y) > 0) { - WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom); + w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom); } #undef scrollspeed } @@ -1855,44 +1886,34 @@ void MouseLoop(MouseClick click, int mousewheel) { - int x,y; - Window *w; - ViewPort *vp; - bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); - DecreaseWindowCounters(); HandlePlacePresize(); UpdateTileSelection(); if (!VpHandlePlaceSizingDrag()) return; if (!HandleDragDrop()) return; - if (!HandlePopupMenu()) return; if (!HandleWindowDragging()) return; if (!HandleScrollbarScrolling()) return; if (!HandleViewportScroll()) return; if (!HandleMouseOver()) return; - x = _cursor.pos.x; - y = _cursor.pos.y; - + bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); if (click == MC_NONE && mousewheel == 0 && !scrollwheel_scrolling) return; - w = FindWindowFromPt(x, y); + int x = _cursor.pos.x; + int y = _cursor.pos.y; + Window *w = FindWindowFromPt(x, y); if (w == NULL) return; + if (!MaybeBringWindowToFront(w)) return; - vp = IsPtInWindowViewport(w, x, y); + ViewPort *vp = IsPtInWindowViewport(w, x, y); /* Don't allow any action in a viewport if either in menu of in generating world */ if (vp != NULL && (_game_mode == GM_MENU || IsGeneratingWorld())) return; if (mousewheel != 0) { if (_patches.scrollwheel_scrolling == 0) { - /* Scrollwheel is in zoom mode. Make the zoom event. */ - WindowEvent e; - - /* Send WE_MOUSEWHEEL event to window */ - e.event = WE_MOUSEWHEEL; - e.we.wheel.wheel = mousewheel; - w->wndproc(w, &e); + /* Send mousewheel event to window */ + w->OnMouseWheel(mousewheel); } /* Dispatch a MouseWheelEvent for widgets if it is not a viewport */ @@ -1933,9 +1954,14 @@ } } else { switch (click) { - case MC_DOUBLE_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, true); - /* fallthough, and also give a single-click for backwards compatible */ - case MC_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, false); break; + case MC_DOUBLE_LEFT: + DispatchLeftClickEvent(w, x - w->left, y - w->top, true); + if (_mouseover_last_w == NULL) break; // The window got removed. + /* fallthough, and also give a single-click for backwards compatibility */ + case MC_LEFT: + DispatchLeftClickEvent(w, x - w->left, y - w->top, false); + break; + default: if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break; /* We try to use the scrollwheel to scroll since we didn't touch any of the buttons. @@ -1954,8 +1980,6 @@ static int double_click_time = 0; static int double_click_x = 0; static int double_click_y = 0; - MouseClick click; - int mousewheel; /* * During the generation of the world, there might be @@ -1969,7 +1993,7 @@ if (!IsGeneratingWorld()) _current_player = _local_player; /* Mouse event? */ - click = MC_NONE; + MouseClick click = MC_NONE; if (_left_button_down && !_left_button_clicked) { click = MC_LEFT; if (double_click_time != 0 && _realtime_tick - double_click_time < TIME_BETWEEN_DOUBLE_CLICK && @@ -1988,7 +2012,7 @@ _input_events_this_tick++; } - mousewheel = 0; + int mousewheel = 0; if (_cursor.wheel) { mousewheel = _cursor.wheel; _cursor.wheel = 0; @@ -2018,7 +2042,7 @@ if (t >= 100) { for (wz = _last_z_window; wz != _z_windows;) { - CallWindowEventNP(*--wz, WE_4); + (*--wz)->OnHundredthTick(); } t = 0; } @@ -2029,7 +2053,7 @@ if (w->flags4 & WF_WHITE_BORDER_MASK) { w->flags4 -= WF_WHITE_BORDER_ONE; - if (!(w->flags4 & WF_WHITE_BORDER_MASK)) SetWindowDirty(w); + if (!(w->flags4 & WF_WHITE_BORDER_MASK)) w->SetDirty(); } } @@ -2043,27 +2067,6 @@ DrawMouseCursor(); } - -/** - * In a window with menu_d custom extension, retrieve the menu item number from a position - * @param w Window holding the menu items - * @param x X coordinate of the position - * @param y Y coordinate of the position - * @return Index number of the menu item, or \c -1 if no valid selection under position - */ -int GetMenuItemIndex(const Window *w, int x, int y) -{ - if ((x -= w->left) >= 0 && x < w->width && (y -= w->top + 1) >= 0) { - y /= 10; - - if (y < WP(w, const menu_d).item_count && - !HasBit(WP(w, const menu_d).disabled_items, y)) { - return y; - } - } - return -1; -} - /** * Mark window as dirty (in need of repainting) * @param cls Window class @@ -2075,7 +2078,7 @@ FOR_ALL_WINDOWS(wz) { const Window *w = *wz; - if (w->window_class == cls && w->window_number == number) SetWindowDirty(w); + if (w->window_class == cls && w->window_number == number) w->SetDirty(); } } @@ -2106,7 +2109,7 @@ Window* const *wz; FOR_ALL_WINDOWS(wz) { - if ((*wz)->window_class == cls) SetWindowDirty(*wz); + if ((*wz)->window_class == cls) (*wz)->SetDirty(); } } @@ -2114,10 +2117,10 @@ * Mark window data as invalid (in need of re-computing) * @param w Window with invalid data */ -void InvalidateThisWindowData(Window *w) +void InvalidateThisWindowData(Window *w, int data) { - CallWindowEventNP(w, WE_INVALIDATE_DATA); - SetWindowDirty(w); + w->OnInvalidateData(data); + w->SetDirty(); } /** @@ -2125,13 +2128,13 @@ * @param cls Window class * @param number Window number within the class */ -void InvalidateWindowData(WindowClass cls, WindowNumber number) +void InvalidateWindowData(WindowClass cls, WindowNumber number, int data) { Window* const *wz; FOR_ALL_WINDOWS(wz) { Window *w = *wz; - if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w); + if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w, data); } } @@ -2139,12 +2142,12 @@ * Mark window data of all windows of a given class as invalid (in need of re-computing) * @param cls Window class */ -void InvalidateWindowClassesData(WindowClass cls) +void InvalidateWindowClassesData(WindowClass cls, int data) { Window* const *wz; FOR_ALL_WINDOWS(wz) { - if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz); + if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz, data); } } @@ -2154,7 +2157,7 @@ void CallWindowTickEvent() { for (Window * const *wz = _last_z_window; wz != _z_windows;) { - CallWindowEventNP(*--wz, WE_TICK); + (*--wz)->OnTick(); } } @@ -2182,7 +2185,7 @@ w->window_class != WC_TOOLTIPS && (w->flags4 & WF_STICKY) == 0) { // do not delete windows which are 'pinned' - DeleteWindow(w); + delete w; goto restart_search; } } @@ -2206,7 +2209,7 @@ * anywhere in the z-array */ FOR_ALL_WINDOWS(wz) { if ((*wz)->flags4 & WF_STICKY) { - DeleteWindow(*wz); + delete *wz; goto restart_search; } } @@ -2271,13 +2274,13 @@ if (neww - w->width != 0) { ResizeWindow(w, min(neww, 640) - w->width, 0); - WindowEvent e; - e.event = WE_RESIZE; - e.we.sizing.size.x = w->width; - e.we.sizing.size.y = w->height; - e.we.sizing.diff.x = neww - w->width; - e.we.sizing.diff.y = 0; - w->wndproc(w, &e); + Point size; + Point delta; + size.x = w->width; + size.y = w->height; + delta.x = neww - w->width; + delta.y = 0; + w->OnResize(size, delta); } top = w->top; @@ -2339,3 +2342,13 @@ w->top = top; } } + +/** Destructor of the base class PickerWindowBase + * Main utility is to stop the base Window destructor from triggering + * a free while the child will already be free, in this case by the ResetObjectToPlace(). + */ +PickerWindowBase::~PickerWindowBase() +{ + this->window_class = WC_INVALID; // stop the ancestor from freeing the already (to be) child + ResetObjectToPlace(); +}