Changeset - r5124:e841fbeef573
[Not reviewed]
master
0 7 0
Darkvater - 18 years ago 2006-11-18 16:47:02
darkvater@openttd.org
(svn r7205) -Fix [FS#350, SF#1560913]: Window allocation and deletion messed with the actual window
structs inside their array, and possibly invalidating pointers higher up.
Meaning that any function called within an wndproc could cause unknown/invalid pointers
once control was returned to this function. Solved by the introduction of an extra
abstraction layer, an array of z-window positions that is only concerned with the
pointers.
7 files changed with 302 insertions and 234 deletions:
0 comments (0 inline, 0 general)
sound.c
Show inline comments
 
@@ -185,30 +185,30 @@ void SndCopyToPool(void)
 
	for (i = 0; i < _file_count; i++) {
 
		FileEntry *orig = &_files[_sound_idx[i]];
 
		FileEntry *fe = AllocateFileEntry();
 

	
 
		*fe = *orig;
 
		fe->volume = _sound_base_vol[i];
 
		fe->priority = 0;
 
	}
 
}
 

	
 
static void SndPlayScreenCoordFx(SoundFx sound, int x, int y)
 
{
 
	const Window *w;
 
	const Window* const *wz;
 

	
 
	if (msf.effect_vol == 0) return;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
		const ViewPort* vp = w->viewport;
 
	FOR_ALL_WINDOWS(wz) {
 
		const ViewPort *vp = (*wz)->viewport;
 

	
 
		if (vp != NULL &&
 
				IS_INSIDE_1D(x, vp->virtual_left, vp->virtual_width) &&
 
				IS_INSIDE_1D(y, vp->virtual_top, vp->virtual_height)) {
 
			int left = (x - vp->virtual_left);
 

	
 
			StartSound(
 
				sound,
 
				left / (vp->virtual_width / ((PANNING_LEVELS << 1) + 1)) - PANNING_LEVELS,
 
				(GetSound(sound)->volume * msf.effect_vol * _vol_factor_by_zoom[vp->zoom]) >> 15
 
			);
 
			return;
station_gui.c
Show inline comments
 
@@ -139,39 +139,41 @@ typedef enum StationListFlags {
 

	
 
typedef struct plstations_d {
 
	const Station** sort_list;
 
	uint16 list_length;
 
	byte sort_type;
 
	StationListFlags flags;
 
	uint16 resort_timer;  //was byte refresh_counter;
 
} plstations_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(plstations_d));
 

	
 
void RebuildStationLists(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; ++w) {
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == WC_STATION_LIST) {
 
			WP(w, plstations_d).flags |= SL_REBUILD;
 
			SetWindowDirty(w);
 
		}
 
	}
 
}
 

	
 
void ResortStationLists(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; ++w) {
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == WC_STATION_LIST) {
 
			WP(w, plstations_d).flags |= SL_RESORT;
 
			SetWindowDirty(w);
 
		}
 
	}
 
}
 

	
 
static void BuildStationsList(plstations_d* sl, PlayerID owner, byte facilities, uint16 cargo_filter)
 
{
 
	uint n = 0;
 
	uint i, j;
 
	const Station** station_sort;
train_gui.c
Show inline comments
 
@@ -655,26 +655,25 @@ static const WindowDesc _new_rail_vehicl
 
	WC_BUILD_VEHICLE,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 
	_new_rail_vehicle_widgets,
 
	NewRailVehicleWndProc
 
};
 

	
 
void ShowBuildTrainWindow(TileIndex tile)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_BUILD_VEHICLE, tile);
 

	
 
	w = AllocateWindowDesc(&_new_rail_vehicle_desc);
 
	w->window_number = tile;
 
	w = AllocateWindowDescFront(&_new_rail_vehicle_desc, tile);
 
	w->vscroll.cap = 8;
 
	w->widget[BUILD_TRAIN_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
 

	
 
	w->resize.step_height = 14;
 
	w->resize.height = w->height - 14 * 4; // Minimum of 4 vehicles in the display
 

	
 
	if (tile != 0) {
 
		w->caption_color = GetTileOwner(tile);
 
		WP(w,buildvehicle_d).railtype = GetRailType(tile);
 
	} else {
 
		w->caption_color = _local_player;
 
		WP(w,buildvehicle_d).railtype = GetBestRailtype(GetPlayer(_local_player));
vehicle_gui.c
Show inline comments
 
@@ -93,50 +93,58 @@ static const StringID _vehicle_sort_list
 
};
 

	
 
static const StringID _rail_types_list[] = {
 
	STR_RAIL_VEHICLES,
 
	STR_ELRAIL_VEHICLES,
 
	STR_MONORAIL_VEHICLES,
 
	STR_MAGLEV_VEHICLES,
 
	INVALID_STRING_ID
 
};
 

	
 
void RebuildVehicleLists(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; ++w)
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		switch (w->window_class) {
 
		case WC_TRAINS_LIST: case WC_ROADVEH_LIST:
 
		case WC_SHIPS_LIST:  case WC_AIRCRAFT_LIST:
 
			WP(w, vehiclelist_d).l.flags |= VL_REBUILD;
 
			SetWindowDirty(w);
 
			break;
 
		default: break;
 
			case WC_TRAINS_LIST:
 
			case WC_ROADVEH_LIST:
 
			case WC_SHIPS_LIST:
 
			case WC_AIRCRAFT_LIST:
 
				WP(w, vehiclelist_d).l.flags |= VL_REBUILD;
 
				SetWindowDirty(w);
 
				break;
 
		}
 
	}
 
}
 

	
 
