src/window.cpp
author KUDr
Sat, 03 Mar 2007 16:11:18 +0000
branchcpp_gui
changeset 6282 c5b92f2d924f
parent 6279 c75b2c7222ff
child 6283 7072ee68c676
permissions -rw-r--r--
(svn r8996) [cpp_gui] -Add: CloseBox added into CaptionBar
-Add: when pressing gui button and holding the mouse button down you can now move mouse out of the button and it will get released silently. So if you release mouse button after leaving gui button the button will not raise OnLeftClick() event.
-Fix: OnCreate() is now propagated properly to base classes
-Codechange: old OnLeftClick() is now OnLeftButtonDown() (same with OnRightClick() and OnRightButtonDown()). New OnLeftClick() is now raised only by button when released.
/* $Id$ */

/** @file window.cpp windowing system, widgets and events */

#include "stdafx.h"
#include <stdarg.h>
#include "openttd.h"
#include "debug.h"
#include "functions.h"
#include "map.h"
#include "player.h"
#include "window.h"
#include "gfx.h"
#include "viewport.h"
#include "console.h"
#include "variables.h"
#include "table/sprites.h"
#include "genworld.h"
#include "helpers.hpp"
#include "widget/window_events.hpp"

// delta between mouse cursor and upper left corner of dragged window
static Point _drag_delta;

/*static*/ WindowList BaseWindow::s_list;

//static BaseWindow _windows[25];
//BaseWindow *_z_windows[lengthof(_windows)];
//BaseWindow **_last_z_window; ///< always points to the next free space in the z-array

void WindowList::Add(BaseWindow *w)
{
	/* we will add the new window before first vital window or at the end */
	Iterator it = w->IsVital() ? m_list.end() : FindFirstVitalWindow();
	/* it should be now at the proper position where new window should be added */
	m_list.insert(it, w);
}

void WindowList::Remove(BaseWindow *w)
{
	Iterator it = Find(w);
	if (it != m_list.end()) {
		assert(it != m_list.end());
		Item &item = (*it);
		assert(item.w == w);
//		w->Close();
		m_list.erase(it);
	}
}

void WindowList::CloseAll()
{
	BaseWindow *w_prev = NULL;
	while (!m_list.empty()) {
		Iterator it = m_list.begin();
		Item &item = (*it);
		BaseWindow *w = item.w;
		assert(w != w_prev);
		w_prev = w;
		w->Close();
	}
}

static bool MatchWindow(WindowList::Iterator it, BaseWindow *w)
{
	return (*it).w == w;
}

WindowList::Iterator WindowList::Find(BaseWindow *w)
{
	Iterator it = EnumT(&MatchWindow, w);
	if (it == m_list.end()) {
		DEBUG(misc, 3, "BaseWindow (cls %d, number %d) is not open, probably removed by recursive calls",
			w->window_class, w->window_number);
	}
	return it;
}

static bool MatchNonVitalWindow(WindowList::Iterator it)
{
	return !(*it).w->IsVital();
}

WindowList::Iterator WindowList::FindFirstVitalWindow()
{
	Iterator it = ReverseEnumT(&MatchNonVitalWindow);
	/* we have stopped on last non-vital window. Move one step forward to locate first vital window. */
	if (it != m_list.end()) ++it;
	return it;
}

static bool MatchClass(WindowList::Iterator it, WindowClass cls)
{
	return (*it).w->window_class == cls;
}

WindowList::Iterator WindowList::FindByClass(WindowClass cls)
{
	return EnumT(&MatchClass, cls);
}

static bool MatchClassAndId(WindowList::Iterator it, WindowClass cls, WindowNumber num)
{
	return (*it).w->window_class == cls && (*it).w->window_number == num;
}

WindowList::Iterator WindowList::FindById(WindowClass cls, WindowNumber num)
{
	return EnumT(&MatchClassAndId, cls, num);
}

static bool MatchXY(WindowList::Iterator it, int x, int y)
{
	BaseWindow *w = (*it).w;
	return IS_INSIDE_1D(x, w->Left(), w->Width()) && IS_INSIDE_1D(y, w->Top(), w->Height());
}

/** Do a search for a window at specific coordinates. For this we start
* at the topmost window, obviously and work our way down to the bottom
* @return a pointer to the found window if any, NULL otherwise */
WindowList::Iterator WindowList::FindFromPt(int x, int y)
{
	return ReverseEnumT(&MatchXY, x, y);
}




int16 BaseWindow::Left() const
{
	assert(super::TopLeft() == Point16(0, 0));
	return m_wnd_pos.x;
}

int16 BaseWindow::Top() const
{
	assert(super::TopLeft() == Point16(0, 0));
	return m_wnd_pos.y;
}

int16 BaseWindow::Right() const
{
	assert(super::TopLeft() == Point16(0, 0));
	return m_wnd_pos.x + super::Right();
}

int16 BaseWindow::Bottom() const
{
	assert(super::TopLeft() == Point16(0, 0));
	return m_wnd_pos.y + super::Bottom();
}

const Point16& BaseWindow::TopLeft() const
{
	assert(super::TopLeft() == Point16(0, 0));
	return m_wnd_pos;
}

Point16 BaseWindow::BottomRight() const
{
	assert(super::TopLeft() == Point16(0, 0));
	return m_wnd_pos + super::BottomRight();
}

Rect16 BaseWindow::GetRect() const
{
	assert(super::TopLeft() == Point16(0, 0));
	return super::GetRect() + m_wnd_pos;
}


void BaseWindow::SetLeft(int16 val)
{
	assert(super::TopLeft() == Point16(0, 0));
	m_wnd_pos.x = val;
}

void BaseWindow::SetTop(int16 val)
{
	assert(super::TopLeft() == Point16(0, 0));
	m_wnd_pos.y = val;
}

void BaseWindow::SetRight(int16 val)
{
	assert(super::TopLeft() == Point16(0, 0));
	assert(val >= m_wnd_pos.x);
	super::SetRight(val - m_wnd_pos.x);
}

void BaseWindow::SetBottom(int16 val)
{
	assert(super::TopLeft() == Point16(0, 0));
	assert(val >= m_wnd_pos.y);
	super::SetBottom(val - m_wnd_pos.y);
}

void BaseWindow::SetTopLeft(const Point16 &pt)
{
	assert(super::TopLeft() == Point16(0, 0));
	m_wnd_pos = pt;
}

void BaseWindow::SetBottomRight(const Point16 &pt)
{
	assert(super::TopLeft() == Point16(0, 0));
	super::SetBottomRight(pt - m_wnd_pos);
}

void BaseWindow::SetSize(const Point16 &pt)
{
	assert(super::TopLeft() == Point16(0, 0));
	super::SetSize(pt);
}

void BaseWindow::SetRect(const Rect16 &rect)
{
	assert(super::TopLeft() == Point16(0, 0));
	m_wnd_pos = rect.top_left;
	super::SetBottomRight(rect.Size());
}


/*virtual*/ bool BaseWindow::Create(WindowNumber num)
{
	window_number = num;
	flash_timeout = 3; // just opened windows have a white border
	caption_color = 0xFF;
	wndproc = &BaseWindow::DefaultWndProc;

	/* add our new window into z-order list */
	BaseWindow::s_list.Add(this);

	/* Finalize the creation process */
	CallEventNP(WE_CREATE);

	/* window should have some widgets now */
	assert(widget != NULL || m_widgets.size() > 0);

	SetDirty();
	return true;
}

