src/window.cpp
branchnoai
changeset 10513 33cb70ff2f5d
parent 10455 22c441f5adf9
child 10645 8cbdb511a674
--- a/src/window.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/window.cpp	Sun May 11 20:09:34 2008 +0000
@@ -44,13 +44,16 @@
 
 bool _scrolling_scrollbar;
 bool _scrolling_viewport;
-bool _popup_menu_active;
 
 byte _special_mouse_mode;
 
 
 /**
- * Call the window event handler for handling event \a e
+ * 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)
@@ -58,6 +61,208 @@
 	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::OnDoubleClick(Point pt, int widget)
+{
+	WindowEvent e;
+	e.event = WE_DOUBLE_CLICK;
+	e.we.click.pt     = pt;
+	e.we.click.widget = widget;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnRightClick(Point pt, int widget)
+{
+	WindowEvent e;
+	e.event = WE_RCLICK;
+	e.we.click.pt     = pt;
+	e.we.click.widget = widget;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnDragDrop(Point pt, int widget)
+{
+	WindowEvent e;
+	e.event = WE_DRAGDROP;
+	e.we.click.pt     = pt;
+	e.we.click.widget = widget;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnScroll(Point delta)
+{
+	WindowEvent e;
+	e.event = WE_SCROLL;
+	e.we.scroll.delta = delta;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnMouseOver(Point pt, int widget)
+{
+	WindowEvent e;
+	e.event = WE_MOUSEOVER;
+	e.we.click.pt     = pt;
+	e.we.click.widget = widget;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnMouseWheel(int wheel)
+{
+	WindowEvent e;
+	e.event = WE_MOUSEWHEEL;
+	e.we.wheel.wheel = wheel;
+	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;
@@ -139,19 +344,15 @@
  */
 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click)
 {
-	WindowEvent e;
-	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;
 
-		const Widget *wi = &w->widget[e.we.click.widget];
+		const Widget *wi = &w->widget[widget];
 
 		if (wi->type & WWB_MASK) {
 			/* special widget handling for buttons*/
@@ -159,20 +360,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' */
+			if (widget == 0) { /* 'X' */
 				delete w;
 				return;
 			}
 
-			if (e.we.click.widget == 1) { /* 'Title bar' */
+			if (widget == 1) { /* 'Title bar' */
 				StartWindowDrag(w);
 				return;
 			}
@@ -180,18 +381,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->HandleWindowEvent(&e);
+	Point pt = { x, y };
+
+	if (double_click) {
+		w->OnDoubleClick(pt, widget);
+	} else {
+		w->OnClick(pt, widget);
+	}
 }
 
 /**
@@ -202,23 +409,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->HandleWindowEvent(&e);
+	Point pt = { x, y };
+	w->OnRightClick(pt, widget);
 }
 
 /**
@@ -316,7 +521,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();
 }
 
 /**
@@ -346,20 +551,6 @@
 }
 
 /**
- * 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
- */
-void CallWindowEventNP(Window *w, int event)
-{
-	WindowEvent e;
-
-	e.event = event;
-	w->HandleWindowEvent(&e);
-}
-
-/**
  * Mark entire window as dirty (in need of re-paint)
  * @param w Window to redraw
  * @ingroup dirty
@@ -438,7 +629,9 @@
 	/* Delete any children a window might have in a head-recursive manner */
 	delete FindChildWindow(this);
 
-	CallWindowEventNP(this, WE_DESTROY);
+	WindowEvent e;
+	e.event = WE_DESTROY;
+	this->HandleWindowEvent(&e);
 	if (this->viewport != NULL) DeleteWindowViewport(this);
 
 	this->SetDirty();
@@ -656,10 +849,18 @@
 	return (w->original_widget == widget);
 }
 
-/** Copies 'widget' to 'w->widget' to allow for resizable windows
+/**
+ * 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 */
-void AssignWidgetToWindow(Window *w, const Widget *widget)
+ * @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->original_widget points to the original widgets,
+ *       \c w->widget_count contains number of widgets in the allocated memory.
+ */
+static void AssignWidgetToWindow(Window *w, const Widget *widget)
 {
 	w->original_widget = widget;
 
@@ -668,7 +869,7 @@
 
 		for (const Widget *wi = widget; wi->type != WWT_LAST; wi++) index++;
 
-		w->widget = ReallocT(w->widget, index);
+		w->widget = MallocT<Widget>(index);
 		memcpy(w->widget, widget, sizeof(*w->widget) * index);
 		w->widget_count = index - 1;
 	} else {
@@ -677,88 +878,88 @@
 	}
 }
 
-/** 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,
+void Window::Initialize(int x, int y, int min_width, int min_height,
 				WindowProc *proc, WindowClass cls, const Widget *widget, int window_number, void *data)
 {
-	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();
 		delete w;
 	}
 
-	w = new Window(proc);
-
 	/* 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;
-	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->HandleWindowEvent(&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);
@@ -766,45 +967,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->HandleWindowEvent(&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;
 
-	w->SetDirty();
+	this->SetDirty();
+}
 
-	return w;
+void Window::FindWindowPlacementAndResize(const WindowDesc *desc)
+{
+	this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
 }
 
 /**
@@ -821,10 +1025,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, void *data)
 {
-	return LocalAllocateWindow(x, y, width, height, width, height, proc, cls, widget, 0, data);
+	this->Initialize(x, y, width, height, proc, cls, widget, 0, data);
+
+	if (proc != NULL) this->FindWindowPlacementAndResize(width, height);
 }
 
 
@@ -1014,37 +1219,13 @@
  *
  * @return Window pointer of the newly created window
  */