void ResortVehicleLists(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; ++w)
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		switch (w->window_class) {
 
		case WC_TRAINS_LIST: case WC_ROADVEH_LIST:
 
		case WC_SHIPS_LIST:  case WC_AIRCRAFT_LIST:
 
			WP(w, vehiclelist_d).l.flags |= VL_RESORT;
 
			SetWindowDirty(w);
 
			break;
 
		default: break;
 
			case WC_TRAINS_LIST:
 
			case WC_ROADVEH_LIST:
 
			case WC_SHIPS_LIST:
 
			case WC_AIRCRAFT_LIST:
 
				WP(w, vehiclelist_d).l.flags |= VL_RESORT;
 
				SetWindowDirty(w);
 
				break;
 
		}
 
	}
 
}
 

	
 
static void BuildVehicleList(vehiclelist_d* vl, PlayerID owner, StationID station, OrderID order, uint16 depot_airport_index, uint16 window_type)
 
{
 
	if (!(vl->l.flags & VL_REBUILD)) return;
 

	
 
	DEBUG(misc, 1) ("Building vehicle list for player %d station %d...", owner, station);
 

	
 
	vl->l.list_length = GenerateVehicleSortList(&vl->sort_list, &vl->length_of_sort_list, vl->vehicle_type, owner, station, order, depot_airport_index, window_type);
 

	
 
	vl->l.flags &= ~VL_REBUILD;
 
	vl->l.flags |= VL_RESORT;
viewport.c
Show inline comments
 
@@ -174,53 +174,56 @@ void AssignWindowViewport(Window *w, int
 
		pt = MapXYZToViewport(vp, x, y, GetSlopeZ(x, y));
 
	}
 

	
 
	WP(w, vp_d).scrollpos_x = pt.x;
 
	WP(w, vp_d).scrollpos_y = pt.y;
 
	w->viewport = vp;
 
	vp->virtual_left = 0;//pt.x;
 
	vp->virtual_top = 0;//pt.y;
 
}
 

	
 
static Point _vp_move_offs;
 

	
 
static void DoSetViewportPosition(const Window *w, int left, int top, int width, int height)
 
static void DoSetViewportPosition(const Window* const *wz, int left, int top, int width, int height)
 