/*virtual*/ bool BaseWindow::Create(const WindowDesc *desc, WindowNumber num)
{
	window_class = desc->cls;
	flash_timeout = 3; // just opened windows have a white border
	caption_color = 0xFF;
	SetTopLeft(Point16(desc->left, desc->top));
	SetSize(Point16(desc->width, desc->height));

	wndproc = desc->proc != NULL ? desc->proc : &BaseWindow::DefaultWndProc;
	desc_flags = desc->flags;
	AssignWidget(desc->widgets);
	resize.width = Width();
	resize.height = Height();
	resize.step_width = 1;
	resize.step_height = 1;
	window_number = num;

	/* add our new window into z-order list */
	BaseWindow::s_list.Add(this);

	/* Finalize the creation process */
	CallEventNP(WE_CREATE);

	SetDirty();
	return true;
}

/*virtual*/ void BaseWindow::CreateWidgets()
{
	/* only old windows are allowed to use this fake 'CreateWidgets()' method << new gui must have its own */
	assert(widget != NULL);
}

/**
* Open a new window. If there is no space for a new window, close an open
* window. Try to avoid stickied windows, but if there is no else, close one of
* those as well. Then make sure all created windows are below some always-on-top
* ones. Finally set all variables and call the WE_CREATE event
* @param x offset in pixels from the left of the screen
* @param y offset in pixels from the top of the screen
* @param width width in pixels of the window
* @param height 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 OldWidget pointer to the window layout and various elements
* @return @see BaseWindow pointer of the newly created window
*/
/*static*/ BaseWindow* BaseWindow::Allocate(int x, int y, int width, int height, WindowProc *proc, WindowClass cls, const OldWidget *widget)
{
	WindowDesc desc = {x, y, width, height, cls, WC_NONE, 0, widget, proc};
	return new BaseWindow(&desc, 0);
}

/**
* Open a new window.
* @param *desc The pointer to the WindowDesc to be created
* @param window_number the window number of the new window
* @return @see BaseWindow pointer of the newly created window
*/
BaseWindow *BaseWindow::AllocateFront(const WindowDesc *desc, int window_number)
{
	if (BaseWindow::BringToFrontById(desc->cls, window_number) != NULL) return NULL;
	BaseWindow *w = Allocate(desc, window_number);
	return w;
}

static Point GetAutoPlacePosition(int width, int height);