-static Window *LocalAllocateWindowDesc(const WindowDesc *desc, int window_number, void *data)
+Window::Window(const WindowDesc *desc, void *data, WindowNumber window_number)
 {
 	Point pt = LocalGetWindowPlacement(desc, window_number);
-	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;
-}
+	this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->proc, desc->cls, desc->widgets, window_number, data);
+	this->desc_flags = desc->flags;
 
-/**
- * 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
- * @return Window pointer of the newly created window
- */
-Window *AllocateWindowDesc(const WindowDesc *desc, void *data)
-{
-	return LocalAllocateWindowDesc(desc, 0, data);
-}
-
-/**
- * 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)
-{
-	if (BringWindowToFrontById(desc->cls, window_number)) return NULL;
-	return LocalAllocateWindowDesc(desc, window_number, data);
+	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
@@ -1108,14 +1289,14 @@
 			w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
 			w->SetDirty();
 		}
-		CallWindowEventNP(w, WE_MOUSELOOP);
+		w->OnMouseLoop();
 	}
 
 	for (wz = _last_z_window; wz != _z_windows;) {
 		Window *w = *--wz;
 
 		if (w->flags4 & WF_TIMEOUT_MASK && !(--w->flags4 & WF_TIMEOUT_MASK)) {
-			CallWindowEventNP(w, WE_TIMEOUT);
+			w->OnTimeout();
 			if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons();
 		}
 	}
@@ -1133,15 +1314,13 @@
 	Window *w = GetCallbackWnd();
 	if (w == NULL) return;
 
-	WindowEvent e;
-	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->HandleWindowEvent(&e);
+
+	w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
 }
 
 static bool HandleDragDrop()
@@ -1153,12 +1332,10 @@
 
 	if (w != NULL) {
 		/* send an event in client coordinates. */
-		WindowEvent e;
-		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->HandleWindowEvent(&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();
@@ -1166,31 +1343,6 @@
 	return false;
 }
 
-static bool HandlePopupMenu()
-{
-	if (!_popup_menu_active) return true;
-
-	Window *w = FindWindowById(WC_TOOLBAR_MENU, 0);
-	if (w == NULL) {
-		_popup_menu_active = false;
-		return false;
-	}
-
-	WindowEvent e;
-	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->HandleWindowEvent(&e);
-
-	return false;
-}
-
 static bool HandleMouseOver()
 {
 	Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
@@ -1198,11 +1350,8 @@
 	/* 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 */
-		WindowEvent e;
-		e.event = WE_MOUSEOVER;
-		e.we.mouseover.pt.x = -1;
-		e.we.mouseover.pt.y = -1;
-		_mouseover_last_w->HandleWindowEvent(&e);
+		Point pt = { -1, -1 };
+		_mouseover_last_w->OnMouseOver(pt, 0);
 	}
 
 	/* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
@@ -1210,14 +1359,12 @@
 
 	if (w != NULL) {
 		/* send an event in client coordinates. */
-		WindowEvent e;
-		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->HandleWindowEvent(&e);
+		w->OnMouseOver(pt, widget);
 	}
 
 	/* Mouseover never stops execution */
@@ -1421,7 +1568,6 @@
 			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 */
@@ -1458,12 +1604,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->HandleWindowEvent(&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;
 		}
 	}
@@ -1554,8 +1701,6 @@
 
 static bool HandleViewportScroll()
 {
-	WindowEvent e;
-
 	bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
 
 	if (!_scrolling_viewport) return true;
@@ -1568,32 +1713,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->HandleWindowEvent(&e);
+	w->OnScroll(delta);
 
 	_cursor.delta.x = 0;
 	_cursor.delta.y = 0;
@@ -1652,59 +1797,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->HandleWindowEvent(&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. */
@@ -1722,10 +1819,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
@@ -1734,12 +1829,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 ||
@@ -1765,15 +1860,13 @@
 				w->window_class != WC_COMPANY_PASSWORD_WINDOW) {
 			continue;
 		}
-		w->HandleWindowEvent(&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->HandleWindowEvent(&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);
 }
 
 /**
@@ -1781,16 +1874,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->HandleWindowEvent(&e);
-		if (!e.we.ctrl.cont) break;
+		if (!w->OnCTRLStateChange()) break;
 	}
 }
 
@@ -1828,14 +1915,14 @@
 			/* 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
 		}
@@ -1862,7 +1949,6 @@
 	UpdateTileSelection();
 	if (!VpHandlePlaceSizingDrag())  return;
 	if (!HandleDragDrop())           return;
-	if (!HandlePopupMenu())          return;
 	if (!HandleWindowDragging())     return;
 	if (!HandleScrollbarScrolling()) return;
 	if (!HandleViewportScroll())     return;
@@ -1884,13 +1970,8 @@
 
 	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->HandleWindowEvent(&e);
+			w->OnMouseWheel(mousewheel);
 		}
 
 		/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
@@ -1931,9 +2012,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.
@@ -2014,7 +2100,7 @@
 
 	if (t >= 100) {
 		for (wz = _last_z_window; wz != _z_windows;) {
-			CallWindowEventNP(*--wz, WE_4);
+			(*--wz)->OnHundredthTick();
 		}
 		t = 0;
 	}
@@ -2039,27 +2125,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
@@ -2110,9 +2175,9 @@
  * 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);
+	w->OnInvalidateData(data);
 	w->SetDirty();
 }
 
@@ -2121,13 +2186,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);
 	}
 }
 
@@ -2135,12 +2200,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);
 	}
 }
 
@@ -2150,7 +2215,7 @@
 void CallWindowTickEvent()
 {
 	for (Window * const *wz = _last_z_window; wz != _z_windows;) {
-		CallWindowEventNP(*--wz, WE_TICK);
+		(*--wz)->OnTick();
 	}
 }
 
@@ -2267,13 +2332,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->HandleWindowEvent(&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;