{
 
	for (; w < _last_window; w++) {
 

	
 
	for (; wz != _last_z_window; wz++) {
 
		const Window *w = *wz;
 

	
 
		if (left + width > w->left &&
 
				w->left + w->width > left &&
 
				top + height > w->top &&
 
				w->top + w->height > top) {
 

	
 
			if (left < w->left) {
 
				DoSetViewportPosition(w, left, top, w->left - left, height);
 
				DoSetViewportPosition(w, left + (w->left - left), top, width - (w->left - left), height);
 
				DoSetViewportPosition(wz, left, top, w->left - left, height);
 
				DoSetViewportPosition(wz, left + (w->left - left), top, width - (w->left - left), height);
 
				return;
 
			}
 

	
 
			if (left + width > w->left + w->width) {
 
				DoSetViewportPosition(w, left, top, (w->left + w->width - left), height);
 
				DoSetViewportPosition(w, left + (w->left + w->width - left), top, width - (w->left + w->width - left) , height);
 
				DoSetViewportPosition(wz, left, top, (w->left + w->width - left), height);
 
				DoSetViewportPosition(wz, left + (w->left + w->width - left), top, width - (w->left + w->width - left) , height);
 
				return;
 
			}
 

	
 
			if (top < w->top) {
 
				DoSetViewportPosition(w, left, top, width, (w->top - top));
 
				DoSetViewportPosition(w, left, top + (w->top - top), width, height - (w->top - top));
 
				DoSetViewportPosition(wz, left, top, width, (w->top - top));
 
				DoSetViewportPosition(wz, left, top + (w->top - top), width, height - (w->top - top));
 
				return;
 
			}
 

	
 
			if (top + height > w->top + w->height) {
 
				DoSetViewportPosition(w, left, top, width, (w->top + w->height - top));
 
				DoSetViewportPosition(w, left, top + (w->top + w->height - top), width , height - (w->top + w->height - top));
 
				DoSetViewportPosition(wz, left, top, width, (w->top + w->height - top));
 
				DoSetViewportPosition(wz, left, top + (w->top + w->height - top), width , height - (w->top + w->height - top));
 
				return;
 
			}
 

	
 
			return;
 
		}
 
	}
 

	
 
	{
 
		int xo = _vp_move_offs.x;
 
		int yo = _vp_move_offs.y;
 

	
 
		if (abs(xo) >= width || abs(yo) >= height) {
 
@@ -285,25 +288,25 @@ static void SetViewportPosition(Window *
 
	i = left + width - _screen.width;
 
	if (i >= 0) width -= i;
 

	
 
	if (width > 0) {
 
		if (top < 0) {
 
			height += top;
 
			top = 0;
 
		}
 

	
 
		i = top + height - _screen.height;
 
		if (i >= 0) height -= i;
 

	
 
		if (height > 0) DoSetViewportPosition(w + 1, left, top, width, height);
 
		if (height > 0) DoSetViewportPosition(FindWindowZPosition(w) + 1, left, top, width, height);
 
	}
 
}
 

	
 

	
 
ViewPort *IsPtInWindowViewport(const Window *w, int x, int y)
 
{
 
	ViewPort *vp = w->viewport;
 

	
 
	if (vp != NULL &&
 
	    IS_INT_INSIDE(x, vp->left, vp->left + vp->width) &&
 
			IS_INT_INSIDE(y, vp->top, vp->top + vp->height))
 
		return vp;
window.c
Show inline comments
 
@@ -9,24 +9,28 @@
 
#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"
 

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

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

	
 
void CDECL SetWindowWidgetsDisabledState(Window *w, bool disab_stat, int widgets, ...)
 
{
 
	va_list wdg_list;
 

	
 
	va_start(wdg_list, widgets);
 

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

	
 
	va_end(wdg_list);
 
@@ -72,26 +76,26 @@ void RaiseWindowButtons(Window *w)
 
		}
 
	}
 
}
 

	
 
void HandleButtonClick(Window *w, byte widget)
 
{
 
	LowerWindowWidget(w, widget);
 
	w->flags4 |= 5 << WF_TIMEOUT_SHL;
 
	InvalidateWidget(w, widget);
 
}
 

	
 

	
 
static Window *StartWindowDrag(Window *w);
 
static Window *StartWindowSizing(Window *w);
 
static void StartWindowDrag(Window *w);
 
static void StartWindowSizing(Window *w);
 

	
 
static void DispatchLeftClickEvent(Window *w, int x, int y)
 
{
 
	WindowEvent e;
 
	const Widget *wi;
 

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

	
 
	if (w->desc_flags & WDF_DEF_WIDGET) {
 
		e.we.click.widget = GetWidgetFromPos(w, x, y);
 
@@ -113,31 +117,31 @@ static void DispatchLeftClickEvent(Windo
 
			}
 
		} 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);
 
		}
 

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

	
 
			if (e.we.click.widget == 1) { /* 'Title bar' */
 
				StartWindowDrag(w); // if not return then w = StartWindowDrag(w); to get correct pointer
 
				StartWindowDrag(w);
 
				return;
 
			}
 
		}
 

	
 
		if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
 
			StartWindowSizing(w); // if not return then w = StartWindowSizing(w); to get correct pointer
 
			StartWindowSizing(w);
 
			return;
 
		}
 

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

	
 
	w->wndproc(w, &e);
 
}
 
@@ -188,302 +192,320 @@ static void DispatchMouseWheelEvent(Wind
 
			(sb = &w->vscroll2, wi2->type == WWT_SCROLL2BAR) || (sb = &w->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;
 
				SetWindowDirty(w);
 
			}
 
		}
 
	}
 
}
 

	
 
static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom);
 
static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right, int bottom);
 

	
 
void DrawOverlappedWindowForAll(int left, int top, int right, int bottom)
 
{
 
	Window *w;
 
	Window* const *wz;
 
	DrawPixelInfo bk;
 
	_cur_dpi = &bk;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (right > w->left &&
 
				bottom > w->top &&
 
				left < w->left + w->width &&
 
				top < w->top + w->height) {
 
			DrawOverlappedWindow(w, left, top, right, bottom);
 
			DrawOverlappedWindow(wz, left, top, right, bottom);
 
		}
 
	}
 
}
 

	
 
static void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
 
static void DrawOverlappedWindow(Window* const *wz, int left, int top, int right, int bottom)
 