/*static*/ BaseWindow* BaseWindow::Allocate(const WindowDesc *desc, int window_number)
{
	Point pt;
	BaseWindow *w;

	/* By default position a child window at an offset of 10/10 of its parent.
	* However if it falls too extremely outside window positions, reposition
	* it to an automatic place */
	if (desc->parent_cls != WC_MAIN_WINDOW &&
		(w = BaseWindow::FindById(desc->parent_cls, window_number)) != NULL &&
		w->Left() < _screen.width - 20 && w->Left() > -60 && w->Top() < _screen.height - 20) {

			pt.x = w->Left() + 10;
			if (pt.x > _screen.width + 10 - desc->width) {
				pt.x = (_screen.width + 10 - desc->width) - 20;
			}
			pt.y = w->Top() + 10;
	} else {
		switch (desc->left) {
			case WDP_ALIGN_TBR: { /* Align the right side with the top toolbar */
				w = BaseWindow::FindById(WC_MAIN_TOOLBAR, 0);
				pt.x = (w->Left() + w->Width()) - desc->width;
													}	break;
			case WDP_ALIGN_TBL: /* Align the left side with the top toolbar */
				pt.x = BaseWindow::FindById(WC_MAIN_TOOLBAR, 0)->Left();
				break;
			case WDP_AUTO: /* Find a good automatic position for the window */
				pt = GetAutoPlacePosition(desc->width, desc->height);
				goto allocate_window;
			case WDP_CENTER: /* Centre the window horizontally */
				pt.x = (_screen.width - desc->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 */
				pt.y = (_screen.height - desc->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
				break;
		}
	}

allocate_window:
	WindowDesc d2 = *desc;
	d2.left = pt.x;
	d2.top  = pt.y;
	w = new BaseWindow(&d2, window_number);
	return w;
}


/* Open a new window.
* This function is called from AllocateWindow() or AllocateWindowDesc()
* 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
*/
//BaseWindow::BaseWindow(
//							 int x, int y, int w, int h,
//							 WindowProc *proc, WindowClass cls, const OldWidget *widget, int wnd_number)
//{
//	ZeroInit();
//
//	window_class = cls;
//	flash_timeout = 3; // just opened windows have a white border
//	caption_color = 0xFF;
//	left = x;
//	top = y;
//	width = w;
//	height = h;
//	wndproc = proc;
//	this->AssignWidget(widget);
//	resize.width = width;
//	resize.height = height;
//	resize.step_width = 1;
//	resize.step_height = 1;
//	window_number = wnd_number;
//
//	/* add our new window into z-order list */
//	BaseWindow::s_list.Add(this);
//
//	SetDirty();
//	CallEventNP(WE_CREATE);
//}

/*virtual*/ BaseWindow* BaseWindow::GetWindow()
{
	return this;
}


/** Find the BaseWindow whose parent pointer points to this window
* @parent w BaseWindow to find child of
* @return return a BaseWindow pointer that is the child of w, or NULL otherwise */
BaseWindow* BaseWindow::FindChild() const
{
	BaseWindow *v;
	FOR_ALL_WINDOWS(v) {
		if (v->parent == this) return v;
	}

	return NULL;
}

void BaseWindow::SetDirty() const
{
	if (this == NULL) return;
	SetDirtyBlocks(Left(), Top(), Left() + Width(), Top() + Height());
}

/*virtual*/ void BaseWindow::FinalRelease()
{
	assert(m_is_closing); // should be closed already

	/* Delete any children a window might have in a head-recursive manner */
	BaseWindow *v = FindChild();
	if (v != NULL) v->Close();

	if (_thd.place_mode != VHM_NONE &&
		_thd.window_class == window_class &&
		_thd.window_number == window_number) {
			ResetObjectToPlace();
	}

	if (viewport != NULL) DeleteWindowViewport(this);

	SetDirty();
	free(widget);
	widget = NULL;
	widget_count = 0;
	parent = NULL;
}

/*virtual*/ void BaseWindow::Close()
{
	super::Close();
	CallEventNP(WE_DESTROY);
	s_list.Remove(this);
}

/*virtual*/ bool BaseWindow::IsVital()
{
	WindowClass wc = window_class;
	return (wc == WC_MAIN_TOOLBAR || wc == WC_STATUS_BAR || wc == WC_NEWS_WINDOW || wc == WC_SEND_NETWORK_MSG);
}

/*static*/ BaseWindow* BaseWindow::Get(WindowList::Iterator it)
{
	return (it != s_list.m_list.end()) ? (*it).w : NULL;
}

/** Do a search for a window at specific coordinates. For this we start
 * at the topmost window, obviously and work our way down to the bottom
 * @return a pointer to the found window if any, NULL otherwise */
/*static*/ BaseWindow *BaseWindow::FindFromPt(int x, int y)
{
	return Get(s_list.FindFromPt(x, y));
}


/*static*/ BaseWindow* BaseWindow::FindById(WindowClass cls, WindowNumber num)
{
	return Get(s_list.FindById(cls, num));
}

/*static*/ void BaseWindow::SetDirtyById(WindowClass cls, WindowNumber num)
{
	BaseWindow *w = FindById(cls, num);
	if (w != NULL) w->SetDirty();
}

void CDECL BaseWindow::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
{
	va_list wdg_list;

	va_start(wdg_list, widgets);

	while (widgets != WIDGET_LIST_END) {
		SetWidgetDisabledState(widgets, disab_stat);
		widgets = va_arg(wdg_list, int);
	}

	va_end(wdg_list);
}

void CDECL BaseWindow::SetWidgetsHiddenState(bool hidden_stat, int widgets, ...)
{
	va_list wdg_list;

	va_start(wdg_list, widgets);

	while (widgets != WIDGET_LIST_END) {
		SetWidgetHiddenState(widgets, hidden_stat);
		widgets = va_arg(wdg_list, int);
	}

	va_end(wdg_list);
}

void CDECL BaseWindow::SetWidgetsLoweredState(bool lowered_stat, int widgets, ...)
{
	va_list wdg_list;

	va_start(wdg_list, widgets);

	while (widgets != WIDGET_LIST_END) {
		SetWidgetLoweredState(widgets, lowered_stat);
		widgets = va_arg(wdg_list, int);
	}

	va_end(wdg_list);
}

void BaseWindow::RaiseButtons()
{
	uint i;

	for (i = 0; i < widget_count; i++) {
		if (IsWidgetLowered(i)) {
			RaiseWidget(i);
			this->InvalidateWidget(i);
		}
	}
}

void BaseWindow::HandleButtonClick(byte widget)
{
	LowerWidget(widget);
	autorepeat_timeout = 5;
	InvalidateWidget(widget);
}

/** On clicking on a window, make it the frontmost window of all. However
* there are certain windows that always need to be on-top; these include
* - Toolbar, Statusbar (always on)
* - New window, Chatbar (only if open)
* The window is marked dirty for a repaint if the window is actually moved
* @param w window that is put into the foreground
* @return pointer to the window, the same as the input pointer
*/
void BaseWindow::BringToFront()
{
	WindowPtr wp = this;
	WindowList::Iterator it = BaseWindow::s_list.Find(this);
	BaseWindow::s_list.Remove(this);
	BaseWindow::s_list.Add(this);

	SetDirty();
}

/** Find a window and make it the top-window on the screen. The window
* gets a white border for a brief period of time to visualize its
* "activation"
* @return a pointer to the window thus activated */
/*static*/ BaseWindow* BaseWindow::BringToFrontById(WindowClass cls, WindowNumber num)
{
	BaseWindow *w = FindById(cls, num);
	if (w != NULL) {
		w->flash_timeout = 3;
		w->BringToFront();
	}
	return w;
}

static bool _dragging_window;

void BaseWindow::StartDrag()
{
	flags4 |= WF_DRAGGING;
	_dragging_window = true;

	_drag_delta.x = Left() - _cursor.pos.x;
	_drag_delta.y = Top()  - _cursor.pos.y;

	BringToFront();
	DeleteWindowById(WC_DROPDOWN_MENU, 0);
}

bool BaseWindow::ContinueDrag()
{
	const OldWidget *t = &widget[1]; // the title bar ... ugh
	const BaseWindow *v;
	int x;
	int y;
	int nx;
	int ny;

	// Stop the dragging if the left mouse button was released
	if (!_left_button_down) {
		flags4 &= ~WF_DRAGGING;
		_dragging_window = false;
		return false;
	}

	SetDirty();

	x = _cursor.pos.x + _drag_delta.x;
	y = _cursor.pos.y + _drag_delta.y;
	nx = x;
	ny = y;

	if (_patches.window_snap_radius != 0) {
		BaseWindow *v;

		int hsnap = _patches.window_snap_radius;
		int vsnap = _patches.window_snap_radius;
		int delta;

		FOR_ALL_WINDOWS(v) {
			if (v == this) continue; // Don't snap at yourself

			if (y + Height() > v->Top() && y < v->Top() + v->Height()) {
				// Your left border <-> other right border
				delta = abs(v->Left() + v->Width() - x);
				if (delta <= hsnap) {
					nx = v->Left() + v->Width();
					hsnap = delta;
				}

				// Your right border <-> other left border
				delta = abs(v->Left() - x - Width());
				if (delta <= hsnap) {
					nx = v->Left() - Width();
					hsnap = delta;
				}
			}

			if (Top() + Height() >= v->Top() && Top() <= v->Top() + v->Height()) {
				// Your left border <-> other left border
				delta = abs(v->Left() - x);
				if (delta <= hsnap) {
					nx = v->Left();
					hsnap = delta;
				}

				// Your right border <-> other right border
				delta = abs(v->Left() + v->Width() - x - Width());
				if (delta <= hsnap) {
					nx = v->Left() + v->Width() - Width();
					hsnap = delta;
				}
			}

			if (x + Width() > v->Left() && x < v->Left() + v->Width()) {
				// Your top border <-> other bottom border
				delta = abs(v->Top() + v->Height() - y);
				if (delta <= vsnap) {
					ny = v->Top() + v->Height();
					vsnap = delta;
				}

				// Your bottom border <-> other top border
				delta = abs(v->Top() - y - Height());
				if (delta <= vsnap) {
					ny = v->Top() - Height();
					vsnap = delta;
				}
			}

			if (Left() + Width() >= v->Left() && Left() <= v->Left() + v->Width()) {
				// Your top border <-> other top border
				delta = abs(v->Top() - y);
				if (delta <= vsnap) {
					ny = v->Top();
					vsnap = delta;
				}

				// Your bottom border <-> other bottom border
				delta = abs(v->Top() + v->Height() - y - Height());
				if (delta <= vsnap) {
					ny = v->Top() + v->Height() - Height();
					vsnap = delta;
				}
			}
		}
	}

	// Make sure the window doesn't leave the screen
	// 13 is the height of the title bar
	nx = clamp(nx, 13 - t->right, _screen.width - 13 - t->left);
	ny = clamp(ny, 0, _screen.height - 13);

	// Make sure the title bar isn't hidden by behind the main tool bar
	v = BaseWindow::FindById(WC_MAIN_TOOLBAR, 0);
	if (v != NULL) {
		int v_bottom = v->Top() + v->Height();
		int v_right = v->Left() + v->Width();
		if (ny + t->top >= v->Top() && ny + t->top < v_bottom) {
			if ((v->Left() < 13 && nx + t->left < v->Left()) ||
				(v_right > _screen.width - 13 && nx + t->right > v_right)) {
					ny = v_bottom;
			} else {
				if (nx + t->left > v->Left() - 13 &&
					nx + t->right < v_right + 13) {
						if (Top() >= v_bottom) {
							ny = v_bottom;
						} else if (Left() < nx) {
							nx = v->Left() - 13 - t->left;
						} else {
							nx = v_right + 13 - t->right;
						}
				}
			}
		}
	}

	if (viewport != NULL) {
		viewport->left += nx - Left();
		viewport->top  += ny - Top();
	}
	SetTopLeft(Point16(nx, ny));

	SetDirty();
	return false;
}

void BaseWindow::StartSizing()
{
	flags4 |= WF_SIZING;
	_dragging_window = true;

	_drag_delta.x = _cursor.pos.x;
	_drag_delta.y = _cursor.pos.y;

	BringToFront();
	DeleteWindowById(WC_DROPDOWN_MENU, 0);
}

bool BaseWindow::ContinueSizing()
{
	WindowEvent e;
	int x, y;

	/* Stop the sizing if the left mouse button was released */
	if (!_left_button_down) {
		flags4 &= ~WF_SIZING;
		SetDirty();
		_dragging_window = false;
		return false;
	}

	x = _cursor.pos.x - _drag_delta.x;
	y = _cursor.pos.y - _drag_delta.y;

	/* 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 (resize.step_width > 1) x -= x % (int)resize.step_width;

	if (resize.step_height > 1) y -= y % (int)resize.step_height;

	/* Check if we don't go below the minimum set size */
	if (Width() + x < (int)resize.width)
		x = resize.width - Width();
	if (Height() + y < (int)resize.height)
		y = resize.height - Height();

	/* BaseWindow already on size */
	if (x == 0 && y == 0) return false;

	/* Now find the new cursor pos.. this is NOT _cursor, because
	we move in steps. */
	_drag_delta.x += x;
	_drag_delta.y += y;

	/* Resize sets both pre- and after-size to dirty for redrawal */
	this->Resize(x, y);

	e.event = WE_RESIZE;
	e.we.sizing.size.x = x + Width();
	e.we.sizing.size.y = y + Height();
	e.we.sizing.diff.x = x;
	e.we.sizing.diff.y = y;
	wndproc(this, &e);
	return false;

}

/*static*/ bool BaseWindow::HandleWindowDragging(void)
{
	// Get out immediately if no window is being dragged at all.
	if (!_dragging_window) return true;

	// Otherwise find the window...
	BaseWindow *w;
	FOR_ALL_WINDOWS(w) {
		if (w->flags4 & WF_DRAGGING) {
			return w->ContinueDrag();
		} else if (w->flags4 & WF_SIZING) {
			return w->ContinueSizing();
		}
	}
	_dragging_window = false;
	return false;
}



void BaseWindow::DispatchLeftClickEvent(int x, int y)
{
	WindowEvent e;
	const OldWidget *wi;

	e.we.click.pt.x = x;
	e.we.click.pt.y = y;
	e.event = WE_CLICK;

	if (desc_flags & WDF_DEF_WIDGET) {
		e.we.click.widget = GetWidgetFromPos(x, y);
		if (e.we.click.widget < 0) {
			OldWndProc(&e); // try to handle it by new window infrastructure
			return; /* exit if clicked outside of widgets */
		}

		/* don't allow any interaction if the button has been disabled */
		if (IsWidgetDisabled(e.we.click.widget)) return;

		wi = &widget[e.we.click.widget];

		if (wi->type & WWB_MASK) {
			/* special widget handling for buttons*/
			switch (wi->type) {
				case WWT_PANEL   | WWB_PUSHBUTTON: /* WWT_PUSHBTN */
				case WWT_IMGBTN  | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */
				case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */
					HandleButtonClick(e.we.click.widget);
					break;
				default:
					break;
			}
		} else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
			ScrollbarClickHandler(wi, e.we.click.pt.x, e.we.click.pt.y);
		}

		if (desc_flags & WDF_STD_BTN) {
			if (e.we.click.widget == 0) { /* 'X' */
				Close();
				return;
			}

			if (e.we.click.widget == 1) { /* 'Title bar' */
				StartDrag();
				return;
			}
		}

		if (desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
			StartSizing();
			InvalidateWidget(e.we.click.widget);
			return;
		}

		if (desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) {
			flags4 ^= WF_STICKY;
			InvalidateWidget(e.we.click.widget);
			return;
		}
	}

	wndproc(this, &e);
}

void BaseWindow::DispatchRightClickEvent(int x, int y)
{
	WindowEvent e;
	e.event = WE_RCLICK;
	e.we.click.pt.x = x;
	e.we.click.pt.y = y;

	/* default tooltips handler? */
	if (desc_flags & WDF_STD_TOOLTIPS) {
		e.we.click.widget = GetWidgetFromPos(x, y);
		if (e.we.click.widget < 0) {
			OldWndProc(&e); // try to handle it by new window infrastructure
			return; /* exit if clicked outside of widgets */
		}
		if (widget[e.we.click.widget].tooltips != 0) {
			GuiShowTooltips(widget[e.we.click.widget].tooltips);
			return;
		}
	}

	wndproc(this, &e);
}

/** Dispatch the mousewheel-action to the window which will scroll any
 * compatible scrollbars if the mouse is pointed over the bar or its contents
 * @param *w BaseWindow
 * @param widget the widget where the scrollwheel was used
 * @param wheel scroll up or down
 */
void BaseWindow::DispatchMouseWheelEvent(int widget_idx, int wheel)
{
	const OldWidget *wi1, *wi2;
	Scrollbar *sb;

	if (widget_idx < 0) return;

	wi1 = &widget[widget_idx];
	wi2 = &widget[widget_idx + 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 */
	if ((sb = &vscroll,  wi1->type == WWT_SCROLLBAR)  || (sb = &vscroll2, wi1->type == WWT_SCROLL2BAR)  ||
			(sb = &vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &vscroll, wi2->type == WWT_SCROLLBAR) ) {

		if (sb->count > sb->cap) {
			int pos = clamp(sb->pos + wheel, 0, sb->count - sb->cap);
			if (pos != sb->pos) {
				sb->pos = pos;
				SetDirty();
			}
		}
	}
}

static void DrawOverlappedWindow(WindowList::Iterator wit, int left, int top, int right, int bottom)
{
	BaseWindow *w = (*wit).w;

	for (WindowList::Iterator vit = wit; ++vit != BaseWindow::s_list.m_list.end(); ) {
		const BaseWindow *v = (*vit).w;
		int x;

		if (right  > v->Left() &&
				bottom > v->Top() &&
				left < v->Left() + v->Width() &&
				top  < v->Top() + v->Height()) {
			if (left < (x = v->Left())) {
				DrawOverlappedWindow(wit, left, top, x, bottom);
				DrawOverlappedWindow(wit, x, top, right, bottom);
				return;
			}

			if (right > (x=v->Left() + v->Width())) {
				DrawOverlappedWindow(wit, left, top, x, bottom);
				DrawOverlappedWindow(wit, x, top, right, bottom);
				return;
			}

			if (top < (x=v->Top())) {
				DrawOverlappedWindow(wit, left, top, right, x);
				DrawOverlappedWindow(wit, left, x, right, bottom);
				return;
			}

			if (bottom > (x=v->Top() + v->Height())) {
				DrawOverlappedWindow(wit, left, top, right, x);
				DrawOverlappedWindow(wit, left, x, right, bottom);
				return;
			}

			return;
		}
	}

	{
		DrawPixelInfo *dp = _cur_dpi;
		dp->width = right - left;
		dp->height = bottom - top;
		dp->left = left - w->Left();
		dp->top = top - w->Top();
		dp->pitch = _screen.pitch;
		dp->dst_ptr = _screen.dst_ptr + top * _screen.pitch + left;
		dp->zoom = 0;
		w->CallEventNP(WE_PAINT);
	}
}

void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
{
	DrawPixelInfo bk;
	_cur_dpi = &bk;

	const BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		if (right > w->Left() &&
			bottom > w->Top() &&
			left < w->Left() + w->Width() &&
			top < w->Top() + w->Height()) {
				DrawOverlappedWindow(it, left, top, right, bottom);
		}
	}
}


void BaseWindow::CallEventNP(int event)
{
	WindowEvent e;

	e.event = event;
	wndproc(this, &e);
}

void BaseWindow::OldWndProc(WindowEvent *e)
{
	gui::EventBase *evt = NULL;

	switch(e->event) {
		case WE_CREATE:
			evt = new gui::EventT<gui::EVT_CREATE>();
			break;

		case WE_DESTROY:
			evt = new gui::EventT<gui::EVT_DESTROY>();
			break;

		case WE_PAINT:
			evt = new gui::EventT<gui::EVT_PAINT>();
			break;

		case WE_KEYPRESS:
			evt = new gui::EventT<gui::EVT_KEYPRESS>(e->we.keypress.cont, e->we.keypress.key, e->we.keypress.keycode);
			break;

		case WE_CLICK:
			evt = new gui::EventT<gui::EVT_LBUTTON_DOWN>(e->we.click.pt);
			break;

		case WE_RCLICK:
			evt = new gui::EventT<gui::EVT_RBUTTON_DOWN>(e->we.click.pt);
			break;

		default:
			break;
	}
	if (evt == NULL) return;
	DispatchEvent(*evt);
	delete evt;
}

/*static*/ void BaseWindow::DefaultWndProc(BaseWindow *w, WindowEvent *e)
{
	w->OldWndProc(e);
}


/** Find the z-value of a window. A window must already be open
 * or the behaviour is undefined but function should never fail */
//WindowList::Iterator FindWindowZPosition(const BaseWindow *w)
//{
//	BaseWindow *wz;
//	FOR_ALL_WINDOWS(wz) {
//		if (wz == w) return it;
//	}
//
//	DEBUG(misc, 3, "BaseWindow (cls %d, number %d) is not open, probably removed by recursive calls",
//		w->window_class, w->window_number);
//	return BaseWindow::s_list.m_list.end();
//}

//BaseWindow *FindWindowById(WindowClass cls, WindowNumber number)
//{
//	BaseWindow *w;
//
//	FOR_ALL_WINDOWS(w) {
//		if (w->window_class == cls && w->window_number == number) return w;
//	}
//
//	return NULL;
//}

void DeleteWindowById(WindowClass cls, WindowNumber number)
{
	BaseWindow *w = BaseWindow::FindById(cls, number);
	if (w != NULL) w->Close();
}

void DeleteWindowByClass(WindowClass cls)
{

restart_search:
	/* 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 */
	BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		if (w->window_class == cls) {
			w->Close();
			goto restart_search;
		}
	}
}