{
 
	const Window *v = w;
 
	const Window* const *vz = wz;
 
	int x;
 

	
 
	while (++v != _last_window) {
 
	while (++vz != _last_z_window) {
 
		const Window *v = *vz;
 

	
 
		if (right > v->left &&
 
				bottom > v->top &&
 
				left < v->left + v->width &&
 
				top < v->top + v->height) {
 
			if (left < (x=v->left)) {
 
				DrawOverlappedWindow(w, left, top, x, bottom);
 
				DrawOverlappedWindow(w, x, top, right, bottom);
 
				DrawOverlappedWindow(wz, left, top, x, bottom);
 
				DrawOverlappedWindow(wz, x, top, right, bottom);
 
				return;
 
			}
 

	
 
			if (right > (x=v->left + v->width)) {
 
				DrawOverlappedWindow(w, left, top, x, bottom);
 
				DrawOverlappedWindow(w, x, top, right, bottom);
 
				DrawOverlappedWindow(wz, left, top, x, bottom);
 
				DrawOverlappedWindow(wz, x, top, right, bottom);
 
				return;
 
			}
 

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

	
 
			if (bottom > (x=v->top + v->height)) {
 
				DrawOverlappedWindow(w, left, top, right, x);
 
				DrawOverlappedWindow(w, left, x, right, bottom);
 
				DrawOverlappedWindow(wz, left, top, right, x);
 
				DrawOverlappedWindow(wz, 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->left = left - (*wz)->left;
 
		dp->top = top - (*wz)->top;
 
		dp->pitch = _screen.pitch;
 
		dp->dst_ptr = _screen.dst_ptr + top * _screen.pitch + left;
 
		dp->zoom = 0;
 
		CallWindowEventNP(w, WE_PAINT);
 
		CallWindowEventNP(*wz, WE_PAINT);
 
	}
 
}
 

	
 
void CallWindowEventNP(Window *w, int event)
 
{
 
	WindowEvent e;
 

	
 
	e.event = event;
 
	w->wndproc(w, &e);
 
}
 

	
 
void SetWindowDirty(const Window *w)
 
{
 
	if (w == NULL) return;
 
	SetDirtyBlocks(w->left, w->top, w->left + w->width, w->top + w->height);
 
}
 

	
 
/** Find the z-value of a window. A window must already be open
 
 * or the behaviour is undefined but function should never fail */
 
Window **FindWindowZPosition(const Window *w)
 
{
 
	Window **wz;
 

	
 
	for (wz = _z_windows;; wz++) {
 
		assert(wz < _last_z_window);
 
		if (*wz == w) return wz;
 
	}
 
}
 

	
 
void DeleteWindow(Window *w)
 
{
 
	WindowClass wc;
 
	WindowNumber wn;
 
	Window *v;
 
	int count;
 

	
 
	Window **wz;
 
	if (w == NULL) return;
 

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

	
 
	wc = w->window_class;
 
	wn = w->window_number;
 

	
 
	CallWindowEventNP(w, WE_DESTROY);
 

	
 
	w = FindWindowById(wc, wn);
 

	
 
	if (w->viewport != NULL) DeleteWindowViewport(w);
 

	
 
	SetWindowDirty(w);
 

	
 
	free(w->widget);
 
	w->widget = NULL;
 

	
 
	v = --_last_window;
 
	count = (byte*)v - (byte*)w;
 
	memmove(w, w + 1, count);
 
	/* Find the window in the z-array, and effectively remove it
 
	 * by moving all windows after it one to the left */
 
	wz = FindWindowZPosition(w);
 
	memmove(wz, wz + 1, (byte*)_last_z_window - (byte*)wz);
 
	_last_z_window--;
 
}
 

	
 
Window *FindWindowById(WindowClass cls, WindowNumber number)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == cls && w->window_number == number) return w;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
void DeleteWindowById(WindowClass cls, WindowNumber number)
 
{
 
	DeleteWindow(FindWindowById(cls, number));
 
}
 

	
 
void DeleteWindowByClass(WindowClass cls)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
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 */
 
	for (w = _windows; w != _last_window; w++) {
 
	 * as deleting this window could cascade in deleting (many) others
 
	 * anywhere in the z-array */
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == cls) {
 
			DeleteWindow(w);
 
			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)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
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 */
 
	for (w = _windows; w != _last_window; w++) {
 
	 * as deleting this window could cascade in deleting (many) others
 
	 * anywhere in the z-array */
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->caption_color == id) {
 
			DeleteWindow(w);
 
			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)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		if (w->caption_color != old_player)      continue;
 
		if (w->window_class == WC_PLAYER_COLOR)  continue;
 
		if (w->window_class == WC_FINANCES)      continue;
 
		if (w->window_class == WC_STATION_LIST)  continue;
 
		if (w->window_class == WC_TRAINS_LIST)   continue;
 
		if (w->window_class == WC_ROADVEH_LIST)  continue;
 
		if (w->window_class == WC_SHIPS_LIST)    continue;
 
		if (w->window_class == WC_AIRCRAFT_LIST) continue;
 
		if (w->window_class == WC_BUY_COMPANY)   continue;
 
		if (w->window_class == WC_COMPANY)       continue;
 

	
 
		w->caption_color = new_player;
 
	}
 
}
 

	
 
static void BringWindowToFront(const Window *w);
 

	
 
static Window *BringWindowToFront(Window *w);
 

	
 
/** 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 */
 
Window *BringWindowToFrontById(WindowClass cls, WindowNumber number)
 
{
 
	Window *w = FindWindowById(cls, number);
 

	
 
	if (w != NULL) {
 
		w->flags4 |= WF_WHITE_BORDER_MASK;
 
		BringWindowToFront(w);
 
		SetWindowDirty(w);
 
		w = BringWindowToFront(w);
 
	}
 

	
 
	return w;
 
}
 

	
 
static inline bool IsVitalWindow(const Window *w)
 
{
 
	WindowClass wc = w->window_class;
 
	return (wc == WC_MAIN_TOOLBAR || wc == WC_STATUS_BAR || wc == WC_NEWS_WINDOW || wc == WC_SEND_NETWORK_MSG);
 
}
 

	
 
/** 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, can be different!
 
 * @return pointer to the window, the same as the input pointer
 
 */
 
static Window *BringWindowToFront(Window *w)
 
static void BringWindowToFront(const Window *w)
 
{
 
	Window *v;
 
	Window temp;
 

	
 
	v = _last_window;
 
	do {
 
		if (--v < _windows) return w;
 
	} while (IsVitalWindow(v));
 
	Window *tempz;
 
	Window **wz = FindWindowZPosition(w);
 
	Window **vz = _last_z_window;
 

	
 
	if (w == v) return w;
 

	
 
	assert(w < v);
 
	/* Bring the window just below the vital windows */
 
	do {
 
		if (--vz < _z_windows) return;
 
	} while (IsVitalWindow(*vz));
 

	
 
	temp = *w;
 
	memmove(w, w + 1, (v - w) * sizeof(Window));
 
	*v = temp;
 
	if (wz == vz) return; // window is already in the right position
 
	assert(wz < vz);
 

	
 
	SetWindowDirty(v);
 
	tempz = *wz;
 
	memmove(wz, wz + 1, (byte*)vz - (byte*)wz);
 
	*vz = tempz;
 

	
 
	return v;
 
	SetWindowDirty(w);
 
}
 

	
 
/** 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 Window *FindDeletableWindow(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w < endof(_windows); w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w) && !(w->flags4 & WF_STICKY)) {
 
			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 Window, 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 Window *ForceFindDeletableWindow(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows;; w++) {
 
		assert(w < _last_window);
 
	for (wz = _z_windows;; wz++) {
 
		Window *w = *wz;
 
		assert(wz < _last_z_window);
 
		if (w->window_class != WC_MAIN_WINDOW && !IsVitalWindow(w)) return w;
 
	}
 
}
 

	
 
bool IsWindowOfPrototype(const Window *w, const Widget *widget)
 
{
 
	return (w->original_widget == widget);
 
}
 

	
 
/* Copies 'widget' to 'w->widget' to allow for resizable windows */
 
void AssignWidgetToWindow(Window *w, const Widget *widget)
 
{
 
@@ -493,98 +515,108 @@ void AssignWidgetToWindow(Window *w, con
 
		uint index = 1;
 
		const Widget *wi;
 

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

	
 
		w->widget = realloc(w->widget, sizeof(*w->widget) * index);
 
		memcpy(w->widget, widget, sizeof(*w->widget) * index);
 
	} else {
 
		w->widget = NULL;
 
	}
 
}
 

	
 
static Window *FindFreeWindow(void)
 
{
 
	Window *w;
 

	
 
	for (w = _windows; w < endof(_windows); w++) {
 
		Window* 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;
 
}
 

	
 
/* 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
 
 */
 
static Window *LocalAllocateWindow(
 
							int x, int y, int width, int height,
 
							WindowProc *proc, WindowClass cls, const Widget *widget, int window_number)
 
{
 
	Window *w = _last_window; // last window keeps track of the highest open window
 

	
 
	// We have run out of windows, close one and use that as the place for our new one
 
	if (w >= endof(_windows)) {
 
		w = FindDeletableWindow();
 

	
 
		if (w == NULL) w = ForceFindDeletableWindow();
 

	
 
		DeleteWindow(w);
 
		w = _last_window;
 
	}
 

	
 
	/* XXX - This very strange construction makes sure that the chatbar is always
 
	 * on top of other windows. Why? It is created as last_window (so, on top).
 
	 * Any other window will go below toolbar/statusbar/news window, which implicitely
 
	 * also means it is below the chatbar. Very likely needs heavy improvement
 
	 * to de-braindeadize */
 
	if (w != _windows && cls != WC_SEND_NETWORK_MSG) {
 
		Window *v;
 
	Window *w = FindFreeWindow();
 

	
 
		/* XXX - if not this order (toolbar/statusbar and then news), game would
 
		 * crash because it will try to copy a negative size for the news-window.
 
		 * Eg. window was already moved BELOW news (which is below toolbar/statusbar)
 
		 * and now needs to move below those too. That is a negative move. */
 
		v = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
		if (v != NULL) {
 
			memmove(v+1, v, (byte*)w - (byte*)v);
 
			w = v;
 
		}
 

	
 
		v = FindWindowById(WC_STATUS_BAR, 0);
 
		if (v != NULL) {
 
			memmove(v+1, v, (byte*)w - (byte*)v);
 
			w = v;
 
		}
 

	
 
		v = FindWindowById(WC_NEWS_WINDOW, 0);
 
		if (v != NULL) {
 
			memmove(v+1, v, (byte*)w - (byte*)v);
 
			w = v;
 
		}
 
	/* We have run out of windows, close one and use that as the place for our new one */
 
	if (w == NULL) {
 
		w = FindDeletableWindow();
 
		if (w == NULL) w = ForceFindDeletableWindow();
 
		DeleteWindow(w);
 
	}
 

	
 
	// Set up window properties
 
	memset(w, 0, sizeof(*w));
 
	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 = width;
 
	w->height = height;
 
	w->wndproc = proc;
 
	AssignWidgetToWindow(w, widget);
 
	w->resize.width = width;
 
	w->resize.height = height;
 
	w->resize.step_width = 1;
 
	w->resize.step_height = 1;
 
	w->window_number = window_number;
 

	
 
	_last_window++;
 
	{
 
		Window **wz = _last_z_window;
 

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

	
 
		*wz = w;
 
		_last_z_window++;
 
	}
 

	
 
	SetWindowDirty(w);
 

	
 
	CallWindowEventNP(w, WE_CREATE);
 

	
 
	return w;
 
}
 

	
 
/**
 
 * 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
 
@@ -603,114 +635,120 @@ Window *AllocateWindow(
 
}
 

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

	
 

	
 
static SizeRect _awap_r;
 

	
 
static bool IsGoodAutoPlace1(int left, int top)
 
{
 
	int right,bottom;
 
	Window *w;
 
	Window* const *wz;
 

	
 
	_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.
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		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;
 
	Window *w;
 
	Window* const *wz;
 

	
 
	_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.
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		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)
 
{
 
	Window *w;
 
	Window* const *wz;
 
	Point pt;
 

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

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

	
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		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 (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		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 (w = _windows; w != _last_window; w++) {
 
		FOR_ALL_WINDOWS(wz) {
 
			const Window *w = *wz;
 

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

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

	
 
@@ -801,83 +839,83 @@ Window *AllocateWindowDescFront(const Wi
 
	Window *w;
 

	
 
	if (BringWindowToFrontById(desc->cls, window_number)) return NULL;
 
	w = LocalAllocateWindowDesc(desc, window_number);
 
	return w;
 
}
 

	
 
/** 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 */
 
Window *FindWindowFromPt(int x, int y)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _last_window; w != _windows;) {
 
		--w;
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		Window *w = *--wz;
 
		if (IS_INSIDE_1D(x, w->left, w->width) && IS_INSIDE_1D(y, w->top, w->height)) {
 
			return w;
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
void InitWindowSystem(void)
 
{
 
	IConsoleClose();
 

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

	
 
void UnInitWindowSystem(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 
	// delete all malloced widgets
 
	for (w = _windows; w != _last_window; w++) {
 
		free(w->widget);
 
		w->widget = NULL;
 
	FOR_ALL_WINDOWS(wz) {
 
		free((*wz)->widget);
 
		(*wz)->widget = NULL;
 
	}
 
}
 

	
 
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)
 
{
 
	Window *w;
 

	
 
	Window* const *wz;
 

	
 
	for (w = _last_window; w != _windows;) {
 
		--w;
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		w = *--wz;
 
		// Unclick scrollbar buttons if they are pressed.
 
		if (w->flags4 & (WF_SCROLL_DOWN | WF_SCROLL_UP)) {
 
			w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
 
			SetWindowDirty(w);
 
		}
 
		CallWindowEventNP(w, WE_MOUSELOOP);
 
	}
 

	
 
	for (w = _last_window; w != _windows;) {
 
		--w;
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		w = *--wz;
 

	
 
		if (w->flags4&WF_TIMEOUT_MASK && !(--w->flags4&WF_TIMEOUT_MASK)) {
 
			CallWindowEventNP(w, WE_TIMEOUT);
 
			if (w->desc_flags & WDF_UNCLICK_BUTTONS) RaiseWindowButtons(w);
 
		}
 
	}
 
}
 

	
 
Window *GetCallbackWnd(void)
 
{
 
	return FindWindowById(_thd.window_class, _thd.window_number);
 
}
 
@@ -981,57 +1019,63 @@ static bool HandleMouseOver(void)
 
		w->wndproc(w, &e);
 
	}
 

	
 
	// Mouseover never stops execution
 
	return true;
 
}
 

	
 

	
 
static bool _dragging_window;
 

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

	
 
	// Otherwise find the window...
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		if (w->flags4 & WF_DRAGGING) {
 
			const Widget *t = &w->widget[1]; // the title bar ... ugh
 
			const Window *v;
 
			int x;
 
			int y;
 
			int nx;
 
			int ny;
 

	
 
			// Stop the dragging if the left mouse button was released
 
			if (!_left_button_down) {
 
				w->flags4 &= ~WF_DRAGGING;
 
				break;
 
			}
 

	
 
			SetWindowDirty(w);
 

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

	
 
			if (_patches.window_snap_radius != 0) {
 
				const Window* const *vz;
 

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

	
 
				for (v = _windows; v != _last_window; ++v) {
 
				FOR_ALL_WINDOWS(vz) {
 
					const Window *v = *vz;
 

	
 
					if (v == w) continue; // Don't snap at yourself
 

	
 
					if (y + w->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 - w->width);
 
@@ -1211,64 +1255,63 @@ static bool HandleWindowDragging(void)
 
			e.we.sizing.diff.y = y;
 
			w->wndproc(w, &e);
 

	
 
			SetWindowDirty(w);
 
			return false;
 
		}
 
	}
 

	
 
	_dragging_window = false;
 
	return false;
 
}
 

	
 
static Window *StartWindowDrag(Window *w)
 
static void StartWindowDrag(Window *w)
 
{
 
	w->flags4 |= WF_DRAGGING;
 
	_dragging_window = true;
 

	
 
	_drag_delta.x = w->left - _cursor.pos.x;
 
	_drag_delta.y = w->top  - _cursor.pos.y;
 

	
 
	w = BringWindowToFront(w);
 
	BringWindowToFront(w);
 
	DeleteWindowById(WC_DROPDOWN_MENU, 0);
 
	return w;
 
}
 

	
 
static Window *StartWindowSizing(Window *w)
 
static void StartWindowSizing(Window *w)
 
{
 
	w->flags4 |= WF_SIZING;
 
	_dragging_window = true;
 

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

	
 
	w = BringWindowToFront(w);
 
	BringWindowToFront(w);
 
	DeleteWindowById(WC_DROPDOWN_MENU, 0);
 
	SetWindowDirty(w);
 
	return w;
 
}
 

	
 

	
 
static bool HandleScrollbarScrolling(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 
	int i;
 
	int pos;
 
	Scrollbar *sb;
 

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

	
 
	// Find the scrolling window
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 

	
 
		if (w->flags4 & WF_SCROLL_MIDDLE) {
 
			// Abort if no button is clicked any more.
 
			if (!_left_button_down) {
 
				w->flags4 &= ~WF_SCROLL_MIDDLE;
 
				SetWindowDirty(w);
 
				break;
 
			}
 

	
 
			if (w->flags4 & WF_HSCROLL) {
 
				sb = &w->hscroll;
 
				i = _cursor.pos.x - _cursorpos_drag_start.x;
 
			} else if (w->flags4 & WF_SCROLL2){
 
@@ -1316,54 +1359,57 @@ static bool HandleViewportScroll(void)
 
		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;
 
}
 

	
 
static Window *MaybeBringWindowToFront(Window *w)
 
static void MaybeBringWindowToFront(const Window *w)
 
{
 
	Window *u;
 
	const Window* const *wz;
 
	const Window* const *uz;
 

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

	
 
	for (u = w; ++u != _last_window;) {
 
	wz = FindWindowZPosition(w);
 
	for (uz = wz; ++uz != _last_z_window;) {
 
		const Window *u = *uz;
 

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

	
 
		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;
 
		}
 

	
 
		return BringWindowToFront(w);
 
		BringWindowToFront(w);
 
		return;
 
	}
 

	
 
	return w;
 
}
 

	
 
/** Send a message from one window to another. The receiving window is found by
 
 * @param w @see 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;
 

	
 
@@ -1388,37 +1434,37 @@ void SendWindowMessage(WindowClass wnd_c
 
	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, uint msg, uint wparam, uint lparam)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
		if (w->window_class == wnd_class) SendWindowMessageW(w, msg, wparam, lparam);
 
	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)
 
{
 
	Window *w;
 
	Window* const *wz;
 
	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.
 
	*
 
@@ -1434,41 +1480,42 @@ void HandleKeypress(uint32 key)
 
	e.we.keypress.cont = true;
 

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

	
 
	// Call the event, start with the uppermost window.
 
	for (w = _last_window; w != _windows;) {
 
		--w;
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		Window *w = *--wz;
 

	
 
		// 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) {
 
		w = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
		Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
 
		// When there is no toolbar w is null, check for that
 
		if (w != NULL) w->wndproc(w, &e);
 
	}
 
}
 

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

	
 
static int _input_events_this_tick = 0;
 

	
 
static void HandleAutoscroll(void)
 
{
 
@@ -1523,25 +1570,25 @@ void MouseLoop(int click, int mousewheel
 
	if (!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 = FindWindowFromPt(x, y);
 
	if (w == NULL) return;
 
	w = MaybeBringWindowToFront(w);
 
	MaybeBringWindowToFront(w);
 
	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);
 
@@ -1622,180 +1669,186 @@ void HandleMouseEvents(void)
 

	
 
	MouseLoop(click, mousewheel);
 
}
 

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

	
 
void UpdateWindows(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 
	static int we4_timer = 0;
 
	int t = we4_timer + 1;
 

	
 
	if (t >= 100) {
 
		for (w = _last_window; w != _windows;) {
 
			w--;
 
			CallWindowEventNP(w, WE_4);
 
		for (wz = _last_z_window; wz != _z_windows;) {
 
			CallWindowEventNP(*--wz, WE_4);
 
		}
 
		t = 0;
 
	}
 
	we4_timer = t;
 

	
 
	for (w = _last_window; w != _windows;) {
 
		w--;
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		Window *w = *--wz;
 
		if (w->flags4 & WF_WHITE_BORDER_MASK) {
 
			w->flags4 -= WF_WHITE_BORDER_ONE;
 

	
 
			if (!(w->flags4 & WF_WHITE_BORDER_MASK)) SetWindowDirty(w);
 
		}
 
	}
 

	
 
	DrawDirtyBlocks();
 

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

	
 

	
 
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;
 
}
 

	
 
void InvalidateWindow(WindowClass cls, WindowNumber number)
 
{
 
	const Window *w;
 
	const Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (w->window_class == cls && w->window_number == number) SetWindowDirty(w);
 
	}
 
}
 

	
 
void InvalidateWidget(const Window *w, byte widget_index)
 
{
 
	const Widget *wi = &w->widget[widget_index];
 

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

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

	
 
void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index)
 
{
 
	const Window *w;
 
	const Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		const Window *w = *wz;
 
		if (w->window_class == cls && w->window_number == number) {
 
			InvalidateWidget(w, widget_index);
 
		}
 
	}
 
}
 

	
 
void InvalidateWindowClasses(WindowClass cls)
 
{
 
	const Window *w;
 
	const Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
		if (w->window_class == cls) SetWindowDirty(w);
 
	FOR_ALL_WINDOWS(wz) {
 
		if ((*wz)->window_class == cls) SetWindowDirty(*wz);
 
	}
 
}
 

	
 
void InvalidateThisWindowData(Window *w)
 
{
 
	CallWindowEventNP(w, WE_INVALIDATE_DATA);
 
}
 

	
 
void InvalidateWindowData(WindowClass cls, WindowNumber number)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w);
 
	}
 
}
 

	
 
void InvalidateWindowClassesData(WindowClass cls)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
		if (w->window_class == cls) InvalidateThisWindowData(w);
 
	FOR_ALL_WINDOWS(wz) {
 
		if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz);
 
	}
 
}
 

	
 
void CallWindowTickEvent(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _last_window; w != _windows;) {
 
		--w;
 
		CallWindowEventNP(w, WE_TICK);
 
	for (wz = _last_z_window; wz != _z_windows;) {
 
		CallWindowEventNP(*--wz, WE_TICK);
 
	}
 
}
 

	
 
void DeleteNonVitalWindows(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
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 */
 
	for (w = _windows; w != _last_window; w++) {
 
	 * as deleting this window could cascade in deleting (many) others
 
	 * anywhere in the z-array */
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		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'
 

	
 
			DeleteWindow(w);
 
			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 DeleteAllNonVitalWindows(void)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	/* 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 */
 
	for (w = _windows; w != _last_window; w++) {
 
		if (w->flags4 & WF_STICKY) {
 
			DeleteWindow(w);
 
	 * as deleting this window could cascade in deleting (many) others
 
	 * anywhere in the z-array */
 
	FOR_ALL_WINDOWS(wz) {
 
		if ((*wz)->flags4 & WF_STICKY) {
 
			DeleteWindow(*wz);
 
			goto restart_search;
 
		}
 
	}
 
}
 

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

	
 
@@ -1809,27 +1862,28 @@ int PositionMainToolbar(Window *w)
 

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

	
 
void RelocateAllWindows(int neww, int newh)
 
{
 
	Window *w;
 
	Window* const *wz;
 

	
 
	for (w = _windows; w != _last_window; w++) {
 
	FOR_ALL_WINDOWS(wz) {
 
		Window *w = *wz;
 
		int left, top;
 

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

	
 
		IConsoleResize();
window.h
Show inline comments
 
@@ -824,28 +824,30 @@ static inline void GuiShowTooltips(Strin
 
/* widget.c */
 
int GetWidgetFromPos(const Window *w, int x, int y);
 
void DrawWindowWidgets(const Window *w);
 
void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask);
 

	
 
void HandleButtonClick(Window *w, byte widget);
 

	
 
Window *GetCallbackWnd(void);
 
void DeleteNonVitalWindows(void);
 
void DeleteAllNonVitalWindows(void);
 
void HideVitalWindows(void);
 
void ShowVitalWindows(void);
 
Window **FindWindowZPosition(const Window *w);
 

	
 
/* window.c */
 
VARDEF Window _windows[25];
 
VARDEF Window *_last_window;
 
extern Window *_z_windows[];
 
extern Window **_last_z_window;
 
#define FOR_ALL_WINDOWS(wz) for (wz = _z_windows; wz != _last_z_window; wz++)
 

	
 
VARDEF Point _cursorpos_drag_start;
 

	
 
VARDEF bool _left_button_down;
 
VARDEF bool _left_button_clicked;
 

	
 
VARDEF bool _right_button_down;
 
VARDEF bool _right_button_clicked;
 

	
 
VARDEF int _scrollbar_start_pos;
 
VARDEF int _scrollbar_size;
 
VARDEF byte _scroller_click_timeout;
0 comments (0 inline, 0 general)