/** Delete all windows of a player. We identify windows of a player
 * by looking at the caption colour. If it is equal to the player ID
 * then we say the window belongs to the player and should be deleted
 * @param id PlayerID player identifier */
void DeletePlayerWindows(PlayerID id)
{

restart_search:
	/* 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 */
	BaseWindow *w;
	FOR_ALL_WINDOWS(w) {
		if (w->caption_color == id) {
			w->Close();
			goto restart_search;
		}
	}

	/* Also delete the player specific windows, that don't have a player-colour */
	DeleteWindowById(WC_BUY_COMPANY, id);
}

/** Change the owner of all the windows one player can take over from another
 * player in the case of a company merger. Do not change ownership of windows
 * that need to be deleted once takeover is complete
 * @param old_player PlayerID of original owner of the window
 * @param new_player PlayerID of the new owner of the window */
void ChangeWindowOwner(PlayerID old_player, PlayerID new_player)
{
	BaseWindow *w = NULL;

	FOR_ALL_WINDOWS(w) {

		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;

		w->caption_color = new_player;
	}
}

/** We have run out of windows, so find a suitable candidate for replacement.
 * Keep all important windows intact. These are
 * - Main window (gamefield), Toolbar, Statusbar (always on)
 * - News window, Chatbar (when on)
 * - Any sticked windows since we wanted to keep these
 * @return w pointer to the window that is going to be deleted
 */
//static BaseWindow *FindDeletableWindow(void)
//{
//	BaseWindow *w;
//	FOR_ALL_WINDOWS(w) {
//		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w) && !(w->flags4 & WF_STICKY)) {
//			return w;
//		}
//	}
//	FOR_ALL_WINDOWS(w) {
//		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) {
//			return w;
//		}
//	}
//	return NULL;
//}

/** A window must be freed, and all are marked as important windows. Ease the
 * restriction a bit by allowing to delete sticky windows. Keep important/vital
 * windows intact (Main window, Toolbar, Statusbar, News BaseWindow, Chatbar)
 * Start finding an appropiate candidate from the lowest z-values (bottom)
 * @see FindDeletableWindow()
 * @return w Pointer to the window that is being deleted
 */
//static BaseWindow *ForceFindDeletableWindow(void)
//{
//	BaseWindow* const *wz;
//
//	for (wz = _z_windows;; wz++) {
//		BaseWindow *w = *wz;
//		assert(wz < _last_z_window);
//		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w;
//	}
//}

bool BaseWindow::IsOfPrototype(const OldWidget *widgets) const
{
	return (original_widget == widgets);
}

/* Copies 'widget' to 'w->widget' to allow for resizable windows */
void BaseWindow::AssignWidget(const OldWidget *widget_array)
{
	original_widget = widget_array;

	if (widget_array != NULL) {
		uint index = 1;
		const OldWidget *wi;

		for (wi = widget_array; wi->type != WWT_LAST; wi++) index++;

		widget = ReallocT(widget, index);
		memcpy(widget, widget_array, sizeof(*widget) * index);
		widget_count = index - 1;
	} else {
		widget = NULL;
		widget_count = 0;
	}
}

//static BaseWindow *FindFreeWindow(void)
//{
//	BaseWindow *w;
//
//	for (w = _windows; w < endof(_windows); w++) {
//		BaseWindow* const *wz;
//		bool window_in_use = false;
//
//		FOR_ALL_WINDOWS(wz) {
//			if (*wz == w) {
//				window_in_use = true;
//				break;
//			}
//		}
//
//		if (!window_in_use) return w;
//	}
//
//	assert(_last_z_window == endof(_z_windows));
//	return NULL;
//}


typedef struct SizeRect {
	int left,top,width,height;
} SizeRect;


static SizeRect _awap_r;

static bool IsGoodAutoPlace1(int left, int top)
{
	int right,bottom;

	_awap_r.left= left;
	_awap_r.top = top;
	right = _awap_r.width + left;
	bottom = _awap_r.height + top;

	if (left < 0 || top < 22 || right > _screen.width || bottom > _screen.height)
		return false;

	// Make sure it is not obscured by any window.
	BaseWindow *w;
	FOR_ALL_WINDOWS(w) {
		if (w->window_class == WC_MAIN_WINDOW) continue;

		if (right > w->Left() &&
				w->Left() + w->Width() > left &&
				bottom > w->Top() &&
				w->Top() + w->Height() > top) {
			return false;
		}
	}

	return true;
}

static bool IsGoodAutoPlace2(int left, int top)
{
	int width,height;

	_awap_r.left= left;
	_awap_r.top = top;
	width = _awap_r.width;
	height = _awap_r.height;

	if (left < -(width>>2) || left > _screen.width - (width>>1)) return false;
	if (top < 22 || top > _screen.height - (height>>2)) return false;

	// Make sure it is not obscured by any window.
	BaseWindow *w;
	FOR_ALL_WINDOWS(w) {
		if (w->window_class == WC_MAIN_WINDOW) continue;

		if (left + width > w->Left() &&
				w->Left() + w->Width() > left &&
				top + height > w->Top() &&
				w->Top() + w->Height() > top) {
			return false;
		}
	}

	return true;
}

static Point GetAutoPlacePosition(int width, int height)
{
	Point pt;
	BaseWindow *w = NULL;

	_awap_r.width = width;
	_awap_r.height = height;

	if (IsGoodAutoPlace1(0, 24)) goto ok_pos;

	FOR_ALL_WINDOWS(w) {
		if (w->window_class == WC_MAIN_WINDOW) continue;

		if (IsGoodAutoPlace1(w->Left() + w->Width() + 2, w->Top())) goto ok_pos;
		if (IsGoodAutoPlace1(w->Left() - width - 2, w->Top())) goto ok_pos;
		if (IsGoodAutoPlace1(w->Left(), w->Top() + w->Height() + 2)) goto ok_pos;
		if (IsGoodAutoPlace1(w->Left(), w->Top() - height - 2)) goto ok_pos;
		if (IsGoodAutoPlace1(w->Left() + w->Width() + 2, w->Top() + w->Height() - height)) goto ok_pos;
		if (IsGoodAutoPlace1(w->Left() - width - 2, w->Top() + w->Height() - height)) goto ok_pos;
		if (IsGoodAutoPlace1(w->Left() + w->Width() - width, w->Top() + w->Height() + 2)) goto ok_pos;
		if (IsGoodAutoPlace1(w->Left() + w->Width() - width, w->Top() - height - 2)) goto ok_pos;
	}

	FOR_ALL_WINDOWS(w) {
		if (w->window_class == WC_MAIN_WINDOW) continue;

		if (IsGoodAutoPlace2(w->Left() + w->Width() + 2, w->Top())) goto ok_pos;
		if (IsGoodAutoPlace2(w->Left() - width - 2, w->Top())) goto ok_pos;
		if (IsGoodAutoPlace2(w->Left(), w->Top() + w->Height() + 2)) goto ok_pos;
		if (IsGoodAutoPlace2(w->Left(), w->Top() - height - 2)) goto ok_pos;
	}

	{
		int left=0,top=24;

restart:;
		FOR_ALL_WINDOWS(w) {
			if (w->Left() == left && w->Top() == top) {
				left += 5;
				top += 5;
				goto restart;
			}
		}

		pt.x = left;
		pt.y = top;
		return pt;
	}

ok_pos:;
	pt.x = _awap_r.left;
	pt.y = _awap_r.top;
	return pt;
}

///**
// * Open a new window.
// * @param *desc The pointer to the WindowDesc to be created
// * @return @see BaseWindow pointer of the newly created window
// */
//BaseWindow *AllocateWindowDesc(const WindowDesc *desc)
//{
//	return LocalAllocateWindowDesc(desc, 0);
//}

void InitWindowSystem(void)
{
	IConsoleClose();

	//memset(&_windows, 0, sizeof(_windows));
	//_last_z_window = _z_windows;
	InitViewports();
	_no_scroll = 0;
}

void UnInitWindowSystem(void)
{
	BaseWindow::s_list.CloseAll();

//	BaseWindow **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);
}

void ResetWindowSystem(void)
{
	UnInitWindowSystem();
	InitWindowSystem();
	_thd.pos.x = 0;
	_thd.pos.y = 0;
	_thd.new_pos.x = 0;
	_thd.new_pos.y = 0;
}

static void DecreaseWindowCounters(void)
{
	BaseWindow *w = NULL;
	REVERSED_FOR_ALL_WINDOWS(w) {
		// Unclick scrollbar buttons if they are pressed.
		if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
			w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
			w->SetDirty();
		}
		w->CallEventNP(WE_MOUSELOOP);
	}

	REVERSED_FOR_ALL_WINDOWS(w) {
		if (w->autorepeat_timeout > 0 && (--w->autorepeat_timeout) == 0) {
			w->CallEventNP(WE_TIMEOUT);
			if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons();
		}
	}
}

BaseWindow *GetCallbackWnd(void)
{
	return BaseWindow::FindById(_thd.window_class, _thd.window_number);
}

static void HandlePlacePresize(void)
{
	BaseWindow *w;
	WindowEvent e;

	if (_special_mouse_mode != WSM_PRESIZE) return;

	w = GetCallbackWnd();
	if (w == NULL) return;

	e.we.place.pt = GetTileBelowCursor();
	if (e.we.place.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);
}

static bool HandleDragDrop(void)
{
	BaseWindow *w;
	WindowEvent e;

	if (_special_mouse_mode != WSM_DRAGDROP) return true;

	if (_left_button_down) return false;

	w = GetCallbackWnd();

	ResetObjectToPlace();

	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 = w->GetWidgetFromPos(e.we.dragdrop.pt.x, e.we.dragdrop.pt.y);
		w->wndproc(w, &e);
	}
	return false;
}

static bool HandlePopupMenu(void)
{
	BaseWindow *w;
	WindowEvent e;

	if (!_popup_menu_active) return true;

	w = BaseWindow::FindById(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(void)
{
	Point ptGlobal(_cursor.pos.x, _cursor.pos.y);
	/* try if the event is being captured */
	gui::EvtMouseOver evt_capture(ptGlobal);
	if (gui::EventCaptureStack::HandleEvent(evt_capture)) {
		return true;
	}

	static WindowClass  last_cls;
	static WindowNumber last_num;

	BaseWindow *w;
	WindowEvent e;

	BaseWindow *last_w = BaseWindow::FindById(last_cls, last_num);

	w = BaseWindow::FindFromPt(_cursor.pos.x, _cursor.pos.y);

	// We changed window, put a MOUSEOVER event to the last window
	if (last_w != NULL && last_w != w) {
		e.event = WE_MOUSEOVER;
		e.we.mouseover.pt.x = -1;
		e.we.mouseover.pt.y = -1;
		if (last_w->wndproc) last_w->wndproc(last_w, &e);
	}

	if (w == NULL) {
		last_cls = WC_NONE;
		last_num = -1;
		return false;
	}

	last_cls = w->window_class;
	last_num = w->window_number;

	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();
		if (w->widget != NULL) {
			e.we.mouseover.widget = w->GetWidgetFromPos(e.we.mouseover.pt.x, e.we.mouseover.pt.y);
		}
		w->wndproc(w, &e);
	}

	// Mouseover never stops execution
	return true;
}

/** Update all the widgets of a window based on their resize flags
 * Both the areas of the old window and the new sized window are set dirty
 * ensuring proper redrawal.
 * @param x delta x-size of changed window (positive if larger, etc.(
 * @param y delta y-size of changed window */
void BaseWindow::Resize(int x, int y)
{
	OldWidget *wi;
	bool resize_height = false;
	bool resize_width = false;

	if (x == 0 && y == 0) return;

	this->SetDirty();
	for (wi = widget; wi->type != WWT_LAST; wi++) {
		/* Isolate the resizing flags */
		byte rsizeflag = GB(wi->m_display_flags, 0, 4);

		if (rsizeflag == RESIZE_NONE) continue;

		/* Resize the widget based on its resize-flag */
		if (rsizeflag & RESIZE_LEFT) {
			wi->left += x;
			resize_width = true;
		}

		if (rsizeflag & RESIZE_RIGHT) {
			wi->right += x;
			resize_width = true;
		}

		if (rsizeflag & RESIZE_TOP) {
			wi->top += y;
			resize_height = true;
		}

		if (rsizeflag & RESIZE_BOTTOM) {
			wi->bottom += y;
			resize_height = true;
		}
	}

	/* We resized at least 1 widget, so let's resize the window totally */
	if (resize_width)  SetWidth(Width() + x);
	if (resize_height) SetHeight(Height() + y);

	SetDirty();
}

static bool HandleScrollbarScrolling(void)
{
	int i;
	int pos;
	Scrollbar *sb;

	// Get out quickly if no item is being scrolled
	if (!_scrolling_scrollbar) return true;

	// Find the scrolling window
	BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		if (w->flags4 & WF_SCROLL_MIDDLE) {
			// Abort if no button is clicked any more.
			if (!_left_button_down) {
				w->flags4 &= ~WF_SCROLL_MIDDLE;
				w->SetDirty();
				break;
			}

			if (w->flags4 & WF_HSCROLL) {
				sb = &w->hscroll;
				i = _cursor.pos.x - _cursorpos_drag_start.x;
			} else if (w->flags4 & WF_SCROLL2){
				sb = &w->vscroll2;
				i = _cursor.pos.y - _cursorpos_drag_start.y;
			} else {
				sb = &w->vscroll;
				i = _cursor.pos.y - _cursorpos_drag_start.y;
			}

			// 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));
			if (pos != sb->pos) {
				sb->pos = pos;
				w->SetDirty();
			}
			return false;
		}
	}

	_scrolling_scrollbar = false;
	return false;
}

static bool HandleViewportScroll(void)
{
	WindowEvent e;
	BaseWindow *w;

	if (!_scrolling_viewport) return true;

	w = BaseWindow::FindFromPt(_cursor.pos.x, _cursor.pos.y);

	if (!_right_button_down || w == NULL) {
		_cursor.fix_at = false;
		_scrolling_viewport = false;
		return true;
	}

	if (_patches.reverse_scroll) {
		e.we.scroll.delta.x = -_cursor.delta.x;
		e.we.scroll.delta.y = -_cursor.delta.y;
	} else {
		e.we.scroll.delta.x = _cursor.delta.x;
		e.we.scroll.delta.y = _cursor.delta.y;
	}

	/* Create a scroll-event and send it to the window */
	e.event = WE_SCROLL;
	w->wndproc(w, &e);

	_cursor.delta.x = 0;
	_cursor.delta.y = 0;
	return false;
}

/** Check if a window can be made top-most window, and if so do
 * it. If a window does not obscure any other windows, it will not
 * be brought to the foreground. Also if the only obscuring windows
 * are so-called system-windows, the window will not be moved.
 * The function will return false when a child window of this window is a
 * modal-popup; function returns a false and child window gets a white border
 * @param w BaseWindow to bring on-top
 * @return false if the window has an active modal child, true otherwise */
static bool MaybeBringWindowToFront(BaseWindow *w)
{
	bool bring_to_front = false;

	if (w->window_class == WC_MAIN_WINDOW ||
			w->IsVital() ||
			w->window_class == WC_TOOLTIPS ||
			w->window_class == WC_DROPDOWN_MENU) {
		return true;
	}

	WindowList::Iterator wit = BaseWindow::s_list.Find(w);
	for (WindowList::Iterator it = wit; it != BaseWindow::s_list.m_list.end(); it++) {
		BaseWindow *u = (*it).w;

		/* A modal child will prevent the activation of the parent window */
		if (u->parent == w && (u->desc_flags & WDF_MODAL)) {
			u->flash_timeout = 3;
			u->SetDirty();
			return false;
		}

		if (u->window_class == WC_MAIN_WINDOW ||
				u->IsVital() ||
				u->window_class == WC_TOOLTIPS ||
				u->window_class == WC_DROPDOWN_MENU) {
			continue;
		}

		/* BaseWindow sizes don't interfere, leave z-order alone */
		if (w->Left() + w->Width() <= u->Left() ||
				u->Left() + u->Width() <= w->Left() ||
				w->Top()  + w->Height() <= u->Top() ||
				u->Top() + u->Height() <= w->Top()) {
			continue;
		}

		bring_to_front = true;
	}

	if (bring_to_front) w->BringToFront();
	return true;
}

/** Send a message from one window to another. The receiving window is found by
 * @param w @see BaseWindow 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(BaseWindow *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
 */
void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, int msg, int wparam, int lparam)
{
	BaseWindow *w = BaseWindow::FindById(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)
{
	BaseWindow *w = NULL;

	FOR_ALL_WINDOWS(w) {
		if (w->window_class == wnd_class) SendWindowMessageW(w, 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. */
	bool query_open = false;

	/*
	* During the generation of the world, there might be
	* another thread that is currently building for example
	* a road. To not interfere with those tasks, we should
	* NOT change the _current_player here.
	*
	* This is not necessary either, as the only events that
	* can be handled are the 'close application' events
	*/
	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;

	// check if we have a query string window open before allowing hotkeys
	if (BaseWindow::FindById(WC_QUERY_STRING,       0) != NULL ||
			BaseWindow::FindById(WC_SEND_NETWORK_MSG,   0) != NULL ||
			BaseWindow::FindById(WC_GENERATE_LANDSCAPE, 0) != NULL ||
			BaseWindow::FindById(WC_CONSOLE,            0) != NULL ||
			BaseWindow::FindById(WC_SAVELOAD,           0) != NULL) {
		query_open = true;
	}

	// Call the event, start with the uppermost window.
	BaseWindow *w;
	REVERSED_FOR_ALL_WINDOWS(w) {
		// if a query window is open, only call the event for certain window types
		if (query_open &&
				w->window_class != WC_QUERY_STRING &&
				w->window_class != WC_SEND_NETWORK_MSG &&
				w->window_class != WC_GENERATE_LANDSCAPE &&
				w->window_class != WC_CONSOLE &&
				w->window_class != WC_SAVELOAD) {
			continue;
		}
		w->wndproc(w, &e);
		if (!e.we.keypress.cont) break;
	}

	if (e.we.keypress.cont) {
		BaseWindow *w = BaseWindow::FindById(WC_MAIN_TOOLBAR, 0);
		// When there is no toolbar w is null, check for that
		if (w != NULL) w->wndproc(w, &e);
	}
}

extern void UpdateTileSelection(void);
extern bool VpHandlePlaceSizingDrag(void);

static int _input_events_this_tick = 0;

static void HandleAutoscroll(void)
{
	BaseWindow *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;
		/* there were some inputs this tick, don't scroll ??? */
		return;
	}

	if (_patches.autoscroll && _game_mode != GM_MENU && !IsGeneratingWorld()) {
		w = BaseWindow::FindFromPt(x, y);
		if (w == NULL || w->flags4 & WF_DISABLE_VP_SCROLL) return;
		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).scrollpos_x += (x - 15) * scrollspeed << vp->zoom;
			} else if (15 - (vp->width - x) > 0) {
				WP(w, vp_d).scrollpos_x += (15 - (vp->width - x)) * scrollspeed << vp->zoom;
			}
			if (y - 15 < 0) {
				WP(w, vp_d).scrollpos_y += (y - 15) * scrollspeed << vp->zoom;
			} else if (15 - (vp->height - y) > 0) {
				WP(w,vp_d).scrollpos_y += (15 - (vp->height - y)) * scrollspeed << vp->zoom;
			}
#undef scrollspeed
		}
	}
}

void MouseLoop(int click, int mousewheel)
{
	int x,y;
	BaseWindow *w;
	ViewPort *vp;

	DecreaseWindowCounters();
	HandlePlacePresize();
	UpdateTileSelection();
	if (!VpHandlePlaceSizingDrag())      return;
	if (!HandleDragDrop())               return;
	if (!HandlePopupMenu())              return;
	if (!BaseWindow::HandleWindowDragging()) return;
	if (!HandleScrollbarScrolling())     return;
	if (!HandleViewportScroll())         return;
	if (!HandleMouseOver())              return;

	x = _cursor.pos.x;
	y = _cursor.pos.y;

	if (click == 0 && mousewheel == 0) return;

	w = BaseWindow::FindFromPt(x, y);
	if (w == NULL) return;
	if (!MaybeBringWindowToFront(w)) return;
	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) {
		WindowEvent e;

		/* Send WE_MOUSEWHEEL event to window */
		e.event = WE_MOUSEWHEEL;
		e.we.wheel.wheel = mousewheel;
		w->wndproc(w, &e);

		/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
		if (vp == NULL) w->DispatchMouseWheelEvent(w->GetWidgetFromPos(x - w->Left(), y - w->Top()), mousewheel);
	}

	if (vp != NULL) {
		switch (click) {
			case 1:
				DEBUG(misc, 2, "Cursor: 0x%X (%d)", _cursor.sprite, _cursor.sprite);
				if (_thd.place_mode != 0 &&
						// query button and place sign button work in pause mode
						_cursor.sprite != SPR_CURSOR_QUERY &&
						_cursor.sprite != SPR_CURSOR_SIGN &&
						_pause != 0 &&
						!_cheats.build_in_pause.value) {
					return;
				}

				if (_thd.place_mode == 0) {
					HandleViewportClicked(vp, x, y);
				} else {
					PlaceObject();
				}
				break;

			case 2:
				if (!(w->flags4 & WF_DISABLE_VP_SCROLL)) {
					_scrolling_viewport = true;
					_cursor.fix_at = true;
				}
				break;
		}
	} else {
		switch (click) {
			case 1: w->DispatchLeftClickEvent (x - w->Left(), y - w->Top()); break;
			case 2: w->DispatchRightClickEvent(x - w->Left(), y - w->Top()); break;
		}
	}
}

void HandleMouseEvents(void)
{
	int click;
	int mousewheel;

	/*
	 * During the generation of the world, there might be
	 * another thread that is currently building for example
	 * a road. To not interfere with those tasks, we should
	 * NOT change the _current_player here.
	 *
	 * This is not necessary either, as the only events that
	 * can be handled are the 'close application' events
	 */
	if (!IsGeneratingWorld()) _current_player = _local_player;

	// Mouse event?
	click = 0;
	if (_left_button_down && !_left_button_clicked) {
		_left_button_clicked = true;
		click = 1;
		_input_events_this_tick++;
	} else if (_right_button_clicked) {
		_right_button_clicked = false;
		click = 2;
		_input_events_this_tick++;
	}

	mousewheel = 0;
	if (_cursor.wheel) {
		mousewheel = _cursor.wheel;
		_cursor.wheel = 0;
		_input_events_this_tick++;
	}

	MouseLoop(click, mousewheel);
}

void InputLoop(void)
{
	HandleMouseEvents();
	HandleAutoscroll();
}

void UpdateWindows(void)
{
	static int we4_timer = 0;
	int t = we4_timer + 1;

	BaseWindow *w;
	if (t >= 100) {
		REVERSED_FOR_ALL_WINDOWS(w) {
			w->CallEventNP(WE_4);
		}
		t = 0;
	}
	we4_timer = t;

	REVERSED_FOR_ALL_WINDOWS(w) {
		if (w->flash_timeout > 0 && (--w->flash_timeout) == 0) w->SetDirty();
		w->SetDirty();
	}

	DrawDirtyBlocks();

	FOR_ALL_WINDOWS(w) {
		if (w->viewport != NULL) UpdateViewportPosition(w);
	}
	DrawTextMessage();
	// Redraw mouse cursor in case it was hidden
	DrawMouseCursor();
}


int BaseWindow::GetMenuItemIndex(int x, int y) const
{
	if ((x -= Left()) >= 0 && x < Width() && (y -= Top() + 1) >= 0) {
		y /= 10;

		if (y < WP(this, const menu_d).item_count &&
				!HASBIT(WP(this, const menu_d).disabled_items, y)) {
			return y;
		}
	}
	return -1;
}

void InvalidateWindow(WindowClass cls, WindowNumber number)
{
	BaseWindow *w = NULL;

	FOR_ALL_WINDOWS(w) {
		if (w->window_class == cls && w->window_number == number) w->SetDirty();
	}
}

void BaseWindow::InvalidateWidget(byte widget_index) const
{
	const OldWidget *wi = &widget[widget_index];

	/* Don't redraw the window if the widget is invisible or of no-type */
	if (wi->type == WWT_EMPTY || IsWidgetHidden(widget_index)) return;

	SetDirtyBlocks(Left() + wi->left, Top() + wi->top, Left() + wi->right + 1, Top() + wi->bottom + 1);
}

void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index)
{
	BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		if (w->window_class == cls && w->window_number == number) {
			w->InvalidateWidget(widget_index);
		}
	}
}

void InvalidateWindowClasses(WindowClass cls)
{
	BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		if (w->window_class == cls) w->SetDirty();
	}
}

void BaseWindow::InvalidateData()
{
	this->CallEventNP(WE_INVALIDATE_DATA);
	this->SetDirty();
}

void InvalidateWindowData(WindowClass cls, WindowNumber number)
{
	BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		if (w->window_class == cls && w->window_number == number) w->InvalidateData();
	}
}

void InvalidateWindowClassesData(WindowClass cls)
{
	BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		if (w->window_class == cls) w->InvalidateData();
	}
}

void CallWindowTickEvent(void)
{
	BaseWindow *w = NULL;
	REVERSED_FOR_ALL_WINDOWS(w) {
		w->CallEventNP(WE_TICK);
	}
}

void BaseWindow::DeleteNonVitalWindows(void)
{

restart_search:
	/* 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 */
	BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		if (w->window_class != WC_MAIN_WINDOW &&
				w->window_class != WC_SELECT_GAME &&
				w->window_class != WC_MAIN_TOOLBAR &&
				w->window_class != WC_STATUS_BAR &&
				w->window_class != WC_TOOLBAR_MENU &&
				w->window_class != WC_TOOLTIPS &&
				(w->flags4 & WF_STICKY) == 0) { // do not delete windows which are 'pinned'

			w->Close();
			goto restart_search;
		}
	}
}

/* It is possible that a stickied window gets to a position where the
 * 'close' button is outside the gaming area. You cannot close it then; except
 * with this function. It closes all windows calling the standard function,
 * then, does a little hacked loop of closing all stickied windows. Note
 * that standard windows (status bar, etc.) are not stickied, so these aren't affected */
void BaseWindow::DeleteAllNonVitalWindows(void)
{
	/* Delete every window except for stickied ones, then sticky ones as well */
	DeleteNonVitalWindows();

restart_search:
	/* 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 */
	BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		if (w->flags4 & WF_STICKY) {
			w->Close();
			goto restart_search;
		}
	}
}

/* Delete all always on-top windows to get an empty screen */
void BaseWindow::HideVitalWindows(void)
{
	DeleteWindowById(WC_TOOLBAR_MENU, 0);
	DeleteWindowById(WC_MAIN_TOOLBAR, 0);
	DeleteWindowById(WC_STATUS_BAR, 0);
}

int PositionMainToolbar(BaseWindow *w)
{
	DEBUG(misc, 5, "Repositioning Main Toolbar...");

	if (w == NULL || w->window_class != WC_MAIN_TOOLBAR) {
		w = BaseWindow::FindById(WC_MAIN_TOOLBAR, 0);
	}

	switch (_patches.toolbar_pos) {
		case 1:  w->SetLeft((_screen.width - w->Width()) / 2); break;
		case 2:  w->SetLeft(_screen.width - w->Width()); break;
		default: w->SetLeft(0);
	}
	SetDirtyBlocks(0, 0, _screen.width, w->Height()); // invalidate the whole top part
	return w->Left();
}

void RelocateAllWindows(int neww, int newh)
{
	BaseWindow *w = NULL;
	FOR_ALL_WINDOWS(w) {
		int left, top;

		if (w->window_class == WC_MAIN_WINDOW) {
			ViewPort *vp = w->viewport;
			w->SetSize(Point(neww, newh));
			vp->width = neww;
			vp->height = newh;
			vp->virtual_width = neww << vp->zoom;
			vp->virtual_height = newh << vp->zoom;
			continue; // don't modify top,left
		}

		/* XXX - this probably needs something more sane. For example specying
		 * in a 'backup'-desc that the window should always be centred. */
		switch (w->window_class) {
			case WC_MAIN_TOOLBAR:
				top = w->Top();
				left = PositionMainToolbar(w); // changes toolbar orientation
				break;

			case WC_SELECT_GAME:
			case WC_GAME_OPTIONS:
			case WC_NETWORK_WINDOW:
				top = (newh - w->Height()) >> 1;
				left = (neww - w->Width()) >> 1;
				break;

			case WC_NEWS_WINDOW:
				top = newh - w->Height();
				left = (neww - w->Width()) >> 1;
				break;

			case WC_STATUS_BAR:
				top = newh - w->Height();
				left = (neww - w->Width()) >> 1;
				break;

			case WC_SEND_NETWORK_MSG:
				top = (newh - 26); // 26 = height of status bar + height of chat bar
				left = (neww - w->Width()) >> 1;
				break;

			case WC_CONSOLE:
				IConsoleResize(w);
				continue;

			default:
				left = w->Left();
				if (left + (w->Width() >> 1) >= neww) left = neww - w->Width();
				top = w->Top();
				if (top + (w->Height() >> 1) >= newh) top = newh - w->Height();
				break;
		}

		if (w->viewport != NULL) {
			w->viewport->left += left - w->Left();
			w->viewport->top += top - w->Top();
		}

		w->SetTopLeft(Point(left, top));
	}
}