(svn r13056) [NoAI] -Sync: with trunk r12996:13055. noai
authorrubidium
Sun, 11 May 2008 20:09:34 +0000
branchnoai
changeset 10513 33cb70ff2f5d
parent 10455 22c441f5adf9
child 10514 8d9dbe82af3c
(svn r13056) [NoAI] -Sync: with trunk r12996:13055.
config.lib
projects/openttd_vs80.vcproj
projects/openttd_vs90.vcproj
source.list
src/ai/ai_gui.cpp
src/ai/api/ai_airport.cpp
src/ai/api/ai_road.cpp
src/airport_gui.cpp
src/autoreplace_cmd.cpp
src/autoreplace_gui.cpp
src/bridge_gui.cpp
src/build_vehicle_gui.cpp
src/cheat_gui.cpp
src/console.cpp
src/depot_gui.cpp
src/dock_gui.cpp
src/driver.h
src/economy.cpp
src/elrail.cpp
src/elrail_func.h
src/engine_gui.cpp
src/fileio.cpp
src/fileio.h
src/fios.cpp
src/functions.h
src/genworld_gui.cpp
src/graph_gui.cpp
src/group_gui.cpp
src/gui.h
src/industry_gui.cpp
src/intro_gui.cpp
src/main_gui.cpp
src/minilzo.cpp
src/misc.cpp
src/misc/dbg_helpers.cpp
src/misc_gui.cpp
src/music_gui.cpp
src/network/network_gui.cpp
src/network/network_udp.cpp
src/newgrf.cpp
src/newgrf_config.cpp
src/newgrf_gui.cpp
src/news_gui.cpp
src/news_type.h
src/openttd.cpp
src/order_gui.cpp
src/osk_gui.cpp
src/player_gui.cpp
src/querystring_gui.h
src/rail.h
src/rail_cmd.cpp
src/rail_gui.cpp
src/road_cmd.cpp
src/road_gui.cpp
src/saveload.cpp
src/settings.cpp
src/settings_gui.cpp
src/signs.cpp
src/signs_gui.cpp
src/smallmap_gui.cpp
src/spritecache.cpp
src/spriteloader/png.cpp
src/station_cmd.cpp
src/station_gui.cpp
src/statusbar_gui.cpp
src/string_func.h
src/subsidy_gui.cpp
src/table/engines.h
src/terraform_gui.cpp
src/textbuf_gui.h
src/tilehighlight_func.h
src/tilehighlight_type.h
src/timetable_gui.cpp
src/toolbar_gui.cpp
src/town_gui.cpp
src/transparency_gui.cpp
src/tree_gui.cpp
src/tunnelbridge_cmd.cpp
src/vehicle.cpp
src/vehicle_gui.cpp
src/vehicle_gui.h
src/viewport.cpp
src/viewport_func.h
src/viewport_type.h
src/widgets/dropdown.cpp
src/win32.cpp
src/window.cpp
src/window_func.h
src/window_gui.h
--- a/config.lib	Wed May 07 21:09:51 2008 +0000
+++ b/config.lib	Sun May 11 20:09:34 2008 +0000
@@ -921,7 +921,7 @@
 		if [ $cc_version -ge 29 ]; then
 			CFLAGS="$CFLAGS -Wall -Wno-multichar -Wsign-compare -Wundef"
 			CFLAGS="$CFLAGS -Wwrite-strings -Wpointer-arith"
-			CFLAGS="$CFLAGS -Wno-uninitialized -Wredundant-decls"
+			CFLAGS="$CFLAGS -Wno-uninitialized"
 
 			CC_CFLAGS="$CC_CFLAGS -Wstrict-prototypes"
 		fi
@@ -935,6 +935,7 @@
 
 		if [ $cc_version -ge 30 ]; then
 			CFLAGS="$CFLAGS -W -Wno-unused-parameter -Wformat=2"
+			CFLAGS="$CFLAGS -Wredundant-decls"
 		fi
 
 		if [ $cc_version -ge 34 ]; then
--- a/projects/openttd_vs80.vcproj	Wed May 07 21:09:51 2008 +0000
+++ b/projects/openttd_vs80.vcproj	Sun May 11 20:09:34 2008 +0000
@@ -468,10 +468,6 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\autoreplace_cmd.cpp"
-				>
-			</File>
-			<File
 				RelativePath=".\..\src\aystar.cpp"
 				>
 			</File>
@@ -500,10 +496,6 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\cheat_gui.cpp"
-				>
-			</File>
-			<File
 				RelativePath=".\..\src\command.cpp"
 				>
 			</File>
@@ -732,10 +724,6 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\statusbar_gui.cpp"
-				>
-			</File>
-			<File
 				RelativePath=".\..\src\string.cpp"
 				>
 			</File>
@@ -980,6 +968,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\elrail_func.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\core\endian_func.hpp"
 				>
 			</File>
@@ -1664,6 +1656,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\cheat_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\depot_gui.cpp"
 				>
 			</File>
@@ -1764,6 +1760,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\statusbar_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\subsidy_gui.cpp"
 				>
 			</File>
@@ -1792,18 +1792,26 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\tree_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\vehicle_gui.cpp"
 				>
 			</File>
 		</Filter>
 		<Filter
-			Name="Landscape"
+			Name="Command handlers"
 			>
 			<File
 				RelativePath=".\..\src\aircraft_cmd.cpp"
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\autoreplace_cmd.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\clear_cmd.cpp"
 				>
 			</File>
--- a/projects/openttd_vs90.vcproj	Wed May 07 21:09:51 2008 +0000
+++ b/projects/openttd_vs90.vcproj	Sun May 11 20:09:34 2008 +0000
@@ -465,10 +465,6 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\autoreplace_cmd.cpp"
-				>
-			</File>
-			<File
 				RelativePath=".\..\src\aystar.cpp"
 				>
 			</File>
@@ -497,10 +493,6 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\cheat_gui.cpp"
-				>
-			</File>
-			<File
 				RelativePath=".\..\src\command.cpp"
 				>
 			</File>
@@ -729,10 +721,6 @@
 				>
 			</File>
 			<File
-				RelativePath=".\..\src\statusbar_gui.cpp"
-				>
-			</File>
-			<File
 				RelativePath=".\..\src\string.cpp"
 				>
 			</File>
@@ -977,6 +965,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\elrail_func.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\core\endian_func.hpp"
 				>
 			</File>
@@ -1661,6 +1653,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\cheat_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\depot_gui.cpp"
 				>
 			</File>
@@ -1761,6 +1757,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\statusbar_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\subsidy_gui.cpp"
 				>
 			</File>
@@ -1789,18 +1789,26 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\tree_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\vehicle_gui.cpp"
 				>
 			</File>
 		</Filter>
 		<Filter
-			Name="Landscape"
+			Name="Command handlers"
 			>
 			<File
 				RelativePath=".\..\src\aircraft_cmd.cpp"
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\autoreplace_cmd.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\clear_cmd.cpp"
 				>
 			</File>
--- a/source.list	Wed May 07 21:09:51 2008 +0000
+++ b/source.list	Sun May 11 20:09:34 2008 +0000
@@ -3,7 +3,6 @@
 core/alloc_func.cpp
 animated_tile.cpp
 articulated_vehicles.cpp
-autoreplace_cmd.cpp
 aystar.cpp
 core/bitmath_func.cpp
 bmp.cpp
@@ -11,7 +10,6 @@
 cargopacket.cpp
 cargotype.cpp
 cheat.cpp
-cheat_gui.cpp
 command.cpp
 console.cpp
 console_cmds.cpp
@@ -76,7 +74,6 @@
 sound.cpp
 spritecache.cpp
 station.cpp
-statusbar_gui.cpp
 string.cpp
 strings.cpp
 squirrel.cpp
@@ -170,6 +167,7 @@
 economy_type.h
 effectvehicle_func.h
 effectvehicle_base.h
+elrail_func.h
 core/endian_func.hpp
 engine_base.h
 engine_func.h
@@ -356,6 +354,7 @@
 autoreplace_gui.cpp
 bridge_gui.cpp
 build_vehicle_gui.cpp
+cheat_gui.cpp
 depot_gui.cpp
 dock_gui.cpp
 engine_gui.cpp
@@ -381,6 +380,7 @@
 signs_gui.cpp
 smallmap_gui.cpp
 station_gui.cpp
+statusbar_gui.cpp
 subsidy_gui.cpp
 terraform_gui.cpp
 timetable_gui.cpp
@@ -388,10 +388,12 @@
 town_gui.cpp
 train_gui.cpp
 transparency_gui.cpp
+tree_gui.cpp
 vehicle_gui.cpp
 
-# Landscape
+# Command handlers
 aircraft_cmd.cpp
+autoreplace_cmd.cpp
 clear_cmd.cpp
 disaster_cmd.cpp
 dummy_land.cpp
--- a/src/ai/ai_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/ai/ai_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -254,5 +254,5 @@
 
 void ShowAIDebugWindow()
 {
-	AllocateWindowDescFront(&_ai_debug_desc, 0);
+	AllocateWindowDescFront<Window>(&_ai_debug_desc, 0);
 }
--- a/src/ai/api/ai_airport.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/ai/api/ai_airport.cpp	Sun May 11 20:09:34 2008 +0000
@@ -9,6 +9,7 @@
 #include "../../station_map.h"
 #include "../../player_func.h"
 #include "../../settings_type.h"
+#include "../../command_type.h"
 
 /* static */ bool AIAirport::IsHangarTile(TileIndex tile)
 {
--- a/src/ai/api/ai_road.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/ai/api/ai_road.cpp	Sun May 11 20:09:34 2008 +0000
@@ -6,6 +6,7 @@
 #include "../../openttd.h"
 #include "../../road_map.h"
 #include "../../station_map.h"
+#include "../../command_type.h"
 
 /* static */ bool AIRoad::IsRoadTile(TileIndex tile)
 {
--- a/src/airport_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/airport_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -95,7 +95,7 @@
 
 		case WE_PLACE_MOUSEUP:
 			if (e->we.place.pt.x != -1 && e->we.place.select_proc == DDSP_DEMOLISH_AREA) {
-				DoCommandP(e->we.place.tile, e->we.place.starttile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA));
+				GUIPlaceProcDragXY(e);
 			}
 			break;
 
@@ -136,7 +136,7 @@
 	if (!IsValidPlayer(_current_player)) return;
 
 	DeleteWindowByClass(WC_BUILD_TOOLBAR);
-	Window *w = AllocateWindowDescFront(&_air_toolbar_desc, TRANSPORT_AIR);
+	Window *w = AllocateWindowDescFront<Window>(&_air_toolbar_desc, TRANSPORT_AIR);
 	if (_patches.link_terraform_toolbar) ShowTerraformToolbar(w);
 }
 
@@ -230,7 +230,7 @@
 			}
 		} break;
 
-		case WE_MOUSELOOP: {
+		case WE_TICK: {
 			if (WP(w, def_d).close) {
 				delete w;
 				return;
@@ -282,7 +282,7 @@
 
 static void ShowBuildAirportPicker()
 {
-	AllocateWindowDesc(&_build_airport_desc);
+	new Window(&_build_airport_desc);
 }
 
 void InitializeAirportGui()
--- a/src/autoreplace_cmd.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/autoreplace_cmd.cpp	Sun May 11 20:09:34 2008 +0000
@@ -244,11 +244,13 @@
 				}
 			}
 		}
-		/* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */
-		MoveVehicleCargo(new_v->type == VEH_TRAIN ? new_v->First() : new_v, old_v);
+		if (CmdSucceeded(cost)) {
+			/* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */
+			MoveVehicleCargo(new_v->type == VEH_TRAIN ? new_v->First() : new_v, old_v);
 
-		// Get the name of the old vehicle if it has a custom name.
-		if (old_v->name != NULL) vehicle_name = strdup(old_v->name);
+			/* Get the name of the old vehicle if it has a custom name. */
+			if (old_v->name != NULL) vehicle_name = strdup(old_v->name);
+		}
 	} else { // flags & DC_EXEC not set
 		CommandCost tmp_move;
 
@@ -282,6 +284,8 @@
 	/* sell the engine/ find out how much you get for the old engine (income is returned as negative cost) */
 	cost.AddCost(DoCommand(0, old_v->index, 0, flags, GetCmdSellVeh(old_v)));
 
+	if (CmdFailed(cost)) return cost;
+
 	if (new_front) {
 		/* now we assign the old unitnumber to the new vehicle */
 		new_v->unitnumber = cached_unitnumber;
@@ -423,19 +427,23 @@
 				/* We are going to try to replace a vehicle but we don't have any backup so we will make one. */
 				backup.Backup(v, p);
 			}
-			/* Now replace the vehicle */
+			/* Now replace the vehicle.
+			 * First we need to cache if it's the front vehicle as we need to update the v pointer if it is.
+			 * If the replacement fails then we can't trust the data in the vehicle hence the reason to cache the result. */
+			bool IsFront = w->type != VEH_TRAIN || w->u.rail.first_engine == INVALID_ENGINE;
+
 			cost.AddCost(ReplaceVehicle(&w, DC_EXEC, cost.GetCost(), p, new_engine));
 
-			if (w->type != VEH_TRAIN || w->u.rail.first_engine == INVALID_ENGINE) {
+			if (IsFront) {
 				/* now we bought a new engine and sold the old one. We need to fix the
 				 * pointers in order to avoid pointing to the old one for trains: these
 				 * pointers should point to the front engine and not the cars
 				 */
 				v = w;
 			}
-		} while (w->type == VEH_TRAIN && (w = GetNextVehicle(w)) != NULL);
+		} while (CmdSucceeded(cost) && w->type == VEH_TRAIN && (w = GetNextVehicle(w)) != NULL);
 
-		if (v->type == VEH_TRAIN && p->renew_keep_length) {
+		if (CmdSucceeded(cost) && v->type == VEH_TRAIN && p->renew_keep_length) {
 			/* Remove wagons until the wanted length is reached */
 			cost.AddCost(WagonRemoval(v, old_total_length));
 		}
--- a/src/autoreplace_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/autoreplace_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -25,23 +25,7 @@
 #include "table/sprites.h"
 #include "table/strings.h"
 
-struct replaceveh_d {
-	byte sel_index[2];
-	EngineID sel_engine[2];
-	uint16 count[2];
-	bool wagon_btnstate; ///< true means engine is selected
-	EngineList list[2];
-	bool update_left;
-	bool update_right;
-	bool init_lists;
-	GroupID sel_group;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(replaceveh_d));
-
-static RailType _railtype_selected_in_replace_gui;
-
-static bool _rebuild_left_list;
-static bool _rebuild_right_list;
+void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, int count_location, GroupID selected_group);
 
 static const StringID _rail_types_list[] = {
 	STR_RAIL_VEHICLES,
@@ -82,12 +66,6 @@
 	return r;
 }
 
-/* General Vehicle GUI based procedures that are independent of vehicle types */
-void InitializeVehiclesGuiList()
-{
-	_railtype_selected_in_replace_gui = RAILTYPE_RAIL;
-}
-
 /** Rebuild the left autoreplace list if an engine is removed or added
  * @param e Engine to check if it is removed or added
  * @param id_g The group the engine belongs to
@@ -99,19 +77,14 @@
 void InvalidateAutoreplaceWindow(EngineID e, GroupID id_g)
 {
 	Player *p = GetPlayer(_local_player);
-	VehicleType type = GetEngine(e)->type;
 	uint num_engines = GetGroupNumEngines(_local_player, id_g, e);
 
 	if (num_engines == 0 || p->num_engines[e] == 0) {
 		/* We don't have any of this engine type.
 		 * Either we just sold the last one, we build a new one or we stopped replacing it.
 		 * In all cases, we need to update the left list */
-		_rebuild_left_list = true;
-	} else {
-		_rebuild_left_list = false;
+		InvalidateWindowData(WC_REPLACE_VEHICLE, GetEngine(e)->type, true);
 	}
-	_rebuild_right_list = false;
-	InvalidateWindowData(WC_REPLACE_VEHICLE, type);
 }
 
 /** When an engine is made buildable or is removed from being buildable, add/remove it from the build/autoreplace lists
@@ -119,9 +92,7 @@
  */
 void AddRemoveEngineFromAutoreplaceAndBuildWindows(VehicleType type)
 {
-	_rebuild_left_list = false; // left list is only for the vehicles the player owns and is not related to being buildable
-	_rebuild_right_list = true;
-	InvalidateWindowData(WC_REPLACE_VEHICLE, type); // Update the autoreplace window
+	InvalidateWindowData(WC_REPLACE_VEHICLE, type, false); // Update the autoreplace window
 	InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
 }
 
@@ -144,30 +115,6 @@
 	}
 }
 
-/** Figure out if an engine should be added to a list
- * @param e The EngineID
- * @param draw_left If true, then the left list is drawn (the engines specific to the railtype you selected)
- * @param show_engines if truem then locomotives are drawn, else wagons (never both)
- * @return true if the engine should be in the list (based on this check)
- */
-static bool GenerateReplaceRailList(EngineID e, bool draw_left, bool show_engines)
-{
-	const RailVehicleInfo *rvi = RailVehInfo(e);
-
-	/* Ensure that the wagon/engine selection fits the engine. */
-	if ((rvi->railveh_type == RAILVEH_WAGON) == show_engines) return false;
-
-	if (draw_left && show_engines) {
-		/* Ensure that the railtype is specific to the selected one */
-		if (rvi->railtype != _railtype_selected_in_replace_gui) return false;
-	} else {
-		/* Ensure that it's a compatible railtype to the selected one (like electric <-> diesel)
-		 * The vehicle do not have to have power on the railtype in question, only able to drive (pulled if needed) */
-		if (!IsCompatibleRail(rvi->railtype, _railtype_selected_in_replace_gui)) return false;
-	}
-	return true;
-}
-
 /** Figure out if two engines got at least one type of cargo in common (refitting if needed)
  * @param engine_a one of the EngineIDs
  * @param engine_b the other EngineID
@@ -186,301 +133,374 @@
 	return false;
 }
 
-/** Generate a list
- * @param w Window, that contains the list
- * @param draw_left true if generating the left list, otherwise false
+/**
+ * Window for the autoreplacing of vehicles.
  */
-static void GenerateReplaceVehList(Window *w, bool draw_left)
-{
-	EngineID selected_engine = INVALID_ENGINE;
-	VehicleType type = (VehicleType)w->window_number;
-	byte i = draw_left ? 0 : 1;
-
-	EngineList *list = &WP(w, replaceveh_d).list[i];
-	list->clear();
+class ReplaceVehicleWindow : public Window {
+	byte sel_index[2];
+	EngineID sel_engine[2];
+	uint16 count[2];
+	bool wagon_btnstate; ///< true means engine is selected
+	EngineList list[2];
+	bool update_left;
+	bool update_right;
+	bool init_lists;
+	GroupID sel_group;
+	static RailType sel_railtype;
 
-	const Engine *e;
-	FOR_ALL_ENGINES_OF_TYPE(e, type) {
-		EngineID eid = e->index;
-		if (type == VEH_TRAIN && !GenerateReplaceRailList(eid, draw_left, WP(w, replaceveh_d).wagon_btnstate)) continue; // special rules for trains
-
-		if (draw_left) {
-			const GroupID selected_group = WP(w, replaceveh_d).sel_group;
-			const uint num_engines = GetGroupNumEngines(_local_player, selected_group, eid);
+	/** Figure out if an engine should be added to a list
+	 * @param e The EngineID
+	 * @param draw_left If true, then the left list is drawn (the engines specific to the railtype you selected)
+	 * @param show_engines if truem then locomotives are drawn, else wagons (never both)
+	 * @return true if the engine should be in the list (based on this check)
+	 */
+	bool GenerateReplaceRailList(EngineID e, bool draw_left, bool show_engines)
+	{
+		const RailVehicleInfo *rvi = RailVehInfo(e);
 
-			/* Skip drawing the engines we don't have any of and haven't set for replacement */
-			if (num_engines == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), eid, selected_group) == INVALID_ENGINE) continue;
+		/* Ensure that the wagon/engine selection fits the engine. */
+		if ((rvi->railveh_type == RAILVEH_WAGON) == show_engines) return false;
+
+		if (draw_left && show_engines) {
+			/* Ensure that the railtype is specific to the selected one */
+			if (rvi->railtype != this->sel_railtype) return false;
 		} else {
-			/* This is for engines we can replace to and they should depend on what we selected to replace from */
-			if (!IsEngineBuildable(eid, type, _local_player)) continue; // we need to be able to build the engine
-			if (!EnginesGotCargoInCommon(eid, WP(w, replaceveh_d).sel_engine[0])) continue; // the engines needs to be able to carry the same cargo
+			/* Ensure that it's a compatible railtype to the selected one (like electric <-> diesel)
+			 * The vehicle do not have to have power on the railtype in question, only able to drive (pulled if needed) */
+			if (!IsCompatibleRail(rvi->railtype, this->sel_railtype)) return false;
+		}
+		return true;
+	}
 
-			/* Road vehicles can't be replaced by trams and vice-versa */
-			if (type == VEH_ROAD && HasBit(EngInfo(WP(w, replaceveh_d).sel_engine[0])->misc_flags, EF_ROAD_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue;
-			if (eid == WP(w, replaceveh_d).sel_engine[0]) continue; // we can't replace an engine into itself (that would be autorenew)
+
+	/** Generate a list
+	 * @param w Window, that contains the list
+	 * @param draw_left true if generating the left list, otherwise false
+	 */
+	void GenerateReplaceVehList(Window *w, bool draw_left)
+	{
+		EngineID selected_engine = INVALID_ENGINE;
+		VehicleType type = (VehicleType)this->window_number;
+		byte i = draw_left ? 0 : 1;
+
+		EngineList *list = &this->list[i];
+		list->clear();
+
+		const Engine *e;
+		FOR_ALL_ENGINES_OF_TYPE(e, type) {
+			EngineID eid = e->index;
+			if (type == VEH_TRAIN && !GenerateReplaceRailList(eid, draw_left, this->wagon_btnstate)) continue; // special rules for trains
+
+			if (draw_left) {
+				const GroupID selected_group = this->sel_group;
+				const uint num_engines = GetGroupNumEngines(_local_player, selected_group, eid);
+
+				/* Skip drawing the engines we don't have any of and haven't set for replacement */
+				if (num_engines == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), eid, selected_group) == INVALID_ENGINE) continue;
+			} else {
+				/* This is for engines we can replace to and they should depend on what we selected to replace from */
+				if (!IsEngineBuildable(eid, type, _local_player)) continue; // we need to be able to build the engine
+				if (!EnginesGotCargoInCommon(eid, this->sel_engine[0])) continue; // the engines needs to be able to carry the same cargo
+
+				/* Road vehicles can't be replaced by trams and vice-versa */
+				if (type == VEH_ROAD && HasBit(EngInfo(this->sel_engine[0])->misc_flags, EF_ROAD_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) continue;
+				if (eid == this->sel_engine[0]) continue; // we can't replace an engine into itself (that would be autorenew)
+			}
+
+			list->push_back(eid);
+			if (eid == this->sel_engine[i]) selected_engine = eid; // The selected engine is still in the list
+		}
+		this->sel_engine[i] = selected_engine; // update which engine we selected (the same or none, if it's not in the list anymore)
+		if (type == VEH_TRAIN) EngList_Sort(list, &TrainEngineNumberSorter);
+	}
+
+	/** Generate the lists */
+	void GenerateLists()
+	{
+		EngineID e = this->sel_engine[0];
+
+		if (this->update_left == true) {
+			/* We need to rebuild the left list */
+			GenerateReplaceVehList(this, true);
+			SetVScrollCount(this, this->list[0].size());
+			if (this->init_lists && this->sel_engine[0] == INVALID_ENGINE && this->list[0].size() != 0) {
+				this->sel_engine[0] = this->list[0][0];
+			}
 		}
 
-		list->push_back(eid);
-		if (eid == WP(w, replaceveh_d).sel_engine[i]) selected_engine = eid; // The selected engine is still in the list
-	}
-	WP(w, replaceveh_d).sel_engine[i] = selected_engine; // update which engine we selected (the same or none, if it's not in the list anymore)
-	if (type == VEH_TRAIN) EngList_Sort(list, &TrainEngineNumberSorter);
-}
-
-/** Generate the lists
- * @param w Window containing the lists
- */
-static void GenerateLists(Window *w)
-{
-	EngineID e = WP(w, replaceveh_d).sel_engine[0];
-
-	if (WP(w, replaceveh_d).update_left == true) {
-		/* We need to rebuild the left list */
-		GenerateReplaceVehList(w, true);
-		SetVScrollCount(w, WP(w, replaceveh_d).list[0].size());
-		if (WP(w, replaceveh_d).init_lists && WP(w, replaceveh_d).sel_engine[0] == INVALID_ENGINE && WP(w, replaceveh_d).list[0].size() != 0) {
-			WP(w, replaceveh_d).sel_engine[0] = WP(w, replaceveh_d).list[0][0];
+		if (this->update_right || e != this->sel_engine[0]) {
+			/* Either we got a request to rebuild the right list or the left list selected a different engine */
+			if (this->sel_engine[0] == INVALID_ENGINE) {
+				/* Always empty the right list when nothing is selected in the left list */
+				this->list[1].clear();
+				this->sel_engine[1] = INVALID_ENGINE;
+			} else {
+				GenerateReplaceVehList(this, false);
+				SetVScroll2Count(this, this->list[1].size());
+				if (this->init_lists && this->sel_engine[1] == INVALID_ENGINE && this->list[1].size() != 0) {
+					this->sel_engine[1] = this->list[1][0];
+				}
+			}
 		}
+		/* Reset the flags about needed updates */
+		this->update_left  = false;
+		this->update_right = false;
+		this->init_lists   = false;
 	}
 
-	if (WP(w, replaceveh_d).update_right || e != WP(w, replaceveh_d).sel_engine[0]) {
-		/* Either we got a request to rebuild the right list or the left list selected a different engine */
-		if (WP(w, replaceveh_d).sel_engine[0] == INVALID_ENGINE) {
-			/* Always empty the right list when nothing is selected in the left list */
-			WP(w, replaceveh_d).list[1].clear();
-			WP(w, replaceveh_d).sel_engine[1] = INVALID_ENGINE;
+public:
+	ReplaceVehicleWindow(const WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc, NULL, window_number)
+	{
+		this->wagon_btnstate = true; // start with locomotives (all other vehicles will not read this bool)
+		new (&this->list[0]) EngineList();
+		new (&this->list[1]) EngineList();
+		this->update_left   = true;
+		this->update_right  = true;
+		this->init_lists    = true;
+		this->sel_engine[0] = INVALID_ENGINE;
+		this->sel_engine[1] = INVALID_ENGINE;
+
+		this->resize.step_height = GetVehicleListHeight(vehicletype);
+		this->vscroll.cap = this->resize.step_height == 14 ? 8 : 4;
+
+		Widget *widget = this->widget;
+		widget[RVW_WIDGET_LEFT_MATRIX].data = widget[RVW_WIDGET_RIGHT_MATRIX].data = (this->vscroll.cap << 8) + 1;
+
+		if (vehicletype == VEH_TRAIN) {
+			this->wagon_btnstate = true;
+			/* The train window is bigger so we will move some of the widgets to fit the new size.
+			* We will start by moving the resize button to the lower right corner.                 */
+			widget[RVW_WIDGET_RESIZE].top         = widget[RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE].top;
+			widget[RVW_WIDGET_RESIZE].bottom      = widget[RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE].bottom;
+			widget[RVW_WIDGET_STOP_REPLACE].right = widget[RVW_WIDGET_RESIZE].right;
+
+			/* The detail panel is one line taller for trains so we will move some of the widgets one line (10 pixels) down. */
+			widget[RVW_WIDGET_LEFT_DETAILS].bottom  += 10;
+			widget[RVW_WIDGET_RIGHT_DETAILS].bottom += 10;
+			for (int i = RVW_WIDGET_START_REPLACE; i < RVW_WIDGET_RESIZE; i++) {
+				widget[i].top    += 10;
+				widget[i].bottom += 10;
+			}
 		} else {
-			GenerateReplaceVehList(w, false);
-			SetVScroll2Count(w, WP(w, replaceveh_d).list[1].size());
-			if (WP(w, replaceveh_d).init_lists && WP(w, replaceveh_d).sel_engine[1] == INVALID_ENGINE && WP(w, replaceveh_d).list[1].size() != 0) {
-				WP(w, replaceveh_d).sel_engine[1] = WP(w, replaceveh_d).list[1][0];
+			/* Since it's not a train we will hide the train only widgets. */
+			this->SetWidgetsHiddenState(true,
+									RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE,
+									RVW_WIDGET_TRAIN_FLUFF_LEFT,
+									RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN,
+									RVW_WIDGET_TRAIN_FLUFF_RIGHT,
+									RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE,
+									WIDGET_LIST_END);
+		}
+
+		ResizeWindow(this, 0, this->resize.step_height * this->vscroll.cap);
+
+		/* Set the minimum window size to the current window size */
+		this->resize.width  = this->width;
+		this->resize.height = this->height;
+
+		this->caption_color = _local_player;
+		this->sel_group = id_g;
+		this->vscroll2.cap = this->vscroll.cap;   // these two are always the same
+
+		this->FindWindowPlacementAndResize(desc);
+	}
+
+	virtual void OnPaint()
+	{
+		static const StringID _vehicle_type_names[] = {
+			STR_019F_TRAIN,
+			STR_019C_ROAD_VEHICLE,
+			STR_019E_SHIP,
+			STR_019D_AIRCRAFT
+		};
+
+		if (this->update_left || this->update_right) this->GenerateLists();
+
+		Player *p = GetPlayer(_local_player);
+		EngineID selected_id[2];
+		const GroupID selected_group = this->sel_group;
+
+		selected_id[0] = this->sel_engine[0];
+		selected_id[1] = this->sel_engine[1];
+
+		/* Disable the "Start Replacing" button if:
+		 *    Either list is empty
+		 * or The selected replacement engine has a replacement (to prevent loops)
+		 * or The right list (new replacement) has the existing replacement vehicle selected */
+		this->SetWidgetDisabledState(RVW_WIDGET_START_REPLACE,
+										selected_id[0] == INVALID_ENGINE ||
+										selected_id[1] == INVALID_ENGINE ||
+										EngineReplacementForPlayer(p, selected_id[1], selected_group) != INVALID_ENGINE ||
+										EngineReplacementForPlayer(p, selected_id[0], selected_group) == selected_id[1]);
+
+		/* Disable the "Stop Replacing" button if:
+		 *   The left list (existing vehicle) is empty
+		 *   or The selected vehicle has no replacement set up */
+		this->SetWidgetDisabledState(RVW_WIDGET_STOP_REPLACE,
+										selected_id[0] == INVALID_ENGINE ||
+										!EngineHasReplacementForPlayer(p, selected_id[0], selected_group));
+
+		/* now the actual drawing of the window itself takes place */
+		SetDParam(0, _vehicle_type_names[this->window_number]);
+
+		if (this->window_number == VEH_TRAIN) {
+			/* set on/off for renew_keep_length */
+			SetDParam(1, p->renew_keep_length ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
+
+			/* set wagon/engine button */
+			SetDParam(2, this->wagon_btnstate ? STR_ENGINES : STR_WAGONS);
+
+			/* sets the colour of that art thing */
+			this->widget[RVW_WIDGET_TRAIN_FLUFF_LEFT].color  = _player_colors[_local_player];
+			this->widget[RVW_WIDGET_TRAIN_FLUFF_RIGHT].color = _player_colors[_local_player];
+		}
+
+		if (this->window_number == VEH_TRAIN) {
+			/* Show the selected railtype in the pulldown menu */
+			this->widget[RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN].data = _rail_types_list[sel_railtype];
+		}
+
+		DrawWindowWidgets(this);
+
+		/* sets up the string for the vehicle that is being replaced to */
+		if (selected_id[0] != INVALID_ENGINE) {
+			if (!EngineHasReplacementForPlayer(p, selected_id[0], selected_group)) {
+				SetDParam(0, STR_NOT_REPLACING);
+			} else {
+				SetDParam(0, STR_ENGINE_NAME);
+				SetDParam(1, EngineReplacementForPlayer(p, selected_id[0], selected_group));
+			}
+		} else {
+			SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED);
+		}
+
+		DrawString(145, this->widget[RVW_WIDGET_INFO_TAB].top + 1, STR_02BD, TC_BLACK);
+
+		/* Draw the lists */
+		for (byte i = 0; i < 2; i++) {
+			uint widget     = (i == 0) ? RVW_WIDGET_LEFT_MATRIX : RVW_WIDGET_RIGHT_MATRIX;
+			EngineList list = this->list[i]; // which list to draw
+			EngineID start  = i == 0 ? this->vscroll.pos : this->vscroll2.pos; // what is the offset for the start (scrolling)
+			EngineID end    = min((i == 0 ? this->vscroll.cap : this->vscroll2.cap) + start, list.size());
+
+			/* Do the actual drawing */
+			DrawEngineList((VehicleType)this->window_number, this->widget[widget].left + 2, this->widget[widget].top + 1, list, start, end, this->sel_engine[i], i == 0 ? this->widget[RVW_WIDGET_LEFT_MATRIX].right - 2 : 0, selected_group);
+
+			/* Also draw the details if an engine is selected */
+			if (this->sel_engine[i] != INVALID_ENGINE) {
+				const Widget *wi = &this->widget[i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS];
+				int text_end = DrawVehiclePurchaseInfo(wi->left + 2, wi->top + 1, wi->right - wi->left - 2, this->sel_engine[i]);
+
+				if (text_end > wi->bottom) {
+					this->SetDirty();
+					ResizeWindowForWidget(this, i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS, 0, text_end - wi->bottom);
+					this->SetDirty();
+				}
 			}
 		}
 	}
-	/* Reset the flags about needed updates */
-	WP(w, replaceveh_d).update_left  = false;
-	WP(w, replaceveh_d).update_right = false;
-	WP(w, replaceveh_d).init_lists   = false;
-}
-
-
-void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, int count_location, GroupID selected_group);
-
-static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
-{
-	/* Strings for the pulldown menu */
-	static const StringID _vehicle_type_names[] = {
-		STR_019F_TRAIN,
-		STR_019C_ROAD_VEHICLE,
-		STR_019E_SHIP,
-		STR_019D_AIRCRAFT
-	};
-
-	switch (e->event) {
-		case WE_CREATE:
-			WP(w, replaceveh_d).wagon_btnstate = true; // start with locomotives (all other vehicles will not read this bool)
-			new (&WP(w, replaceveh_d).list[0]) EngineList();
-			new (&WP(w, replaceveh_d).list[1]) EngineList();
-			WP(w, replaceveh_d).update_left   = true;
-			WP(w, replaceveh_d).update_right  = true;
-			WP(w, replaceveh_d).init_lists    = true;
-			WP(w, replaceveh_d).sel_engine[0] = INVALID_ENGINE;
-			WP(w, replaceveh_d).sel_engine[1] = INVALID_ENGINE;
-			break;
-
-		case WE_PAINT: {
-			if (WP(w, replaceveh_d).update_left || WP(w, replaceveh_d).update_right) GenerateLists(w);
-
-			Player *p = GetPlayer(_local_player);
-			EngineID selected_id[2];
-			const GroupID selected_group = WP(w, replaceveh_d).sel_group;
-
-			selected_id[0] = WP(w, replaceveh_d).sel_engine[0];
-			selected_id[1] = WP(w, replaceveh_d).sel_engine[1];
-
-			/* Disable the "Start Replacing" button if:
-			 *    Either list is empty
-			 * or The selected replacement engine has a replacement (to prevent loops)
-			 * or The right list (new replacement) has the existing replacement vehicle selected */
-			w->SetWidgetDisabledState(RVW_WIDGET_START_REPLACE,
-										 selected_id[0] == INVALID_ENGINE ||
-										 selected_id[1] == INVALID_ENGINE ||
-										 EngineReplacementForPlayer(p, selected_id[1], selected_group) != INVALID_ENGINE ||
-										 EngineReplacementForPlayer(p, selected_id[0], selected_group) == selected_id[1]);
-
-			/* Disable the "Stop Replacing" button if:
-			 *   The left list (existing vehicle) is empty
-			 *   or The selected vehicle has no replacement set up */
-			w->SetWidgetDisabledState(RVW_WIDGET_STOP_REPLACE,
-										 selected_id[0] == INVALID_ENGINE ||
-										 !EngineHasReplacementForPlayer(p, selected_id[0], selected_group));
-
-			/* now the actual drawing of the window itself takes place */
-			SetDParam(0, _vehicle_type_names[w->window_number]);
-
-			if (w->window_number == VEH_TRAIN) {
-				/* set on/off for renew_keep_length */
-				SetDParam(1, p->renew_keep_length ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
-
-				/* set wagon/engine button */
-				SetDParam(2, WP(w, replaceveh_d).wagon_btnstate ? STR_ENGINES : STR_WAGONS);
-
-				/* sets the colour of that art thing */
-				w->widget[RVW_WIDGET_TRAIN_FLUFF_LEFT].color  = _player_colors[_local_player];
-				w->widget[RVW_WIDGET_TRAIN_FLUFF_RIGHT].color = _player_colors[_local_player];
-			}
-
-			if (w->window_number == VEH_TRAIN) {
-				/* Show the selected railtype in the pulldown menu */
-				RailType railtype = _railtype_selected_in_replace_gui;
-				w->widget[RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN].data = _rail_types_list[railtype];
-			}
-
-			DrawWindowWidgets(w);
-
-			/* sets up the string for the vehicle that is being replaced to */
-			if (selected_id[0] != INVALID_ENGINE) {
-				if (!EngineHasReplacementForPlayer(p, selected_id[0], selected_group)) {
-					SetDParam(0, STR_NOT_REPLACING);
-				} else {
-					SetDParam(0, STR_ENGINE_NAME);
-					SetDParam(1, EngineReplacementForPlayer(p, selected_id[0], selected_group));
-				}
-			} else {
-				SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED);
-			}
-
-			DrawString(145, w->widget[RVW_WIDGET_INFO_TAB].top + 1, STR_02BD, TC_BLACK);
-
-			/* Draw the lists */
-			for(byte i = 0; i < 2; i++) {
-				uint widget     = (i == 0) ? RVW_WIDGET_LEFT_MATRIX : RVW_WIDGET_RIGHT_MATRIX;
-				EngineList list = WP(w, replaceveh_d).list[i]; // which list to draw
-				EngineID start  = i == 0 ? w->vscroll.pos : w->vscroll2.pos; // what is the offset for the start (scrolling)
-				EngineID end    = min((i == 0 ? w->vscroll.cap : w->vscroll2.cap) + start, list.size());
-
-				/* Do the actual drawing */
-				DrawEngineList((VehicleType)w->window_number, w->widget[widget].left + 2, w->widget[widget].top + 1, list, start, end, WP(w, replaceveh_d).sel_engine[i], i == 0 ? w->widget[RVW_WIDGET_LEFT_MATRIX].right - 2 : 0, selected_group);
-
-				/* Also draw the details if an engine is selected */
-				if (WP(w, replaceveh_d).sel_engine[i] != INVALID_ENGINE) {
-					const Widget *wi = &w->widget[i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS];
-					int text_end = DrawVehiclePurchaseInfo(wi->left + 2, wi->top + 1, wi->right - wi->left - 2, WP(w, replaceveh_d).sel_engine[i]);
 
-					if (text_end > wi->bottom) {
-						w->SetDirty();
-						ResizeWindowForWidget(w, i == 0 ? RVW_WIDGET_LEFT_DETAILS : RVW_WIDGET_RIGHT_DETAILS, 0, text_end - wi->bottom);
-						w->SetDirty();
-					}
-				}
-			}
-
-		} break;   // end of paint
-
-		case WE_CLICK: {
-			switch (e->we.click.widget) {
-				case RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE:
-					WP(w, replaceveh_d).wagon_btnstate = !(WP(w, replaceveh_d).wagon_btnstate);
-					WP(w, replaceveh_d).update_left = true;
-					WP(w, replaceveh_d).init_lists  = true;
-					w->SetDirty();
-					break;
-
-				case RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: /* Railtype selection dropdown menu */
-					ShowDropDownMenu(w, _rail_types_list, _railtype_selected_in_replace_gui, RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN, 0, ~GetPlayer(_local_player)->avail_railtypes);
-					break;
-
-				case RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE: /* toggle renew_keep_length */
-					DoCommandP(0, 5, GetPlayer(_local_player)->renew_keep_length ? 0 : 1, NULL, CMD_SET_AUTOREPLACE);
-					break;
-
-				case RVW_WIDGET_START_REPLACE: { /* Start replacing */
-					EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
-					EngineID veh_to = WP(w, replaceveh_d).sel_engine[1];
-					DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16) , veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
-				} break;
-
-				case RVW_WIDGET_STOP_REPLACE: { /* Stop replacing */
-					EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
-					DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16), veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
-				} break;
-
-				case RVW_WIDGET_LEFT_MATRIX:
-				case RVW_WIDGET_RIGHT_MATRIX: {
-					uint i = (e->we.click.pt.y - 14) / w->resize.step_height;
-					uint16 click_scroll_pos = e->we.click.widget == RVW_WIDGET_LEFT_MATRIX ? w->vscroll.pos : w->vscroll2.pos;
-					uint16 click_scroll_cap = e->we.click.widget == RVW_WIDGET_LEFT_MATRIX ? w->vscroll.cap : w->vscroll2.cap;
-					byte click_side         = e->we.click.widget == RVW_WIDGET_LEFT_MATRIX ? 0 : 1;
-					uint16 engine_count     = WP(w, replaceveh_d).list[click_side].size();
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE:
+				this->wagon_btnstate = !(this->wagon_btnstate);
+				this->update_left = true;
+				this->init_lists  = true;
+				this->SetDirty();
+				break;
 
-					if (i < click_scroll_cap) {
-						i += click_scroll_pos;
-						EngineID e = engine_count > i ? WP(w, replaceveh_d).list[click_side][i] : INVALID_ENGINE;
-						if (e == WP(w, replaceveh_d).sel_engine[click_side]) break; // we clicked the one we already selected
-						WP(w, replaceveh_d).sel_engine[click_side] = e;
-						if (click_side == 0) {
-							WP(w, replaceveh_d).update_right = true;
-							WP(w, replaceveh_d).init_lists   = true;
-						}
-						w->SetDirty();
-						}
-					break;
-					}
-			}
-			break;
-		}
-
-		case WE_DROPDOWN_SELECT: { /* we have selected a dropdown item in the list */
-			RailType temp = (RailType)e->we.dropdown.index;
-			if (temp == _railtype_selected_in_replace_gui) break; // we didn't select a new one. No need to change anything
-			_railtype_selected_in_replace_gui = temp;
-			/* Reset scrollbar positions */
-			w->vscroll.pos  = 0;
-			w->vscroll2.pos = 0;
-			/* Rebuild the lists */
-			WP(w, replaceveh_d).update_left  = true;
-			WP(w, replaceveh_d).update_right = true;
-			WP(w, replaceveh_d).init_lists   = true;
-			w->SetDirty();
-		} break;
+			case RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: /* Railtype selection dropdown menu */
+				ShowDropDownMenu(this, _rail_types_list, sel_railtype, RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN, 0, ~GetPlayer(_local_player)->avail_railtypes);
+				break;
 
-		case WE_RESIZE:
-		{
-			w->vscroll.cap  += e->we.sizing.diff.y / (int)w->resize.step_height;
-			w->vscroll2.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
-
-			Widget *widget = w->widget;
-
-			widget[RVW_WIDGET_LEFT_MATRIX].data = widget[RVW_WIDGET_RIGHT_MATRIX].data = (w->vscroll2.cap << 8) + 1;
+			case RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE: /* toggle renew_keep_length */
+				DoCommandP(0, 5, GetPlayer(_local_player)->renew_keep_length ? 0 : 1, NULL, CMD_SET_AUTOREPLACE);
+				break;
 
-			if (e->we.sizing.diff.x != 0) {
-				/* We changed the width of the window so we have to resize the lists.
-				 * Because ResizeButtons() makes each widget the same size it can't be used on the lists
-				 * because then the lists would have the same size as the scrollbars.
-				 * Instead we use it on the detail panels.
-				 * Afterwards we use the new location of the detail panels (the middle of the window)
-				 * to place the lists.
-				 * This way the lists will have equal size while keeping the width of the scrollbars unchanged. */
-				ResizeButtons(w, RVW_WIDGET_LEFT_DETAILS, RVW_WIDGET_RIGHT_DETAILS);
-				widget[RVW_WIDGET_RIGHT_MATRIX].left    = widget[RVW_WIDGET_RIGHT_DETAILS].left;
-				widget[RVW_WIDGET_LEFT_SCROLLBAR].right = widget[RVW_WIDGET_LEFT_DETAILS].right;
-				widget[RVW_WIDGET_LEFT_SCROLLBAR].left  = widget[RVW_WIDGET_LEFT_SCROLLBAR].right - 11;
-				widget[RVW_WIDGET_LEFT_MATRIX].right    = widget[RVW_WIDGET_LEFT_SCROLLBAR].left - 1;
-			}
-		} break;
+			case RVW_WIDGET_START_REPLACE: { /* Start replacing */
+				EngineID veh_from = this->sel_engine[0];
+				EngineID veh_to = this->sel_engine[1];
+				DoCommandP(0, 3 + (this->sel_group << 16) , veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
+			} break;
 
-		case WE_INVALIDATE_DATA:
-			if (_rebuild_left_list) WP(w, replaceveh_d).update_left = true;
-			if (_rebuild_right_list) WP(w, replaceveh_d).update_right = true;
-			w->SetDirty();
-			break;
+			case RVW_WIDGET_STOP_REPLACE: { /* Stop replacing */
+				EngineID veh_from = this->sel_engine[0];
+				DoCommandP(0, 3 + (this->sel_group << 16), veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
+			} break;
 
-		case WE_DESTROY:
-			WP(w, replaceveh_d).list[0].~EngineList(); // call destructor explicitly
-			WP(w, replaceveh_d).list[1].~EngineList();
-		break;
+			case RVW_WIDGET_LEFT_MATRIX:
+			case RVW_WIDGET_RIGHT_MATRIX: {
+				uint i = (pt.y - 14) / this->resize.step_height;
+				uint16 click_scroll_pos = widget == RVW_WIDGET_LEFT_MATRIX ? this->vscroll.pos : this->vscroll2.pos;
+				uint16 click_scroll_cap = widget == RVW_WIDGET_LEFT_MATRIX ? this->vscroll.cap : this->vscroll2.cap;
+				byte click_side         = widget == RVW_WIDGET_LEFT_MATRIX ? 0 : 1;
+				size_t engine_count     = this->list[click_side].size();
+
+				if (i < click_scroll_cap) {
+					i += click_scroll_pos;
+					EngineID e = engine_count > i ? this->list[click_side][i] : INVALID_ENGINE;
+					if (e == this->sel_engine[click_side]) break; // we clicked the one we already selected
+					this->sel_engine[click_side] = e;
+					if (click_side == 0) {
+						this->update_right = true;
+						this->init_lists   = true;
+					}
+					this->SetDirty();
+					}
+				break;
+				}
+		}
 	}
-}
+
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		RailType temp = (RailType)index;
+		if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything
+		sel_railtype = temp;
+		/* Reset scrollbar positions */
+		this->vscroll.pos  = 0;
+		this->vscroll2.pos = 0;
+		/* Rebuild the lists */
+		this->update_left  = true;
+		this->update_right = true;
+		this->init_lists   = true;
+		this->SetDirty();
+	}
+
+	virtual void OnResize(Point new_size, Point delta) {
+		this->vscroll.cap  += delta.y / (int)this->resize.step_height;
+		this->vscroll2.cap += delta.y / (int)this->resize.step_height;
+
+		Widget *widget = this->widget;
+
+		widget[RVW_WIDGET_LEFT_MATRIX].data = widget[RVW_WIDGET_RIGHT_MATRIX].data = (this->vscroll2.cap << 8) + 1;
+
+		if (delta.x != 0) {
+			/* We changed the width of the window so we have to resize the lists.
+				* Because ResizeButtons() makes each widget the same size it can't be used on the lists
+				* because then the lists would have the same size as the scrollbars.
+				* Instead we use it on the detail panels.
+				* Afterwards we use the new location of the detail panels (the middle of the window)
+				* to place the lists.
+				* This way the lists will have equal size while keeping the width of the scrollbars unchanged. */
+			ResizeButtons(this, RVW_WIDGET_LEFT_DETAILS, RVW_WIDGET_RIGHT_DETAILS);
+			widget[RVW_WIDGET_RIGHT_MATRIX].left    = widget[RVW_WIDGET_RIGHT_DETAILS].left;
+			widget[RVW_WIDGET_LEFT_SCROLLBAR].right = widget[RVW_WIDGET_LEFT_DETAILS].right;
+			widget[RVW_WIDGET_LEFT_SCROLLBAR].left  = widget[RVW_WIDGET_LEFT_SCROLLBAR].right - 11;
+			widget[RVW_WIDGET_LEFT_MATRIX].right    = widget[RVW_WIDGET_LEFT_SCROLLBAR].left - 1;
+		}
+	}
+
+	virtual void OnInvalidateData(int data)
+	{
+		if (data != 0) {
+			this->update_left = true;
+		} else {
+			this->update_right = true;
+		}
+	}
+};
 
 static const Widget _replace_vehicle_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
@@ -512,7 +532,7 @@
 	WC_REPLACE_VEHICLE, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_replace_vehicle_widgets,
-	ReplaceVehicleWndProc
+	NULL
 };
 
 static const WindowDesc _replace_vehicle_desc = {
@@ -520,55 +540,13 @@
 	WC_REPLACE_VEHICLE, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_replace_vehicle_widgets,
-	ReplaceVehicleWndProc
+	NULL
 };
 
+RailType ReplaceVehicleWindow::sel_railtype = RAILTYPE_RAIL;
 
 void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype)
 {
 	DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
-	Window *w = vehicletype == VEH_TRAIN ? AllocateWindowDescFront(&_replace_rail_vehicle_desc, VEH_TRAIN)
-										 : AllocateWindowDescFront(&_replace_vehicle_desc, vehicletype);
-
-	w->resize.step_height = GetVehicleListHeight(vehicletype);
-	w->vscroll.cap = w->resize.step_height == 14 ? 8 : 4;
-
-	Widget *widget = w->widget;
-	widget[RVW_WIDGET_LEFT_MATRIX].data = widget[RVW_WIDGET_RIGHT_MATRIX].data = (w->vscroll.cap << 8) + 1;
-
-	if (vehicletype == VEH_TRAIN) {
-		WP(w, replaceveh_d).wagon_btnstate = true;
-		/* The train window is bigger so we will move some of the widgets to fit the new size.
-		 * We will start by moving the resize button to the lower right corner.                 */
-		widget[RVW_WIDGET_RESIZE].top         = widget[RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE].top;
-		widget[RVW_WIDGET_RESIZE].bottom      = widget[RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE].bottom;
-		widget[RVW_WIDGET_STOP_REPLACE].right = widget[RVW_WIDGET_RESIZE].right;
-
-		/* The detail panel is one line taller for trains so we will move some of the widgets one line (10 pixels) down. */
-		widget[RVW_WIDGET_LEFT_DETAILS].bottom  += 10;
-		widget[RVW_WIDGET_RIGHT_DETAILS].bottom += 10;
-		for (int i = RVW_WIDGET_START_REPLACE; i < RVW_WIDGET_RESIZE; i++) {
-			widget[i].top    += 10;
-			widget[i].bottom += 10;
-		}
-	} else {
-		/* Since it's not a train we will hide the train only widgets. */
-		w->SetWidgetsHiddenState(true,
-								 RVW_WIDGET_TRAIN_ENGINEWAGON_TOGGLE,
-								 RVW_WIDGET_TRAIN_FLUFF_LEFT,
-								 RVW_WIDGET_TRAIN_RAILTYPE_DROPDOWN,
-								 RVW_WIDGET_TRAIN_FLUFF_RIGHT,
-								 RVW_WIDGET_TRAIN_WAGONREMOVE_TOGGLE,
-								 WIDGET_LIST_END);
-	}
-
-	ResizeWindow(w, 0, w->resize.step_height * w->vscroll.cap);
-
-	/* Set the minimum window size to the current window size */
-	w->resize.width  = w->width;
-	w->resize.height = w->height;
-
-	w->caption_color = _local_player;
-	WP(w, replaceveh_d).sel_group = id_g;
-	w->vscroll2.cap = w->vscroll.cap;   // these two are always the same
+	new ReplaceVehicleWindow(vehicletype == VEH_TRAIN ? &_replace_rail_vehicle_desc : &_replace_vehicle_desc, vehicletype, id_g);
 }
--- a/src/bridge_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/bridge_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -184,7 +184,7 @@
 	}
 
 	if (j != 0) {
-		Window *w = AllocateWindowDesc(&_build_bridge_desc);
+		Window *w = new Window(&_build_bridge_desc);
 		/* Change the data, or the caption of the gui. Set it to road or rail, accordingly */
 		w->widget[BBSW_CAPTION].data = (transport_type == TRANSPORT_ROAD) ? STR_1803_SELECT_ROAD_BRIDGE : STR_100D_SELECT_RAIL_BRIDGE;
 	} else {
--- a/src/build_vehicle_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/build_vehicle_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -27,6 +27,7 @@
 #include "settings_type.h"
 #include "gfx_func.h"
 #include "widgets/dropdown_func.h"
+#include "string_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -1034,7 +1035,7 @@
 
 		case BUILD_VEHICLE_WIDGET_LIST: {
 			uint i = (e->we.click.pt.y - w->widget[BUILD_VEHICLE_WIDGET_LIST].top) / GetVehicleListHeight(bv->vehicle_type) + w->vscroll.pos;
-			uint num_items = bv->eng_list.size();
+			size_t num_items = bv->eng_list.size();
 			bv->sel_engine = (i < num_items) ? bv->eng_list[i] : INVALID_ENGINE;
 			w->SetDirty();
 			break;
@@ -1136,7 +1137,7 @@
 			break;
 
 		case WE_ON_EDIT_TEXT: {
-			if (e->we.edittext.str[0] != '\0') {
+			if (!StrEmpty(e->we.edittext.str)) {
 				StringID str = STR_NULL;
 				_cmd_text = e->we.edittext.str;
 				switch (bv->vehicle_type) {
@@ -1192,7 +1193,7 @@
 
 	DeleteWindowById(WC_BUILD_VEHICLE, num);
 
-	w = AllocateWindowDescFront(&_build_vehicle_desc, num, &type);
+	w = AllocateWindowDescFront<Window>(&_build_vehicle_desc, num, &type);
 
 	if (w == NULL) return;
 
--- a/src/cheat_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/cheat_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -229,5 +229,5 @@
 void ShowCheatWindow()
 {
 	DeleteWindowById(WC_CHEATS, 0);
-	AllocateWindowDesc(&_cheats_desc);
+	new Window(&_cheats_desc);
 }
--- a/src/console.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/console.cpp	Sun May 11 20:09:34 2008 +0000
@@ -318,7 +318,7 @@
 {
 	switch (_iconsole_mode) {
 		case ICONSOLE_CLOSED: {
-			Window *w = AllocateWindowDesc(&_iconsole_window_desc);
+			Window *w = new Window(&_iconsole_window_desc);
 			w->height = _screen.height / 3;
 			w->width = _screen.width;
 			_iconsole_mode = ICONSOLE_OPENED;
--- a/src/depot_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/depot_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -100,6 +100,20 @@
 	{   WIDGETS_END},
 };
 
+
+struct depot_d {
+	VehicleID sel;
+	VehicleType type;
+	bool generate_list;
+	uint16 engine_list_length;
+	uint16 wagon_list_length;
+	uint16 engine_count;
+	uint16 wagon_count;
+	Vehicle **vehicle_list;
+	Vehicle **wagon_list;
+};
+assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(depot_d));
+
 static void DepotWndProc(Window *w, WindowEvent *e);
 
 static const WindowDesc _train_depot_desc = {
@@ -997,13 +1011,13 @@
 	switch (type) {
 		default: NOT_REACHED();
 		case VEH_TRAIN:
-			w = AllocateWindowDescFront(&_train_depot_desc, tile); break;
+			w = AllocateWindowDescFront<Window>(&_train_depot_desc, tile); break;
 		case VEH_ROAD:
-			w = AllocateWindowDescFront(&_road_depot_desc, tile); break;
+			w = AllocateWindowDescFront<Window>(&_road_depot_desc, tile); break;
 		case VEH_SHIP:
-			w = AllocateWindowDescFront(&_ship_depot_desc, tile); break;
+			w = AllocateWindowDescFront<Window>(&_ship_depot_desc, tile); break;
 		case VEH_AIRCRAFT:
-			w = AllocateWindowDescFront(&_aircraft_depot_desc, tile); break;
+			w = AllocateWindowDescFront<Window>(&_aircraft_depot_desc, tile); break;
 	}
 
 	if (w != NULL) {
--- a/src/dock_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/dock_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -229,7 +229,7 @@
 	if (!IsValidPlayer(_current_player)) return;
 
 	DeleteWindowByClass(WC_BUILD_TOOLBAR);
-	Window *w = AllocateWindowDescFront(&_build_docks_toolbar_desc, TRANSPORT_WATER);
+	Window *w = AllocateWindowDescFront<Window>(&_build_docks_toolbar_desc, TRANSPORT_WATER);
 	if (_patches.link_terraform_toolbar) ShowTerraformToolbar(w);
 }
 
@@ -274,7 +274,7 @@
 		}
 		break;
 
-	case WE_MOUSELOOP:
+	case WE_TICK:
 		if (WP(w, def_d).close) {
 			delete w;
 			return;
@@ -309,7 +309,7 @@
 
 static void ShowBuildDockStationPicker()
 {
-	AllocateWindowDesc(&_build_dock_station_desc);
+	new Window(&_build_dock_station_desc);
 }
 
 static void UpdateDocksDirection()
@@ -349,7 +349,7 @@
 		}
 	} break;
 
-	case WE_MOUSELOOP:
+	case WE_TICK:
 		if (WP(w, def_d).close) delete w;
 		break;
 
@@ -379,7 +379,7 @@
 
 static void ShowBuildDocksDepotPicker()
 {
-	AllocateWindowDesc(&_build_docks_depot_desc);
+	new Window(&_build_docks_depot_desc);
 	UpdateDocksDirection();
 }
 
--- a/src/driver.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/driver.h	Sun May 11 20:09:34 2008 +0000
@@ -43,7 +43,7 @@
 
 	static Drivers &GetDrivers()
 	{
-		static Drivers &s_drivers = *new Drivers();
+		static Drivers s_drivers;
 		return s_drivers;
 	}
 
@@ -67,7 +67,23 @@
 		name(NULL)
 	{}
 
-	virtual ~DriverFactoryBase() { if (this->name != NULL) GetDrivers().erase(this->name); free(this->name); }
+	/** Frees memory used for this->name
+	 */
+	virtual ~DriverFactoryBase() {
+		if (this->name == NULL) return;
+		GetDrivers().erase(this->name);
+		free(this->name);
+	}
+
+	/** Shuts down all active drivers
+	 */
+	static void ShutdownDrivers()
+	{
+		for (Driver::Type dt = Driver::DT_BEGIN; dt < Driver::DT_END; dt++) {
+			Driver *driver = *GetActiveDriver(dt);
+			if (driver != NULL) driver->Stop();
+		}
+	}
 
 	static const Driver *SelectDriver(const char *name, Driver::Type type);
 	static char *GetDriversInfo(char *p, const char *last);
--- a/src/economy.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/economy.cpp	Sun May 11 20:09:34 2008 +0000
@@ -1151,7 +1151,7 @@
 
 		if (s->age == 12-1) {
 			pair = SetupSubsidyDecodeParam(s, 1);
-			AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NM_NORMAL, NF_TILE, NT_SUBSIDIES, DNC_NONE, pair.a, pair.b);
+			AddNewsItem(STR_202E_OFFER_OF_SUBSIDY_EXPIRED, NM_NORMAL, NF_TILE | NF_TILE2, NT_SUBSIDIES, DNC_NONE, pair.a, pair.b);
 			s->cargo_type = CT_INVALID;
 			modified = true;
 			for (PlayerID i = PLAYER_FIRST; i != MAX_PLAYERS; i++) {
@@ -1161,7 +1161,7 @@
 			st = GetStation(s->to);
 			if (st->owner == _local_player) {
 				pair = SetupSubsidyDecodeParam(s, 1);
-				AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NM_NORMAL, NF_TILE, NT_SUBSIDIES, DNC_NONE, pair.a, pair.b);
+				AddNewsItem(STR_202F_SUBSIDY_WITHDRAWN_SERVICE, NM_NORMAL, NF_TILE | NF_TILE2, NT_SUBSIDIES, DNC_NONE, pair.a, pair.b);
 			}
 			s->cargo_type = CT_INVALID;
 			modified = true;
@@ -1203,7 +1203,7 @@
 				if (!CheckSubsidyDuplicate(s)) {
 					s->age = 0;
 					pair = SetupSubsidyDecodeParam(s, 0);
-					AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NM_NORMAL, NF_TILE, NT_SUBSIDIES, DNC_NONE, pair.a, pair.b);
+					AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NM_NORMAL, NF_TILE | NF_TILE2, NT_SUBSIDIES, DNC_NONE, pair.a, pair.b);
 					for (PlayerID i = PLAYER_FIRST; i != MAX_PLAYERS; i++) {
 						AI_Event(i, new AIEventSubsidyOffer(s - _subsidies));
 					}
@@ -1423,7 +1423,7 @@
 			SetDParam(0, _current_player);
 			AddNewsItem(
 				STR_2031_SERVICE_SUBSIDY_AWARDED + _opt.diff.subsidy_multiplier,
-				NM_NORMAL, NF_TILE, NT_SUBSIDIES, DNC_NONE,
+				NM_NORMAL, NF_TILE | NF_TILE2, NT_SUBSIDIES, DNC_NONE,
 				pair.a, pair.b
 			);
 			for (PlayerID i = PLAYER_FIRST; i != MAX_PLAYERS; i++) {
--- a/src/elrail.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/elrail.cpp	Sun May 11 20:09:34 2008 +0000
@@ -67,6 +67,7 @@
 #include "player_base.h"
 #include "tunnelbridge.h"
 #include "engine_func.h"
+#include "elrail_func.h"
 #include "engine_base.h"
 
 #include "table/sprites.h"
@@ -182,8 +183,6 @@
 		{ 1, 0, 15, 16 }, // NW
 	};
 
-	if (!HasCatenary(GetRailType(ti->tile)) || _patches.disable_elrails) return;
-
 	DiagDirection dir = GetTunnelBridgeDirection(ti->tile);
 
 	const SortableSpriteStruct *sss = &CatenarySpriteData_Tunnel[dir];
@@ -382,11 +381,6 @@
 
 void DrawCatenaryOnBridge(const TileInfo *ti)
 {
-	if (_patches.disable_elrails) return;
-
-	/* Do not draw catenary if it is invisible */
-	if (IsInvisibilitySet(TO_CATENARY)) return;
-
 	TileIndex end = GetSouthernBridgeEnd(ti->tile);
 	TileIndex start = GetOtherBridgeEnd(end);
 
@@ -440,11 +434,6 @@
 
 void DrawCatenary(const TileInfo *ti)
 {
-	if (_patches.disable_elrails) return;
-
-	/* Do not draw catenary if it is invisible */
-	if (IsInvisibilitySet(TO_CATENARY)) return;
-
 	switch (GetTileType(ti->tile)) {
 		case MP_RAILWAY:
 			if (IsRailDepot(ti->tile)) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/elrail_func.h	Sun May 11 20:09:34 2008 +0000
@@ -0,0 +1,42 @@
+/* $Id$ */
+
+/** @file elrail_func.h header file for electrified rail specific functions */
+
+#ifndef ELRAIL_FUNC_H
+#define ELRAIL_FUNC_H
+
+#include "rail.h"
+#include "transparency.h"
+#include "tile_cmd.h"
+#include "settings_type.h"
+
+/**
+ * Test if a rail type has catenary
+ * @param rt Rail type to test
+ */
+static inline bool HasCatenary(RailType rt)
+{
+	return HasBit(GetRailTypeInfo(rt)->flags, RTF_CATENARY);
+}
+
+/**
+ * Test if we should draw rail catenary
+ * @param rt Rail type to test
+ */
+static inline bool HasCatenaryDrawn(RailType rt)
+{
+	return HasCatenary(rt) && !IsInvisibilitySet(TO_CATENARY) && !_patches.disable_elrails;
+}
+
+/**
+ * Draws overhead wires and pylons for electric railways.
+ * @param ti The TileInfo struct of the tile being drawn
+ * @see DrawCatenaryRailway
+ */
+void DrawCatenary(const TileInfo *ti);
+void DrawCatenaryOnTunnel(const TileInfo *ti);
+void DrawCatenaryOnBridge(const TileInfo *ti);
+
+int32 SettingsDisableElrail(int32 p1); ///< _patches.disable_elrail callback
+
+#endif /* ELRAIL_FUNC_H */
--- a/src/engine_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/engine_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -114,7 +114,7 @@
 
 void ShowEnginePreviewWindow(EngineID engine)
 {
-	AllocateWindowDescFront(&_engine_preview_desc, engine);
+	AllocateWindowDescFront<Window>(&_engine_preview_desc, engine);
 }
 
 static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw)
--- a/src/fileio.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/fileio.cpp	Sun May 11 20:09:34 2008 +0000
@@ -27,7 +27,7 @@
 
 struct Fio {
 	byte *buffer, *buffer_end;             ///< position pointer in local buffer and last valid byte of buffer
-	uint32 pos;                            ///< current (system) position in file
+	size_t pos;                            ///< current (system) position in file
 	FILE *cur_fh;                          ///< current file handle
 	const char *filename;                  ///< current filename
 	FILE *handles[MAX_FILE_SLOTS];         ///< array of file handles we can have open
@@ -125,7 +125,7 @@
 	return (FioReadWord() << 16) | b;
 }
 
-void FioReadBlock(void *ptr, uint size)
+void FioReadBlock(void *ptr, size_t size)
 {
 	FioSeekTo(FioGetPos(), SEEK_SET);
 	_fio.pos += fread(ptr, 1, size, _fio.cur_fh);
@@ -480,7 +480,7 @@
 	TarHeader th;
 	char buf[sizeof(th.name) + 1], *end;
 	char name[sizeof(th.prefix) + 1 + sizeof(th.name) + 1];
-	int num = 0, pos = 0;
+	size_t num = 0, pos = 0;
 
 	/* Make a char of 512 empty bytes */
 	char empty[512];
@@ -499,7 +499,7 @@
 		}
 
 		name[0] = '\0';
-		int len = 0;
+		size_t len = 0;
 
 		/* The prefix contains the directory-name */
 		if (th.prefix[0] != '\0') {
@@ -550,7 +550,7 @@
 	return true;
 }
 
-static int ScanPathForTarFiles(const char *path, int basepath_length)
+static int ScanPathForTarFiles(const char *path, size_t basepath_length)
 {
 	extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
 
--- a/src/fileio.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/fileio.h	Sun May 11 20:09:34 2008 +0000
@@ -18,7 +18,7 @@
 uint32 FioReadDword();
 void FioCloseAll();
 void FioOpenFile(int slot, const char *filename);
-void FioReadBlock(void *ptr, uint size);
+void FioReadBlock(void *ptr, size_t size);
 void FioSkipBytes(int n);
 void FioCreateDirectory(const char *filename);
 
@@ -70,8 +70,8 @@
 };
 struct TarFileListEntry {
 	TarListEntry *tar;
-	int size;
-	int position;
+	size_t size;
+	size_t position;
 };
 typedef std::map<std::string, TarListEntry *> TarList;
 typedef std::map<std::string, TarFileListEntry> TarFileList;
--- a/src/fios.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/fios.cpp	Sun May 11 20:09:34 2008 +0000
@@ -18,6 +18,9 @@
 
 #ifdef WIN32
 # include <tchar.h>
+# ifndef UNICODE
+#  include <io.h>
+# endif
 # define access _taccess
 # define unlink _tunlink
 #else
--- a/src/functions.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/functions.h	Sun May 11 20:09:34 2008 +0000
@@ -23,9 +23,6 @@
 bool CheckOwnership(Owner owner);
 bool CheckTileOwnership(TileIndex tile);
 
-/* misc_cmd.cpp */
-void PlaceTreesRandomly();
-
 void InitializeLandscapeVariables(bool only_constants);
 
 /* misc functions */
--- a/src/genworld_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/genworld_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -29,6 +29,7 @@
 #include "widgets/dropdown_func.h"
 #include "core/random_func.hpp"
 #include "landscape_type.h"
+#include "querystring_gui.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -43,14 +44,6 @@
 	GLWP_END
 };
 
-struct generate_d {
-	uint widget_id;
-	uint x;
-	uint y;
-	char name[64];
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(generate_d));
-
 extern void SwitchMode(int new_mode);
 
 static inline void SetNewLandscapeType(byte landscape)
@@ -244,314 +237,324 @@
 	return list;
 }
 
-static void GenerateLandscapeWndProc(Window *w, WindowEvent *e)
-{
-	static const StringID elevations[]  = {STR_682A_VERY_FLAT, STR_682B_FLAT, STR_682C_HILLY, STR_682D_MOUNTAINOUS, INVALID_STRING_ID};
-	static const StringID sea_lakes[]   = {STR_VERY_LOW, STR_6820_LOW, STR_6821_MEDIUM, STR_6822_HIGH, INVALID_STRING_ID};
-	static const StringID smoothness[]  = {STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_ROUGH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_ROUGH, INVALID_STRING_ID};
-	static const StringID tree_placer[] = {STR_CONFIG_PATCHES_TREE_PLACER_NONE, STR_CONFIG_PATCHES_TREE_PLACER_ORIGINAL, STR_CONFIG_PATCHES_TREE_PLACER_IMPROVED, INVALID_STRING_ID};
-	static const StringID rotation[]    = {STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_COUNTER_CLOCKWISE, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_CLOCKWISE, INVALID_STRING_ID};
-	static const StringID landscape[]   = {STR_CONFIG_PATCHES_LAND_GENERATOR_ORIGINAL, STR_CONFIG_PATCHES_LAND_GENERATOR_TERRA_GENESIS, INVALID_STRING_ID};
-	static const StringID num_towns[]   = {STR_NUM_VERY_LOW, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
-	static const StringID num_inds[]    = {STR_NONE, STR_NUM_VERY_LOW, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
-
-	/* Data used for the generate seed edit box */
-	static querystr_d _genseed_query;
-	static char _genseed_buffer[11];
-
-	glwp_modes mode = (glwp_modes)w->window_number;
-
-	switch (e->event) {
-		case WE_CREATE:
-			w->LowerWidget(_opt_newgame.landscape + GLAND_TEMPERATE);
-
-			snprintf(_genseed_buffer, sizeof(_genseed_buffer), "%u", _patches_newgame.generation_seed);
-			InitializeTextBuffer(&_genseed_query.text, _genseed_buffer, lengthof(_genseed_buffer), 120);
-			_genseed_query.caption = STR_NULL;
-			_genseed_query.afilter = CS_NUMERAL;
-			break;
+static const StringID _elevations[]  = {STR_682A_VERY_FLAT, STR_682B_FLAT, STR_682C_HILLY, STR_682D_MOUNTAINOUS, INVALID_STRING_ID};
+static const StringID _sea_lakes[]   = {STR_VERY_LOW, STR_6820_LOW, STR_6821_MEDIUM, STR_6822_HIGH, INVALID_STRING_ID};
+static const StringID _smoothness[]  = {STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_SMOOTH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_ROUGH, STR_CONFIG_PATCHES_ROUGHNESS_OF_TERRAIN_VERY_ROUGH, INVALID_STRING_ID};
+static const StringID _tree_placer[] = {STR_CONFIG_PATCHES_TREE_PLACER_NONE, STR_CONFIG_PATCHES_TREE_PLACER_ORIGINAL, STR_CONFIG_PATCHES_TREE_PLACER_IMPROVED, INVALID_STRING_ID};
+static const StringID _rotation[]    = {STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_COUNTER_CLOCKWISE, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION_CLOCKWISE, INVALID_STRING_ID};
+static const StringID _landscape[]   = {STR_CONFIG_PATCHES_LAND_GENERATOR_ORIGINAL, STR_CONFIG_PATCHES_LAND_GENERATOR_TERRA_GENESIS, INVALID_STRING_ID};
+static const StringID _num_towns[]   = {STR_NUM_VERY_LOW, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
+static const StringID _num_inds[]    = {STR_NONE, STR_NUM_VERY_LOW, STR_6816_LOW, STR_6817_NORMAL, STR_6818_HIGH, INVALID_STRING_ID};
 
-		case WE_PAINT:
-			/* You can't select smoothness if not terragenesis */
-			if (mode == GLWP_GENERATE) {
-				w->SetWidgetDisabledState(GLAND_SMOOTHNESS_PULLDOWN, _patches_newgame.land_generator == 0);
-			}
-			/* Disable snowline if not hilly */
-			w->SetWidgetDisabledState(GLAND_SNOW_LEVEL_TEXT, _opt_newgame.landscape != LT_ARCTIC);
-			/* Disable town, industry and trees in SE */
-			w->SetWidgetDisabledState(GLAND_TOWN_PULLDOWN,     _game_mode == GM_EDITOR);
-			w->SetWidgetDisabledState(GLAND_INDUSTRY_PULLDOWN, _game_mode == GM_EDITOR);
-			w->SetWidgetDisabledState(GLAND_TREE_PULLDOWN,     _game_mode == GM_EDITOR);
-
-			w->SetWidgetDisabledState(GLAND_START_DATE_DOWN, _patches_newgame.starting_year <= MIN_YEAR);
-			w->SetWidgetDisabledState(GLAND_START_DATE_UP,   _patches_newgame.starting_year >= MAX_YEAR);
-			w->SetWidgetDisabledState(GLAND_SNOW_LEVEL_DOWN, _patches_newgame.snow_line_height <= 2 || _opt_newgame.landscape != LT_ARCTIC);
-			w->SetWidgetDisabledState(GLAND_SNOW_LEVEL_UP,   _patches_newgame.snow_line_height >= MAX_SNOWLINE_HEIGHT || _opt_newgame.landscape != LT_ARCTIC);
+struct GenerateLandscapeWindow : public QueryStringBaseWindow {
+	uint widget_id;
+	uint x;
+	uint y;
+	char name[64];
+	glwp_modes mode;
 
-			w->SetWidgetLoweredState(GLAND_TEMPERATE, _opt_newgame.landscape == LT_TEMPERATE);
-			w->SetWidgetLoweredState(GLAND_ARCTIC,    _opt_newgame.landscape == LT_ARCTIC);
-			w->SetWidgetLoweredState(GLAND_TROPICAL,  _opt_newgame.landscape == LT_TROPIC);
-			w->SetWidgetLoweredState(GLAND_TOYLAND,   _opt_newgame.landscape == LT_TOYLAND);
+	GenerateLandscapeWindow(const WindowDesc *desc, void *data = NULL, WindowNumber number = 0) : QueryStringBaseWindow(desc, NULL, number)
+	{
+		this->LowerWidget(_opt_newgame.landscape + GLAND_TEMPERATE);
 
-			if (_game_mode == GM_EDITOR) {
-				w->widget[GLAND_TOWN_PULLDOWN].data     = STR_6836_OFF;
-				w->widget[GLAND_INDUSTRY_PULLDOWN].data = STR_6836_OFF;
+		snprintf(this->edit_str_buf, sizeof(this->edit_str_buf), "%u", _patches_newgame.generation_seed);
+		InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120);
+		this->caption = STR_NULL;
+		this->afilter = CS_NUMERAL;
+
+		this->mode = (glwp_modes)this->window_number;
+
+		this->FindWindowPlacementAndResize(desc);
+	}
+
+	virtual void OnPaint()
+	{
+		/* You can't select smoothness if not terragenesis */
+		if (mode == GLWP_GENERATE) {
+			this->SetWidgetDisabledState(GLAND_SMOOTHNESS_PULLDOWN, _patches_newgame.land_generator == 0);
+		}
+		/* Disable snowline if not hilly */
+		this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_TEXT, _opt_newgame.landscape != LT_ARCTIC);
+		/* Disable town, industry and trees in SE */
+		this->SetWidgetDisabledState(GLAND_TOWN_PULLDOWN,     _game_mode == GM_EDITOR);
+		this->SetWidgetDisabledState(GLAND_INDUSTRY_PULLDOWN, _game_mode == GM_EDITOR);
+		this->SetWidgetDisabledState(GLAND_TREE_PULLDOWN,     _game_mode == GM_EDITOR);
+
+		this->SetWidgetDisabledState(GLAND_START_DATE_DOWN, _patches_newgame.starting_year <= MIN_YEAR);
+		this->SetWidgetDisabledState(GLAND_START_DATE_UP,   _patches_newgame.starting_year >= MAX_YEAR);
+		this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_DOWN, _patches_newgame.snow_line_height <= 2 || _opt_newgame.landscape != LT_ARCTIC);
+		this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_UP,   _patches_newgame.snow_line_height >= MAX_SNOWLINE_HEIGHT || _opt_newgame.landscape != LT_ARCTIC);
+
+		this->SetWidgetLoweredState(GLAND_TEMPERATE, _opt_newgame.landscape == LT_TEMPERATE);
+		this->SetWidgetLoweredState(GLAND_ARCTIC,    _opt_newgame.landscape == LT_ARCTIC);
+		this->SetWidgetLoweredState(GLAND_TROPICAL,  _opt_newgame.landscape == LT_TROPIC);
+		this->SetWidgetLoweredState(GLAND_TOYLAND,   _opt_newgame.landscape == LT_TOYLAND);
+
+		if (_game_mode == GM_EDITOR) {
+			this->widget[GLAND_TOWN_PULLDOWN].data     = STR_6836_OFF;
+			this->widget[GLAND_INDUSTRY_PULLDOWN].data = STR_6836_OFF;
+		} else {
+			this->widget[GLAND_TOWN_PULLDOWN].data     = _num_towns[_opt_newgame.diff.number_towns];
+			this->widget[GLAND_INDUSTRY_PULLDOWN].data = _num_inds[_opt_newgame.diff.number_industries];
+		}
+
+		if (mode == GLWP_GENERATE) {
+			this->widget[GLAND_LANDSCAPE_PULLDOWN].data  = _landscape[_patches_newgame.land_generator];
+			this->widget[GLAND_TREE_PULLDOWN].data       = _tree_placer[_patches_newgame.tree_placer];
+			this->widget[GLAND_TERRAIN_PULLDOWN].data    = _elevations[_opt_newgame.diff.terrain_type];
+			this->widget[GLAND_WATER_PULLDOWN].data      = _sea_lakes[_opt_newgame.diff.quantity_sea_lakes];
+			this->widget[GLAND_SMOOTHNESS_PULLDOWN].data = _smoothness[_patches_newgame.tgen_smoothness];
+		} else {
+			this->widget[GLAND_TREE_PULLDOWN].data               = _tree_placer[_patches_newgame.tree_placer];
+			this->widget[GLAND_HEIGHTMAP_ROTATION_PULLDOWN].data = _rotation[_patches_newgame.heightmap_rotation];
+		}
+
+		/* Set parameters for widget text that requires them. */
+		SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); // GLAND_START_DATE_TEXT
+		SetDParam(1, 1 << _patches_newgame.map_x); // GLAND_MAPSIZE_X_PULLDOWN
+		SetDParam(2, 1 << _patches_newgame.map_y); // GLAND_MAPSIZE_Y_PULLDOWN
+		SetDParam(3, _patches_newgame.snow_line_height); // GLAND_SNOW_LEVEL_TEXT
+
+		DrawWindowWidgets(this);
+
+		this->DrawEditBox(GLAND_RANDOM_EDITBOX);
+
+		if (mode != GLWP_GENERATE) {
+			char buffer[512];
+
+			if (_patches_newgame.heightmap_rotation == HM_CLOCKWISE) {
+				SetDParam(0, this->y);
+				SetDParam(1, this->x);
 			} else {
-				w->widget[GLAND_TOWN_PULLDOWN].data     = num_towns[_opt_newgame.diff.number_towns];
-				w->widget[GLAND_INDUSTRY_PULLDOWN].data = num_inds[_opt_newgame.diff.number_industries];
-			}
-
-			if (mode == GLWP_GENERATE) {
-				w->widget[GLAND_LANDSCAPE_PULLDOWN].data  = landscape[_patches_newgame.land_generator];
-				w->widget[GLAND_TREE_PULLDOWN].data       = tree_placer[_patches_newgame.tree_placer];
-				w->widget[GLAND_TERRAIN_PULLDOWN].data    = elevations[_opt_newgame.diff.terrain_type];
-				w->widget[GLAND_WATER_PULLDOWN].data      = sea_lakes[_opt_newgame.diff.quantity_sea_lakes];
-				w->widget[GLAND_SMOOTHNESS_PULLDOWN].data = smoothness[_patches_newgame.tgen_smoothness];
-			} else {
-				w->widget[GLAND_TREE_PULLDOWN].data               = tree_placer[_patches_newgame.tree_placer];
-				w->widget[GLAND_HEIGHTMAP_ROTATION_PULLDOWN].data = rotation[_patches_newgame.heightmap_rotation];
+				SetDParam(0, this->x);
+				SetDParam(1, this->y);
 			}
-
-			/* Set parameters for widget text that requires them. */
-			SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); // GLAND_START_DATE_TEXT
-			SetDParam(1, 1 << _patches_newgame.map_x); // GLAND_MAPSIZE_X_PULLDOWN
-			SetDParam(2, 1 << _patches_newgame.map_y); // GLAND_MAPSIZE_Y_PULLDOWN
-			SetDParam(3, _patches_newgame.snow_line_height); // GLAND_SNOW_LEVEL_TEXT
-
-			DrawWindowWidgets(w);
-
-			DrawEditBox(w, &_genseed_query, GLAND_RANDOM_EDITBOX);
-
-			if (mode != GLWP_GENERATE) {
-				char buffer[512];
-
-				if (_patches_newgame.heightmap_rotation == HM_CLOCKWISE) {
-					SetDParam(0, WP(w, generate_d).y);
-					SetDParam(1, WP(w, generate_d).x);
-				} else {
-					SetDParam(0, WP(w, generate_d).x);
-					SetDParam(1, WP(w, generate_d).y);
-				}
-				GetString(buffer, STR_HEIGHTMAP_SIZE, lastof(buffer));
-				DrawStringRightAligned(326, 91, STR_HEIGHTMAP_SIZE, TC_BLACK);
-
-				DrawString( 12,  91, STR_HEIGHTMAP_NAME, TC_BLACK);
-				SetDParamStr(0, WP(w, generate_d).name);
-				DrawStringTruncated(114,  91, STR_ORANGE, TC_BLACK, 326 - 114 - GetStringBoundingBox(buffer).width - 5);
-			}
-			break;
+			GetString(buffer, STR_HEIGHTMAP_SIZE, lastof(buffer));
+			DrawStringRightAligned(326, 91, STR_HEIGHTMAP_SIZE, TC_BLACK);
 
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case 0: delete w; break;
-
-				case GLAND_TEMPERATE:
-				case GLAND_ARCTIC:
-				case GLAND_TROPICAL:
-				case GLAND_TOYLAND:
-					w->RaiseWidget(_opt_newgame.landscape + GLAND_TEMPERATE);
-					SetNewLandscapeType(e->we.click.widget - GLAND_TEMPERATE);
-					break;
-
-				case GLAND_MAPSIZE_X_PULLDOWN: // Mapsize X
-					ShowDropDownList(w, BuildMapsizeDropDown(), _patches_newgame.map_x, GLAND_MAPSIZE_X_PULLDOWN);
-					break;
+			DrawString( 12,  91, STR_HEIGHTMAP_NAME, TC_BLACK);
+			SetDParamStr(0, this->name);
+			DrawStringTruncated(114,  91, STR_ORANGE, TC_BLACK, 326 - 114 - GetStringBoundingBox(buffer).width - 5);
+		}
+	}
 
-				case GLAND_MAPSIZE_Y_PULLDOWN: // Mapsize Y
-					ShowDropDownList(w, BuildMapsizeDropDown(), _patches_newgame.map_y, GLAND_MAPSIZE_Y_PULLDOWN);
-					break;
-
-				case GLAND_TOWN_PULLDOWN: // Number of towns
-					ShowDropDownMenu(w, num_towns, _opt_newgame.diff.number_towns, GLAND_TOWN_PULLDOWN, 0, 0);
-					break;
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case 0: delete this; break;
 
-				case GLAND_INDUSTRY_PULLDOWN: // Number of industries
-					ShowDropDownMenu(w, num_inds, _opt_newgame.diff.number_industries, GLAND_INDUSTRY_PULLDOWN, 0, 0);
-					break;
+			case GLAND_TEMPERATE:
+			case GLAND_ARCTIC:
+			case GLAND_TROPICAL:
+			case GLAND_TOYLAND:
+				this->RaiseWidget(_opt_newgame.landscape + GLAND_TEMPERATE);
+				SetNewLandscapeType(widget - GLAND_TEMPERATE);
+				break;
 
-				case GLAND_RANDOM_BUTTON: // Random seed
-					_patches_newgame.generation_seed = InteractiveRandom();
-					snprintf(_genseed_buffer, lengthof(_genseed_buffer), "%u", _patches_newgame.generation_seed);
-					UpdateTextBufferSize(&_genseed_query.text);
-					w->SetDirty();
-					break;
+			case GLAND_MAPSIZE_X_PULLDOWN: // Mapsize X
+				ShowDropDownList(this, BuildMapsizeDropDown(), _patches_newgame.map_x, GLAND_MAPSIZE_X_PULLDOWN);
+				break;
 
-				case GLAND_RANDOM_EDITBOX: // edit box for random seed
-					ShowOnScreenKeyboard(w, & _genseed_query, GLAND_RANDOM_EDITBOX, 0, 0);
+			case GLAND_MAPSIZE_Y_PULLDOWN: // Mapsize Y
+				ShowDropDownList(this, BuildMapsizeDropDown(), _patches_newgame.map_y, GLAND_MAPSIZE_Y_PULLDOWN);
+				break;
+
+			case GLAND_TOWN_PULLDOWN: // Number of towns
+				ShowDropDownMenu(this, _num_towns, _opt_newgame.diff.number_towns, GLAND_TOWN_PULLDOWN, 0, 0);
+				break;
+
+			case GLAND_INDUSTRY_PULLDOWN: // Number of industries
+				ShowDropDownMenu(this, _num_inds, _opt_newgame.diff.number_industries, GLAND_INDUSTRY_PULLDOWN, 0, 0);
+				break;
+
+			case GLAND_RANDOM_BUTTON: // Random seed
+				_patches_newgame.generation_seed = InteractiveRandom();
+				snprintf(this->edit_str_buf, lengthof(this->edit_str_buf), "%u", _patches_newgame.generation_seed);
+				UpdateTextBufferSize(&this->text);
+				this->SetDirty();
+				break;
+
+			case GLAND_RANDOM_EDITBOX: // edit box for random seed
+				ShowOnScreenKeyboard(this, GLAND_RANDOM_EDITBOX, 0, 0);
+				break;
+
+			case GLAND_GENERATE_BUTTON: // Generate
+				UpdatePatches();
+
+				if (_patches.town_layout == TL_NO_ROADS) {
+					ShowQuery(
+						STR_TOWN_LAYOUT_WARNING_CAPTION,
+						STR_TOWN_LAYOUT_WARNING_MESSAGE,
+						this,
+						LandscapeGenerationCallback);
+				} else if (mode == GLWP_HEIGHTMAP &&
+						(this->x * 2 < (1U << _patches_newgame.map_x) ||
+						this->x / 2 > (1U << _patches_newgame.map_x) ||
+						this->y * 2 < (1U << _patches_newgame.map_y) ||
+						this->y / 2 > (1U << _patches_newgame.map_y))) {
+					ShowQuery(
+						STR_HEIGHTMAP_SCALE_WARNING_CAPTION,
+						STR_HEIGHTMAP_SCALE_WARNING_MESSAGE,
+						this,
+						LandscapeGenerationCallback);
+				} else {
+					StartGeneratingLandscape(mode);
+				}
+				break;
+
+			case GLAND_START_DATE_DOWN:
+			case GLAND_START_DATE_UP: // Year buttons
+				/* Don't allow too fast scrolling */
+				if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
+					this->HandleButtonClick(widget);
+					this->SetDirty();
+
+					_patches_newgame.starting_year = Clamp(_patches_newgame.starting_year + widget - GLAND_START_DATE_TEXT, MIN_YEAR, MAX_YEAR);
+				}
+				_left_button_clicked = false;
+				break;
+
+			case GLAND_START_DATE_TEXT: // Year text
+				this->widget_id = GLAND_START_DATE_TEXT;
+				SetDParam(0, _patches_newgame.starting_year);
+				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, this, CS_NUMERAL);
+				break;
+
+			case GLAND_SNOW_LEVEL_DOWN:
+			case GLAND_SNOW_LEVEL_UP: // Snow line buttons
+				/* Don't allow too fast scrolling */
+				if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
+					this->HandleButtonClick(widget);
+					this->SetDirty();
+
+					_patches_newgame.snow_line_height = Clamp(_patches_newgame.snow_line_height + widget - GLAND_SNOW_LEVEL_TEXT, 2, MAX_SNOWLINE_HEIGHT);
+				}
+				_left_button_clicked = false;
+				break;
+
+			case GLAND_SNOW_LEVEL_TEXT: // Snow line text
+				this->widget_id = GLAND_SNOW_LEVEL_TEXT;
+				SetDParam(0, _patches_newgame.snow_line_height);
+				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_SNOW_LINE_QUERY_CAPT, 3, 100, this, CS_NUMERAL);
+				break;
+
+			case GLAND_TREE_PULLDOWN: // Tree placer
+				ShowDropDownMenu(this, _tree_placer, _patches_newgame.tree_placer, GLAND_TREE_PULLDOWN, 0, 0);
+				break;
+
+			case GLAND_LANDSCAPE_PULLDOWN: // Landscape generator OR Heightmap rotation
+			/* case GLAND_HEIGHTMAP_ROTATION_TEXT: case GLAND_HEIGHTMAP_ROTATION_PULLDOWN:*/
+				if (mode == GLWP_HEIGHTMAP) {
+					ShowDropDownMenu(this, _rotation, _patches_newgame.heightmap_rotation, GLAND_HEIGHTMAP_ROTATION_PULLDOWN, 0, 0);
+				} else {
+					ShowDropDownMenu(this, _landscape, _patches_newgame.land_generator, GLAND_LANDSCAPE_PULLDOWN, 0, 0);
+				}
+				break;
+
+			case GLAND_TERRAIN_PULLDOWN: // Terrain type
+				ShowDropDownMenu(this, _elevations, _opt_newgame.diff.terrain_type, GLAND_TERRAIN_PULLDOWN, 0, 0);
+				break;
+
+			case GLAND_WATER_PULLDOWN: // Water quantity
+				ShowDropDownMenu(this, _sea_lakes, _opt_newgame.diff.quantity_sea_lakes, GLAND_WATER_PULLDOWN, 0, 0);
+				break;
+
+			case GLAND_SMOOTHNESS_PULLDOWN: // Map smoothness
+				ShowDropDownMenu(this, _smoothness, _patches_newgame.tgen_smoothness, GLAND_SMOOTHNESS_PULLDOWN, 0, 0);
+				break;
+		}
+	}
+
+	virtual void OnMouseLoop()
+	{
+		this->HandleEditBox(GLAND_RANDOM_EDITBOX);
+	}
+
+	virtual bool OnKeyPress(uint16 key, uint16 keycode)
+	{
+		bool cont;
+		this->HandleEditBoxKey(GLAND_RANDOM_EDITBOX, key, keycode, cont);
+		/* the seed is unsigned, therefore atoi cannot be used.
+			* As 2^32 - 1 (MAX_UVALUE(uint32)) is a 'magic' value
+			* (use random seed) it should not be possible to be
+			* entered into the input field; the generate seed
+			* button can be used instead. */
+		_patches_newgame.generation_seed = minu(strtoul(this->edit_str_buf, NULL, sizeof(this->edit_str_buf) - 1), MAX_UVALUE(uint32) - 1);
+		return cont;
+	}
+
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		switch (widget) {
+			case GLAND_MAPSIZE_X_PULLDOWN:  _patches_newgame.map_x = index; break;
+			case GLAND_MAPSIZE_Y_PULLDOWN:  _patches_newgame.map_y = index; break;
+			case GLAND_TREE_PULLDOWN:       _patches_newgame.tree_placer = index; break;
+			case GLAND_SMOOTHNESS_PULLDOWN: _patches_newgame.tgen_smoothness = index;  break;
+
+			case GLAND_TOWN_PULLDOWN:
+				_opt_newgame.diff.number_towns = index;
+				if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
+				DoCommandP(0, 2, _opt_newgame.diff.number_towns, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+				break;
+
+			case GLAND_INDUSTRY_PULLDOWN:
+				_opt_newgame.diff.number_industries = index;
+				if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
+				DoCommandP(0, 3, _opt_newgame.diff.number_industries, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+				break;
+
+			case GLAND_LANDSCAPE_PULLDOWN:
+			/* case GLAND_HEIGHTMAP_PULLDOWN: */
+				if (mode == GLWP_HEIGHTMAP) {
+					_patches_newgame.heightmap_rotation = index;
+				} else {
+					_patches_newgame.land_generator = index;
+				}
+				break;
+
+			case GLAND_TERRAIN_PULLDOWN:
+				_opt_newgame.diff.terrain_type = index;
+				if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
+				DoCommandP(0, 12, _opt_newgame.diff.terrain_type, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+				break;
+
+			case GLAND_WATER_PULLDOWN:
+				_opt_newgame.diff.quantity_sea_lakes = index;
+				if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
+				DoCommandP(0, 13, _opt_newgame.diff.quantity_sea_lakes, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+				break;
+		}
+		this->SetDirty();
+	}
+
+	virtual void OnQueryTextFinished(char *str)
+	{
+		if (!StrEmpty(str)) {
+			int32 value = atoi(str);
+
+			switch (this->widget_id) {
+				case GLAND_START_DATE_TEXT:
+					this->InvalidateWidget(GLAND_START_DATE_TEXT);
+					_patches_newgame.starting_year = Clamp(value, MIN_YEAR, MAX_YEAR);
 					break;
 
-				case GLAND_GENERATE_BUTTON: // Generate
-					UpdatePatches();
-
-					if (_patches.town_layout == TL_NO_ROADS) {
-						ShowQuery(
-							STR_TOWN_LAYOUT_WARNING_CAPTION,
-							STR_TOWN_LAYOUT_WARNING_MESSAGE,
-							w,
-							LandscapeGenerationCallback);
-					} else if (mode == GLWP_HEIGHTMAP &&
-							(WP(w, generate_d).x * 2 < (1U << _patches_newgame.map_x) ||
-							WP(w, generate_d).x / 2 > (1U << _patches_newgame.map_x) ||
-							WP(w, generate_d).y * 2 < (1U << _patches_newgame.map_y) ||
-							WP(w, generate_d).y / 2 > (1U << _patches_newgame.map_y))) {
-						ShowQuery(
-							STR_HEIGHTMAP_SCALE_WARNING_CAPTION,
-							STR_HEIGHTMAP_SCALE_WARNING_MESSAGE,
-							w,
-							LandscapeGenerationCallback);
-					} else {
-						StartGeneratingLandscape(mode);
-					}
-					break;
-
-				case GLAND_START_DATE_DOWN:
-				case GLAND_START_DATE_UP: // Year buttons
-					/* Don't allow too fast scrolling */
-					if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
-						w->HandleButtonClick(e->we.click.widget);
-						w->SetDirty();
-
-						_patches_newgame.starting_year = Clamp(_patches_newgame.starting_year + e->we.click.widget - GLAND_START_DATE_TEXT, MIN_YEAR, MAX_YEAR);
-					}
-					_left_button_clicked = false;
-					break;
-
-				case GLAND_START_DATE_TEXT: // Year text
-					WP(w, generate_d).widget_id = GLAND_START_DATE_TEXT;
-					SetDParam(0, _patches_newgame.starting_year);
-					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, w, CS_NUMERAL);
-					break;
-
-				case GLAND_SNOW_LEVEL_DOWN:
-				case GLAND_SNOW_LEVEL_UP: // Snow line buttons
-					/* Don't allow too fast scrolling */
-					if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
-						w->HandleButtonClick(e->we.click.widget);
-						w->SetDirty();
-
-						_patches_newgame.snow_line_height = Clamp(_patches_newgame.snow_line_height + e->we.click.widget - GLAND_SNOW_LEVEL_TEXT, 2, MAX_SNOWLINE_HEIGHT);
-					}
-					_left_button_clicked = false;
-					break;
-
-				case GLAND_SNOW_LEVEL_TEXT: // Snow line text
-					WP(w, generate_d).widget_id = GLAND_SNOW_LEVEL_TEXT;
-					SetDParam(0, _patches_newgame.snow_line_height);
-					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_SNOW_LINE_QUERY_CAPT, 3, 100, w, CS_NUMERAL);
-					break;
-
-				case GLAND_TREE_PULLDOWN: // Tree placer
-					ShowDropDownMenu(w, tree_placer, _patches_newgame.tree_placer, GLAND_TREE_PULLDOWN, 0, 0);
-					break;
-
-				case GLAND_LANDSCAPE_PULLDOWN: // Landscape generator OR Heightmap rotation
-				/* case GLAND_HEIGHTMAP_ROTATION_TEXT: case GLAND_HEIGHTMAP_ROTATION_PULLDOWN:*/
-					if (mode == GLWP_HEIGHTMAP) {
-						ShowDropDownMenu(w, rotation, _patches_newgame.heightmap_rotation, GLAND_HEIGHTMAP_ROTATION_PULLDOWN, 0, 0);
-					} else {
-						ShowDropDownMenu(w, landscape, _patches_newgame.land_generator, GLAND_LANDSCAPE_PULLDOWN, 0, 0);
-					}
-					break;
-
-				case GLAND_TERRAIN_PULLDOWN: // Terrain type
-					ShowDropDownMenu(w, elevations, _opt_newgame.diff.terrain_type, GLAND_TERRAIN_PULLDOWN, 0, 0);
-					break;
-
-				case GLAND_WATER_PULLDOWN: // Water quantity
-					ShowDropDownMenu(w, sea_lakes, _opt_newgame.diff.quantity_sea_lakes, GLAND_WATER_PULLDOWN, 0, 0);
-					break;
-
-				case GLAND_SMOOTHNESS_PULLDOWN: // Map smoothness
-					ShowDropDownMenu(w, smoothness, _patches_newgame.tgen_smoothness, GLAND_SMOOTHNESS_PULLDOWN, 0, 0);
+				case GLAND_SNOW_LEVEL_TEXT:
+					this->InvalidateWidget(GLAND_SNOW_LEVEL_TEXT);
+					_patches_newgame.snow_line_height = Clamp(value, 2, MAX_SNOWLINE_HEIGHT);
 					break;
 			}
-			break;
-
-		case WE_MOUSELOOP:
-			HandleEditBox(w, &_genseed_query, GLAND_RANDOM_EDITBOX);
-			break;
-
-		case WE_KEYPRESS:
-			HandleEditBoxKey(w, &_genseed_query, GLAND_RANDOM_EDITBOX, e);
-			/* the seed is unsigned, therefore atoi cannot be used.
-			 * As 2^32 - 1 (MAX_UVALUE(uint32)) is a 'magic' value
-			 * (use random seed) it should not be possible to be
-			 * entered into the input field; the generate seed
-			 * button can be used instead. */
-			_patches_newgame.generation_seed = minu(strtoul(_genseed_buffer, NULL, sizeof(_genseed_buffer) - 1), MAX_UVALUE(uint32) - 1);
-			break;
-
-		case WE_DROPDOWN_SELECT:
-			switch (e->we.dropdown.button) {
-				case GLAND_MAPSIZE_X_PULLDOWN:  _patches_newgame.map_x = e->we.dropdown.index; break;
-				case GLAND_MAPSIZE_Y_PULLDOWN:  _patches_newgame.map_y = e->we.dropdown.index; break;
-				case GLAND_TREE_PULLDOWN:       _patches_newgame.tree_placer = e->we.dropdown.index; break;
-				case GLAND_SMOOTHNESS_PULLDOWN: _patches_newgame.tgen_smoothness = e->we.dropdown.index;  break;
-
-				case GLAND_TOWN_PULLDOWN:
-					_opt_newgame.diff.number_towns = e->we.dropdown.index;
-					if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
-					DoCommandP(0, 2, _opt_newgame.diff.number_towns, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
-					break;
-
-				case GLAND_INDUSTRY_PULLDOWN:
-					_opt_newgame.diff.number_industries = e->we.dropdown.index;
-					if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
-					DoCommandP(0, 3, _opt_newgame.diff.number_industries, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
-					break;
 
-				case GLAND_LANDSCAPE_PULLDOWN:
-				/* case GLAND_HEIGHTMAP_PULLDOWN: */
-					if (mode == GLWP_HEIGHTMAP) {
-						_patches_newgame.heightmap_rotation = e->we.dropdown.index;
-					} else {
-						_patches_newgame.land_generator = e->we.dropdown.index;
-					}
-					break;
-
-				case GLAND_TERRAIN_PULLDOWN:
-					_opt_newgame.diff.terrain_type = e->we.dropdown.index;
-					if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
-					DoCommandP(0, 12, _opt_newgame.diff.terrain_type, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
-					break;
-
-				case GLAND_WATER_PULLDOWN:
-					_opt_newgame.diff.quantity_sea_lakes = e->we.dropdown.index;
-					if (_opt_newgame.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
-					DoCommandP(0, 13, _opt_newgame.diff.quantity_sea_lakes, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
-					break;
-			}
-			w->SetDirty();
-			break;
-
-		case WE_ON_EDIT_TEXT:
-			if (e->we.edittext.str != NULL) {
-				int32 value = atoi(e->we.edittext.str);
-
-				switch (WP(w, generate_d).widget_id) {
-					case GLAND_START_DATE_TEXT:
-						w->InvalidateWidget(GLAND_START_DATE_TEXT);
-						_patches_newgame.starting_year = Clamp(value, MIN_YEAR, MAX_YEAR);
-						break;
-
-					case GLAND_SNOW_LEVEL_TEXT:
-						w->InvalidateWidget(GLAND_SNOW_LEVEL_TEXT);
-						_patches_newgame.snow_line_height = Clamp(value, 2, MAX_SNOWLINE_HEIGHT);
-						break;
-				}
-
-				w->SetDirty();
-			}
-			break;
+			this->SetDirty();
+		}
 	}
-}
+};
 
 static const WindowDesc _generate_landscape_desc = {
 	WDP_CENTER, WDP_CENTER, 338, 268, 338, 268,
 	WC_GENERATE_LANDSCAPE, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 	_generate_landscape_widgets,
-	GenerateLandscapeWndProc,
+	NULL,
 };
 
 static const WindowDesc _heightmap_load_desc = {
@@ -559,7 +562,7 @@
 	WC_GENERATE_LANDSCAPE, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS,
 	_heightmap_load_widgets,
-	GenerateLandscapeWndProc,
+	NULL,
 };
 
 static void _ShowGenerateLandscape(glwp_modes mode)
@@ -577,14 +580,12 @@
 		if (!GetHeightmapDimensions(_file_to_saveload.name, &x, &y)) return;
 	}
 
-	Window *w = AllocateWindowDescFront((mode == GLWP_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc, mode);
-
-	if (w == NULL) return;
+	GenerateLandscapeWindow *w = AllocateWindowDescFront<GenerateLandscapeWindow>((mode == GLWP_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc, mode);
 
 	if (mode == GLWP_HEIGHTMAP) {
-		WP(w, generate_d).x = x;
-		WP(w, generate_d).y = y;
-		strecpy(WP(w, generate_d).name, _file_to_saveload.title, lastof(WP(w, generate_d).name));
+		w->x = x;
+		w->y = y;
+		strecpy(w->name, _file_to_saveload.title, lastof(w->name));
 	}
 
 	InvalidateWindow(WC_GENERATE_LANDSCAPE, mode);
@@ -639,127 +640,131 @@
 };
 
 
-static void CreateScenarioWndProc(Window *w, WindowEvent *e)
+struct CreateScenarioWindow : public Window
 {
-	switch (e->event) {
-		case WE_CREATE:
-			w->LowerWidget(_opt_newgame.landscape + CSCEN_TEMPERATE);
-			break;
-
-		case WE_PAINT:
-			w->SetWidgetDisabledState(CSCEN_START_DATE_DOWN,       _patches_newgame.starting_year <= MIN_YEAR);
-			w->SetWidgetDisabledState(CSCEN_START_DATE_UP,         _patches_newgame.starting_year >= MAX_YEAR);
-			w->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_DOWN, _patches_newgame.se_flat_world_height <= 0);
-			w->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_UP,   _patches_newgame.se_flat_world_height >= MAX_TILE_HEIGHT);
-
-			w->SetWidgetLoweredState(CSCEN_TEMPERATE, _opt_newgame.landscape == LT_TEMPERATE);
-			w->SetWidgetLoweredState(CSCEN_ARCTIC,    _opt_newgame.landscape == LT_ARCTIC);
-			w->SetWidgetLoweredState(CSCEN_TROPICAL,  _opt_newgame.landscape == LT_TROPIC);
-			w->SetWidgetLoweredState(CSCEN_TOYLAND,   _opt_newgame.landscape == LT_TOYLAND);
-
-			/* Set parameters for widget text that requires them */
-			SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); // CSCEN_START_DATE_TEXT
-			SetDParam(1, 1 << _patches_newgame.map_x); // CSCEN_MAPSIZE_X_PULLDOWN
-			SetDParam(2, 1 << _patches_newgame.map_y); // CSCEN_MAPSIZE_Y_PULLDOWN
-			SetDParam(3, _patches_newgame.se_flat_world_height); // CSCEN_FLAT_LAND_HEIGHT_TEXT
-
-			DrawWindowWidgets(w);
+	uint widget_id;
 
-			break;
-
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case CSCEN_TEMPERATE:
-				case CSCEN_ARCTIC:
-				case CSCEN_TROPICAL:
-				case CSCEN_TOYLAND:
-					w->RaiseWidget(_opt_newgame.landscape + CSCEN_TEMPERATE);
-					SetNewLandscapeType(e->we.click.widget - CSCEN_TEMPERATE);
-					break;
+	CreateScenarioWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, NULL, window_number)
+	{
+		this->LowerWidget(_opt_newgame.landscape + CSCEN_TEMPERATE);
+	}
 
-				case CSCEN_MAPSIZE_X_PULLDOWN: // Mapsize X
-					ShowDropDownList(w, BuildMapsizeDropDown(), _patches_newgame.map_x, CSCEN_MAPSIZE_X_PULLDOWN);
-					break;
+	virtual void OnPaint()
+	{
+		this->SetWidgetDisabledState(CSCEN_START_DATE_DOWN,       _patches_newgame.starting_year <= MIN_YEAR);
+		this->SetWidgetDisabledState(CSCEN_START_DATE_UP,         _patches_newgame.starting_year >= MAX_YEAR);
+		this->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_DOWN, _patches_newgame.se_flat_world_height <= 0);
+		this->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_UP,   _patches_newgame.se_flat_world_height >= MAX_TILE_HEIGHT);
 
-				case CSCEN_MAPSIZE_Y_PULLDOWN: // Mapsize Y
-					ShowDropDownList(w, BuildMapsizeDropDown(), _patches_newgame.map_y, CSCEN_MAPSIZE_Y_PULLDOWN);
-					break;
+		this->SetWidgetLoweredState(CSCEN_TEMPERATE, _opt_newgame.landscape == LT_TEMPERATE);
+		this->SetWidgetLoweredState(CSCEN_ARCTIC,    _opt_newgame.landscape == LT_ARCTIC);
+		this->SetWidgetLoweredState(CSCEN_TROPICAL,  _opt_newgame.landscape == LT_TROPIC);
+		this->SetWidgetLoweredState(CSCEN_TOYLAND,   _opt_newgame.landscape == LT_TOYLAND);
 
-				case CSCEN_EMPTY_WORLD: // Empty world / flat world
-					StartGeneratingLandscape(GLWP_SCENARIO);
-					break;
+		/* Set parameters for widget text that requires them */
+		SetDParam(0, ConvertYMDToDate(_patches_newgame.starting_year, 0, 1)); // CSCEN_START_DATE_TEXT
+		SetDParam(1, 1 << _patches_newgame.map_x); // CSCEN_MAPSIZE_X_PULLDOWN
+		SetDParam(2, 1 << _patches_newgame.map_y); // CSCEN_MAPSIZE_Y_PULLDOWN
+		SetDParam(3, _patches_newgame.se_flat_world_height); // CSCEN_FLAT_LAND_HEIGHT_TEXT
 
-				case CSCEN_RANDOM_WORLD: // Generate
-					ShowGenerateLandscape();
+		DrawWindowWidgets(this);
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case CSCEN_TEMPERATE:
+			case CSCEN_ARCTIC:
+			case CSCEN_TROPICAL:
+			case CSCEN_TOYLAND:
+				this->RaiseWidget(_opt_newgame.landscape + CSCEN_TEMPERATE);
+				SetNewLandscapeType(widget - CSCEN_TEMPERATE);
+				break;
+
+			case CSCEN_MAPSIZE_X_PULLDOWN: // Mapsize X
+				ShowDropDownList(this, BuildMapsizeDropDown(), _patches_newgame.map_x, CSCEN_MAPSIZE_X_PULLDOWN);
+				break;
+
+			case CSCEN_MAPSIZE_Y_PULLDOWN: // Mapsize Y
+				ShowDropDownList(this, BuildMapsizeDropDown(), _patches_newgame.map_y, CSCEN_MAPSIZE_Y_PULLDOWN);
+				break;
+
+			case CSCEN_EMPTY_WORLD: // Empty world / flat world
+				StartGeneratingLandscape(GLWP_SCENARIO);
+				break;
+
+			case CSCEN_RANDOM_WORLD: // Generate
+				ShowGenerateLandscape();
+				break;
+
+			case CSCEN_START_DATE_DOWN:
+			case CSCEN_START_DATE_UP: // Year buttons
+				/* Don't allow too fast scrolling */
+				if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
+					this->HandleButtonClick(widget);
+					this->SetDirty();
+
+					_patches_newgame.starting_year = Clamp(_patches_newgame.starting_year + widget - CSCEN_START_DATE_TEXT, MIN_YEAR, MAX_YEAR);
+				}
+				_left_button_clicked = false;
+				break;
+
+			case CSCEN_START_DATE_TEXT: // Year text
+				this->widget_id = CSCEN_START_DATE_TEXT;
+				SetDParam(0, _patches_newgame.starting_year);
+				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, this, CS_NUMERAL);
+				break;
+
+			case CSCEN_FLAT_LAND_HEIGHT_DOWN:
+			case CSCEN_FLAT_LAND_HEIGHT_UP: // Height level buttons
+				/* Don't allow too fast scrolling */
+				if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
+					this->HandleButtonClick(widget);
+					this->SetDirty();
+
+					_patches_newgame.se_flat_world_height = Clamp(_patches_newgame.se_flat_world_height + widget - CSCEN_FLAT_LAND_HEIGHT_TEXT, 0, MAX_TILE_HEIGHT);
+				}
+				_left_button_clicked = false;
+				break;
+
+			case CSCEN_FLAT_LAND_HEIGHT_TEXT: // Height level text
+				this->widget_id = CSCEN_FLAT_LAND_HEIGHT_TEXT;
+				SetDParam(0, _patches_newgame.se_flat_world_height);
+				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_FLAT_WORLD_HEIGHT_QUERY_CAPT, 3, 100, this, CS_NUMERAL);
+				break;
+		}
+	}
+
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		switch (widget) {
+			case CSCEN_MAPSIZE_X_PULLDOWN: _patches_newgame.map_x = index; break;
+			case CSCEN_MAPSIZE_Y_PULLDOWN: _patches_newgame.map_y = index; break;
+		}
+		this->SetDirty();
+	}
+
+	virtual void OnQueryTextFinished(char *str)
+	{
+		if (!StrEmpty(str)) {
+			int32 value = atoi(str);
+
+			switch (this->widget_id) {
+				case CSCEN_START_DATE_TEXT:
+					this->InvalidateWidget(CSCEN_START_DATE_TEXT);
+					_patches_newgame.starting_year = Clamp(value, MIN_YEAR, MAX_YEAR);
 					break;
 
-				case CSCEN_START_DATE_DOWN:
-				case CSCEN_START_DATE_UP: // Year buttons
-					/* Don't allow too fast scrolling */
-					if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
-						w->HandleButtonClick(e->we.click.widget);
-						w->SetDirty();
-
-						_patches_newgame.starting_year = Clamp(_patches_newgame.starting_year + e->we.click.widget - CSCEN_START_DATE_TEXT, MIN_YEAR, MAX_YEAR);
-					}
-					_left_button_clicked = false;
-					break;
-
-				case CSCEN_START_DATE_TEXT: // Year text
-					WP(w, generate_d).widget_id = CSCEN_START_DATE_TEXT;
-					SetDParam(0, _patches_newgame.starting_year);
-					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_START_DATE_QUERY_CAPT, 8, 100, w, CS_NUMERAL);
-					break;
-
-				case CSCEN_FLAT_LAND_HEIGHT_DOWN:
-				case CSCEN_FLAT_LAND_HEIGHT_UP: // Height level buttons
-					/* Don't allow too fast scrolling */
-					if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
-						w->HandleButtonClick(e->we.click.widget);
-						w->SetDirty();
-
-						_patches_newgame.se_flat_world_height = Clamp(_patches_newgame.se_flat_world_height + e->we.click.widget - CSCEN_FLAT_LAND_HEIGHT_TEXT, 0, MAX_TILE_HEIGHT);
-					}
-					_left_button_clicked = false;
-					break;
-
-				case CSCEN_FLAT_LAND_HEIGHT_TEXT: // Height level text
-					WP(w, generate_d).widget_id = CSCEN_FLAT_LAND_HEIGHT_TEXT;
-					SetDParam(0, _patches_newgame.se_flat_world_height);
-					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_FLAT_WORLD_HEIGHT_QUERY_CAPT, 3, 100, w, CS_NUMERAL);
+				case CSCEN_FLAT_LAND_HEIGHT_TEXT:
+					this->InvalidateWidget(CSCEN_FLAT_LAND_HEIGHT_TEXT);
+					_patches_newgame.se_flat_world_height = Clamp(value, 0, MAX_TILE_HEIGHT);
 					break;
 			}
-			break;
-
-		case WE_DROPDOWN_SELECT:
-			switch (e->we.dropdown.button) {
-				case CSCEN_MAPSIZE_X_PULLDOWN: _patches_newgame.map_x = e->we.dropdown.index; break;
-				case CSCEN_MAPSIZE_Y_PULLDOWN: _patches_newgame.map_y = e->we.dropdown.index; break;
-			}
-			w->SetDirty();
-			break;
-
-		case WE_ON_EDIT_TEXT:
-			if (e->we.edittext.str != NULL) {
-				int32 value = atoi(e->we.edittext.str);
 
-				switch (WP(w, generate_d).widget_id) {
-					case CSCEN_START_DATE_TEXT:
-						w->InvalidateWidget(CSCEN_START_DATE_TEXT);
-						_patches_newgame.starting_year = Clamp(value, MIN_YEAR, MAX_YEAR);
-						break;
-
-					case CSCEN_FLAT_LAND_HEIGHT_TEXT:
-						w->InvalidateWidget(CSCEN_FLAT_LAND_HEIGHT_TEXT);
-						_patches_newgame.se_flat_world_height = Clamp(value, 0, MAX_TILE_HEIGHT);
-						break;
-				}
-
-				w->SetDirty();
-			}
-			break;
+			this->SetDirty();
+		}
 	}
-}
+};
 
 static const Widget _create_scenario_widgets[] = {
 {   WWT_CLOSEBOX, RESIZE_NONE, 13,   0,  10,   0,  13, STR_00C5,                STR_018B_CLOSE_WINDOW},
@@ -796,13 +801,13 @@
 	WC_GENERATE_LANDSCAPE, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS,
 	_create_scenario_widgets,
-	CreateScenarioWndProc,
+	NULL,
 };
 
 void ShowCreateScenario()
 {
 	DeleteWindowByClass(WC_GENERATE_LANDSCAPE);
-	AllocateWindowDescFront(&_create_scenario_desc, GLWP_SCENARIO);
+	new CreateScenarioWindow(&_create_scenario_desc, GLWP_SCENARIO);
 }
 
 
@@ -896,7 +901,7 @@
  */
 void ShowGenerateWorldProgress()
 {
-	AllocateWindowDescFront(&_show_terrain_progress_desc, 0);
+	AllocateWindowDescFront<Window>(&_show_terrain_progress_desc, 0);
 }
 
 static void _SetGeneratingWorldProgress(gwp_class cls, uint progress, uint total)
--- a/src/graph_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/graph_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -339,7 +339,7 @@
 
 static void ShowGraphLegend()
 {
-	AllocateWindowDescFront(&_graph_legend_desc, 0);
+	AllocateWindowDescFront<Window>(&_graph_legend_desc, 0);
 }
 
 /********************/
@@ -438,7 +438,7 @@
 
 void ShowOperatingProfitGraph()
 {
-	if (AllocateWindowDescFront(&_operating_profit_desc, 0)) {
+	if (AllocateWindowDescFront<Window>(&_operating_profit_desc, 0)) {
 		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 	}
 }
@@ -506,7 +506,7 @@
 
 void ShowIncomeGraph()
 {
-	if (AllocateWindowDescFront(&_income_graph_desc, 0)) {
+	if (AllocateWindowDescFront<Window>(&_income_graph_desc, 0)) {
 		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 	}
 }
@@ -573,7 +573,7 @@
 
 void ShowDeliveredCargoGraph()
 {
-	if (AllocateWindowDescFront(&_delivered_cargo_graph_desc, 0)) {
+	if (AllocateWindowDescFront<Window>(&_delivered_cargo_graph_desc, 0)) {
 		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 	}
 }
@@ -642,7 +642,7 @@
 
 void ShowPerformanceHistoryGraph()
 {
-	if (AllocateWindowDescFront(&_performance_history_desc, 0)) {
+	if (AllocateWindowDescFront<Window>(&_performance_history_desc, 0)) {
 		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 	}
 }
@@ -709,7 +709,7 @@
 
 void ShowCompanyValueGraph()
 {
-	if (AllocateWindowDescFront(&_company_value_graph_desc, 0)) {
+	if (AllocateWindowDescFront<Window>(&_company_value_graph_desc, 0)) {
 		InvalidateWindow(WC_GRAPH_LEGEND, 0);
 	}
 }
@@ -807,7 +807,7 @@
 
 void ShowCargoPaymentRates()
 {
-	Window *w = AllocateWindowDescFront(&_cargo_payment_rates_desc, 0);
+	Window *w = AllocateWindowDescFront<Window>(&_cargo_payment_rates_desc, 0);
 	if (w == NULL) return;
 
 	/* Count the number of active cargo types */
@@ -928,7 +928,7 @@
 
 void ShowCompanyLeagueTable()
 {
-	AllocateWindowDescFront(&_company_league_desc, 0);
+	AllocateWindowDescFront<Window>(&_company_league_desc, 0);
 }
 
 /*****************************/
@@ -1163,5 +1163,5 @@
 
 void ShowPerformanceRatingDetail()
 {
-	AllocateWindowDescFront(&_performance_rating_detail_desc, 0);
+	AllocateWindowDescFront<Window>(&_performance_rating_detail_desc, 0);
 }
--- a/src/group_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/group_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -28,53 +28,29 @@
 #include "table/strings.h"
 #include "table/sprites.h"
 
-struct grouplist_d {
-	const Group **sort_list;
-	list_d l;                   // General list struct
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(grouplist_d));
-
-struct groupveh_d : vehiclelist_d {
-	GroupID group_sel;
-	VehicleID vehicle_sel;
-
-	grouplist_d gl;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(groupveh_d));
+typedef GUIList<const Group*> GUIGroupList;
 
-struct Sorting {
-	Listing aircraft;
-	Listing roadveh;
-	Listing ship;
-	Listing train;
-};
-
-static Sorting _sorting;
-
-
-static void BuildGroupList(grouplist_d* gl, PlayerID owner, VehicleType vehicle_type)
+static void BuildGroupList(GUIGroupList *gl, PlayerID owner, VehicleType vehicle_type)
 {
-	const Group** list;
-	const Group *g;
 	uint n = 0;
 
-	if (!(gl->l.flags & VL_REBUILD)) return;
+	if (!(gl->flags & VL_REBUILD)) return;
 
-	list = MallocT<const Group*>(GetGroupArraySize());
+	const Group **list = MallocT<const Group*>(GetGroupArraySize());
 
+	const Group *g;
 	FOR_ALL_GROUPS(g) {
 		if (g->owner == owner && g->vehicle_type == vehicle_type) list[n++] = g;
 	}
 
-	free((void*)gl->sort_list);
-	gl->sort_list = MallocT<const Group *>(n);
-	gl->l.list_length = n;
+	gl->sort_list = ReallocT(gl->sort_list, n);
+	gl->list_length = n;
 
 	for (uint i = 0; i < n; ++i) gl->sort_list[i] = list[i];
-	free((void*)list);
+	free(list);
 
-	gl->l.flags &= ~VL_REBUILD;
-	gl->l.flags |= VL_RESORT;
+	gl->flags &= ~VL_REBUILD;
+	gl->flags |= VL_RESORT;
 }
 
 
@@ -107,14 +83,14 @@
 }
 
 
-static void SortGroupList(grouplist_d *gl)
+static void SortGroupList(GUIGroupList *gl)
 {
-	if (!(gl->l.flags & VL_RESORT)) return;
+	if (!(gl->flags & VL_RESORT)) return;
 
-	qsort((void*)gl->sort_list, gl->l.list_length, sizeof(gl->sort_list[0]), GroupNameSorter);
+	qsort((void*)gl->sort_list, gl->list_length, sizeof(gl->sort_list[0]), GroupNameSorter);
 
-	gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
-	gl->l.flags &= ~VL_RESORT;
+	gl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+	gl->flags &= ~VL_RESORT;
 }
 
 
@@ -146,6 +122,35 @@
 	GRP_WIDGET_RESIZE,
 };
 
+enum GroupActionListFunction {
+	GALF_REPLACE,
+	GALF_SERVICE,
+	GALF_DEPOT,
+	GALF_ADD_SHARED,
+	GALF_REMOVE_ALL,
+};
+
+/**
+ * Update/redraw the group action dropdown
+ * @param w   the window the dropdown belongs to
+ * @param gid the currently selected group in the window
+ */
+static void ShowGroupActionDropdown(Window *w, GroupID gid)
+{
+	DropDownList *list = new DropDownList();
+
+	list->push_back(new DropDownListStringItem(STR_REPLACE_VEHICLES,    GALF_REPLACE, false));
+	list->push_back(new DropDownListStringItem(STR_SEND_FOR_SERVICING,  GALF_SERVICE, false));
+	list->push_back(new DropDownListStringItem(STR_SEND_TRAIN_TO_DEPOT, GALF_DEPOT,   false));
+
+	if (IsValidGroupID(gid)) {
+		list->push_back(new DropDownListStringItem(STR_GROUP_ADD_SHARED_VEHICLE,  GALF_ADD_SHARED, false));
+		list->push_back(new DropDownListStringItem(STR_GROUP_REMOVE_ALL_VEHICLES, GALF_REMOVE_ALL, false));
+	}
+
+	ShowDropDownList(w, list, 0, GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN);
+}
+
 
 static const Widget _group_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,             STR_018B_CLOSE_WINDOW},
@@ -177,614 +182,575 @@
 };
 
 
-static void CreateVehicleGroupWindow(Window *w)
-{
-	const PlayerID owner = (PlayerID)GB(w->window_number, 0, 8);
-	groupveh_d *gv = &WP(w, groupveh_d);
-	grouplist_d *gl = &WP(w, groupveh_d).gl;
-
-	w->caption_color = owner;
-	w->hscroll.cap = 224;
-	w->resize.step_width = 1;
-
-	switch (gv->vehicle_type) {
-		default: NOT_REACHED();
-		case VEH_TRAIN:
-		case VEH_ROAD:
-			w->vscroll.cap = 9;
-			w->vscroll2.cap = 6;
-			w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
-			break;
-		case VEH_SHIP:
-		case VEH_AIRCRAFT:
-			w->vscroll.cap = 9;
-			w->vscroll2.cap = 4;
-			w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_BIG2;
-			break;
-	}
-
-	w->widget[GRP_WIDGET_LIST_GROUP].data = (w->vscroll.cap << 8) + 1;
-	w->widget[GRP_WIDGET_LIST_VEHICLE].data = (w->vscroll2.cap << 8) + 1;
-
-	switch (gv->vehicle_type) {
-		default: NOT_REACHED();
-		case VEH_TRAIN:    gv->_sorting = &_sorting.train;    break;
-		case VEH_ROAD:     gv->_sorting = &_sorting.roadveh;  break;
-		case VEH_SHIP:     gv->_sorting = &_sorting.ship;     break;
-		case VEH_AIRCRAFT: gv->_sorting = &_sorting.aircraft; break;
-	}
-
-	gv->sort_list = NULL;
-	gv->vehicle_type = (VehicleType)GB(w->window_number, 11, 5);
-	gv->l.sort_type = gv->_sorting->criteria;
-	gv->l.flags = VL_REBUILD | (gv->_sorting->order ? VL_DESC : VL_NONE);
-	gv->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
-
-	gl->sort_list = NULL;
-	gl->l.flags = VL_REBUILD | VL_NONE;
-	gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
-
-	gv->group_sel = ALL_GROUP;
-	gv->vehicle_sel = INVALID_VEHICLE;
-
-	switch (gv->vehicle_type) {
-		case VEH_TRAIN:
-			w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_883D_TRAINS_CLICK_ON_TRAIN_FOR;
-			w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_TRAINS;
+struct VehicleGroupWindow : public Window, public VehicleListBase {
+	GroupID group_sel;
+	VehicleID vehicle_sel;
+	GUIGroupList groups;
 
-			w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_TRAIN;
-			w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_TRAIN;
-			w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_TRAIN;
-			break;
-
-		case VEH_ROAD:
-			w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_901A_ROAD_VEHICLES_CLICK_ON;
-			w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_ROAD_VEHICLES;
-
-			w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_ROADVEH;
-			w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_ROADVEH;
-			w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_ROADVEH;
-			break;
-
-		case VEH_SHIP:
-			w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_9823_SHIPS_CLICK_ON_SHIP_FOR;
-			w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_SHIPS;
-
-			w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_SHIP;
-			w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_SHIP;
-			w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_SHIP;
-			break;
-
-		case VEH_AIRCRAFT:
-			w->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT;
-			w->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_AIRCRAFT;
+	VehicleGroupWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, NULL, window_number)
+	{
+		const PlayerID owner = (PlayerID)GB(this->window_number, 0, 8);
 
-			w->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_AIRCRAFT;
-			w->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_AIRCRAFT;
-			w->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_AIRCRAFT;
-			break;
-
-		default: NOT_REACHED();
-	}
-}
+		this->caption_color = owner;
+		this->hscroll.cap = 224;
+		this->resize.step_width = 1;
 
-enum GroupActionListFunction {
-	GALF_REPLACE,
-	GALF_SERVICE,
-	GALF_DEPOT,
-	GALF_ADD_SHARED,
-	GALF_REMOVE_ALL,
-};
+		switch (this->vehicle_type) {
+			default: NOT_REACHED();
+			case VEH_TRAIN:
+			case VEH_ROAD:
+				this->vscroll.cap = 9;
+				this->vscroll2.cap = 6;
+				this->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
+				break;
+			case VEH_SHIP:
+			case VEH_AIRCRAFT:
+				this->vscroll.cap = 9;
+				this->vscroll2.cap = 4;
+				this->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_BIG2;
+				break;
+		}
 
-/**
- * Update/redraw the group action dropdown
- * @param w   the window the dropdown belongs to
- * @param gid the currently selected group in the window
- */
-static void ShowGroupActionDropdown(Window *w, GroupID gid)
-{
-	DropDownList *list = new DropDownList();
+		this->widget[GRP_WIDGET_LIST_GROUP].data = (this->vscroll.cap << 8) + 1;
+		this->widget[GRP_WIDGET_LIST_VEHICLE].data = (this->vscroll2.cap << 8) + 1;
 
-	list->push_back(new DropDownListStringItem(STR_REPLACE_VEHICLES,    GALF_REPLACE, false));
-	list->push_back(new DropDownListStringItem(STR_SEND_FOR_SERVICING,  GALF_SERVICE, false));
-	list->push_back(new DropDownListStringItem(STR_SEND_TRAIN_TO_DEPOT, GALF_DEPOT,   false));
+		switch (this->vehicle_type) {
+			default: NOT_REACHED();
+			case VEH_TRAIN:    this->sorting = &_sorting.train;    break;
+			case VEH_ROAD:     this->sorting = &_sorting.roadveh;  break;
+			case VEH_SHIP:     this->sorting = &_sorting.ship;     break;
+			case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break;
+		}
 
-	if (IsValidGroupID(gid)) {
-		list->push_back(new DropDownListStringItem(STR_GROUP_ADD_SHARED_VEHICLE,  GALF_ADD_SHARED, false));
-		list->push_back(new DropDownListStringItem(STR_GROUP_REMOVE_ALL_VEHICLES, GALF_REMOVE_ALL, false));
+		this->vehicle_type = (VehicleType)GB(this->window_number, 11, 5);
+
+		this->vehicles.sort_list = NULL;
+		this->vehicles.sort_type = this->sorting->criteria;
+		this->vehicles.flags = VL_REBUILD | (this->sorting->order ? VL_DESC : VL_NONE);
+		this->vehicles.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
+
+		this->groups.sort_list = NULL;
+		this->groups.flags = VL_REBUILD | VL_NONE;
+		this->groups.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
+
+		this->group_sel = ALL_GROUP;
+		this->vehicle_sel = INVALID_VEHICLE;
+
+		switch (this->vehicle_type) {
+			case VEH_TRAIN:
+				this->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_883D_TRAINS_CLICK_ON_TRAIN_FOR;
+				this->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_TRAINS;
+
+				this->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_TRAIN;
+				this->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_TRAIN;
+				this->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_TRAIN;
+				break;
+
+			case VEH_ROAD:
+				this->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_901A_ROAD_VEHICLES_CLICK_ON;
+				this->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_ROAD_VEHICLES;
+
+				this->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_ROADVEH;
+				this->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_ROADVEH;
+				this->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_ROADVEH;
+				break;
+
+			case VEH_SHIP:
+				this->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_9823_SHIPS_CLICK_ON_SHIP_FOR;
+				this->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_SHIPS;
+
+				this->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_SHIP;
+				this->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_SHIP;
+				this->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_SHIP;
+				break;
+
+			case VEH_AIRCRAFT:
+				this->widget[GRP_WIDGET_LIST_VEHICLE].tooltips = STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT;
+				this->widget[GRP_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_AIRCRAFT;
+
+				this->widget[GRP_WIDGET_CREATE_GROUP].data = SPR_GROUP_CREATE_AIRCRAFT;
+				this->widget[GRP_WIDGET_RENAME_GROUP].data = SPR_GROUP_RENAME_AIRCRAFT;
+				this->widget[GRP_WIDGET_DELETE_GROUP].data = SPR_GROUP_DELETE_AIRCRAFT;
+				break;
+
+			default: NOT_REACHED();
+		}
+
+		this->FindWindowPlacementAndResize(desc);
 	}
 
-	ShowDropDownList(w, list, 0, GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN);
-}
-
-/**
- * bitmask for w->window_number
- * 0-7   PlayerID (owner)
- * 11-15 vehicle type
- **/
-static void GroupWndProc(Window *w, WindowEvent *e)
-{
-	const PlayerID owner = (PlayerID)GB(w->window_number, 0, 8);
-	groupveh_d *gv = &WP(w, groupveh_d);
-	grouplist_d *gl = &WP(w, groupveh_d).gl;
-
-	gv->vehicle_type = (VehicleType)GB(w->window_number, 11, 5);
+	~VehicleGroupWindow()
+	{
+		free((void*)this->vehicles.sort_list);
+		free((void*)this->groups.sort_list);
+	}
 
-	switch(e->event) {
-		case WE_INVALIDATE_DATA:
-			gv->l.flags |= VL_REBUILD;
-			gl->l.flags |= VL_REBUILD;
-			if (!(IsAllGroupID(gv->group_sel) || IsDefaultGroupID(gv->group_sel) || IsValidGroupID(gv->group_sel))) {
-				gv->group_sel = ALL_GROUP;
-				HideDropDownMenu(w);
-			}
-			w->SetDirty();
-			break;
+	virtual void OnInvalidateData(int data)
+	{
+		this->vehicles.flags |= VL_REBUILD;
+		this->groups.flags |= VL_REBUILD;
+		if (!(IsAllGroupID(this->group_sel) || IsDefaultGroupID(this->group_sel) || IsValidGroupID(this->group_sel))) {
+			this->group_sel = ALL_GROUP;
+			HideDropDownMenu(this);
+		}
+		this->SetDirty();
+	}
 
-		case WE_CREATE:
-			CreateVehicleGroupWindow(w);
-			break;
+	virtual void OnPaint()
+	{
+		const PlayerID owner = (PlayerID)GB(this->window_number, 0, 8);
+		int x = 203;
+		int y2 = PLY_WND_PRC__OFFSET_TOP_WIDGET;
+		int y1 = PLY_WND_PRC__OFFSET_TOP_WIDGET + 2;
+		int max;
+		int i;
 
-		case WE_PAINT: {
-			int x = 203;
-			int y2 = PLY_WND_PRC__OFFSET_TOP_WIDGET;
-			int y1 = PLY_WND_PRC__OFFSET_TOP_WIDGET + 2;
-			int max;
-			int i;
-
-			/* If we select the all vehicles, gv->list will contain all vehicles of the player
-			 * else gv->list will contain all vehicles which belong to the selected group */
-			BuildVehicleList(gv, owner, gv->group_sel, IsAllGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST);
-			SortVehicleList(gv);
+		/* If we select the all vehicles, this->list will contain all vehicles of the player
+			* else this->list will contain all vehicles which belong to the selected group */
+		BuildVehicleList(this, owner, this->group_sel, IsAllGroupID(this->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST);
+		SortVehicleList(this);
 
 
-			BuildGroupList(gl, owner, gv->vehicle_type);
-			SortGroupList(gl);
-
-			SetVScrollCount(w, gl->l.list_length);
-			SetVScroll2Count(w, gv->l.list_length);
-
-			/* The drop down menu is out, *but* it may not be used, retract it. */
-			if (gv->l.list_length == 0 && w->IsWidgetLowered(GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN)) {
-				w->RaiseWidget(GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN);
-				HideDropDownMenu(w);
-			}
+		BuildGroupList(&this->groups, owner, this->vehicle_type);
+		SortGroupList(&this->groups);
 
-			/* Disable all lists management button when the list is empty */
-			w->SetWidgetsDisabledState(gv->l.list_length == 0 || _local_player != owner,
-					GRP_WIDGET_STOP_ALL,
-					GRP_WIDGET_START_ALL,
-					GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
-					WIDGET_LIST_END);
+		SetVScrollCount(this, this->groups.list_length);
+		SetVScroll2Count(this, this->vehicles.list_length);
 
-			/* Disable the group specific function when we select the default group or all vehicles */
-			w->SetWidgetsDisabledState(IsDefaultGroupID(gv->group_sel) || IsAllGroupID(gv->group_sel) || _local_player != owner,
-					GRP_WIDGET_DELETE_GROUP,
-					GRP_WIDGET_RENAME_GROUP,
-					GRP_WIDGET_REPLACE_PROTECTION,
-					WIDGET_LIST_END);
+		/* The drop down menu is out, *but* it may not be used, retract it. */
+		if (this->vehicles.list_length == 0 && this->IsWidgetLowered(GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN)) {
+			this->RaiseWidget(GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN);
+			HideDropDownMenu(this);
+		}
 
-			/* Disable remaining buttons for non-local player
-			 * Needed while changing _local_player, eg. by cheats
-			 * All procedures (eg. move vehicle to another group)
-			 *  verify, whether you are the owner of the vehicle,
-			 *  so it doesn't have to be disabled
-			 */
-			w->SetWidgetsDisabledState(_local_player != owner,
-					GRP_WIDGET_CREATE_GROUP,
-					GRP_WIDGET_AVAILABLE_VEHICLES,
-					WIDGET_LIST_END);
+		/* Disable all lists management button when the list is empty */
+		this->SetWidgetsDisabledState(this->vehicles.list_length == 0 || _local_player != owner,
+				GRP_WIDGET_STOP_ALL,
+				GRP_WIDGET_START_ALL,
+				GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
+				WIDGET_LIST_END);
+
+		/* Disable the group specific function when we select the default group or all vehicles */
+		this->SetWidgetsDisabledState(IsDefaultGroupID(this->group_sel) || IsAllGroupID(this->group_sel) || _local_player != owner,
+				GRP_WIDGET_DELETE_GROUP,
+				GRP_WIDGET_RENAME_GROUP,
+				GRP_WIDGET_REPLACE_PROTECTION,
+				WIDGET_LIST_END);
+
+		/* Disable remaining buttons for non-local player
+			* Needed while changing _local_player, eg. by cheats
+			* All procedures (eg. move vehicle to another group)
+			*  verify, whether you are the owner of the vehicle,
+			*  so it doesn't have to be disabled
+			*/
+		this->SetWidgetsDisabledState(_local_player != owner,
+				GRP_WIDGET_CREATE_GROUP,
+				GRP_WIDGET_AVAILABLE_VEHICLES,
+				WIDGET_LIST_END);
 
 
-			/* If selected_group == DEFAULT_GROUP || ALL_GROUP, draw the standard caption
-			   We list all vehicles or ungrouped vehicles */
-			if (IsDefaultGroupID(gv->group_sel) || IsAllGroupID(gv->group_sel)) {
-				SetDParam(0, owner);
-				SetDParam(1, gv->l.list_length);
-
-				switch (gv->vehicle_type) {
-					case VEH_TRAIN:
-						w->widget[GRP_WIDGET_CAPTION].data = STR_881B_TRAINS;
-						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_TRAIN;
-						break;
-					case VEH_ROAD:
-						w->widget[GRP_WIDGET_CAPTION].data = STR_9001_ROAD_VEHICLES;
-						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_ROADVEH;
-						break;
-					case VEH_SHIP:
-						w->widget[GRP_WIDGET_CAPTION].data = STR_9805_SHIPS;
-						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_SHIP;
-						break;
-					case VEH_AIRCRAFT:
-						w->widget[GRP_WIDGET_CAPTION].data =  STR_A009_AIRCRAFT;
-						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_AIRCRAFT;
-						break;
-					default: NOT_REACHED();
-				}
-			} else {
-				const Group *g = GetGroup(gv->group_sel);
-
-				SetDParam(0, g->index);
-				SetDParam(1, g->num_vehicle);
+		/* If selected_group == DEFAULT_GROUP || ALL_GROUP, draw the standard caption
+				We list all vehicles or ungrouped vehicles */
+		if (IsDefaultGroupID(this->group_sel) || IsAllGroupID(this->group_sel)) {
+			SetDParam(0, owner);
+			SetDParam(1, this->vehicles.list_length);
 
-				switch (gv->vehicle_type) {
-					case VEH_TRAIN:
-						w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_TRAINS_CAPTION;
-						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_TRAIN : SPR_GROUP_REPLACE_OFF_TRAIN;
-						break;
-					case VEH_ROAD:
-						w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_ROADVEH_CAPTION;
-						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_ROADVEH : SPR_GROUP_REPLACE_OFF_ROADVEH;
-						break;
-					case VEH_SHIP:
-						w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_SHIPS_CAPTION;
-						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_SHIP : SPR_GROUP_REPLACE_OFF_SHIP;
-						break;
-					case VEH_AIRCRAFT:
-						w->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_AIRCRAFTS_CAPTION;
-						w->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_AIRCRAFT : SPR_GROUP_REPLACE_OFF_AIRCRAFT;
-						break;
-					default: NOT_REACHED();
-				}
-			}
-
-			/* Set text of sort by dropdown */
-			w->widget[GRP_WIDGET_SORT_BY_DROPDOWN].data = _vehicle_sort_listing[gv->l.sort_type];
-
-			DrawWindowWidgets(w);
-
-			/* Draw Matrix Group
-			 * The selected group is drawn in white */
-			StringID str_all_veh, str_no_group_veh;
-
-			switch (gv->vehicle_type) {
+			switch (this->vehicle_type) {
 				case VEH_TRAIN:
-					str_all_veh = STR_GROUP_ALL_TRAINS;
-					str_no_group_veh = STR_GROUP_DEFAULT_TRAINS;
+					this->widget[GRP_WIDGET_CAPTION].data = STR_881B_TRAINS;
+					this->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_TRAIN;
 					break;
 				case VEH_ROAD:
-					str_all_veh = STR_GROUP_ALL_ROADS;
-					str_no_group_veh = STR_GROUP_DEFAULT_ROADS;
+					this->widget[GRP_WIDGET_CAPTION].data = STR_9001_ROAD_VEHICLES;
+					this->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_ROADVEH;
 					break;
 				case VEH_SHIP:
-					str_all_veh = STR_GROUP_ALL_SHIPS;
-					str_no_group_veh = STR_GROUP_DEFAULT_SHIPS;
+					this->widget[GRP_WIDGET_CAPTION].data = STR_9805_SHIPS;
+					this->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_SHIP;
 					break;
 				case VEH_AIRCRAFT:
-					str_all_veh = STR_GROUP_ALL_AIRCRAFTS;
-					str_no_group_veh = STR_GROUP_DEFAULT_AIRCRAFTS;
+					this->widget[GRP_WIDGET_CAPTION].data =  STR_A009_AIRCRAFT;
+					this->widget[GRP_WIDGET_REPLACE_PROTECTION].data = SPR_GROUP_REPLACE_OFF_AIRCRAFT;
 					break;
 				default: NOT_REACHED();
 			}
-			DrawString(10, y1, str_all_veh, IsAllGroupID(gv->group_sel) ? TC_WHITE : TC_BLACK);
-
-			y1 += 13;
-
-			DrawString(10, y1, str_no_group_veh, IsDefaultGroupID(gv->group_sel) ? TC_WHITE : TC_BLACK);
-
-			max = min(w->vscroll.pos + w->vscroll.cap, gl->l.list_length);
-			for (i = w->vscroll.pos ; i < max ; ++i) {
-				const Group *g = gl->sort_list[i];
-
-				assert(g->owner == owner);
-
-				y1 += PLY_WND_PRC__SIZE_OF_ROW_TINY;
-
-				/* draw the selected group in white, else we draw it in black */
-				SetDParam(0, g->index);
-				DrawString(10, y1, STR_GROUP_NAME, (gv->group_sel == g->index) ? TC_WHITE : TC_BLACK);
-
-				/* draw the number of vehicles of the group */
-				SetDParam(0, g->num_vehicle);
-				DrawStringRightAligned(187, y1 + 1, STR_GROUP_TINY_NUM, (gv->group_sel == g->index) ? TC_WHITE : TC_BLACK);
-			}
-
-			DrawSortButtonState(w, GRP_WIDGET_SORT_BY_ORDER, gv->l.flags & VL_DESC ? SBS_DOWN : SBS_UP);
+		} else {
+			const Group *g = GetGroup(this->group_sel);
 
-			/* Draw Matrix Vehicle according to the vehicle list built before */
-			max = min(w->vscroll2.pos + w->vscroll2.cap, gv->l.list_length);
-			for (i = w->vscroll2.pos ; i < max ; ++i) {
-				const Vehicle* v = gv->sort_list[i];
-
-				assert(v->type == gv->vehicle_type && v->owner == owner);
-
-				DrawVehicleImage(v, x + 19, y2 + 6, gv->vehicle_sel, w->hscroll.cap, 0);
-				DrawVehicleProfitButton(v, x, y2 + 13);
-
-				SetDParam(0, v->unitnumber);
-				DrawString(x, y2 + 2, v->IsInDepot() ? STR_021F : (v->age > v->max_age - 366 ? STR_00E3 : STR_00E2), TC_FROMSTRING);
+			SetDParam(0, g->index);
+			SetDParam(1, g->num_vehicle);
 
-				if (w->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG2) DrawSmallOrderList(v, x + 138, y2);
-
-				SetDParam(0, v->GetDisplayProfitThisYear());
-				SetDParam(1, v->GetDisplayProfitLastYear());
-				DrawString(x + 19, y2 + w->resize.step_height - 8, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, TC_FROMSTRING);
-
-				if (IsValidGroupID(v->group_id)) {
-					SetDParam(0, v->group_id);
-					DrawString(x + 19, y2, STR_GROUP_TINY_NAME, TC_BLACK);
-				}
-
-				y2 += w->resize.step_height;
+			switch (this->vehicle_type) {
+				case VEH_TRAIN:
+					this->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_TRAINS_CAPTION;
+					this->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_TRAIN : SPR_GROUP_REPLACE_OFF_TRAIN;
+					break;
+				case VEH_ROAD:
+					this->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_ROADVEH_CAPTION;
+					this->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_ROADVEH : SPR_GROUP_REPLACE_OFF_ROADVEH;
+					break;
+				case VEH_SHIP:
+					this->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_SHIPS_CAPTION;
+					this->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_SHIP : SPR_GROUP_REPLACE_OFF_SHIP;
+					break;
+				case VEH_AIRCRAFT:
+					this->widget[GRP_WIDGET_CAPTION].data = STR_GROUP_AIRCRAFTS_CAPTION;
+					this->widget[GRP_WIDGET_REPLACE_PROTECTION].data = (g->replace_protection) ? SPR_GROUP_REPLACE_ON_AIRCRAFT : SPR_GROUP_REPLACE_OFF_AIRCRAFT;
+					break;
+				default: NOT_REACHED();
 			}
-
-			break;
 		}
 
-		case WE_CLICK:
-			if (e->we.click.widget != GRP_WIDGET_SORT_BY_DROPDOWN && e->we.click.widget != GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN) HideDropDownMenu(w);
-
-			switch(e->we.click.widget) {
-				case GRP_WIDGET_SORT_BY_ORDER: // Flip sorting method ascending/descending
-					gv->l.flags ^= VL_DESC;
-					gv->l.flags |= VL_RESORT;
-
-					gv->_sorting->order = !!(gv->l.flags & VL_DESC);
-					w->SetDirty();
-					break;
-
-				case GRP_WIDGET_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
-					ShowDropDownMenu(w, _vehicle_sort_listing, gv->l.sort_type,  GRP_WIDGET_SORT_BY_DROPDOWN, 0, (gv->vehicle_type == VEH_TRAIN || gv->vehicle_type == VEH_ROAD) ? 0 : (1 << 10));
-					return;
+		/* Set text of sort by dropdown */
+		this->widget[GRP_WIDGET_SORT_BY_DROPDOWN].data = _vehicle_sort_listing[this->vehicles.sort_type];
 
-				case GRP_WIDGET_ALL_VEHICLES: // All vehicles button
-					if (!IsAllGroupID(gv->group_sel)) {
-						gv->group_sel = ALL_GROUP;
-						gv->l.flags |= VL_REBUILD;
-						w->SetDirty();
-					}
-					break;
+		DrawWindowWidgets(this);
 
-				case GRP_WIDGET_DEFAULT_VEHICLES: // Ungrouped vehicles button
-					if (!IsDefaultGroupID(gv->group_sel)) {
-						gv->group_sel = DEFAULT_GROUP;
-						gv->l.flags |= VL_REBUILD;
-						w->SetDirty();
-					}
-					break;
-
-				case GRP_WIDGET_LIST_GROUP: { // Matrix Group
-					uint16 id_g = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 26) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
+		/* Draw Matrix Group
+			* The selected group is drawn in white */
+		StringID str_all_veh, str_no_group_veh;
 
-					if (id_g >= w->vscroll.cap) return;
-
-					id_g += w->vscroll.pos;
+		switch (this->vehicle_type) {
+			case VEH_TRAIN:
+				str_all_veh = STR_GROUP_ALL_TRAINS;
+				str_no_group_veh = STR_GROUP_DEFAULT_TRAINS;
+				break;
+			case VEH_ROAD:
+				str_all_veh = STR_GROUP_ALL_ROADS;
+				str_no_group_veh = STR_GROUP_DEFAULT_ROADS;
+				break;
+			case VEH_SHIP:
+				str_all_veh = STR_GROUP_ALL_SHIPS;
+				str_no_group_veh = STR_GROUP_DEFAULT_SHIPS;
+				break;
+			case VEH_AIRCRAFT:
+				str_all_veh = STR_GROUP_ALL_AIRCRAFTS;
+				str_no_group_veh = STR_GROUP_DEFAULT_AIRCRAFTS;
+				break;
+			default: NOT_REACHED();
+		}
+		DrawString(10, y1, str_all_veh, IsAllGroupID(this->group_sel) ? TC_WHITE : TC_BLACK);
 
-					if (id_g >= gl->l.list_length) return;
+		y1 += 13;
 
-					gv->group_sel = gl->sort_list[id_g]->index;;
+		DrawString(10, y1, str_no_group_veh, IsDefaultGroupID(this->group_sel) ? TC_WHITE : TC_BLACK);
 
-					gv->l.flags |= VL_REBUILD;
-					w->SetDirty();
-					break;
+		max = min(this->vscroll.pos + this->vscroll.cap, this->groups.list_length);
+		for (i = this->vscroll.pos ; i < max ; ++i) {
+			const Group *g = this->groups.sort_list[i];
+
+			assert(g->owner == owner);
+
+			y1 += PLY_WND_PRC__SIZE_OF_ROW_TINY;
+
+			/* draw the selected group in white, else we draw it in black */
+			SetDParam(0, g->index);
+			DrawString(10, y1, STR_GROUP_NAME, (this->group_sel == g->index) ? TC_WHITE : TC_BLACK);
+
+			/* draw the number of vehicles of the group */
+			SetDParam(0, g->num_vehicle);
+			DrawStringRightAligned(187, y1 + 1, STR_GROUP_TINY_NUM, (this->group_sel == g->index) ? TC_WHITE : TC_BLACK);
+		}
+
+		DrawSortButtonState(this, GRP_WIDGET_SORT_BY_ORDER, this->vehicles.flags & VL_DESC ? SBS_DOWN : SBS_UP);
+
+		/* Draw Matrix Vehicle according to the vehicle list built before */
+		max = min(this->vscroll2.pos + this->vscroll2.cap, this->vehicles.list_length);
+		for (i = this->vscroll2.pos ; i < max ; ++i) {
+			const Vehicle* v = this->vehicles.sort_list[i];
+
+			assert(v->type == this->vehicle_type && v->owner == owner);
+
+			DrawVehicleImage(v, x + 19, y2 + 6, this->vehicle_sel, this->hscroll.cap, 0);
+			DrawVehicleProfitButton(v, x, y2 + 13);
+
+			SetDParam(0, v->unitnumber);
+			DrawString(x, y2 + 2, v->IsInDepot() ? STR_021F : (v->age > v->max_age - 366 ? STR_00E3 : STR_00E2), TC_FROMSTRING);
+
+			if (this->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG2) DrawSmallOrderList(v, x + 138, y2);
+
+			SetDParam(0, v->GetDisplayProfitThisYear());
+			SetDParam(1, v->GetDisplayProfitLastYear());
+			DrawString(x + 19, y2 + this->resize.step_height - 8, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, TC_FROMSTRING);
+
+			if (IsValidGroupID(v->group_id)) {
+				SetDParam(0, v->group_id);
+				DrawString(x + 19, y2, STR_GROUP_TINY_NAME, TC_BLACK);
+			}
+
+			y2 += this->resize.step_height;
+		}
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		if (widget != GRP_WIDGET_SORT_BY_DROPDOWN && widget != GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN) HideDropDownMenu(this);
+
+		switch(widget) {
+			case GRP_WIDGET_SORT_BY_ORDER: // Flip sorting method ascending/descending
+				this->vehicles.flags ^= VL_DESC;
+				this->vehicles.flags |= VL_RESORT;
+
+				this->sorting->order = !!(this->vehicles.flags & VL_DESC);
+				this->SetDirty();
+				break;
+
+			case GRP_WIDGET_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
+				ShowDropDownMenu(this, _vehicle_sort_listing, this->vehicles.sort_type,  GRP_WIDGET_SORT_BY_DROPDOWN, 0, (this->vehicle_type == VEH_TRAIN || this->vehicle_type == VEH_ROAD) ? 0 : (1 << 10));
+				return;
+
+			case GRP_WIDGET_ALL_VEHICLES: // All vehicles button
+				if (!IsAllGroupID(this->group_sel)) {
+					this->group_sel = ALL_GROUP;
+					this->vehicles.flags |= VL_REBUILD;
+					this->SetDirty();
+				}
+				break;
+
+			case GRP_WIDGET_DEFAULT_VEHICLES: // Ungrouped vehicles button
+				if (!IsDefaultGroupID(this->group_sel)) {
+					this->group_sel = DEFAULT_GROUP;
+					this->vehicles.flags |= VL_REBUILD;
+					this->SetDirty();
+				}
+				break;
+
+			case GRP_WIDGET_LIST_GROUP: { // Matrix Group
+				uint16 id_g = (pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 26) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
+
+				if (id_g >= this->vscroll.cap) return;
+
+				id_g += this->vscroll.pos;
+
+				if (id_g >= this->groups.list_length) return;
+
+				this->group_sel = this->groups.sort_list[id_g]->index;;
+
+				this->vehicles.flags |= VL_REBUILD;
+				this->SetDirty();
+				break;
+			}
+
+			case GRP_WIDGET_LIST_VEHICLE: { // Matrix Vehicle
+				uint32 id_v = (pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / (int)this->resize.step_height;
+				const Vehicle *v;
+
+				if (id_v >= this->vscroll2.cap) return; // click out of bounds
+
+				id_v += this->vscroll2.pos;
+
+				if (id_v >= this->vehicles.list_length) return; // click out of list bound
+
+				v = this->vehicles.sort_list[id_v];
+
+				this->vehicle_sel = v->index;
+
+				if (v->IsValid()) {
+					SetObjectToPlaceWnd(v->GetImage(DIR_W), GetVehiclePalette(v), VHM_DRAG, this);
+					_cursor.vehchain = true;
 				}
 
-				case GRP_WIDGET_LIST_VEHICLE: { // Matrix Vehicle
-					uint32 id_v = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / (int)w->resize.step_height;
-					const Vehicle *v;
-
-					if (id_v >= w->vscroll2.cap) return; // click out of bounds
-
-					id_v += w->vscroll2.pos;
-
-					if (id_v >= gv->l.list_length) return; // click out of list bound
-
-					v = gv->sort_list[id_v];
-
-					gv->vehicle_sel = v->index;
-
-					if (v->IsValid()) {
-						SetObjectToPlaceWnd(v->GetImage(DIR_W), GetVehiclePalette(v), VHM_DRAG, w);
-						_cursor.vehchain = true;
-					}
+				this->SetDirty();
+				break;
+			}
 
-					w->SetDirty();
-					break;
-				}
-
-				case GRP_WIDGET_CREATE_GROUP: // Create a new group
-					DoCommandP(0, gv->vehicle_type, 0, NULL, CMD_CREATE_GROUP | CMD_MSG(STR_GROUP_CAN_T_CREATE));
-					break;
-
-				case GRP_WIDGET_DELETE_GROUP: { // Delete the selected group
-					GroupID group = gv->group_sel;
-					gv->group_sel = ALL_GROUP;
+			case GRP_WIDGET_CREATE_GROUP: // Create a new group
+				DoCommandP(0, this->vehicle_type, 0, NULL, CMD_CREATE_GROUP | CMD_MSG(STR_GROUP_CAN_T_CREATE));
+				break;
 
-					DoCommandP(0, group, 0, NULL, CMD_DELETE_GROUP | CMD_MSG(STR_GROUP_CAN_T_DELETE));
-					break;
-				}
+			case GRP_WIDGET_DELETE_GROUP: { // Delete the selected group
+				GroupID group = this->group_sel;
+				this->group_sel = ALL_GROUP;
 
-				case GRP_WIDGET_RENAME_GROUP: { // Rename the selected roup
-					assert(IsValidGroupID(gv->group_sel));
+				DoCommandP(0, group, 0, NULL, CMD_DELETE_GROUP | CMD_MSG(STR_GROUP_CAN_T_DELETE));
+				break;
+			}
 
-					const Group *g = GetGroup(gv->group_sel);
+			case GRP_WIDGET_RENAME_GROUP: { // Rename the selected roup
+				assert(IsValidGroupID(this->group_sel));
 
-					SetDParam(0, g->index);
-					ShowQueryString(STR_GROUP_NAME, STR_GROUP_RENAME_CAPTION, 31, 150, w, CS_ALPHANUMERAL);
-				} break;
+				const Group *g = GetGroup(this->group_sel);
+
+				SetDParam(0, g->index);
+				ShowQueryString(STR_GROUP_NAME, STR_GROUP_RENAME_CAPTION, 31, 150, this, CS_ALPHANUMERAL);
+			} break;
 
 
-				case GRP_WIDGET_AVAILABLE_VEHICLES:
-					ShowBuildVehicleWindow(0, gv->vehicle_type);
-					break;
-
-				case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN:
-					ShowGroupActionDropdown(w, gv->group_sel);
-					break;
+			case GRP_WIDGET_AVAILABLE_VEHICLES:
+				ShowBuildVehicleWindow(0, this->vehicle_type);
+				break;
 
-				case GRP_WIDGET_START_ALL:
-				case GRP_WIDGET_STOP_ALL: { // Start/stop all vehicles of the list
-					DoCommandP(0, gv->group_sel, ((IsAllGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
-														| (1 << 6)
-														| (e->we.click.widget == GRP_WIDGET_START_ALL ? (1 << 5) : 0)
-														| gv->vehicle_type, NULL, CMD_MASS_START_STOP);
+			case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN:
+				ShowGroupActionDropdown(this, this->group_sel);
+				break;
 
-					break;
-				}
+			case GRP_WIDGET_START_ALL:
+			case GRP_WIDGET_STOP_ALL: { // Start/stop all vehicles of the list
+				DoCommandP(0, this->group_sel, ((IsAllGroupID(this->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
+													| (1 << 6)
+													| (widget == GRP_WIDGET_START_ALL ? (1 << 5) : 0)
+													| this->vehicle_type, NULL, CMD_MASS_START_STOP);
 
-				case GRP_WIDGET_REPLACE_PROTECTION:
-					if (IsValidGroupID(gv->group_sel)) {
-						const Group *g = GetGroup(gv->group_sel);
-
-						DoCommandP(0, gv->group_sel, !g->replace_protection, NULL, CMD_SET_GROUP_REPLACE_PROTECTION);
-					}
-					break;
+				break;
 			}
 
-			break;
-
-		case WE_DRAGDROP: {
-			switch (e->we.click.widget) {
-				case GRP_WIDGET_ALL_VEHICLES: // All vehicles
-				case GRP_WIDGET_DEFAULT_VEHICLES: // Ungrouped vehicles
-					DoCommandP(0, DEFAULT_GROUP, gv->vehicle_sel, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE));
-
-					gv->vehicle_sel = INVALID_VEHICLE;
-
-					w->SetDirty();
-
-					break;
+			case GRP_WIDGET_REPLACE_PROTECTION:
+				if (IsValidGroupID(this->group_sel)) {
+					const Group *g = GetGroup(this->group_sel);
 
-				case GRP_WIDGET_LIST_GROUP: { // Maxtrix group
-					uint16 id_g = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 26) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
-					const VehicleID vindex = gv->vehicle_sel;
-
-					gv->vehicle_sel = INVALID_VEHICLE;
-
-					w->SetDirty();
+					DoCommandP(0, this->group_sel, !g->replace_protection, NULL, CMD_SET_GROUP_REPLACE_PROTECTION);
+				}
+				break;
+		}
+	}
 
-					if (id_g >= w->vscroll.cap) return;
-
-					id_g += w->vscroll.pos;
+	virtual void OnDragDrop(Point pt, int widget)
+	{
+		switch (widget) {
+			case GRP_WIDGET_ALL_VEHICLES: // All vehicles
+			case GRP_WIDGET_DEFAULT_VEHICLES: // Ungrouped vehicles
+				DoCommandP(0, DEFAULT_GROUP, this->vehicle_sel, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE));
 
-					if (id_g >= gl->l.list_length) return;
+				this->vehicle_sel = INVALID_VEHICLE;
 
-					DoCommandP(0, gl->sort_list[id_g]->index, vindex, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE));
+				this->SetDirty();
 
-					break;
+				break;
+
+			case GRP_WIDGET_LIST_GROUP: { // Maxtrix group
+				uint16 id_g = (pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 26) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
+				const VehicleID vindex = this->vehicle_sel;
+
+				this->vehicle_sel = INVALID_VEHICLE;
+
+				this->SetDirty();
+
+				if (id_g >= this->vscroll.cap) return;
+
+				id_g += this->vscroll.pos;
+
+				if (id_g >= this->groups.list_length) return;
+
+				DoCommandP(0, this->groups.sort_list[id_g]->index, vindex, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE));
+
+				break;
+			}
+
+			case GRP_WIDGET_LIST_VEHICLE: { // Maxtrix vehicle
+				uint32 id_v = (pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / (int)this->resize.step_height;
+				const Vehicle *v;
+				const VehicleID vindex = this->vehicle_sel;
+
+				this->vehicle_sel = INVALID_VEHICLE;
+
+				this->SetDirty();
+
+				if (id_v >= this->vscroll2.cap) return; // click out of bounds
+
+				id_v += this->vscroll2.pos;
+
+				if (id_v >= this->vehicles.list_length) return; // click out of list bound
+
+				v = this->vehicles.sort_list[id_v];
+
+				if (vindex == v->index) {
+					ShowVehicleViewWindow(v);
 				}
 
-				case GRP_WIDGET_LIST_VEHICLE: { // Maxtrix vehicle
-					uint32 id_v = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / (int)w->resize.step_height;
-					const Vehicle *v;
-					const VehicleID vindex = gv->vehicle_sel;
-
-					gv->vehicle_sel = INVALID_VEHICLE;
-
-					w->SetDirty();
-
-					if (id_v >= w->vscroll2.cap) return; // click out of bounds
-
-					id_v += w->vscroll2.pos;
+				break;
+			}
+		}
+		_cursor.vehchain = false;
+	}
 
-					if (id_v >= gv->l.list_length) return; // click out of list bound
-
-					v = gv->sort_list[id_v];
+	virtual void OnQueryTextFinished(char *str)
+	{
+		if (!StrEmpty(str)) {
+			_cmd_text = str;
 
-					if (vindex == v->index) {
-						ShowVehicleViewWindow(v);
-					}
+			DoCommandP(0, this->group_sel, 0, NULL, CMD_RENAME_GROUP | CMD_MSG(STR_GROUP_CAN_T_RENAME));
+		}
+	}
 
-					break;
+	virtual void OnResize(Point new_size, Point delta)
+	{
+		this->hscroll.cap += delta.x;
+		this->vscroll.cap += delta.y / PLY_WND_PRC__SIZE_OF_ROW_TINY;
+		this->vscroll2.cap += delta.y / (int)this->resize.step_height;
+
+		this->widget[GRP_WIDGET_LIST_GROUP].data = (this->vscroll.cap << 8) + 1;
+		this->widget[GRP_WIDGET_LIST_VEHICLE].data = (this->vscroll2.cap << 8) + 1;
+	}
+
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		switch (widget) {
+			case GRP_WIDGET_SORT_BY_DROPDOWN:
+				if (this->vehicles.sort_type != index) {
+					this->vehicles.flags |= VL_RESORT;
+					this->vehicles.sort_type = index;
+					this->sorting->criteria = this->vehicles.sort_type;
 				}
-			}
-			_cursor.vehchain = false;
-			break;
+				break;
+
+			case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN:
+				assert(this->vehicles.list_length != 0);
+
+				switch (index) {
+					case GALF_REPLACE: // Replace window
+						ShowReplaceGroupVehicleWindow(this->group_sel, this->vehicle_type);
+						break;
+					case GALF_SERVICE: // Send for servicing
+						DoCommandP(0, this->group_sel, ((IsAllGroupID(this->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
+									| DEPOT_MASS_SEND
+									| DEPOT_SERVICE, NULL, GetCmdSendToDepot(this->vehicle_type));
+						break;
+					case GALF_DEPOT: // Send to Depots
+						DoCommandP(0, this->group_sel, ((IsAllGroupID(this->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
+									| DEPOT_MASS_SEND, NULL, GetCmdSendToDepot(this->vehicle_type));
+						break;
+					case GALF_ADD_SHARED: // Add shared Vehicles
+						assert(IsValidGroupID(this->group_sel));
+
+						DoCommandP(0, this->group_sel, this->vehicle_type, NULL, CMD_ADD_SHARED_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_SHARED_VEHICLE));
+						break;
+					case GALF_REMOVE_ALL: // Remove all Vehicles from the selected group
+						assert(IsValidGroupID(this->group_sel));
+
+						DoCommandP(0, this->group_sel, this->vehicle_type, NULL, CMD_REMOVE_ALL_VEHICLES_GROUP | CMD_MSG(STR_GROUP_CAN_T_REMOVE_ALL_VEHICLES));
+						break;
+					default: NOT_REACHED();
+				}
+				break;
+
+			default: NOT_REACHED();
 		}
 
-		case WE_ON_EDIT_TEXT:
-			if (!StrEmpty(e->we.edittext.str)) {
-				_cmd_text = e->we.edittext.str;
-
-				DoCommandP(0, gv->group_sel, 0, NULL, CMD_RENAME_GROUP | CMD_MSG(STR_GROUP_CAN_T_RENAME));
-			}
-			break;
-
-		case WE_RESIZE:
-			w->hscroll.cap += e->we.sizing.diff.x;
-			w->vscroll.cap += e->we.sizing.diff.y / PLY_WND_PRC__SIZE_OF_ROW_TINY;
-			w->vscroll2.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
-
-			w->widget[GRP_WIDGET_LIST_GROUP].data = (w->vscroll.cap << 8) + 1;
-			w->widget[GRP_WIDGET_LIST_VEHICLE].data = (w->vscroll2.cap << 8) + 1;
-			break;
-
-
-		case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
-			switch (e->we.dropdown.button) {
-				case GRP_WIDGET_SORT_BY_DROPDOWN:
-					if (gv->l.sort_type != e->we.dropdown.index) {
-						gv->l.flags |= VL_RESORT;
-						gv->l.sort_type = e->we.dropdown.index;
-						gv->_sorting->criteria = gv->l.sort_type;
-					}
-					break;
-
-				case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN:
-					assert(gv->l.list_length != 0);
+		this->SetDirty();
+	}
 
-					switch (e->we.dropdown.index) {
-						case GALF_REPLACE: // Replace window
-							ShowReplaceGroupVehicleWindow(gv->group_sel, gv->vehicle_type);
-							break;
-						case GALF_SERVICE: // Send for servicing
-							DoCommandP(0, gv->group_sel, ((IsAllGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
-										| DEPOT_MASS_SEND
-										| DEPOT_SERVICE, NULL, GetCmdSendToDepot(gv->vehicle_type));
-							break;
-						case GALF_DEPOT: // Send to Depots
-							DoCommandP(0, gv->group_sel, ((IsAllGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
-										| DEPOT_MASS_SEND, NULL, GetCmdSendToDepot(gv->vehicle_type));
-							break;
-						case GALF_ADD_SHARED: // Add shared Vehicles
-							assert(IsValidGroupID(gv->group_sel));
-
-							DoCommandP(0, gv->group_sel, gv->vehicle_type, NULL, CMD_ADD_SHARED_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_SHARED_VEHICLE));
-							break;
-						case GALF_REMOVE_ALL: // Remove all Vehicles from the selected group
-							assert(IsValidGroupID(gv->group_sel));
-
-							DoCommandP(0, gv->group_sel, gv->vehicle_type, NULL, CMD_REMOVE_ALL_VEHICLES_GROUP | CMD_MSG(STR_GROUP_CAN_T_REMOVE_ALL_VEHICLES));
-							break;
-						default: NOT_REACHED();
-					}
-					break;
-
-				default: NOT_REACHED();
-			}
+	virtual void OnTick()
+	{
+		if (_pause_game != 0) return;
+		if (--this->vehicles.resort_timer == 0) {
+			this->vehicles.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+			this->vehicles.flags |= VL_RESORT;
+			this->SetDirty();
+		}
+		if (--this->groups.resort_timer == 0) {
+			this->groups.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+			this->groups.flags |= VL_RESORT;
+			this->SetDirty();
+		}
+	}
 
-			w->SetDirty();
-			break;
-
-
-		case WE_DESTROY:
-			free((void*)gv->sort_list);
-			free((void*)gl->sort_list);
-			break;
-
-
-		case WE_TICK: // resort the lists every 20 seconds orso (10 days)
-			if (_pause_game != 0) break;
-			if (--gv->l.resort_timer == 0) {
-				gv->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
-				gv->l.flags |= VL_RESORT;
-				w->SetDirty();
-			}
-			if (--gl->l.resort_timer == 0) {
-				gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
-				gl->l.flags |= VL_RESORT;
-				w->SetDirty();
-			}
-			break;
-
-		case WE_ABORT_PLACE_OBJ: // called when new object to place is selected from keyboard
+	virtual void OnPlaceObjectAbort()
+	{
 			/* abort drag & drop */
-			gv->vehicle_sel = INVALID_VEHICLE;
-			w->InvalidateWidget(GRP_WIDGET_LIST_VEHICLE);
-			break;
-
+			this->vehicle_sel = INVALID_VEHICLE;
+			this->InvalidateWidget(GRP_WIDGET_LIST_VEHICLE);
 	}
-}
+};
 
 
 static const WindowDesc _group_desc = {
@@ -792,7 +758,7 @@
 	WC_TRAINS_LIST, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_group_widgets,
-	GroupWndProc
+	NULL
 };
 
 void ShowPlayerGroup(PlayerID player, VehicleType vehicle_type)
@@ -811,8 +777,6 @@
 
 	WindowNumber num = (vehicle_type << 11) | VLW_GROUP_LIST | player;
 	DeleteWindowById(wc, num);
-	Window *w = AllocateWindowDescFront(&_group_desc, num);
-	if (w == NULL) return;
-
+	Window *w = new VehicleGroupWindow(&_group_desc, num);
 	w->window_class = wc;
 }
--- a/src/gui.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/gui.h	Sun May 11 20:09:34 2008 +0000
@@ -48,35 +48,6 @@
 void ShowGenerateLandscape();
 void ShowHeightmapLoad();
 
-/** Drag and drop selection process, or, what to do with an area of land when
- * you've selected it. */
-enum {
-	DDSP_DEMOLISH_AREA,
-	DDSP_RAISE_AND_LEVEL_AREA,
-	DDSP_LOWER_AND_LEVEL_AREA,
-	DDSP_LEVEL_AREA,
-	DDSP_CREATE_DESERT,
-	DDSP_CREATE_ROCKS,
-	DDSP_CREATE_WATER,
-	DDSP_CREATE_RIVER,
-	DDSP_PLANT_TREES,
-	DDSP_BUILD_BRIDGE,
-
-	/* Rail specific actions */
-	DDSP_PLACE_RAIL_NE,
-	DDSP_PLACE_RAIL_NW,
-	DDSP_PLACE_AUTORAIL,
-	DDSP_BUILD_SIGNALS,
-	DDSP_BUILD_STATION,
-	DDSP_REMOVE_STATION,
-	DDSP_CONVERT_RAIL,
-
-	/* Road specific actions */
-	DDSP_PLACE_ROAD_X_DIR,
-	DDSP_PLACE_ROAD_Y_DIR,
-	DDSP_PLACE_AUTOROAD,
-};
-
 /* misc_gui.cpp */
 void PlaceLandBlockInfo();
 void ShowAboutWindow();
--- a/src/industry_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/industry_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -25,43 +25,13 @@
 #include "player_func.h"
 #include "settings_type.h"
 #include "tilehighlight_func.h"
+#include "string_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
 
 bool _ignore_restrictions;
 
-/** Names of the widgets of the dynamic place industries gui */
-enum DynamicPlaceIndustriesWidgets {
-	DPIW_CLOSEBOX = 0,
-	DPIW_CAPTION,
-	DPIW_MATRIX_WIDGET,
-	DPIW_SCROLLBAR,
-	DPIW_INFOPANEL,
-	DPIW_FUND_WIDGET,
-	DPIW_RESIZE_WIDGET,
-};
-
-/** Attached struct to the window extended data */
-struct fnd_d {
-	int index;             ///< index of the element in the matrix
-	IndustryType select;   ///< industry corresponding to the above index
-	uint16 callback_timer; ///< timer counter for callback eventual verification
-	bool timer_enabled;    ///< timer can be used
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(fnd_d));
-
-/** Helper struct holding the available industries for current situation */
-static struct IndustryData {
-	uint16 count;                               ///< How many industries are loaded
-	IndustryType index[NUM_INDUSTRYTYPES + 1];  ///< Type of industry, in the order it was loaded
-	StringID text[NUM_INDUSTRYTYPES + 1];       ///< Text coming from CBM_IND_FUND_MORE_TEXT (if ever)
-	bool enabled[NUM_INDUSTRYTYPES + 1];        ///< availability state, coming from CBID_INDUSTRY_AVAILABLE (if ever)
-} _fund_gui;
-
-assert_compile(lengthof(_fund_gui.index) == lengthof(_fund_gui.text));
-assert_compile(lengthof(_fund_gui.index) == lengthof(_fund_gui.enabled));
-
 enum CargoSuffixType {
 	CST_FUND,
 	CST_VIEW,
@@ -91,288 +61,19 @@
 	return STR_EMPTY;
 }
 
-static void BuildDynamicIndustryWndProc(Window *w, WindowEvent *e)
-{
-	switch (e->event) {
-		case WE_CREATE: {
-			IndustryType ind;
-			const IndustrySpec *indsp;
-
-			/* Shorten the window to the equivalant of the additionnal purchase
-			 * info coming from the callback.  SO it will only be available to tis full
-			 * height when newindistries are loaded */
-			if (!_loaded_newgrf_features.has_newindustries) {
-				w->widget[DPIW_INFOPANEL].bottom -= 44;
-				w->widget[DPIW_FUND_WIDGET].bottom -= 44;
-				w->widget[DPIW_FUND_WIDGET].top -= 44;
-				w->widget[DPIW_RESIZE_WIDGET].bottom -= 44;
-				w->widget[DPIW_RESIZE_WIDGET].top -= 44;
-				w->resize.height = w->height -= 44;
-			}
-
-			WP(w, fnd_d).timer_enabled = _loaded_newgrf_features.has_newindustries;
-
-			/* Initilialize structures */
-			_fund_gui.count = 0;
-
-			for (uint i = 0; i < lengthof(_fund_gui.index); i++) {
-				_fund_gui.index[i]   = 0xFF;
-				_fund_gui.text[i]    = STR_NULL;
-				_fund_gui.enabled[i] = false;
-			}
-
-			w->vscroll.cap = 8; // rows in grid, same in scroller
-			w->resize.step_height = 13;
-
-			if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
-				_fund_gui.index[_fund_gui.count] = INVALID_INDUSTRYTYPE;
-				_fund_gui.count++;
-				WP(w, fnd_d).timer_enabled = false;
-			}
-
-			/* Fill the _fund_gui structure with industries.
-			 * The tests performed after the enabled allow to load the industries
-			 * In the same way they are inserted by grf (if any)
-			 */
-			for (ind = 0; ind < NUM_INDUSTRYTYPES; ind++) {
-				indsp = GetIndustrySpec(ind);
-				if (indsp->enabled){
-					/* Rule is that editor mode loads all industries.
-					 * In game mode, all non raw industries are loaded too
-					 * and raw ones are loaded only when setting allows it */
-					if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _patches.raw_industry_construction == 0) continue;
-					_fund_gui.index[_fund_gui.count] = ind;
-					_fund_gui.enabled[_fund_gui.count] = (_game_mode == GM_EDITOR) || CheckIfCallBackAllowsAvailability(ind, IACT_USERCREATION);
-					_fund_gui.count++;
-				}
-			}
-
-			/* first indutry type is selected.
-			 * I'll be damned if there are none available ;) */
-			WP(w, fnd_d).index = 0;
-			WP(w, fnd_d).select = _fund_gui.index[0];
-			WP(w, fnd_d).callback_timer = DAY_TICKS;
-		} break;
-
-		case WE_PAINT: {
-			const IndustrySpec *indsp = (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(WP(w, fnd_d).select);
-			int x_str = w->widget[DPIW_INFOPANEL].left + 3;
-			int y_str = w->widget[DPIW_INFOPANEL].top + 3;
-			const Widget *wi = &w->widget[DPIW_INFOPANEL];
-			int max_width = wi->right - wi->left - 4;
-
-			/* Raw industries might be prospected. Show this fact by changing the string
-			 * In Editor, you just build, while ingame, or you fund or you prospect */
-			if (_game_mode == GM_EDITOR) {
-				/* We've chosen many random industries but no industries have been specified */
-				if (indsp == NULL) _fund_gui.enabled[WP(w, fnd_d).index] = _opt.diff.number_industries != 0;
-				w->widget[DPIW_FUND_WIDGET].data = STR_BUILD_NEW_INDUSTRY;
-			} else {
-				w->widget[DPIW_FUND_WIDGET].data = (_patches.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_PROSPECT_NEW_INDUSTRY : STR_FUND_NEW_INDUSTRY;
-			}
-			w->SetWidgetDisabledState(DPIW_FUND_WIDGET, !_fund_gui.enabled[WP(w, fnd_d).index]);
-
-			SetVScrollCount(w, _fund_gui.count);
-
-			DrawWindowWidgets(w);
-
-			/* and now with the matrix painting */
-			for (byte i = 0; i < w->vscroll.cap && ((i + w->vscroll.pos) < _fund_gui.count); i++) {
-				int offset = i * 13;
-				int x = 3;
-				int y = 16;
-				bool selected = WP(w, fnd_d).index == i + w->vscroll.pos;
-
-				if (_fund_gui.index[i + w->vscroll.pos] == INVALID_INDUSTRYTYPE) {
-					DrawStringTruncated(20, y + offset, STR_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
-					continue;
-				}
-				const IndustrySpec *indsp = GetIndustrySpec(_fund_gui.index[i + w->vscroll.pos]);
-
-				/* Draw the name of the industry in white is selected, otherwise, in orange */
-				DrawStringTruncated(20, y + offset, indsp->name, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
-				GfxFillRect(x,     y + 1 + offset,  x + 10, y + 7 + offset, selected ? 15 : 0);
-				GfxFillRect(x + 1, y + 2 + offset,  x +  9, y + 6 + offset, indsp->map_colour);
-			}
-
-			if (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) {
-				DrawStringMultiLine(x_str, y_str, STR_RANDOM_INDUSTRIES_TIP, max_width, wi->bottom - wi->top - 40);
-				break;
-			}
-
-			if (_game_mode != GM_EDITOR) {
-				SetDParam(0, indsp->GetConstructionCost());
-				DrawStringTruncated(x_str, y_str, STR_482F_COST, TC_FROMSTRING, max_width);
-				y_str += 11;
-			}
-
-			/* Draw the accepted cargos, if any. Otherwhise, will print "Nothing" */
-			StringID str = STR_4827_REQUIRES;
-			byte p = 0;
-			SetDParam(0, STR_00D0_NOTHING);
-			SetDParam(1, STR_EMPTY);
-			for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
-				if (indsp->accepts_cargo[j] == CT_INVALID) continue;
-				if (p > 0) str++;
-				SetDParam(p++, GetCargo(indsp->accepts_cargo[j])->name);
-				SetDParam(p++, GetCargoSuffix(j, CST_FUND, NULL, WP(w, fnd_d).select, indsp));
-			}
-			DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
-			y_str += 11;
-
-			/* Draw the produced cargos, if any. Otherwhise, will print "Nothing" */
-			str = STR_4827_PRODUCES;
-			p = 0;
-			SetDParam(0, STR_00D0_NOTHING);
-			SetDParam(1, STR_EMPTY);
-			for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
-				if (indsp->produced_cargo[j] == CT_INVALID) continue;
-				if (p > 0) str++;
-				SetDParam(p++, GetCargo(indsp->produced_cargo[j])->name);
-				SetDParam(p++, GetCargoSuffix(j + 3, CST_FUND, NULL, WP(w, fnd_d).select, indsp));
-			}
-			DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
-			y_str += 11;
-
-			/* Get the additional purchase info text, if it has not already been */
-			if (_fund_gui.text[WP(w, fnd_d).index] == STR_NULL) {   // Have i been called already?
-				if (HasBit(indsp->callback_flags, CBM_IND_FUND_MORE_TEXT)) {          // No. Can it be called?
-					uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, WP(w, fnd_d).select, INVALID_TILE);
-					if (callback_res != CALLBACK_FAILED) {  // Did it failed?
-						StringID newtxt = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res);  // No. here's the new string
-						_fund_gui.text[WP(w, fnd_d).index] = newtxt;   // Store it for further usage
-					}
-				}
-			}
-
-			/* Draw the Additional purchase text, provided by newgrf callback, if any.
-			 * Otherwhise, will print Nothing */
-			str = _fund_gui.text[WP(w, fnd_d).index];
-			if (str != STR_NULL && str != STR_UNDEFINED) {
-				SetDParam(0, str);
-				DrawStringMultiLine(x_str, y_str, STR_JUST_STRING, max_width, wi->bottom - wi->top - 40);
-			}
-		} break;
-
-		case WE_DOUBLE_CLICK:
-			if (e->we.click.widget != DPIW_MATRIX_WIDGET) break;
-			e->we.click.widget = DPIW_FUND_WIDGET;
-			/* Fall through */
-
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case DPIW_MATRIX_WIDGET: {
-					const IndustrySpec *indsp;
-					int y = (e->we.click.pt.y - w->widget[DPIW_MATRIX_WIDGET].top) / 13 + w->vscroll.pos ;
-
-					if (y >= 0 && y < _fund_gui.count) { // Is it within the boundaries of available data?
-						WP(w, fnd_d).index = y;
-						WP(w, fnd_d).select = _fund_gui.index[WP(w, fnd_d).index];
-						indsp = (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(WP(w, fnd_d).select);
-
-						w->SetDirty();
-
-						if ((_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
-								WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) {
-							/* Reset the button state if going to prospecting or "build many industries" */
-							w->RaiseButtons();
-							ResetObjectToPlace();
-						}
-					}
-				} break;
-
-				case DPIW_FUND_WIDGET: {
-					if (WP(w, fnd_d).select == INVALID_INDUSTRYTYPE) {
-						w->HandleButtonClick(DPIW_FUND_WIDGET);
-
-						if (GetNumTowns() == 0) {
-							ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_CAN_T_GENERATE_INDUSTRIES, 0, 0);
-						} else {
-							extern void GenerateIndustries();
-							_generating_world = true;
-							GenerateIndustries();
-							_generating_world = false;
-						}
-					} else if (_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && GetIndustrySpec(WP(w, fnd_d).select)->IsRawIndustry()) {
-						DoCommandP(0, WP(w, fnd_d).select, InteractiveRandom(), NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
-						w->HandleButtonClick(DPIW_FUND_WIDGET);
-					} else {
-						HandlePlacePushButton(w, DPIW_FUND_WIDGET, SPR_CURSOR_INDUSTRY, VHM_RECT, NULL);
-					}
-				} break;
-			}
-			break;
-
-		case WE_RESIZE: {
-			/* Adjust the number of items in the matrix depending of the rezise */
-			w->vscroll.cap  += e->we.sizing.diff.y / (int)w->resize.step_height;
-			w->widget[DPIW_MATRIX_WIDGET].data = (w->vscroll.cap << 8) + 1;
-		} break;
-
-		case WE_PLACE_OBJ: {
-			bool success = true;
-			/* We do not need to protect ourselves against "Random Many Industries" in this mode */
-			const IndustrySpec *indsp = GetIndustrySpec(WP(w, fnd_d).select);
-			uint32 seed = InteractiveRandom();
-
-			if (_game_mode == GM_EDITOR) {
-				/* Show error if no town exists at all */
-				if (GetNumTowns() == 0) {
-					SetDParam(0, indsp->name);
-					ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_0285_CAN_T_BUILD_HERE, e->we.place.pt.x, e->we.place.pt.y);
-					return;
-				}
-
-				_current_player = OWNER_NONE;
-				_generating_world = true;
-				_ignore_restrictions = true;
-				success = DoCommandP(e->we.place.tile, (InteractiveRandomRange(indsp->num_table) << 16) | WP(w, fnd_d).select, seed, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
-				if (!success) {
-					SetDParam(0, indsp->name);
-					ShowErrorMessage(_error_message, STR_0285_CAN_T_BUILD_HERE, e->we.place.pt.x, e->we.place.pt.y);
-				}
-
-				_ignore_restrictions = false;
-				_generating_world = false;
-			} else {
-				success = DoCommandP(e->we.place.tile, (InteractiveRandomRange(indsp->num_table) << 16) | WP(w, fnd_d).select, seed, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
-			}
-
-			/* If an industry has been built, just reset the cursor and the system */
-			if (success) ResetObjectToPlace();
-		} break;
-
-		case WE_TICK:
-			if (_pause_game != 0) break;
-			if (!WP(w, fnd_d).timer_enabled) break;
-			if (--WP(w, fnd_d).callback_timer == 0) {
-				/* We have just passed another day.
-				 * See if we need to update availability of currently selected industry */
-				WP(w, fnd_d).callback_timer = DAY_TICKS;  //restart counter
-
-				const IndustrySpec *indsp = GetIndustrySpec(WP(w, fnd_d).select);
-
-				if (indsp->enabled) {
-					bool call_back_result = CheckIfCallBackAllowsAvailability(WP(w, fnd_d).select, IACT_USERCREATION);
-
-					/* Only if result does match the previous state would it require a redraw. */
-					if (call_back_result != _fund_gui.enabled[WP(w, fnd_d).index]) {
-						_fund_gui.enabled[WP(w, fnd_d).index] = call_back_result;
-						w->SetDirty();
-					}
-				}
-			}
-			break;
-
-		case WE_TIMEOUT:
-		case WE_ABORT_PLACE_OBJ:
-			w->RaiseButtons();
-			break;
-	}
-}
+/** Names of the widgets of the dynamic place industries gui */
+enum DynamicPlaceIndustriesWidgets {
+	DPIW_CLOSEBOX = 0,
+	DPIW_CAPTION,
+	DPIW_MATRIX_WIDGET,
+	DPIW_SCROLLBAR,
+	DPIW_INFOPANEL,
+	DPIW_FUND_WIDGET,
+	DPIW_RESIZE_WIDGET,
+};
 
 /** Widget definition of the dynamic place industries gui */
-static const Widget _build_dynamic_industry_widgets[] = {
+static const Widget _build_industry_widgets[] = {
 {   WWT_CLOSEBOX,    RESIZE_NONE,    7,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},            // DPIW_CLOSEBOX
 {    WWT_CAPTION,   RESIZE_RIGHT,    7,    11,   169,     0,    13, STR_0314_FUND_NEW_INDUSTRY,     STR_018C_WINDOW_TITLE_DRAG_THIS},  // DPIW_CAPTION
 {     WWT_MATRIX,      RESIZE_RB,    7,     0,   157,    14,   118, 0x801,                          STR_INDUSTRY_SELECTION_HINT},      // DPIW_MATRIX_WIDGET
@@ -384,18 +85,320 @@
 };
 
 /** Window definition of the dynamic place industries gui */
-static const WindowDesc _build_industry_dynamic_desc = {
+static const WindowDesc _build_industry_desc = {
 	WDP_AUTO, WDP_AUTO, 170, 212, 170, 212,
 	WC_BUILD_INDUSTRY, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE,
-	_build_dynamic_industry_widgets,
-	BuildDynamicIndustryWndProc,
+	_build_industry_widgets,
+	NULL,
+};
+
+class BuildIndustryWindow : public Window {
+	int selected_index;                         ///< index of the element in the matrix
+	IndustryType selected_type;                 ///< industry corresponding to the above index
+	uint16 callback_timer;                      ///< timer counter for callback eventual verification
+	bool timer_enabled;                         ///< timer can be used
+	uint16 count;                               ///< How many industries are loaded
+	IndustryType index[NUM_INDUSTRYTYPES + 1];  ///< Type of industry, in the order it was loaded
+	StringID text[NUM_INDUSTRYTYPES + 1];       ///< Text coming from CBM_IND_FUND_MORE_TEXT (if ever)
+	bool enabled[NUM_INDUSTRYTYPES + 1];        ///< availability state, coming from CBID_INDUSTRY_AVAILABLE (if ever)
+
+public:
+	BuildIndustryWindow() : Window(&_build_industry_desc)
+	{
+		IndustryType ind;
+		const IndustrySpec *indsp;
+
+		/* Shorten the window to the equivalant of the additionnal purchase
+		 * info coming from the callback.  SO it will only be available to tis full
+		 * height when newindistries are loaded */
+		if (!_loaded_newgrf_features.has_newindustries) {
+			this->widget[DPIW_INFOPANEL].bottom -= 44;
+			this->widget[DPIW_FUND_WIDGET].bottom -= 44;
+			this->widget[DPIW_FUND_WIDGET].top -= 44;
+			this->widget[DPIW_RESIZE_WIDGET].bottom -= 44;
+			this->widget[DPIW_RESIZE_WIDGET].top -= 44;
+			this->resize.height = this->height -= 44;
+		}
+
+		this->timer_enabled = _loaded_newgrf_features.has_newindustries;
+
+		/* Initialize structures */
+		this->count = 0;
+
+		for (uint i = 0; i < lengthof(this->index); i++) {
+			this->index[i]   = 0xFF;
+			this->text[i]    = STR_NULL;
+			this->enabled[i] = false;
+		}
+
+		this->vscroll.cap = 8; // rows in grid, same in scroller
+		this->resize.step_height = 13;
+
+		if (_game_mode == GM_EDITOR) { // give room for the Many Random "button"
+			this->index[this->count] = INVALID_INDUSTRYTYPE;
+			this->count++;
+			this->timer_enabled = false;
+		}
+
+		/* Fill the _fund_gui structure with industries.
+		 * The tests performed after the enabled allow to load the industries
+		 * In the same way they are inserted by grf (if any)
+		 */
+		for (ind = 0; ind < NUM_INDUSTRYTYPES; ind++) {
+			indsp = GetIndustrySpec(ind);
+			if (indsp->enabled){
+				/* Rule is that editor mode loads all industries.
+				 * In game mode, all non raw industries are loaded too
+				 * and raw ones are loaded only when setting allows it */
+				if (_game_mode != GM_EDITOR && indsp->IsRawIndustry() && _patches.raw_industry_construction == 0) continue;
+				this->index[this->count] = ind;
+				this->enabled[this->count] = (_game_mode == GM_EDITOR) || CheckIfCallBackAllowsAvailability(ind, IACT_USERCREATION);
+				this->count++;
+			}
+		}
+
+		/* first indutry type is selected.
+		 * I'll be damned if there are none available ;) */
+		this->selected_index = 0;
+		this->selected_type = this->index[0];
+		this->callback_timer = DAY_TICKS;
+
+		this->FindWindowPlacementAndResize(&_build_industry_desc);
+	}
+
+	virtual void OnPaint()
+	{
+		const IndustrySpec *indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
+		int x_str = this->widget[DPIW_INFOPANEL].left + 3;
+		int y_str = this->widget[DPIW_INFOPANEL].top + 3;
+		const Widget *wi = &this->widget[DPIW_INFOPANEL];
+		int max_width = wi->right - wi->left - 4;
+
+		/* Raw industries might be prospected. Show this fact by changing the string
+		 * In Editor, you just build, while ingame, or you fund or you prospect */
+		if (_game_mode == GM_EDITOR) {
+			/* We've chosen many random industries but no industries have been specified */
+			if (indsp == NULL) this->enabled[this->selected_index] = _opt.diff.number_industries != 0;
+			this->widget[DPIW_FUND_WIDGET].data = STR_BUILD_NEW_INDUSTRY;
+		} else {
+			this->widget[DPIW_FUND_WIDGET].data = (_patches.raw_industry_construction == 2 && indsp->IsRawIndustry()) ? STR_PROSPECT_NEW_INDUSTRY : STR_FUND_NEW_INDUSTRY;
+		}
+		this->SetWidgetDisabledState(DPIW_FUND_WIDGET, !this->enabled[this->selected_index]);
+
+		SetVScrollCount(this, this->count);
+
+		DrawWindowWidgets(this);
+
+		/* and now with the matrix painting */
+		for (byte i = 0; i < this->vscroll.cap && ((i + this->vscroll.pos) < this->count); i++) {
+			int offset = i * 13;
+			int x = 3;
+			int y = 16;
+			bool selected = this->selected_index == i + this->vscroll.pos;
+
+			if (this->index[i + this->vscroll.pos] == INVALID_INDUSTRYTYPE) {
+				DrawStringTruncated(20, y + offset, STR_MANY_RANDOM_INDUSTRIES, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
+				continue;
+			}
+			const IndustrySpec *indsp = GetIndustrySpec(this->index[i + this->vscroll.pos]);
+
+			/* Draw the name of the industry in white is selected, otherwise, in orange */
+			DrawStringTruncated(20, y + offset, indsp->name, selected ? TC_WHITE : TC_ORANGE, max_width - 25);
+			GfxFillRect(x,     y + 1 + offset,  x + 10, y + 7 + offset, selected ? 15 : 0);
+			GfxFillRect(x + 1, y + 2 + offset,  x +  9, y + 6 + offset, indsp->map_colour);
+		}
+
+		if (this->selected_type == INVALID_INDUSTRYTYPE) {
+			DrawStringMultiLine(x_str, y_str, STR_RANDOM_INDUSTRIES_TIP, max_width, wi->bottom - wi->top - 40);
+			return;
+		}
+
+		if (_game_mode != GM_EDITOR) {
+			SetDParam(0, indsp->GetConstructionCost());
+			DrawStringTruncated(x_str, y_str, STR_482F_COST, TC_FROMSTRING, max_width);
+			y_str += 11;
+		}
+
+		/* Draw the accepted cargos, if any. Otherwhise, will print "Nothing" */
+		StringID str = STR_4827_REQUIRES;
+		byte p = 0;
+		SetDParam(0, STR_00D0_NOTHING);
+		SetDParam(1, STR_EMPTY);
+		for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
+			if (indsp->accepts_cargo[j] == CT_INVALID) continue;
+			if (p > 0) str++;
+			SetDParam(p++, GetCargo(indsp->accepts_cargo[j])->name);
+			SetDParam(p++, GetCargoSuffix(j, CST_FUND, NULL, this->selected_type, indsp));
+		}
+		DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
+		y_str += 11;
+
+		/* Draw the produced cargos, if any. Otherwhise, will print "Nothing" */
+		str = STR_4827_PRODUCES;
+		p = 0;
+		SetDParam(0, STR_00D0_NOTHING);
+		SetDParam(1, STR_EMPTY);
+		for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
+			if (indsp->produced_cargo[j] == CT_INVALID) continue;
+			if (p > 0) str++;
+			SetDParam(p++, GetCargo(indsp->produced_cargo[j])->name);
+			SetDParam(p++, GetCargoSuffix(j + 3, CST_FUND, NULL, this->selected_type, indsp));
+		}
+		DrawStringTruncated(x_str, y_str, str, TC_FROMSTRING, max_width);
+		y_str += 11;
+
+		/* Get the additional purchase info text, if it has not already been */
+		if (this->text[this->selected_index] == STR_NULL) {   // Have i been called already?
+			if (HasBit(indsp->callback_flags, CBM_IND_FUND_MORE_TEXT)) {          // No. Can it be called?
+				uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_FUND_MORE_TEXT, 0, 0, NULL, this->selected_type, INVALID_TILE);
+				if (callback_res != CALLBACK_FAILED) {  // Did it failed?
+					StringID newtxt = GetGRFStringID(indsp->grf_prop.grffile->grfid, 0xD000 + callback_res);  // No. here's the new string
+					this->text[this->selected_index] = newtxt;   // Store it for further usage
+				}
+			}
+		}
+
+		/* Draw the Additional purchase text, provided by newgrf callback, if any.
+		 * Otherwhise, will print Nothing */
+		str = this->text[this->selected_index];
+		if (str != STR_NULL && str != STR_UNDEFINED) {
+			SetDParam(0, str);
+			DrawStringMultiLine(x_str, y_str, STR_JUST_STRING, max_width, wi->bottom - wi->top - 40);
+		}
+	}
+
+	virtual void OnDoubleClick(Point pt, int widget)
+	{
+		if (widget != DPIW_MATRIX_WIDGET) return;
+		this->OnClick(pt, DPIW_FUND_WIDGET);
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case DPIW_MATRIX_WIDGET: {
+				const IndustrySpec *indsp;
+				int y = (pt.y - this->widget[DPIW_MATRIX_WIDGET].top) / 13 + this->vscroll.pos ;
+
+				if (y >= 0 && y < count) { // Is it within the boundaries of available data?
+					this->selected_index = y;
+					this->selected_type = this->index[y];
+					indsp = (this->selected_type == INVALID_INDUSTRYTYPE) ? NULL : GetIndustrySpec(this->selected_type);
+
+					this->SetDirty();
+
+					if ((_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) ||
+							this->selected_type == INVALID_INDUSTRYTYPE) {
+						/* Reset the button state if going to prospecting or "build many industries" */
+						this->RaiseButtons();
+						ResetObjectToPlace();
+					}
+				}
+			} break;
+
+			case DPIW_FUND_WIDGET: {
+				if (this->selected_type == INVALID_INDUSTRYTYPE) {
+					this->HandleButtonClick(DPIW_FUND_WIDGET);
+
+					if (GetNumTowns() == 0) {
+						ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_CAN_T_GENERATE_INDUSTRIES, 0, 0);
+					} else {
+						extern void GenerateIndustries();
+						_generating_world = true;
+						GenerateIndustries();
+						_generating_world = false;
+					}
+				} else if (_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && GetIndustrySpec(this->selected_type)->IsRawIndustry()) {
+					DoCommandP(0, this->selected_type, InteractiveRandom(), NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
+					this->HandleButtonClick(DPIW_FUND_WIDGET);
+				} else {
+					HandlePlacePushButton(this, DPIW_FUND_WIDGET, SPR_CURSOR_INDUSTRY, VHM_RECT, NULL);
+				}
+			} break;
+		}
+	}
+
+	virtual void OnResize(Point new_size, Point delta)
+	{
+		/* Adjust the number of items in the matrix depending of the rezise */
+		this->vscroll.cap  += delta.y / (int)this->resize.step_height;
+		this->widget[DPIW_MATRIX_WIDGET].data = (this->vscroll.cap << 8) + 1;
+	}
+
+	virtual void OnPlaceObject(Point pt, TileIndex tile)
+	{
+		bool success = true;
+		/* We do not need to protect ourselves against "Random Many Industries" in this mode */
+		const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
+		uint32 seed = InteractiveRandom();
+
+		if (_game_mode == GM_EDITOR) {
+			/* Show error if no town exists at all */
+			if (GetNumTowns() == 0) {
+				SetDParam(0, indsp->name);
+				ShowErrorMessage(STR_0286_MUST_BUILD_TOWN_FIRST, STR_0285_CAN_T_BUILD_HERE, pt.x, pt.y);
+				return;
+			}
+
+			_current_player = OWNER_NONE;
+			_generating_world = true;
+			_ignore_restrictions = true;
+			success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 16) | this->selected_type, seed, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
+			if (!success) {
+				SetDParam(0, indsp->name);
+				ShowErrorMessage(_error_message, STR_0285_CAN_T_BUILD_HERE, pt.x, pt.y);
+			}
+
+			_ignore_restrictions = false;
+			_generating_world = false;
+		} else {
+			success = DoCommandP(tile, (InteractiveRandomRange(indsp->num_table) << 16) | this->selected_type, seed, NULL, CMD_BUILD_INDUSTRY | CMD_MSG(STR_4830_CAN_T_CONSTRUCT_THIS_INDUSTRY));
+		}
+
+		/* If an industry has been built, just reset the cursor and the system */
+		if (success) ResetObjectToPlace();
+	}
+
+	virtual void OnTick()
+	{
+		if (_pause_game != 0) return;
+		if (!this->timer_enabled) return;
+		if (--this->callback_timer == 0) {
+			/* We have just passed another day.
+			 * See if we need to update availability of currently selected industry */
+			this->callback_timer = DAY_TICKS;  //restart counter
+
+			const IndustrySpec *indsp = GetIndustrySpec(this->selected_type);
+
+			if (indsp->enabled) {
+				bool call_back_result = CheckIfCallBackAllowsAvailability(this->selected_type, IACT_USERCREATION);
+
+				/* Only if result does match the previous state would it require a redraw. */
+				if (call_back_result != this->enabled[this->selected_index]) {
+					this->enabled[this->selected_index] = call_back_result;
+					this->SetDirty();
+				}
+			}
+		}
+	}
+
+	virtual void OnTimeout()
+	{
+		this->RaiseButtons();
+	}
+
+	virtual void OnPlaceObjectAbort()
+	{
+		this->RaiseButtons();
+	}
 };
 
 void ShowBuildIndustryWindow()
 {
 	if (_game_mode != GM_EDITOR && !IsValidPlayer(_current_player)) return;
-	AllocateWindowDescFront(&_build_industry_dynamic_desc, 0);
+	if (BringWindowToFrontById(WC_BUILD_INDUSTRY, 0)) return;
+	new BuildIndustryWindow();
 }
 
 static void UpdateIndustryProduction(Industry *i);
@@ -426,182 +429,204 @@
 	IVW_INFO,
 	IVW_GOTO,
 	IVW_SPACER,
+	IVW_RESIZE,
 };
 
-/** Information to store about the industry window */
-struct indview_d : public vp_d {
+class IndustryViewWindow : public Window
+{
 	byte editbox_line;        ///< The line clicked to open the edit box
 	byte clicked_line;        ///< The line of the button that has been clicked
 	byte clicked_button;      ///< The button that has been clicked (to raise)
 	byte production_offset_y; ///< The offset of the production texts/buttons
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(indview_d));
-
-
-static void IndustryViewWndProc(Window *w, WindowEvent *e)
-{
-	switch (e->event) {
-		case WE_PAINT: {
-			Industry *i = GetIndustry(w->window_number);
-			const IndustrySpec *ind = GetIndustrySpec(i->type);
-			int y = w->widget[IVW_INFO].top + 1;
-			bool first = true;
-			bool has_accept = false;
-
-			SetDParam(0, w->window_number);
-			DrawWindowWidgets(w);
 
-			if (HasBit(ind->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_flags, CBM_IND_PRODUCTION_256_TICKS)) {
-				for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
-					if (i->accepts_cargo[j] == CT_INVALID) continue;
-					has_accept = true;
-					if (first) {
-						DrawString(2, y, STR_INDUSTRY_WINDOW_WAITING_FOR_PROCESSING, TC_FROMSTRING);
-						y += 10;
-						first = false;
-					}
-					SetDParam(0, i->accepts_cargo[j]);
-					SetDParam(1, i->incoming_cargo_waiting[j]);
-					SetDParam(2, GetCargoSuffix(j, CST_VIEW, i, i->type, ind));
-					DrawString(4, y, STR_INDUSTRY_WINDOW_WAITING_STOCKPILE_CARGO, TC_FROMSTRING);
+public:
+	IndustryViewWindow(const WindowDesc *desc, void *data, WindowNumber window_number) : Window(desc, data, window_number)
+	{
+		this->flags4 |= WF_DISABLE_VP_SCROLL;
+		this->editbox_line = 0;
+		this->clicked_line = 0;
+		this->clicked_button = 0;
+		InitializeWindowViewport(this, 3, 17, 254, 86, GetIndustry(window_number)->xy + TileDiffXY(1, 1), ZOOM_LVL_INDUSTRY);
+		this->FindWindowPlacementAndResize(desc);
+	}
+
+	virtual void OnPaint()
+	{
+		Industry *i = GetIndustry(this->window_number);
+		const IndustrySpec *ind = GetIndustrySpec(i->type);
+		int y = this->widget[IVW_INFO].top + 1;
+		bool first = true;
+		bool has_accept = false;
+
+		SetDParam(0, this->window_number);
+		DrawWindowWidgets(this);
+
+		if (HasBit(ind->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(ind->callback_flags, CBM_IND_PRODUCTION_256_TICKS)) {
+			for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
+				if (i->accepts_cargo[j] == CT_INVALID) continue;
+				has_accept = true;
+				if (first) {
+					DrawStringTruncated(2, y, STR_INDUSTRY_WINDOW_WAITING_FOR_PROCESSING, TC_FROMSTRING, this->widget[IVW_INFO].right - 2);
 					y += 10;
-				}
-			} else {
-				StringID str = STR_4827_REQUIRES;
-				byte p = 0;
-				for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
-					if (i->accepts_cargo[j] == CT_INVALID) continue;
-					has_accept = true;
-					if (p > 0) str++;
-					SetDParam(p++, GetCargo(i->accepts_cargo[j])->name);
-					SetDParam(p++, GetCargoSuffix(j, CST_VIEW, i, i->type, ind));
-				}
-				if (has_accept) {
-					DrawString(2, y, str, TC_FROMSTRING);
-					y += 10;
-				}
-			}
-
-			first = true;
-			for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
-				if (i->produced_cargo[j] == CT_INVALID) continue;
-				if (first) {
-					if (has_accept) y += 10;
-					DrawString(2, y, STR_482A_PRODUCTION_LAST_MONTH, TC_FROMSTRING);
-					y += 10;
-					WP(w, indview_d).production_offset_y = y;
 					first = false;
 				}
-
-				SetDParam(0, i->produced_cargo[j]);
-				SetDParam(1, i->last_month_production[j]);
-				SetDParam(2, GetCargoSuffix(j + 3, CST_VIEW, i, i->type, ind));
+				SetDParam(0, i->accepts_cargo[j]);
+				SetDParam(1, i->incoming_cargo_waiting[j]);
+				SetDParam(2, GetCargoSuffix(j, CST_VIEW, i, i->type, ind));
+				DrawStringTruncated(4, y, STR_INDUSTRY_WINDOW_WAITING_STOCKPILE_CARGO, TC_FROMSTRING, this->widget[IVW_INFO].right - 4);
+				y += 10;
+			}
+		} else {
+			StringID str = STR_4827_REQUIRES;
+			byte p = 0;
+			for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
+				if (i->accepts_cargo[j] == CT_INVALID) continue;
+				has_accept = true;
+				if (p > 0) str++;
+				SetDParam(p++, GetCargo(i->accepts_cargo[j])->name);
+				SetDParam(p++, GetCargoSuffix(j, CST_VIEW, i, i->type, ind));
+			}
+			if (has_accept) {
+				DrawStringTruncated(2, y, str, TC_FROMSTRING, this->widget[IVW_INFO].right - 2);
+				y += 10;
+			}
+		}
 
-				SetDParam(3, i->last_month_pct_transported[j] * 100 >> 8);
-				DrawString(4 + (IsProductionAlterable(i) ? 30 : 0), y, STR_482B_TRANSPORTED, TC_FROMSTRING);
-				/* Let's put out those buttons.. */
-				if (IsProductionAlterable(i)) {
-					DrawArrowButtons(5, y, 3, (WP(w, indview_d).clicked_line == j + 1) ? WP(w, indview_d).clicked_button : 0,
-							!IsProductionMinimum(i, j), !IsProductionMaximum(i, j));
-				}
+		first = true;
+		for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
+			if (i->produced_cargo[j] == CT_INVALID) continue;
+			if (first) {
+				if (has_accept) y += 10;
+				DrawStringTruncated(2, y, STR_482A_PRODUCTION_LAST_MONTH, TC_FROMSTRING, this->widget[IVW_INFO].right - 2);
 				y += 10;
+				this->production_offset_y = y;
+				first = false;
 			}
 
-			/* Get the extra message for the GUI */
-			if (HasBit(ind->callback_flags, CBM_IND_WINDOW_MORE_TEXT)) {
-				uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->xy);
-				if (callback_res != CALLBACK_FAILED) {
-					StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
-					if (message != STR_NULL && message != STR_UNDEFINED) {
-						const Widget *wi = &w->widget[IVW_INFO];
-						y += 10;
+			SetDParam(0, i->produced_cargo[j]);
+			SetDParam(1, i->last_month_production[j]);
+			SetDParam(2, GetCargoSuffix(j + 3, CST_VIEW, i, i->type, ind));
 
-						PrepareTextRefStackUsage(6);
-						/* Use all the available space left from where we stand up to the end of the window */
-						y += DrawStringMultiLine(2, y, message, wi->right - wi->left - 4, -1);
-						StopTextRefStackUsage();
-					}
+			SetDParam(3, i->last_month_pct_transported[j] * 100 >> 8);
+			uint x = 4 + (IsProductionAlterable(i) ? 30 : 0);
+			DrawStringTruncated(x, y, STR_482B_TRANSPORTED, TC_FROMSTRING, this->widget[IVW_INFO].right - x);
+			/* Let's put out those buttons.. */
+			if (IsProductionAlterable(i)) {
+				DrawArrowButtons(5, y, 3, (this->clicked_line == j + 1) ? this->clicked_button : 0,
+						!IsProductionMinimum(i, j), !IsProductionMaximum(i, j));
+			}
+			y += 10;
+		}
+
+		/* Get the extra message for the GUI */
+		if (HasBit(ind->callback_flags, CBM_IND_WINDOW_MORE_TEXT)) {
+			uint16 callback_res = GetIndustryCallback(CBID_INDUSTRY_WINDOW_MORE_TEXT, 0, 0, i, i->type, i->xy);
+			if (callback_res != CALLBACK_FAILED) {
+				StringID message = GetGRFStringID(ind->grf_prop.grffile->grfid, 0xD000 + callback_res);
+				if (message != STR_NULL && message != STR_UNDEFINED) {
+					const Widget *wi = &this->widget[IVW_INFO];
+					y += 10;
+
+					PrepareTextRefStackUsage(6);
+					/* Use all the available space left from where we stand up to the end of the window */
+					y += DrawStringMultiLine(2, y, message, wi->right - wi->left - 4, -1);
+					StopTextRefStackUsage();
 				}
 			}
-
-			if (y > w->widget[IVW_INFO].bottom) {
-				w->SetDirty();
-				ResizeWindowForWidget(w, IVW_INFO, 0, y - w->widget[IVW_INFO].top);
-				w->SetDirty();
-				return;
-			}
-
-			DrawWindowViewport(w);
-		} break;
-
-		case WE_CLICK: {
-			Industry *i;
-
-			switch (e->we.click.widget) {
-				case IVW_INFO: {
-					int line, x;
-
-					i = GetIndustry(w->window_number);
-
-					/* We should work if needed.. */
-					if (!IsProductionAlterable(i)) return;
-					x = e->we.click.pt.x;
-					line = (e->we.click.pt.y - WP(w, indview_d).production_offset_y) / 10;
-					if (e->we.click.pt.y >= WP(w, indview_d).production_offset_y && IsInsideMM(line, 0, 2) && i->produced_cargo[line] != CT_INVALID) {
-						if (IsInsideMM(x, 5, 25) ) {
-							/* Clicked buttons, decrease or increase production */
-							if (x < 15) {
-								if (IsProductionMinimum(i, line)) return;
-								i->production_rate[line] = max(i->production_rate[line] / 2, 0);
-							} else {
-								/* a zero production industry is unlikely to give anything but zero, so push it a little bit */
-								int new_prod = i->production_rate[line] == 0 ? 1 : i->production_rate[line] * 2;
-								if (IsProductionMaximum(i, line)) return;
-								i->production_rate[line] = minu(new_prod, 255);
-							}
+		}
 
-							UpdateIndustryProduction(i);
-							w->SetDirty();
-							w->flags4 |= 5 << WF_TIMEOUT_SHL;
-							WP(w, indview_d).clicked_line = line + 1;
-							WP(w, indview_d).clicked_button = (x < 15 ? 1 : 2);
-						} else if (IsInsideMM(x, 34, 160)) {
-							/* clicked the text */
-							WP(w, indview_d).editbox_line = line;
-							SetDParam(0, i->production_rate[line] * 8);
-							ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_GAME_PRODUCTION, 10, 100, w, CS_ALPHANUMERAL);
+		if (y > this->widget[IVW_INFO].bottom) {
+			this->SetDirty();
+			ResizeWindowForWidget(this, IVW_INFO, 0, y - this->widget[IVW_INFO].top);
+			this->SetDirty();
+			return;
+		}
+
+		DrawWindowViewport(this);
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		Industry *i;
+
+		switch (widget) {
+			case IVW_INFO: {
+				int line, x;
+
+				i = GetIndustry(this->window_number);
+
+				/* We should work if needed.. */
+				if (!IsProductionAlterable(i)) return;
+				x = pt.x;
+				line = (pt.y - this->production_offset_y) / 10;
+				if (pt.y >= this->production_offset_y && IsInsideMM(line, 0, 2) && i->produced_cargo[line] != CT_INVALID) {
+					if (IsInsideMM(x, 5, 25) ) {
+						/* Clicked buttons, decrease or increase production */
+						if (x < 15) {
+							if (IsProductionMinimum(i, line)) return;
+							i->production_rate[line] = max(i->production_rate[line] / 2, 0);
+						} else {
+							/* a zero production industry is unlikely to give anything but zero, so push it a little bit */
+							int new_prod = i->production_rate[line] == 0 ? 1 : i->production_rate[line] * 2;
+							if (IsProductionMaximum(i, line)) return;
+							i->production_rate[line] = minu(new_prod, 255);
 						}
-					}
-				} break;
 
-				case IVW_GOTO:
-					i = GetIndustry(w->window_number);
-					if (_ctrl_pressed) {
-						ShowExtraViewPortWindow(i->xy + TileDiffXY(1, 1));
-					} else {
-						ScrollMainWindowToTile(i->xy + TileDiffXY(1, 1));
+						UpdateIndustryProduction(i);
+						this->SetDirty();
+						this->flags4 |= 5 << WF_TIMEOUT_SHL;
+						this->clicked_line = line + 1;
+						this->clicked_button = (x < 15 ? 1 : 2);
+					} else if (IsInsideMM(x, 34, 160)) {
+						/* clicked the text */
+						this->editbox_line = line;
+						SetDParam(0, i->production_rate[line] * 8);
+						ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_CONFIG_GAME_PRODUCTION, 10, 100, this, CS_ALPHANUMERAL);
 					}
-
+				}
 			} break;
-		} break;
-
-		case WE_TIMEOUT:
-			WP(w, indview_d).clicked_line = 0;
-			WP(w, indview_d).clicked_button = 0;
-			w->SetDirty();
-			break;
 
-		case WE_ON_EDIT_TEXT:
-			if (e->we.edittext.str[0] != '\0') {
-				Industry* i = GetIndustry(w->window_number);
-				int line = WP(w, indview_d).editbox_line;
+			case IVW_GOTO:
+				i = GetIndustry(this->window_number);
+				if (_ctrl_pressed) {
+					ShowExtraViewPortWindow(i->xy + TileDiffXY(1, 1));
+				} else {
+					ScrollMainWindowToTile(i->xy + TileDiffXY(1, 1));
+				}
+				break;
+		}
+	}
 
-				i->production_rate[line] = ClampU(atoi(e->we.edittext.str), 0, 255);
-				UpdateIndustryProduction(i);
-				w->SetDirty();
-			}
+	virtual void OnTimeout()
+	{
+		this->clicked_line = 0;
+		this->clicked_button = 0;
+		this->SetDirty();
 	}
-}
+
+	virtual void OnResize(Point new_size, Point delta)
+	{
+		this->viewport->width            += delta.x;
+		this->viewport->height           += delta.y;
+		this->viewport->virtual_width    += delta.x;
+		this->viewport->virtual_height   += delta.y;
+		this->viewport->dest_scrollpos_x -= delta.x;
+		this->viewport->dest_scrollpos_y -= delta.y;
+		UpdateViewportPosition(this);
+	}
+
+	virtual void OnQueryTextFinished(char *str)
+	{
+		if (StrEmpty(str)) return;
+
+		Industry* i = GetIndustry(this->window_number);
+		int line = this->editbox_line;
+
+		i->production_rate[line] = ClampU(atoi(str), 0, 255);
+		UpdateIndustryProduction(i);
+		this->SetDirty();
+	}
+};
 
 static void UpdateIndustryProduction(Industry *i)
 {
@@ -615,13 +640,14 @@
 /** Widget definition of the view industy gui */
 static const Widget _industry_view_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,     9,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},            // IVW_CLOSEBOX
-{    WWT_CAPTION,   RESIZE_NONE,     9,    11,   247,     0,    13, STR_4801,          STR_018C_WINDOW_TITLE_DRAG_THIS},  // IVW_CAPTION
-{  WWT_STICKYBOX,   RESIZE_NONE,     9,   248,   259,     0,    13, 0x0,               STR_STICKY_BUTTON},                // IVW_STICKY
-{      WWT_PANEL,   RESIZE_NONE,     9,     0,   259,    14,   105, 0x0,               STR_NULL},                         // IVW_BACKGROUND
-{      WWT_INSET,   RESIZE_NONE,     9,     2,   257,    16,   103, 0x0,               STR_NULL},                         // IVW_VIEWPORT
-{      WWT_PANEL, RESIZE_BOTTOM,     9,     0,   259,   106,   107, 0x0,               STR_NULL},                         // IVW_INFO
+{    WWT_CAPTION,  RESIZE_RIGHT,     9,    11,   247,     0,    13, STR_4801,          STR_018C_WINDOW_TITLE_DRAG_THIS},  // IVW_CAPTION
+{  WWT_STICKYBOX,     RESIZE_LR,     9,   248,   259,     0,    13, 0x0,               STR_STICKY_BUTTON},                // IVW_STICKY
+{      WWT_PANEL,     RESIZE_RB,     9,     0,   259,    14,   105, 0x0,               STR_NULL},                         // IVW_BACKGROUND
+{      WWT_INSET,     RESIZE_RB,     9,     2,   257,    16,   103, 0x0,               STR_NULL},                         // IVW_VIEWPORT
+{      WWT_PANEL,    RESIZE_RTB,     9,     0,   259,   106,   107, 0x0,               STR_NULL},                         // IVW_INFO
 { WWT_PUSHTXTBTN,     RESIZE_TB,     9,     0,   129,   108,   119, STR_00E4_LOCATION, STR_482C_CENTER_THE_MAIN_VIEW_ON}, // IVW_GOTO
-{      WWT_PANEL,     RESIZE_TB,     9,   130,   259,   108,   119, 0x0,               STR_NULL},                         // IVW_SPACER
+{      WWT_PANEL,    RESIZE_RTB,     9,   130,   247,   108,   119, 0x0,               STR_NULL},                         // IVW_SPACER
+{  WWT_RESIZEBOX,   RESIZE_LRTB,     9,   248,   259,   108,   119, 0x0,               STR_RESIZE_BUTTON},                // IVW_RESIZE
 {   WIDGETS_END},
 };
 
@@ -629,22 +655,14 @@
 static const WindowDesc _industry_view_desc = {
 	WDP_AUTO, WDP_AUTO, 260, 120, 260, 120,
 	WC_INDUSTRY_VIEW, WC_NONE,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_industry_view_widgets,
-	IndustryViewWndProc
+	NULL
 };
 
 void ShowIndustryViewWindow(int industry)
 {
-	Window *w = AllocateWindowDescFront(&_industry_view_desc, industry);
-
-	if (w != NULL) {
-		w->flags4 |= WF_DISABLE_VP_SCROLL;
-		WP(w, indview_d).editbox_line = 0;
-		WP(w, indview_d).clicked_line = 0;
-		WP(w, indview_d).clicked_button = 0;
-		InitializeWindowViewport(w, 3, 17, 254, 86, GetIndustry(w->window_number)->xy + TileDiffXY(1, 1), ZOOM_LVL_INDUSTRY);
-	}
+	AllocateWindowDescFront<IndustryViewWindow>(&_industry_view_desc, industry);
 }
 
 /** Names of the widgets of the industry directory gui */
@@ -665,16 +683,16 @@
 /** Widget definition of the industy directory gui */
 static const Widget _industry_directory_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,    13,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW},             // IDW_CLOSEBOX
-{    WWT_CAPTION,   RESIZE_NONE,    13,    11,   495,     0,    13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},   // IDW_CAPTION
-{  WWT_STICKYBOX,   RESIZE_NONE,    13,   496,   507,     0,    13, 0x0,                     STR_STICKY_BUTTON},                 // IDW_STICKY
+{    WWT_CAPTION,  RESIZE_RIGHT,    13,    11,   495,     0,    13, STR_INDUSTRYDIR_CAPTION, STR_018C_WINDOW_TITLE_DRAG_THIS},   // IDW_CAPTION
+{  WWT_STICKYBOX,     RESIZE_LR,    13,   496,   507,     0,    13, 0x0,                     STR_STICKY_BUTTON},                 // IDW_STICKY
 { WWT_PUSHTXTBTN,   RESIZE_NONE,    13,     0,   100,    14,    25, STR_SORT_BY_NAME,        STR_SORT_ORDER_TIP},                // IDW_SORTBYNAME
 { WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   101,   200,    14,    25, STR_SORT_BY_TYPE,        STR_SORT_ORDER_TIP},                // IDW_SORTBYTYPE
 { WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   201,   300,    14,    25, STR_SORT_BY_PRODUCTION,  STR_SORT_ORDER_TIP},                // IDW_SORTBYPROD
 { WWT_PUSHTXTBTN,   RESIZE_NONE,    13,   301,   400,    14,    25, STR_SORT_BY_TRANSPORTED, STR_SORT_ORDER_TIP},                // IDW_SORTBYTRANSPORT
-{      WWT_PANEL,   RESIZE_NONE,    13,   401,   495,    14,    25, 0x0,                     STR_NULL},                          // IDW_SPACER
-{      WWT_PANEL, RESIZE_BOTTOM,    13,     0,   495,    26,   189, 0x0,                     STR_200A_TOWN_NAMES_CLICK_ON_NAME}, // IDW_INDUSRTY_LIST
-{  WWT_SCROLLBAR, RESIZE_BOTTOM,    13,   496,   507,    14,   177, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST},  // IDW_SCROLLBAR
-{  WWT_RESIZEBOX,     RESIZE_TB,    13,   496,   507,   178,   189, 0x0,                     STR_RESIZE_BUTTON},                 // IDW_RESIZE
+{      WWT_PANEL,  RESIZE_RIGHT,    13,   401,   495,    14,    25, 0x0,                     STR_NULL},                          // IDW_SPACER
+{      WWT_PANEL,     RESIZE_RB,    13,     0,   495,    26,   189, 0x0,                     STR_200A_TOWN_NAMES_CLICK_ON_NAME}, // IDW_INDUSRTY_LIST
+{  WWT_SCROLLBAR,    RESIZE_LRB,    13,   496,   507,    14,   177, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST},  // IDW_SCROLLBAR
+{  WWT_RESIZEBOX,   RESIZE_LRTB,    13,   496,   507,   178,   189, 0x0,                     STR_RESIZE_BUTTON},                 // IDW_RESIZE
 {   WIDGETS_END},
 };
 
@@ -831,7 +849,7 @@
 				/* Drawing the right string */
 				StringID str = STR_INDUSTRYDIR_ITEM_NOPROD;
 				if (p != 1) str = (p == 5) ? STR_INDUSTRYDIR_ITEM : STR_INDUSTRYDIR_ITEM_TWO;
-				DrawString(4, 28 + n * 10, str, TC_FROMSTRING);
+				DrawStringTruncated(4, 28 + n * 10, str, TC_FROMSTRING, w->widget[IDW_INDUSRTY_LIST].right - 4);
 
 				pos++;
 				if (++n == w->vscroll.cap) break;
@@ -881,7 +899,7 @@
 			}
 			break;
 
-		case WE_4:
+		case WE_100_TICKS:
 			w->SetDirty();
 			break;
 
@@ -902,7 +920,7 @@
 
 void ShowIndustryDirectory()
 {
-	Window *w = AllocateWindowDescFront(&_industry_directory_desc, 0);
+	Window *w = AllocateWindowDescFront<Window>(&_industry_directory_desc, 0);
 
 	if (w != NULL) {
 		w->vscroll.cap = 16;
--- a/src/intro_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/intro_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -133,7 +133,7 @@
 
 void ShowSelectGameWindow()
 {
-	AllocateWindowDesc(&_select_game_desc);
+	new Window(&_select_game_desc);
 }
 
 static void AskExitGameCallback(Window *w, bool confirmed)
--- a/src/main_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/main_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -167,31 +167,31 @@
 			vp->virtual_width >>= 1;
 			vp->virtual_height >>= 1;
 
-			WP(w, vp_d).scrollpos_x += vp->virtual_width >> 1;
-			WP(w, vp_d).scrollpos_y += vp->virtual_height >> 1;
-			WP(w, vp_d).dest_scrollpos_x = WP(w,vp_d).scrollpos_x;
-			WP(w, vp_d).dest_scrollpos_y = WP(w,vp_d).scrollpos_y;
+			w->viewport->scrollpos_x += vp->virtual_width >> 1;
+			w->viewport->scrollpos_y += vp->virtual_height >> 1;
+			w->viewport->dest_scrollpos_x = w->viewport->scrollpos_x;
+			w->viewport->dest_scrollpos_y = w->viewport->scrollpos_y;
 			break;
 		case ZOOM_OUT:
 			if (vp->zoom == ZOOM_LVL_MAX) return false;
 			vp->zoom = (ZoomLevel)((int)vp->zoom + 1);
 
-			WP(w, vp_d).scrollpos_x -= vp->virtual_width >> 1;
-			WP(w, vp_d).scrollpos_y -= vp->virtual_height >> 1;
-			WP(w, vp_d).dest_scrollpos_x = WP(w,vp_d).scrollpos_x;
-			WP(w, vp_d).dest_scrollpos_y = WP(w,vp_d).scrollpos_y;
+			w->viewport->scrollpos_x -= vp->virtual_width >> 1;
+			w->viewport->scrollpos_y -= vp->virtual_height >> 1;
+			w->viewport->dest_scrollpos_x = w->viewport->scrollpos_x;
+			w->viewport->dest_scrollpos_y = w->viewport->scrollpos_y;
 
 			vp->virtual_width <<= 1;
 			vp->virtual_height <<= 1;
 			break;
 	}
 	if (vp != NULL) { // the vp can be null when how == ZOOM_NONE
-		vp->virtual_left = WP(w, vp_d).scrollpos_x;
-		vp->virtual_top = WP(w, vp_d).scrollpos_y;
+		vp->virtual_left = w->viewport->scrollpos_x;
+		vp->virtual_top = w->viewport->scrollpos_y;
 	}
 	w->SetDirty();
 	/* Update the windows that have zoom-buttons to perhaps disable their buttons */
-	SendWindowMessageClass(w->window_class, how, w->window_number, 0);
+	InvalidateThisWindowData(w);
 	return true;
 }
 
@@ -381,19 +381,19 @@
 				_scrolling_viewport = false;
 			}
 
-			WP(w, vp_d).scrollpos_x += ScaleByZoom(e->we.scroll.delta.x, vp->zoom);
-			WP(w, vp_d).scrollpos_y += ScaleByZoom(e->we.scroll.delta.y, vp->zoom);
-			WP(w, vp_d).dest_scrollpos_x = WP(w, vp_d).scrollpos_x;
-			WP(w, vp_d).dest_scrollpos_y = WP(w, vp_d).scrollpos_y;
+			w->viewport->scrollpos_x += ScaleByZoom(e->we.scroll.delta.x, vp->zoom);
+			w->viewport->scrollpos_y += ScaleByZoom(e->we.scroll.delta.y, vp->zoom);
+			w->viewport->dest_scrollpos_x = w->viewport->scrollpos_x;
+			w->viewport->dest_scrollpos_y = w->viewport->scrollpos_y;
 		} break;
 
 		case WE_MOUSEWHEEL:
 			ZoomInOrOutToCursorWindow(e->we.wheel.wheel < 0, w);
 			break;
 
-		case WE_MESSAGE:
+		case WE_INVALIDATE_DATA:
 			/* Forward the message to the appropiate toolbar (ingame or scenario editor) */
-			SendWindowMessage(WC_MAIN_TOOLBAR, 0, e->we.message.msg, e->we.message.wparam, e->we.message.lparam);
+			InvalidateWindowData(WC_MAIN_TOOLBAR, 0, e->we.invalidate.data);
 			break;
 	}
 }
@@ -413,7 +413,7 @@
 	int width = _screen.width;
 	int height = _screen.height;
 
-	Window *w = AllocateWindow(0, 0, width, height, MainWindowWndProc, WC_MAIN_WINDOW, NULL);
+	Window *w = new Window(0, 0, width, height, MainWindowWndProc, WC_MAIN_WINDOW, NULL);
 	InitializeWindowViewport(w, 0, 0, width, height, TileXY(32, 32), ZOOM_LVL_VIEWPORT);
 
 	/* XXX: these are not done */
--- a/src/minilzo.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/minilzo.cpp	Sun May 11 20:09:34 2008 +0000
@@ -1029,7 +1029,7 @@
 		{
 		register const lzo_byte *m_pos;
 		lzo_moff_t m_off;
-		lzo_uint m_len;
+		lzo_ptrdiff_t m_len;
 		lzo_uint dindex;
 
 		DINDEX1(dindex,ip);
--- a/src/misc.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/misc.cpp	Sun May 11 20:09:34 2008 +0000
@@ -95,7 +95,6 @@
 	InitializeIndustries();
 	InitializeBuildingCounts();
 
-	InitializeVehiclesGuiList();
 	InitializeTrains();
 	InitializeNPF();
 
--- a/src/misc/dbg_helpers.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/misc/dbg_helpers.cpp	Sun May 11 20:09:34 2008 +0000
@@ -5,6 +5,7 @@
 #include "../stdafx.h"
 #include "../openttd.h"
 #include "../direction_type.h"
+#include "../strings_type.h"
 #include "../rail.h"
 #include "../rail_map.h"
 #include "dbg_helpers.h"
--- a/src/misc_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/misc_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -44,10 +44,10 @@
 #include "newgrf_cargo.h"
 #include "rail_gui.h"
 #include "tilehighlight_func.h"
+#include "querystring_gui.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
-#include "table/tree_land.h"
 
 /* Variables to display file lists */
 FiosItem *_fios_list;
@@ -57,28 +57,6 @@
 static bool _fios_path_changed;
 static bool _savegame_sort_dirty;
 
-enum {
-	LAND_INFO_LINES          =   7,
-	LAND_INFO_LINE_BUFF_SIZE = 512,
-};
-
-static char _landinfo_data[LAND_INFO_LINES][LAND_INFO_LINE_BUFF_SIZE];
-
-static void LandInfoWndProc(Window *w, WindowEvent *e)
-{
-	if (e->event == WE_PAINT) {
-		DrawWindowWidgets(w);
-
-		DoDrawStringCentered(140, 16, _landinfo_data[0], TC_LIGHT_BLUE);
-		DoDrawStringCentered(140, 27, _landinfo_data[1], TC_FROMSTRING);
-		DoDrawStringCentered(140, 38, _landinfo_data[2], TC_FROMSTRING);
-		DoDrawStringCentered(140, 49, _landinfo_data[3], TC_FROMSTRING);
-		DoDrawStringCentered(140, 60, _landinfo_data[4], TC_FROMSTRING);
-		if (_landinfo_data[5][0] != '\0') DrawStringMultiCenter(140, 76, BindCString(_landinfo_data[5]), w->width - 4);
-		if (_landinfo_data[6][0] != '\0') DoDrawStringCentered(140, 71, _landinfo_data[6], TC_FROMSTRING);
-	}
-}
-
 static const Widget _land_info_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   279,     0,    13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
@@ -91,106 +69,131 @@
 	WC_LAND_INFO, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 	_land_info_widgets,
-	LandInfoWndProc
+	NULL
 };
 
-static void Place_LandInfo(TileIndex tile)
-{
-	DeleteWindowById(WC_LAND_INFO, 0);
-
-	Window *w = AllocateWindowDesc(&_land_info_desc);
-	WP(w, void_d).data = &_landinfo_data;
-
-	Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST);
-	Town *t = ClosestTownFromTile(tile, _patches.dist_local_authority);
-
-	Money old_money = p->player_money;
-	p->player_money = INT64_MAX;
-	CommandCost costclear = DoCommand(tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR);
-	p->player_money = old_money;
+class LandInfoWindow : public Window {
+	enum {
+		LAND_INFO_LINES          =   7,
+		LAND_INFO_LINE_BUFF_SIZE = 512,
+	};
 
-	/* Because build_date is not set yet in every TileDesc, we make sure it is empty */
-	TileDesc td;
-	AcceptedCargo ac;
-
-	td.build_date = 0;
-	GetAcceptedCargo(tile, ac);
-	GetTileDesc(tile, &td);
+public:
+	char landinfo_data[LAND_INFO_LINES][LAND_INFO_LINE_BUFF_SIZE];
 
-	SetDParam(0, td.dparam[0]);
-	GetString(_landinfo_data[0], td.str, lastof(_landinfo_data[0]));
+	virtual void OnPaint()
+	{
+		DrawWindowWidgets(this);
 
-	SetDParam(0, STR_01A6_N_A);
-	if (td.owner != OWNER_NONE && td.owner != OWNER_WATER) GetNameOfOwner(td.owner, tile);
-	GetString(_landinfo_data[1], STR_01A7_OWNER, lastof(_landinfo_data[1]));
-
-	StringID str = STR_01A4_COST_TO_CLEAR_N_A;
-	if (CmdSucceeded(costclear)) {
-		SetDParam(0, costclear.GetCost());
-		str = STR_01A5_COST_TO_CLEAR;
+		DoDrawStringCentered(140, 16, this->landinfo_data[0], TC_LIGHT_BLUE);
+		DoDrawStringCentered(140, 27, this->landinfo_data[1], TC_FROMSTRING);
+		DoDrawStringCentered(140, 38, this->landinfo_data[2], TC_FROMSTRING);
+		DoDrawStringCentered(140, 49, this->landinfo_data[3], TC_FROMSTRING);
+		DoDrawStringCentered(140, 60, this->landinfo_data[4], TC_FROMSTRING);
+		if (!StrEmpty(this->landinfo_data[5])) DrawStringMultiCenter(140, 76, BindCString(this->landinfo_data[5]), this->width - 4);
+		if (!StrEmpty(this->landinfo_data[6])) DoDrawStringCentered(140, 71, this->landinfo_data[6], TC_FROMSTRING);
 	}
-	GetString(_landinfo_data[2], str, lastof(_landinfo_data[2]));
-
-	snprintf(_userstring, lengthof(_userstring), "0x%.4X", tile);
-	SetDParam(0, TileX(tile));
-	SetDParam(1, TileY(tile));
-	SetDParam(2, TileHeight(tile));
-	SetDParam(3, STR_SPEC_USERSTRING);
-	GetString(_landinfo_data[3], STR_LANDINFO_COORDS, lastof(_landinfo_data[3]));
-
-	SetDParam(0, STR_01A9_NONE);
-	if (t != NULL && t->IsValid()) {
-		SetDParam(0, STR_TOWN);
-		SetDParam(1, t->index);
-	}
-	GetString(_landinfo_data[4], STR_01A8_LOCAL_AUTHORITY, lastof(_landinfo_data[4]));
 
-	char *strp = GetString(_landinfo_data[5], STR_01CE_CARGO_ACCEPTED, lastof(_landinfo_data[5]));
-	bool found = false;
+	LandInfoWindow(TileIndex tile) : Window(&_land_info_desc) {
+		Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST);
+		Town *t = ClosestTownFromTile(tile, _patches.dist_local_authority);
 
-	for (CargoID i = 0; i < NUM_CARGO; ++i) {
-		if (ac[i] > 0) {
-			/* Add a comma between each item. */
-			if (found) {
-				*strp++ = ',';
-				*strp++ = ' ';
-			}
-			found = true;
+		Money old_money = p->player_money;
+		p->player_money = INT64_MAX;
+		CommandCost costclear = DoCommand(tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR);
+		p->player_money = old_money;
 
-			/* If the accepted value is less than 8, show it in 1/8:ths */
-			if (ac[i] < 8) {
-				SetDParam(0, ac[i]);
-				SetDParam(1, GetCargo(i)->name);
-				strp = GetString(strp, STR_01D1_8, lastof(_landinfo_data[5]));
-			} else {
-				strp = GetString(strp, GetCargo(i)->name, lastof(_landinfo_data[5]));
+		/* Because build_date is not set yet in every TileDesc, we make sure it is empty */
+		TileDesc td;
+		AcceptedCargo ac;
+
+		td.build_date = 0;
+		GetAcceptedCargo(tile, ac);
+		GetTileDesc(tile, &td);
+
+		SetDParam(0, td.dparam[0]);
+		GetString(this->landinfo_data[0], td.str, lastof(this->landinfo_data[0]));
+
+		SetDParam(0, STR_01A6_N_A);
+		if (td.owner != OWNER_NONE && td.owner != OWNER_WATER) GetNameOfOwner(td.owner, tile);
+		GetString(this->landinfo_data[1], STR_01A7_OWNER, lastof(this->landinfo_data[1]));
+
+		StringID str = STR_01A4_COST_TO_CLEAR_N_A;
+		if (CmdSucceeded(costclear)) {
+			SetDParam(0, costclear.GetCost());
+			str = STR_01A5_COST_TO_CLEAR;
+		}
+		GetString(this->landinfo_data[2], str, lastof(this->landinfo_data[2]));
+
+		snprintf(_userstring, lengthof(_userstring), "0x%.4X", tile);
+		SetDParam(0, TileX(tile));
+		SetDParam(1, TileY(tile));
+		SetDParam(2, TileHeight(tile));
+		SetDParam(3, STR_SPEC_USERSTRING);
+		GetString(this->landinfo_data[3], STR_LANDINFO_COORDS, lastof(this->landinfo_data[3]));
+
+		SetDParam(0, STR_01A9_NONE);
+		if (t != NULL && t->IsValid()) {
+			SetDParam(0, STR_TOWN);
+			SetDParam(1, t->index);
+		}
+		GetString(this->landinfo_data[4], STR_01A8_LOCAL_AUTHORITY, lastof(this->landinfo_data[4]));
+
+		char *strp = GetString(this->landinfo_data[5], STR_01CE_CARGO_ACCEPTED, lastof(this->landinfo_data[5]));
+		bool found = false;
+
+		for (CargoID i = 0; i < NUM_CARGO; ++i) {
+			if (ac[i] > 0) {
+				/* Add a comma between each item. */
+				if (found) {
+					*strp++ = ',';
+					*strp++ = ' ';
+				}
+				found = true;
+
+				/* If the accepted value is less than 8, show it in 1/8:ths */
+				if (ac[i] < 8) {
+					SetDParam(0, ac[i]);
+					SetDParam(1, GetCargo(i)->name);
+					strp = GetString(strp, STR_01D1_8, lastof(this->landinfo_data[5]));
+				} else {
+					strp = GetString(strp, GetCargo(i)->name, lastof(this->landinfo_data[5]));
+				}
 			}
 		}
-	}
-	if (!found) _landinfo_data[5][0] = '\0';
+		if (!found) this->landinfo_data[5][0] = '\0';
 
-	if (td.build_date != 0) {
-		SetDParam(0, td.build_date);
-		GetString(_landinfo_data[6], STR_BUILD_DATE, lastof(_landinfo_data[6]));
-	} else {
-		_landinfo_data[6][0] = '\0';
-	}
+		if (td.build_date != 0) {
+			SetDParam(0, td.build_date);
+			GetString(this->landinfo_data[6], STR_BUILD_DATE, lastof(this->landinfo_data[6]));
+		} else {
+			this->landinfo_data[6][0] = '\0';
+		}
 
 #if defined(_DEBUG)
 #	define LANDINFOD_LEVEL 0
 #else
 #	define LANDINFOD_LEVEL 1
 #endif
-	DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile));
-	DEBUG(misc, LANDINFOD_LEVEL, "type_height  = %#x", _m[tile].type_height);
-	DEBUG(misc, LANDINFOD_LEVEL, "m1           = %#x", _m[tile].m1);
-	DEBUG(misc, LANDINFOD_LEVEL, "m2           = %#x", _m[tile].m2);
-	DEBUG(misc, LANDINFOD_LEVEL, "m3           = %#x", _m[tile].m3);
-	DEBUG(misc, LANDINFOD_LEVEL, "m4           = %#x", _m[tile].m4);
-	DEBUG(misc, LANDINFOD_LEVEL, "m5           = %#x", _m[tile].m5);
-	DEBUG(misc, LANDINFOD_LEVEL, "m6           = %#x", _m[tile].m6);
-	DEBUG(misc, LANDINFOD_LEVEL, "m7           = %#x", _me[tile].m7);
+		DEBUG(misc, LANDINFOD_LEVEL, "TILE: %#x (%i,%i)", tile, TileX(tile), TileY(tile));
+		DEBUG(misc, LANDINFOD_LEVEL, "type_height  = %#x", _m[tile].type_height);
+		DEBUG(misc, LANDINFOD_LEVEL, "m1           = %#x", _m[tile].m1);
+		DEBUG(misc, LANDINFOD_LEVEL, "m2           = %#x", _m[tile].m2);
+		DEBUG(misc, LANDINFOD_LEVEL, "m3           = %#x", _m[tile].m3);
+		DEBUG(misc, LANDINFOD_LEVEL, "m4           = %#x", _m[tile].m4);
+		DEBUG(misc, LANDINFOD_LEVEL, "m5           = %#x", _m[tile].m5);
+		DEBUG(misc, LANDINFOD_LEVEL, "m6           = %#x", _m[tile].m6);
+		DEBUG(misc, LANDINFOD_LEVEL, "m7           = %#x", _me[tile].m7);
 #undef LANDINFOD_LEVEL
+
+		this->FindWindowPlacementAndResize(&_land_info_desc);
+	}
+};
+
+static void Place_LandInfo(TileIndex tile)
+{
+	DeleteWindowById(WC_LAND_INFO, 0);
+	new LandInfoWindow(tile);
 }
 
 void PlaceLandBlockInfo()
@@ -203,6 +206,12 @@
 	}
 }
 
+struct scroller_d {
+	int height;
+	uint16 counter;
+};
+assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(scroller_d));
+
 static const char *credits[] = {
 	/*************************************************************************
 	 *                      maximum length of string which fits in window   -^*/
@@ -318,176 +327,7 @@
 void ShowAboutWindow()
 {
 	DeleteWindowById(WC_GAME_OPTIONS, 0);
-	AllocateWindowDesc(&_about_desc);
-}
-
-static int _tree_to_plant;
-
-static const PalSpriteID _tree_sprites[] = {
-	{ 0x655, PAL_NONE }, { 0x663, PAL_NONE }, { 0x678, PAL_NONE }, { 0x62B, PAL_NONE },
-	{ 0x647, PAL_NONE }, { 0x639, PAL_NONE }, { 0x64E, PAL_NONE }, { 0x632, PAL_NONE },
-	{ 0x67F, PAL_NONE }, { 0x68D, PAL_NONE }, { 0x69B, PAL_NONE }, { 0x6A9, PAL_NONE },
-	{ 0x6AF, PAL_NONE }, { 0x6D2, PAL_NONE }, { 0x6D9, PAL_NONE }, { 0x6C4, PAL_NONE },
-	{ 0x6CB, PAL_NONE }, { 0x6B6, PAL_NONE }, { 0x6BD, PAL_NONE }, { 0x6E0, PAL_NONE },
-	{ 0x72E, PAL_NONE }, { 0x734, PAL_NONE }, { 0x74A, PAL_NONE }, { 0x74F, PAL_NONE },
-	{ 0x76B, PAL_NONE }, { 0x78F, PAL_NONE }, { 0x788, PAL_NONE }, { 0x77B, PAL_NONE },
-	{ 0x75F, PAL_NONE }, { 0x774, PAL_NONE }, { 0x720, PAL_NONE }, { 0x797, PAL_NONE },
-	{ 0x79E, PAL_NONE }, { 0x7A5, PALETTE_TO_GREEN }, { 0x7AC, PALETTE_TO_RED }, { 0x7B3, PAL_NONE },
-	{ 0x7BA, PAL_NONE }, { 0x7C1, PALETTE_TO_RED, }, { 0x7C8, PALETTE_TO_PALE_GREEN }, { 0x7CF, PALETTE_TO_YELLOW }, { 0x7D6, PALETTE_TO_RED }
-};
-
-static void BuildTreesWndProc(Window *w, WindowEvent *e)
-{
-	switch (e->event) {
-		case WE_CREATE:
-			ResetObjectToPlace();
-			break;
-
-		case WE_PAINT: {
-			DrawWindowWidgets(w);
-
-			int i = WP(w, tree_d).base = _tree_base_by_landscape[_opt.landscape];
-			int count = WP(w, tree_d).count = _tree_count_by_landscape[_opt.landscape];
-
-			int x = 18;
-			int y = 54;
-			do {
-				DrawSprite(_tree_sprites[i].sprite, _tree_sprites[i].pal, x, y);
-				x += 35;
-				if (!(++i & 3)) {
-					x -= 35 * 4;
-					y += 47;
-				}
-			} while (--count);
-		} break;
-
-		case WE_CLICK: {
-			int wid = e->we.click.widget;
-
-			switch (wid) {
-				case 0:
-					ResetObjectToPlace();
-					break;
-
-				case 3: case 4: case 5: case 6:
-				case 7: case 8: case 9: case 10:
-				case 11:case 12: case 13: case 14:
-					if (wid - 3 >= WP(w, tree_d).count) break;
-
-					if (HandlePlacePushButton(w, wid, SPR_CURSOR_TREE, VHM_RECT, NULL)) {
-						_tree_to_plant = WP(w, tree_d).base + wid - 3;
-					}
-					break;
-
-				case 15: // tree of random type.
-					if (HandlePlacePushButton(w, 15, SPR_CURSOR_TREE, VHM_RECT, NULL)) {
-						_tree_to_plant = -1;
-					}
-					break;
-
-				case 16: // place trees randomly over the landscape
-					w->LowerWidget(16);
-					w->flags4 |= 5 << WF_TIMEOUT_SHL;
-					SndPlayFx(SND_15_BEEP);
-					PlaceTreesRandomly();
-					MarkWholeScreenDirty();
-					break;
-			}
-		} break;
-
-		case WE_PLACE_OBJ:
-			VpStartPlaceSizing(e->we.place.tile, VPM_X_AND_Y_LIMITED, DDSP_PLANT_TREES);
-			VpSetPlaceSizingLimit(20);
-			break;
-
-		case WE_PLACE_DRAG:
-			VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method);
-			return;
-
-		case WE_PLACE_MOUSEUP:
-			if (e->we.place.pt.x != -1 && e->we.place.select_proc == DDSP_PLANT_TREES) {
-				DoCommandP(e->we.place.tile, _tree_to_plant, e->we.place.starttile, NULL,
-					CMD_PLANT_TREE | CMD_MSG(STR_2805_CAN_T_PLANT_TREE_HERE));
-			}
-			break;
-
-		case WE_TIMEOUT:
-			w->RaiseWidget(16);
-			break;
-
-		case WE_ABORT_PLACE_OBJ:
-			w->RaiseButtons();
-			break;
-	}
-}
-
-static const Widget _build_trees_widgets[] = {
-{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   142,     0,    13, STR_2802_TREES,        STR_018C_WINDOW_TITLE_DRAG_THIS},
-{      WWT_PANEL,   RESIZE_NONE,     7,     0,   142,    14,   170, 0x0,                   STR_NULL},
-{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   140,   157,   168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP},
-{    WIDGETS_END},
-};
-
-static const WindowDesc _build_trees_desc = {
-	497, 22, 143, 171, 143, 171,
-	WC_BUILD_TREES, WC_SCEN_LAND_GEN,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
-	_build_trees_widgets,
-	BuildTreesWndProc
-};
-
-static const Widget _build_trees_scen_widgets[] = {
-{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   142,     0,    13, STR_2802_TREES,        STR_018C_WINDOW_TITLE_DRAG_THIS},
-{      WWT_PANEL,   RESIZE_NONE,     7,     0,   142,    14,   183, 0x0,                   STR_NULL},
-{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
-{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   140,   157,   168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP},
-{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   140,   170,   181, STR_028A_RANDOM_TREES, STR_028B_PLANT_TREES_RANDOMLY_OVER},
-{    WIDGETS_END},
-};
-
-static const WindowDesc _build_trees_scen_desc = {
-	WDP_AUTO, WDP_AUTO, 143, 184, 143, 184,
-	WC_BUILD_TREES, WC_NONE,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
-	_build_trees_scen_widgets,
-	BuildTreesWndProc
-};
-
-
-void ShowBuildTreesToolbar()
-{
-	if (!IsValidPlayer(_current_player)) return;
-	AllocateWindowDescFront(&_build_trees_desc, 0);
-}
-
-void ShowBuildTreesScenToolbar()
-{
-	AllocateWindowDescFront(&_build_trees_scen_desc, 0);
+	new Window(&_about_desc);
 }
 
 static uint64 _errmsg_decode_params[20];
@@ -562,7 +402,7 @@
 			if (_right_button_down) delete w;
 			break;
 
-		case WE_4:
+		case WE_100_TICKS:
 			if (--_errmsg_duration == 0) delete w;
 			break;
 
@@ -615,7 +455,7 @@
 			pt.x = (_screen.width - 240) >> 1;
 			pt.y = (_screen.height - 46) >> 1;
 		}
-		w = AllocateWindow(pt.x, pt.y, 240, 46, ErrmsgWndProc, WC_ERRMSG, _errmsg_widgets);
+		w = new Window(pt.x, pt.y, 240, 46, ErrmsgWndProc, WC_ERRMSG, _errmsg_widgets);
 	} else {
 		if ((x | y) != 0) {
 			pt = RemapCoords2(x, y);
@@ -626,7 +466,7 @@
 			pt.x = (_screen.width - 334) >> 1;
 			pt.y = (_screen.height - 137) >> 1;
 		}
-		w = AllocateWindow(pt.x, pt.y, 334, 137, ErrmsgWndProc, WC_ERRMSG, _errmsg_face_widgets);
+		w = new Window(pt.x, pt.y, 334, 137, ErrmsgWndProc, WC_ERRMSG, _errmsg_face_widgets);
 	}
 
 	w->desc_flags = WDF_STD_BTN | WDF_DEF_WIDGET;
@@ -694,32 +534,50 @@
 {   WIDGETS_END},
 };
 
-
-static void TooltipsWndProc(Window *w, WindowEvent *e)
+struct TooltipsWindow : public Window
 {
-	switch (e->event) {
-		case WE_PAINT:
-			GfxFillRect(0, 0, w->width - 1, w->height - 1, 0);
-			GfxFillRect(1, 1, w->width - 2, w->height - 2, 0x44);
+	StringID string_id;
+	byte paramcount;
+	uint64 params[5];
 
-			for (uint arg = 0; arg < WP(w, tooltips_d).paramcount; arg++) {
-				SetDParam(arg, WP(w, tooltips_d).params[arg]);
-			}
-			DrawStringMultiCenter((w->width >> 1), (w->height >> 1) - 5, WP(w, tooltips_d).string_id, w->width - 2);
-			break;
+	TooltipsWindow(int x, int y, int width, int height, const Widget *widget,
+								 StringID str, uint paramcount, const uint64 params[]) :
+			Window(x, y, width, height, NULL, WC_TOOLTIPS, widget)
+	{
+		this->string_id = str;
+		assert(sizeof(this->params[0]) == sizeof(params[0]));
+		memcpy(this->params, params, sizeof(this->params[0]) * paramcount);
+		this->paramcount = paramcount;
 
-		case WE_MOUSELOOP:
-			/* We can show tooltips while dragging tools. These are shown as long as
-			 * we are dragging the tool. Normal tooltips work with rmb */
-			if (WP(w, tooltips_d).paramcount == 0 ) {
-				if (!_right_button_down) delete w;
-			} else {
-				if (!_left_button_down) delete w;
-			}
+		this->flags4 &= ~WF_WHITE_BORDER_MASK; // remove white-border from tooltip
+		this->widget[0].right = width;
+		this->widget[0].bottom = height;
 
-			break;
+		FindWindowPlacementAndResize(width, height);
 	}
-}
+
+	virtual void OnPaint()
+	{
+		GfxFillRect(0, 0, this->width - 1, this->height - 1, 0);
+		GfxFillRect(1, 1, this->width - 2, this->height - 2, 0x44);
+
+		for (uint arg = 0; arg < this->paramcount; arg++) {
+			SetDParam(arg, this->params[arg]);
+		}
+		DrawStringMultiCenter((this->width >> 1), (this->height >> 1) - 5, this->string_id, this->width - 2);
+	}
+
+	virtual void OnMouseLoop()
+	{
+		/* We can show tooltips while dragging tools. These are shown as long as
+		 * we are dragging the tool. Normal tooltips work with rmb */
+		if (this->paramcount == 0 ) {
+			if (!_right_button_down) delete this;
+		} else {
+			if (!_left_button_down) delete this;
+		}
+	}
+};
 
 /** Shows a tooltip
  * @param str String to be displayed
@@ -753,16 +611,7 @@
 	if (y + br.height > _screen.height - 12) y = _cursor.pos.y + _cursor.offs.y - br.height - 5;
 	int x = Clamp(_cursor.pos.x - (br.width >> 1), 0, _screen.width - br.width);
 
-	Window *w = AllocateWindow(x, y, br.width, br.height, TooltipsWndProc, WC_TOOLTIPS, _tooltips_widgets);
-
-	WP(w, tooltips_d).string_id = str;
-	assert(sizeof(WP(w, tooltips_d).params[0]) == sizeof(params[0]));
-	memcpy(WP(w, tooltips_d).params, params, sizeof(WP(w, tooltips_d).params[0]) * paramcount);
-	WP(w, tooltips_d).paramcount = paramcount;
-
-	w->flags4 &= ~WF_WHITE_BORDER_MASK; // remove white-border from tooltip
-	w->widget[0].right = br.width;
-	w->widget[0].bottom = br.height;
+	new TooltipsWindow(x, y, br.width, br.height, _tooltips_widgets, str, paramcount, params);
 }
 
 
@@ -1032,43 +881,6 @@
 	tb->caretxoffs = tb->width;
 }
 
-int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *e)
-{
-	e->we.keypress.cont = false;
-
-	switch (e->we.keypress.keycode) {
-		case WKC_ESC: return 2;
-
-		case WKC_RETURN: case WKC_NUM_ENTER: return 1;
-
-		case (WKC_CTRL | 'V'):
-			if (InsertTextBufferClipboard(&string->text)) w->InvalidateWidget(wid);
-			break;
-
-		case (WKC_CTRL | 'U'):
-			DeleteTextBufferAll(&string->text);
-			w->InvalidateWidget(wid);
-			break;
-
-		case WKC_BACKSPACE: case WKC_DELETE:
-			if (DeleteTextBufferChar(&string->text, e->we.keypress.keycode)) w->InvalidateWidget(wid);
-			break;
-
-		case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
-			if (MoveTextBufferPos(&string->text, e->we.keypress.keycode)) w->InvalidateWidget(wid);
-			break;
-
-		default:
-			if (IsValidChar(e->we.keypress.key, string->afilter)) {
-				if (InsertTextBufferChar(&string->text, e->we.keypress.key)) w->InvalidateWidget(wid);
-			} else { // key wasn't caught. Continue only if standard entry specified
-				e->we.keypress.cont = (string->afilter == CS_ALPHANUMERAL);
-			}
-	}
-
-	return 0;
-}
-
 bool HandleCaret(Textbuf *tb)
 {
 	/* caret changed? */
@@ -1081,12 +893,49 @@
 	return false;
 }
 
-void HandleEditBox(Window *w, querystr_d *string, int wid)
+int QueryString::HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, bool &cont)
 {
-	if (HandleCaret(&string->text)) w->InvalidateWidget(wid);
+	cont = false;
+
+	switch (keycode) {
+		case WKC_ESC: return 2;
+
+		case WKC_RETURN: case WKC_NUM_ENTER: return 1;
+
+		case (WKC_CTRL | 'V'):
+			if (InsertTextBufferClipboard(&this->text)) w->InvalidateWidget(wid);
+			break;
+
+		case (WKC_CTRL | 'U'):
+			DeleteTextBufferAll(&this->text);
+			w->InvalidateWidget(wid);
+			break;
+
+		case WKC_BACKSPACE: case WKC_DELETE:
+			if (DeleteTextBufferChar(&this->text, keycode)) w->InvalidateWidget(wid);
+			break;
+
+		case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
+			if (MoveTextBufferPos(&this->text, keycode)) w->InvalidateWidget(wid);
+			break;
+
+		default:
+			if (IsValidChar(key, this->afilter)) {
+				if (InsertTextBufferChar(&this->text, key)) w->InvalidateWidget(wid);
+			} else { // key wasn't caught. Continue only if standard entry specified
+				cont = (this->afilter == CS_ALPHANUMERAL);
+			}
+	}
+
+	return 0;
 }
 
-void DrawEditBox(Window *w, querystr_d *string, int wid)
+void QueryString::HandleEditBox(Window *w, int wid)
+{
+	if (HandleCaret(&this->text)) w->InvalidateWidget(wid);
+}
+
+void QueryString::DrawEditBox(Window *w, int wid)
 {
 	const Widget *wi = &w->widget[wid];
 
@@ -1111,7 +960,7 @@
 
 	/* We will take the current widget length as maximum width, with a small
 	 * space reserved at the end for the caret to show */
-	const Textbuf *tb = &string->text;
+	const Textbuf *tb = &this->text;
 
 	delta = (wi->right - wi->left) - tb->width - 10;
 	if (delta > 0) delta = 0;
@@ -1124,6 +973,21 @@
 	_cur_dpi = old_dpi;
 }
 
+int QueryStringBaseWindow::HandleEditBoxKey(int wid, uint16 key, uint16 keycode, bool &cont)
+{
+	return this->QueryString::HandleEditBoxKey(this, wid, key, keycode, cont);
+}
+
+void QueryStringBaseWindow::HandleEditBox(int wid)
+{
+	this->QueryString::HandleEditBox(this, wid);
+}
+
+void QueryStringBaseWindow::DrawEditBox(int wid)
+{
+	this->QueryString::DrawEditBox(this, wid);
+}
+
 enum QueryStringWidgets {
 	QUERY_STR_WIDGET_TEXT = 3,
 	QUERY_STR_WIDGET_CANCEL,
@@ -1131,76 +995,79 @@
 };
 
 
-static void QueryStringWndProc(Window *w, WindowEvent *e)
+struct QueryStringWindow : public QueryStringBaseWindow
 {
-	querystr_d *qs = &WP(w, querystr_d);
-
-	switch (e->event) {
-		case WE_CREATE:
-			SetBit(_no_scroll, SCROLL_EDIT);
-			break;
-
-		case WE_PAINT:
-			SetDParam(0, qs->caption);
-			DrawWindowWidgets(w);
-
-			DrawEditBox(w, qs, QUERY_STR_WIDGET_TEXT);
-			break;
-
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case QUERY_STR_WIDGET_TEXT:
-					ShowOnScreenKeyboard(w, &WP(w, querystr_d), QUERY_STR_WIDGET_TEXT, QUERY_STR_WIDGET_CANCEL, QUERY_STR_WIDGET_OK);
-					break;
-
-				case QUERY_STR_WIDGET_OK:
-		press_ok:;
-					if (qs->orig == NULL || strcmp(qs->text.buf, qs->orig) != 0) {
-						Window *parent = w->parent;
-						qs->handled = true;
+	Window *parent;
 
-						/* If the parent is NULL, the editbox is handled by general function
-						 * HandleOnEditText */
-						if (parent != NULL) {
-							WindowEvent e;
-							e.event = WE_ON_EDIT_TEXT;
-							e.we.edittext.str = qs->text.buf;
-							parent->HandleWindowEvent(&e);
-						} else {
-							HandleOnEditText(qs->text.buf);
-						}
-					}
-					/* Fallthrough */
-				case QUERY_STR_WIDGET_CANCEL:
-					delete w;
-					break;
-			}
-			break;
+	QueryStringWindow(const WindowDesc *desc, Window *parent) : QueryStringBaseWindow(desc), parent(parent)
+	{
+		SetBit(_no_scroll, SCROLL_EDIT);
 
-		case WE_MOUSELOOP:
-			HandleEditBox(w, qs, QUERY_STR_WIDGET_TEXT);
-			break;
-
-		case WE_KEYPRESS:
-			switch (HandleEditBoxKey(w, qs, QUERY_STR_WIDGET_TEXT, e)) {
-				case 1: goto press_ok; // Enter pressed, confirms change
-				case 2: delete w; break; // ESC pressed, closes window, abandons changes
-			}
-			break;
+		this->FindWindowPlacementAndResize(desc);
+	}
 
-		case WE_DESTROY: // Call cancellation of query, if we have not handled it before
-			if (!qs->handled && w->parent != NULL) {
-				WindowEvent e;
-				Window *parent = w->parent;
+	virtual void OnPaint()
+	{
+		SetDParam(0, this->caption);
+		DrawWindowWidgets(this);
 
-				qs->handled = true;
-				e.event = WE_ON_EDIT_TEXT_CANCEL;
-				parent->HandleWindowEvent(&e);
+		this->DrawEditBox(QUERY_STR_WIDGET_TEXT);
+	}
+
+	void OnOk()
+	{
+		if (this->orig == NULL || strcmp(this->text.buf, this->orig) != 0) {
+			/* If the parent is NULL, the editbox is handled by general function
+				* HandleOnEditText */
+			if (this->parent != NULL) {
+				this->parent->OnQueryTextFinished(this->text.buf);
+			} else {
+				HandleOnEditText(this->text.buf);
 			}
-			ClrBit(_no_scroll, SCROLL_EDIT);
-			break;
 		}
-}
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case QUERY_STR_WIDGET_TEXT:
+				ShowOnScreenKeyboard(this, QUERY_STR_WIDGET_TEXT, QUERY_STR_WIDGET_CANCEL, QUERY_STR_WIDGET_OK);
+				break;
+
+			case QUERY_STR_WIDGET_OK:
+				this->OnOk();
+				/* Fallthrough */
+			case QUERY_STR_WIDGET_CANCEL:
+				delete this;
+				break;
+		}
+	}
+
+	virtual void OnMouseLoop()
+	{
+		this->HandleEditBox(QUERY_STR_WIDGET_TEXT);
+	}
+
+	virtual bool OnKeyPress(uint16 key, uint16 keycode)
+	{
+		bool cont;
+		switch (this->HandleEditBoxKey(QUERY_STR_WIDGET_TEXT, key, keycode, cont)) {
+			case 1: this->OnOk(); // Enter pressed, confirms change
+			/* FALL THROUGH */
+			case 2: delete this; break; // ESC pressed, closes window, abandons changes
+		}
+		return cont;
+	}
+
+	~QueryStringWindow()
+	{
+		if (!this->handled && this->parent != NULL) {
+			this->handled = true;
+			this->parent->OnQueryTextFinished(NULL);
+		}
+		ClrBit(_no_scroll, SCROLL_EDIT);
+	}
+};
 
 static const Widget _query_string_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,        STR_018B_CLOSE_WINDOW},
@@ -1217,12 +1084,9 @@
 	WC_QUERY_STRING, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 	_query_string_widgets,
-	QueryStringWndProc
+	NULL
 };
 
-char _edit_str_buf[64];
-char _orig_str_buf[lengthof(_edit_str_buf)];
-
 /** Show a query popup window with a textbox in it.
  * @param str StringID for the text shown in the textbox
  * @param caption StringID of text shown in caption of querywindow
@@ -1236,28 +1100,27 @@
 {
 	uint realmaxlen = maxlen & ~0x1000;
 
-	assert(realmaxlen < lengthof(_edit_str_buf));
-
 	DeleteWindowById(WC_QUERY_STRING, 0);
 	DeleteWindowById(WC_SAVELOAD, 0);
 
-	Window *w = AllocateWindowDesc(&_query_string_desc);
-	w->parent = parent;
+	QueryStringWindow *w = new QueryStringWindow(&_query_string_desc, parent);
 
-	GetString(_edit_str_buf, str, lastof(_edit_str_buf));
-	_edit_str_buf[realmaxlen - 1] = '\0';
+	assert(realmaxlen < lengthof(w->edit_str_buf));
+
+	GetString(w->edit_str_buf, str, lastof(w->edit_str_buf));
+	w->edit_str_buf[realmaxlen - 1] = '\0';
 
 	if (maxlen & 0x1000) {
-		WP(w, querystr_d).orig = NULL;
+		w->orig = NULL;
 	} else {
-		strecpy(_orig_str_buf, _edit_str_buf, lastof(_orig_str_buf));
-		WP(w, querystr_d).orig = _orig_str_buf;
+		strecpy(w->orig_str_buf, w->edit_str_buf, lastof(w->orig_str_buf));
+		w->orig = w->orig_str_buf;
 	}
 
 	w->LowerWidget(QUERY_STR_WIDGET_TEXT);
-	WP(w, querystr_d).caption = caption;
-	WP(w, querystr_d).afilter = afilter;
-	InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, realmaxlen, maxwidth);
+	w->caption = caption;
+	w->afilter = afilter;
+	InitializeTextBuffer(&w->text, w->edit_str_buf, realmaxlen, maxwidth);
 }
 
 
@@ -1267,63 +1130,78 @@
 	QUERY_WIDGET_YES
 };
 
-
-struct query_d {
+/**
+ * Window used for asking the user a YES/NO question.
+ */
+struct QueryWindow : public Window {
 	void (*proc)(Window*, bool); ///< callback function executed on closing of popup. Window* points to parent, bool is true if 'yes' clicked, false otherwise
 	uint64 params[10];           ///< local copy of _decode_parameters
 	StringID message;            ///< message shown for query window
-	bool calledback;             ///< has callback been executed already (internal usage for WE_DESTROY event)
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(query_d));
-
-
-static void QueryWndProc(Window *w, WindowEvent *e)
-{
-	query_d *q = &WP(w, query_d);
-
-	switch (e->event) {
-		case WE_PAINT:
-			CopyInDParam(0, q->params, lengthof(q->params));
-			DrawWindowWidgets(w);
-			CopyInDParam(0, q->params, lengthof(q->params));
-
-			DrawStringMultiCenter(w->width / 2, (w->height / 2) - 10, q->message, w->width - 2);
-			break;
 
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case QUERY_WIDGET_YES:
-					q->calledback = true;
-					if (q->proc != NULL) q->proc(w->parent, true);
-					/* Fallthrough */
-				case QUERY_WIDGET_NO:
-					delete w;
-					break;
-				}
-			break;
+	QueryWindow(const WindowDesc *desc, StringID caption, StringID message, Window *parent, void (*callback)(Window*, bool)) : Window(desc)
+	{
+		if (parent == NULL) parent = FindWindowById(WC_MAIN_WINDOW, 0);
+		this->parent = parent;
+		this->left = parent->left + (parent->width / 2) - (this->width / 2);
+		this->top = parent->top + (parent->height / 2) - (this->height / 2);
 
-		case WE_KEYPRESS: // ESC closes the window, Enter confirms the action
-			switch (e->we.keypress.keycode) {
-				case WKC_RETURN:
-				case WKC_NUM_ENTER:
-					q->calledback = true;
-					if (q->proc != NULL) q->proc(w->parent, true);
-					/* Fallthrough */
-				case WKC_ESC:
-					e->we.keypress.cont = false;
-					delete w;
-					break;
-			}
-			break;
+		/* Create a backup of the variadic arguments to strings because it will be
+		* overridden pretty often. We will copy these back for drawing */
+		CopyOutDParam(this->params, 0, lengthof(this->params));
+		this->widget[QUERY_WIDGET_CAPTION].data = caption;
+		this->message    = message;
+		this->proc       = callback;
 
-		case WE_DESTROY: // Call callback function (if any) on window close if not yet called
-			if (!q->calledback && q->proc != NULL) {
-				q->calledback = true;
-				q->proc(w->parent, false);
-			}
-			break;
+		this->FindWindowPlacementAndResize(desc);
 	}
-}
+
+	~QueryWindow()
+	{
+		if (this->proc != NULL) this->proc(this->parent, false);
+	}
+
+	virtual void OnPaint()
+	{
+		CopyInDParam(0, this->params, lengthof(this->params));
+		DrawWindowWidgets(this);
+		CopyInDParam(0, this->params, lengthof(this->params));
+
+		DrawStringMultiCenter(this->width / 2, (this->height / 2) - 10, this->message, this->width - 2);
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case QUERY_WIDGET_YES:
+				if (this->proc != NULL) {
+					this->proc(this->parent, true);
+					this->proc = NULL;
+				}
+				/* Fallthrough */
+			case QUERY_WIDGET_NO:
+				delete this;
+				break;
+		}
+	}
+
+	virtual bool OnKeyPress(uint16 key, uint16 keycode)
+	{
+		/* ESC closes the window, Enter confirms the action */
+		switch (keycode) {
+			case WKC_RETURN:
+			case WKC_NUM_ENTER:
+				if (this->proc != NULL) {
+					this->proc(this->parent, true);
+					this->proc = NULL;
+				}
+				/* Fallthrough */
+			case WKC_ESC:
+				delete this;
+				return false;
+		}
+		return true;
+	}
+};
 
 
 static const Widget _query_widgets[] = {
@@ -1340,7 +1218,7 @@
 	WC_CONFIRM_POPUP_QUERY, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_DEF_WIDGET | WDF_MODAL,
 	_query_widgets,
-	QueryWndProc
+	NULL
 };
 
 /** Show a modal confirmation window with standard 'yes' and 'no' buttons
@@ -1353,21 +1231,7 @@
  * @param callback callback function pointer to set in the window descriptor*/
 void ShowQuery(StringID caption, StringID message, Window *parent, void (*callback)(Window*, bool))
 {
-	Window *w = AllocateWindowDesc(&_query_desc);
-	if (w == NULL) return;
-
-	if (parent == NULL) parent = FindWindowById(WC_MAIN_WINDOW, 0);
-	w->parent = parent;
-	w->left = parent->left + (parent->width / 2) - (w->width / 2);
-	w->top = parent->top + (parent->height / 2) - (w->height / 2);
-
-	/* Create a backup of the variadic arguments to strings because it will be
-	 * overridden pretty often. We will copy these back for drawing */
-	CopyOutDParam(WP(w, query_d).params, 0, lengthof(WP(w, query_d).params));
-	w->widget[QUERY_WIDGET_CAPTION].data = caption;
-	WP(w, query_d).message    = message;
-	WP(w, query_d).proc       = callback;
-	WP(w, query_d).calledback = false;
+	new QueryWindow(&_query_desc, caption, message, parent, callback);
 }
 
 
@@ -1466,230 +1330,276 @@
 	}
 }
 
-static void GenerateFileName()
-{
-	/* Check if we are not a spectator who wants to generate a name..
-	    Let's use the name of player #0 for now. */
-	const Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST);
-
-	SetDParam(0, p->index);
-	SetDParam(1, _date);
-	GetString(_edit_str_buf, STR_4004, lastof(_edit_str_buf));
-	SanitizeFilename(_edit_str_buf);
-}
-
 extern void StartupEngines();
 
-static void SaveLoadDlgWndProc(Window *w, WindowEvent *e)
-{
-	static FiosItem o_dir;
-
-	switch (e->event) {
-		case WE_CREATE: // Set up OPENTTD button
-			w->vscroll.cap = 10;
-			w->resize.step_width = 2;
-			w->resize.step_height = 10;
-
-			o_dir.type = FIOS_TYPE_DIRECT;
-			switch (_saveload_mode) {
-				case SLD_SAVE_GAME:
-				case SLD_LOAD_GAME:
-					FioGetDirectory(o_dir.name, lengthof(o_dir.name), SAVE_DIR);
-					break;
-
-				case SLD_SAVE_SCENARIO:
-				case SLD_LOAD_SCENARIO:
-					FioGetDirectory(o_dir.name, lengthof(o_dir.name), SCENARIO_DIR);
-					break;
-
-				case SLD_LOAD_HEIGHTMAP:
-					FioGetDirectory(o_dir.name, lengthof(o_dir.name), HEIGHTMAP_DIR);
-					break;
+struct SaveLoadWindow : public QueryStringBaseWindow {
+	FiosItem o_dir;
 
-				default:
-					ttd_strlcpy(o_dir.name, _personal_dir, lengthof(o_dir.name));
-			}
-			break;
-
-		case WE_PAINT: {
-			int pos;
-			int y;
-
-			SetVScrollCount(w, _fios_num);
-			DrawWindowWidgets(w);
-			DrawFiosTexts(w->width);
+	void GenerateFileName()
+	{
+		/* Check if we are not a spectator who wants to generate a name..
+				Let's use the name of player #0 for now. */
+		const Player *p = GetPlayer(IsValidPlayer(_local_player) ? _local_player : PLAYER_FIRST);
 
-			if (_savegame_sort_dirty) {
-				_savegame_sort_dirty = false;
-				MakeSortedSaveGameList();
-			}
-
-			GfxFillRect(w->widget[7].left + 1, w->widget[7].top + 1, w->widget[7].right, w->widget[7].bottom, 0xD7);
-			DrawSortButtonState(w, _savegame_sort_order & SORT_BY_NAME ? 2 : 3, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP);
+		SetDParam(0, p->index);
+		SetDParam(1, _date);
+		GetString(this->edit_str_buf, STR_4004, lastof(this->edit_str_buf));
+		SanitizeFilename(this->edit_str_buf);
+	}
 
-			y = w->widget[7].top + 1;
-			for (pos = w->vscroll.pos; pos < _fios_num; pos++) {
-				const FiosItem *item = _fios_list + pos;
+	SaveLoadWindow(const WindowDesc *desc, SaveLoadDialogMode mode) : QueryStringBaseWindow(desc)
+	{
+		static const StringID saveload_captions[] = {
+			STR_4001_LOAD_GAME,
+			STR_0298_LOAD_SCENARIO,
+			STR_4000_SAVE_GAME,
+			STR_0299_SAVE_SCENARIO,
+			STR_LOAD_HEIGHTMAP,
+		};
 
-				DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], w->width - 18);
-				y += 10;
-				if (y >= w->vscroll.cap * 10 + w->widget[7].top + 1) break;
-			}
+		SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0);
+		SetBit(_no_scroll, SCROLL_SAVE);
 
-			if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
-				DrawEditBox(w, &WP(w, querystr_d), 10);
-			}
-			break;
+		/* Use an array to define what will be the current file type being handled
+		 * by current file mode */
+		switch (mode) {
+			case SLD_SAVE_GAME:     this->GenerateFileName(); break;
+			case SLD_SAVE_SCENARIO: strcpy(this->edit_str_buf, "UNNAMED"); break;
+			default:                break;
 		}
 
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case 2: // Sort save names by name
-					_savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ?
-						SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME;
-					_savegame_sort_dirty = true;
-					w->SetDirty();
-					break;
-
-				case 3: // Sort save names by date
-					_savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ?
-						SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE;
-					_savegame_sort_dirty = true;
-					w->SetDirty();
-					break;
-
-				case 6: // OpenTTD 'button', jumps to OpenTTD directory
-					FiosBrowseTo(&o_dir);
-					w->SetDirty();
-					BuildFileList();
-					break;
-
-				case 7: { // Click the listbox
-					int y = (e->we.click.pt.y - w->widget[e->we.click.widget].top - 1) / 10;
-					char *name;
-					const FiosItem *file;
-
-					if (y < 0 || (y += w->vscroll.pos) >= w->vscroll.count) return;
-
-					file = _fios_list + y;
-
-					name = FiosBrowseTo(file);
-					if (name != NULL) {
-						if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
-							_switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD;
-
-							SetFiosType(file->type);
-							ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
-							ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
+		assert((uint)mode < lengthof(saveload_captions));
 
-							delete w;
-						} else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
-							SetFiosType(file->type);
-							ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
-							ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
+		this->widget[1].data = saveload_captions[mode];
+		this->LowerWidget(7);
 
-							delete w;
-							ShowHeightmapLoad();
-						} else {
-							/* SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox */
-							ttd_strlcpy(WP(w, querystr_d).text.buf, file->title, WP(w, querystr_d).text.maxlength);
-							UpdateTextBufferSize(&WP(w, querystr_d).text);
-							w->InvalidateWidget(10);
-						}
+		this->afilter = CS_ALPHANUMERAL;
+		InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 240);
+
+		/* pause is only used in single-player, non-editor mode, non-menu mode. It
+		* will be unpaused in the WE_DESTROY event handler. */
+		if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) {
+			if (_pause_game >= 0) DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
+		}
+
+		BuildFileList();
+
+		ResetObjectToPlace();
+
+		o_dir.type = FIOS_TYPE_DIRECT;
+		switch (_saveload_mode) {
+			case SLD_SAVE_GAME:
+			case SLD_LOAD_GAME:
+				FioGetDirectory(o_dir.name, lengthof(o_dir.name), SAVE_DIR);
+				break;
+
+			case SLD_SAVE_SCENARIO:
+			case SLD_LOAD_SCENARIO:
+				FioGetDirectory(o_dir.name, lengthof(o_dir.name), SCENARIO_DIR);
+				break;
+
+			case SLD_LOAD_HEIGHTMAP:
+				FioGetDirectory(o_dir.name, lengthof(o_dir.name), HEIGHTMAP_DIR);
+				break;
+
+			default:
+				ttd_strlcpy(o_dir.name, _personal_dir, lengthof(o_dir.name));
+		}
+
+		this->vscroll.cap = 10;
+		this->resize.step_width = 2;
+		this->resize.step_height = 10;
+
+		this->FindWindowPlacementAndResize(desc);
+	}
+
+	virtual ~SaveLoadWindow()
+	{
+		/* pause is only used in single-player, non-editor mode, non menu mode */
+		if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) {
+			if (_pause_game >= 0) DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
+		}
+		FiosFreeSavegameList();
+		ClrBit(_no_scroll, SCROLL_SAVE);
+	}
+
+	virtual void OnPaint()
+	{
+		int pos;
+		int y;
+
+		SetVScrollCount(this, _fios_num);
+		DrawWindowWidgets(this);
+		DrawFiosTexts(this->width);
+
+		if (_savegame_sort_dirty) {
+			_savegame_sort_dirty = false;
+			MakeSortedSaveGameList();
+		}
+
+		GfxFillRect(this->widget[7].left + 1, this->widget[7].top + 1, this->widget[7].right, this->widget[7].bottom, 0xD7);
+		DrawSortButtonState(this, _savegame_sort_order & SORT_BY_NAME ? 2 : 3, _savegame_sort_order & SORT_DESCENDING ? SBS_DOWN : SBS_UP);
+
+		y = this->widget[7].top + 1;
+		for (pos = this->vscroll.pos; pos < _fios_num; pos++) {
+			const FiosItem *item = _fios_list + pos;
+
+			DoDrawStringTruncated(item->title, 4, y, _fios_colors[item->type], this->width - 18);
+			y += 10;
+			if (y >= this->vscroll.cap * 10 + this->widget[7].top + 1) break;
+		}
+
+		if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
+			this->DrawEditBox(10);
+		}
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case 2: // Sort save names by name
+				_savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ?
+					SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME;
+				_savegame_sort_dirty = true;
+				this->SetDirty();
+				break;
+
+			case 3: // Sort save names by date
+				_savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ?
+					SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE;
+				_savegame_sort_dirty = true;
+				this->SetDirty();
+				break;
+
+			case 6: // OpenTTD 'button', jumps to OpenTTD directory
+				FiosBrowseTo(&o_dir);
+				this->SetDirty();
+				BuildFileList();
+				break;
+
+			case 7: { // Click the listbox
+				int y = (pt.y - this->widget[widget].top - 1) / 10;
+				char *name;
+				const FiosItem *file;
+
+				if (y < 0 || (y += this->vscroll.pos) >= this->vscroll.count) return;
+
+				file = _fios_list + y;
+
+				name = FiosBrowseTo(file);
+				if (name != NULL) {
+					if (_saveload_mode == SLD_LOAD_GAME || _saveload_mode == SLD_LOAD_SCENARIO) {
+						_switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD;
+
+						SetFiosType(file->type);
+						ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
+						ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
+
+						delete this;
+					} else if (_saveload_mode == SLD_LOAD_HEIGHTMAP) {
+						SetFiosType(file->type);
+						ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
+						ttd_strlcpy(_file_to_saveload.title, file->title, sizeof(_file_to_saveload.title));
+
+						delete this;
+						ShowHeightmapLoad();
 					} else {
-						/* Changed directory, need repaint. */
-						w->SetDirty();
-						BuildFileList();
+						/* SLD_SAVE_GAME, SLD_SAVE_SCENARIO copy clicked name to editbox */
+						ttd_strlcpy(this->text.buf, file->title, this->text.maxlength);
+						UpdateTextBufferSize(&this->text);
+						this->InvalidateWidget(10);
 					}
-					break;
+				} else {
+					/* Changed directory, need repaint. */
+					this->SetDirty();
+					BuildFileList();
 				}
-
-				case 10: // edit box
-					ShowOnScreenKeyboard(w, &WP(w, querystr_d), e->we.click.widget, 0, 0);
-					break;
-
-				case 11: case 12: // Delete, Save game
-					break;
-			}
-			break;
-
-		case WE_MOUSELOOP:
-			if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
-				HandleEditBox(w, &WP(w, querystr_d), 10);
-			}
-			break;
-
-		case WE_KEYPRESS:
-			if (e->we.keypress.keycode == WKC_ESC) {
-				delete w;
-				return;
+				break;
 			}
 
-			if ((_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) &&
-					HandleEditBoxKey(w, &WP(w, querystr_d), 10, e) == 1) { // Press Enter
-				w->HandleButtonClick(12);
-			}
-			break;
-
-		case WE_TIMEOUT:
-			/* This test protects against using widgets 11 and 12 which are only available
-			* in those two saveload mode  */
-			if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) break;
-
-			if (w->IsWidgetLowered(11)) { // Delete button clicked
-				if (!FiosDelete(WP(w, querystr_d).text.buf)) {
-					ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0);
-				} else {
-					BuildFileList();
-					/* Reset file name to current date on successful delete */
-					if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName();
-				}
-
-				UpdateTextBufferSize(&WP(w, querystr_d).text);
-				w->SetDirty();
-			} else if (w->IsWidgetLowered(12)) { // Save button clicked
-				_switch_mode = SM_SAVE;
-				FiosMakeSavegameName(_file_to_saveload.name, WP(w, querystr_d).text.buf, sizeof(_file_to_saveload.name));
+			case 10: // edit box
+				ShowOnScreenKeyboard(this, widget, 0, 0);
+				break;
 
-				/* In the editor set up the vehicle engines correctly (date might have changed) */
-				if (_game_mode == GM_EDITOR) StartupEngines();
-			}
-			break;
+			case 11: case 12: // Delete, Save game
+				break;
+		}
+	}
 
-		case WE_DESTROY:
-			/* pause is only used in single-player, non-editor mode, non menu mode */
-			if (!_networking && _game_mode != GM_EDITOR && _game_mode != GM_MENU) {
-				if (_pause_game >= 0) DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
-			}
-			FiosFreeSavegameList();
-			ClrBit(_no_scroll, SCROLL_SAVE);
-			break;
+	virtual void OnMouseLoop()
+	{
+		if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
+			this->HandleEditBox(10);
+		}
+	}
 
-		case WE_RESIZE: {
-			/* Widget 2 and 3 have to go with halve speed, make it so obiwan */
-			uint diff = e->we.sizing.diff.x / 2;
-			w->widget[2].right += diff;
-			w->widget[3].left  += diff;
-			w->widget[3].right += e->we.sizing.diff.x;
+	virtual bool OnKeyPress(uint16 key, uint16 keycode)
+	{
+		if (keycode == WKC_ESC) {
+			delete this;
+			return false;
+		}
 
-			/* Same for widget 11 and 12 in save-dialog */
-			if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
-				w->widget[11].right += diff;
-				w->widget[12].left  += diff;
-				w->widget[12].right += e->we.sizing.diff.x;
+		bool cont = true;
+		if ((_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) &&
+				this->HandleEditBoxKey(10, key, keycode, cont) == 1) { // Press Enter
+			this->HandleButtonClick(12);
+		}
+
+		return cont;
+	}
+
+	virtual void OnTimeout()
+	{
+		/* This test protects against using widgets 11 and 12 which are only available
+		 * in those two saveload mode  */
+		if (!(_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO)) return;
+
+		if (this->IsWidgetLowered(11)) { // Delete button clicked
+			if (!FiosDelete(this->text.buf)) {
+				ShowErrorMessage(INVALID_STRING_ID, STR_4008_UNABLE_TO_DELETE_FILE, 0, 0);
+			} else {
+				BuildFileList();
+				/* Reset file name to current date on successful delete */
+				if (_saveload_mode == SLD_SAVE_GAME) GenerateFileName();
 			}
 
-			w->vscroll.cap += e->we.sizing.diff.y / 10;
-		} break;
+			UpdateTextBufferSize(&this->text);
+			this->SetDirty();
+		} else if (this->IsWidgetLowered(12)) { // Save button clicked
+			_switch_mode = SM_SAVE;
+			FiosMakeSavegameName(_file_to_saveload.name, this->text.buf, sizeof(_file_to_saveload.name));
+
+			/* In the editor set up the vehicle engines correctly (date might have changed) */
+			if (_game_mode == GM_EDITOR) StartupEngines();
+		}
 	}
-}
+
+	virtual void OnResize(Point new_size, Point delta)
+	{
+		/* Widget 2 and 3 have to go with halve speed, make it so obiwan */
+		uint diff = delta.x / 2;
+		this->widget[2].right += diff;
+		this->widget[3].left  += diff;
+		this->widget[3].right += delta.x;
+
+		/* Same for widget 11 and 12 in save-dialog */
+		if (_saveload_mode == SLD_SAVE_GAME || _saveload_mode == SLD_SAVE_SCENARIO) {
+			this->widget[11].right += diff;
+			this->widget[12].left  += diff;
+			this->widget[12].right += delta.x;
+		}
+
+		this->vscroll.cap += delta.y / 10;
+	}
+};
 
 static const WindowDesc _load_dialog_desc = {
 	WDP_CENTER, WDP_CENTER, 257, 154, 257, 294,
 	WC_SAVELOAD, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 	_load_dialog_widgets,
-	SaveLoadDlgWndProc,
+	NULL,
 };
 
 static const WindowDesc _save_dialog_desc = {
@@ -1697,7 +1607,7 @@
 	WC_SAVELOAD, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 	_save_dialog_widgets,
-	SaveLoadDlgWndProc,
+	NULL,
 };
 
 /** These values are used to convert the file/operations mode into a corresponding file type.
@@ -1713,50 +1623,22 @@
 
 void ShowSaveLoadDialog(SaveLoadDialogMode mode)
 {
-	static const StringID saveload_captions[] = {
-		STR_4001_LOAD_GAME,
-		STR_0298_LOAD_SCENARIO,
-		STR_4000_SAVE_GAME,
-		STR_0299_SAVE_SCENARIO,
-		STR_LOAD_HEIGHTMAP,
-	};
-
-	const WindowDesc *sld = &_save_dialog_desc;
-
-	SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, VHM_NONE, WC_MAIN_WINDOW, 0);
 	DeleteWindowById(WC_QUERY_STRING, 0);
 	DeleteWindowById(WC_SAVELOAD, 0);
 
-	_saveload_mode = mode;
-	SetBit(_no_scroll, SCROLL_SAVE);
-
-	/* Use an array to define what will be the current file type being handled
-	 * by current file mode */
-	_file_to_saveload.filetype = _file_modetotype[mode];
+	const WindowDesc *sld;
 	switch (mode) {
-		case SLD_SAVE_GAME:     GenerateFileName(); break;
-		case SLD_SAVE_SCENARIO: strcpy(_edit_str_buf, "UNNAMED"); break;
-		default:                sld = &_load_dialog_desc; break;
+		case SLD_SAVE_GAME:
+		case SLD_SAVE_SCENARIO:
+			sld = &_save_dialog_desc; break;
+		default:
+			sld = &_load_dialog_desc; break;
 	}
 
-	assert((uint)mode < lengthof(saveload_captions));
-
-	Window *w = AllocateWindowDesc(sld);
-	w->widget[1].data = saveload_captions[mode];
-	w->LowerWidget(7);
-
-	WP(w, querystr_d).afilter = CS_ALPHANUMERAL;
-	InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, lengthof(_edit_str_buf), 240);
+	_saveload_mode = mode;
+	_file_to_saveload.filetype = _file_modetotype[mode];
 
-	/* pause is only used in single-player, non-editor mode, non-menu mode. It
-	 * will be unpaused in the WE_DESTROY event handler. */
-	if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) {
-		if (_pause_game >= 0) DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
-	}
-
-	BuildFileList();
-
-	ResetObjectToPlace();
+	new SaveLoadWindow(sld, mode);
 }
 
 void RedrawAutosave()
--- a/src/music_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/music_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -334,7 +334,7 @@
 
 static void ShowMusicTrackSelection()
 {
-	AllocateWindowDescFront(&_music_track_selection_desc, 0);
+	AllocateWindowDescFront<Window>(&_music_track_selection_desc, 0);
 }
 
 static void MusicWindowWndProc(Window *w, WindowEvent *e)
@@ -463,7 +463,7 @@
 		}
 		break;
 
-	case WE_MOUSELOOP:
+	case WE_TICK:
 		InvalidateWindowWidget(WC_MUSIC_WINDOW, 0, 7);
 		break;
 	}
@@ -502,5 +502,5 @@
 
 void ShowMusicWindow()
 {
-	AllocateWindowDescFront(&_music_window_desc, 0);
+	AllocateWindowDescFront<Window>(&_music_window_desc, 0);
 }
--- a/src/network/network_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/network/network_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -29,6 +29,7 @@
 #include "../player_func.h"
 #include "../settings_type.h"
 #include "../widgets/dropdown_func.h"
+#include "../querystring_gui.h"
 
 #include "table/strings.h"
 #include "../table/sprites.h"
@@ -36,33 +37,9 @@
 #define BGC 5
 #define BTC 15
 
-struct chatquerystr_d : public querystr_d {
-	DestType dtype;
-	int dest;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(chatquerystr_d));
-
-struct network_d {
-	PlayerID company;        // select company in network lobby
-	byte field;              // select text-field in start-server and game-listing
-	byte widget_id;          ///< The widget that has the pop-up input menu
-	NetworkGameList *server; // selected server in lobby and game-listing
-	FiosItem *map;           // selected map in start-server
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_d));
-
-struct network_ql_d {
-	network_d n;                 // see above; general stuff
-	querystr_d q;                // text-input in start-server and game-listing
-	NetworkGameList **sort_list; // list of games (sorted)
-	list_d l;                    // accompanying list-administration
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_ql_d));
-
 /* Global to remember sorting after window has been closed */
 static Listing _ng_sorting;
 
-static char _edit_str_net_buf[150];
 static bool _chat_tab_completion_active;
 
 static void ShowNetworkStartServerWindow();
@@ -106,7 +83,7 @@
  * @param unselect unselect the currently selected item */
 void UpdateNetworkGameWindow(bool unselect)
 {
-	SendWindowMessage(WC_NETWORK_WINDOW, 0, unselect, 0, 0);
+	InvalidateWindowData(WC_NETWORK_WINDOW, 0, unselect);
 }
 
 static bool _internal_sort_order; // Used for Qsort order-flipping
@@ -160,64 +137,6 @@
 	return _internal_sort_order ? -r : r;
 }
 
-/** (Re)build the network game list as its amount has changed because
- * an item has been added or deleted for example
- * @param ngl list_d struct that contains all necessary information for sorting */
-static void BuildNetworkGameList(network_ql_d *nqld)
-{
-	NetworkGameList *ngl_temp;
-	uint n = 0;
-
-	if (!(nqld->l.flags & VL_REBUILD)) return;
-
-	/* Count the number of games in the list */
-	for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++;
-	if (n == 0) return;
-
-	/* Create temporary array of games to use for listing */
-	free(nqld->sort_list);
-	nqld->sort_list = MallocT<NetworkGameList*>(n);
-	nqld->l.list_length = n;
-
-	for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) {
-		nqld->sort_list[n++] = ngl_temp;
-	}
-
-	/* Force resort */
-	nqld->l.flags &= ~VL_REBUILD;
-	nqld->l.flags |= VL_RESORT;
-}
-
-static void SortNetworkGameList(network_ql_d *nqld)
-{
-	static NGameNameSortFunction * const ngame_sorter[] = {
-		&NGameNameSorter,
-		&NGameClientSorter,
-		&NGameAllowedSorter
-	};
-
-	NetworkGameList *item;
-	uint i;
-
-	if (!(nqld->l.flags & VL_RESORT)) return;
-	if (nqld->l.list_length == 0) return;
-
-	_internal_sort_order = !!(nqld->l.flags & VL_DESC);
-	qsort(nqld->sort_list, nqld->l.list_length, sizeof(nqld->sort_list[0]), ngame_sorter[nqld->l.sort_type]);
-
-	/* After sorting ngl->sort_list contains the sorted items. Put these back
-	 * into the original list. Basically nothing has changed, we are only
-	 * shuffling the ->next pointers */
-	_network_game_list = nqld->sort_list[0];
-	for (item = _network_game_list, i = 1; i != nqld->l.list_length; i++) {
-		item->next = nqld->sort_list[i];
-		item = item->next;
-	}
-	item->next = NULL;
-
-	nqld->l.flags &= ~VL_RESORT;
-}
-
 /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */
 enum NetworkGameWindowWidgets {
 	NGWW_CLOSE,         ///< Close 'X' button
@@ -249,364 +168,433 @@
 	NGWW_CANCEL,        ///< 'Cancel' button
 };
 
-/**
- * Draw a single server line.
- * @param cur_item  the server to draw.
- * @param y         from where to draw?
- * @param highlight does the line need to be highlighted?
- */
-static void DrawServerLine(const Window *w, const NetworkGameList *cur_item, uint y, bool highlight)
-{
-	/* show highlighted item with a different colour */
-	if (highlight) GfxFillRect(w->widget[NGWW_NAME].left + 1, y - 2, w->widget[NGWW_INFO].right - 1, y + 9, 10);
-
-	SetDParamStr(0, cur_item->info.server_name);
-	DrawStringTruncated(w->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, w->widget[NGWW_NAME].right - w->widget[NGWW_NAME].left - 5);
-
-	SetDParam(0, cur_item->info.clients_on);
-	SetDParam(1, cur_item->info.clients_max);
-	SetDParam(2, cur_item->info.companies_on);
-	SetDParam(3, cur_item->info.companies_max);
-	DrawStringCentered(w->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD);
-
-	/* only draw icons if the server is online */
-	if (cur_item->online) {
-		/* draw a lock if the server is password protected */
-		if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, w->widget[NGWW_INFO].left + 5, y - 1);
-
-		/* draw red or green icon, depending on compatibility with server */
-		DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[NGWW_INFO].left + 15, y);
-
-		/* draw flag according to server language */
-		DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, w->widget[NGWW_INFO].left + 25, y);
-	}
-}
-
-/**
- * Handler of actions done in the NetworkStartServer window
- *
- * @param w pointer to the Window structure
- * @param e pointer to window event
- * @note    Uses network_ql_d (network_d, querystr_d and list_d) WP macro
- * @see     struct _network_game_window_widgets
- * @see     enum NetworkGameWindowWidgets
- */
-
-static void NetworkGameWindowWndProc(Window *w, WindowEvent *e)
-{
-	network_d *nd = &WP(w, network_ql_d).n;
-	list_d *ld = &WP(w, network_ql_d).l;
-
-	switch (e->event) {
-		case WE_CREATE: // Focus input box
-			w->vscroll.cap = 11;
-			w->resize.step_height = NET_PRC__SIZE_OF_ROW;
-
-			nd->field = NGWW_PLAYER;
-			nd->server = NULL;
-
-			WP(w, network_ql_d).sort_list = NULL;
-			ld->flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE);
-			ld->sort_type = _ng_sorting.criteria;
-			break;
-
-		case WE_PAINT: {
-			const NetworkGameList *sel = nd->server;
-			const SortButtonState arrow = (ld->flags & VL_DESC) ? SBS_DOWN : SBS_UP;
+typedef GUIList<NetworkGameList*> GUIGameServerList;
 
-			if (ld->flags & VL_REBUILD) {
-				BuildNetworkGameList(&WP(w, network_ql_d));
-				SetVScrollCount(w, ld->list_length);
-			}
-			if (ld->flags & VL_RESORT) SortNetworkGameList(&WP(w, network_ql_d));
-
-			/* 'Refresh' button invisible if no server selected */
-			w->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL);
-			/* 'Join' button disabling conditions */
-			w->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server
-					!sel->online || // Server offline
-					sel->info.clients_on >= sel->info.clients_max || // Server full
-					!sel->info.compatible); // Revision mismatch
-
-			/* 'NewGRF Settings' button invisible if no NewGRF is used */
-			w->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL ||
-					!sel->online ||
-					sel->info.grfconfig == NULL);
-
-			SetDParam(0, 0x00);
-			SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]);
-			DrawWindowWidgets(w);
-
-			/* Edit box to set player name */
-			DrawEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER);
-
-			DrawString(w->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD);
-
-			/* Sort based on widgets: name, clients, compatibility */
-			switch (ld->sort_type) {
-				case NGWW_NAME    - NGWW_NAME: DrawSortButtonState(w, NGWW_NAME,    arrow); break;
-				case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(w, NGWW_CLIENTS, arrow); break;
-				case NGWW_INFO    - NGWW_NAME: DrawSortButtonState(w, NGWW_INFO,    arrow); break;
-			}
+struct NetworkGameWindow : public QueryStringBaseWindow {
+	byte field;                  ///< selected text-field
+	NetworkGameList *server;     ///< selected server
+	GUIGameServerList servers;   ///< list with game servers.
 
-			uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
-			int32 n = 0;
-			int32 pos = w->vscroll.pos;
-			const NetworkGameList *cur_item = _network_game_list;
-
-			while (pos > 0 && cur_item != NULL) {
-				pos--;
-				cur_item = cur_item->next;
-			}
-
-			while (cur_item != NULL) {
-				DrawServerLine(w, cur_item, y, cur_item == sel);
-
-				cur_item = cur_item->next;
-				y += NET_PRC__SIZE_OF_ROW;
-				if (++n == w->vscroll.cap) break; // max number of games in the window
-			}
+	NetworkGameWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc)
+	{
+		ttd_strlcpy(this->edit_str_buf, _network_player_name, lengthof(this->edit_str_buf));
+		this->afilter = CS_ALPHANUMERAL;
+		InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120);
 
-			const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
-			/* Draw the last joined server, if any */
-			if (last_joined != NULL) DrawServerLine(w, last_joined, y = w->widget[NGWW_LASTJOINED].top + 3, last_joined == sel);
+		UpdateNetworkGameWindow(true);
 
-			/* Draw the right menu */
-			GfxFillRect(w->widget[NGWW_DETAILS].left + 1, 43, w->widget[NGWW_DETAILS].right - 1, 92, 157);
-			if (sel == NULL) {
-				DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
-			} else if (!sel->online) {
-				SetDParamStr(0, sel->info.server_name);
-				DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name
+		this->vscroll.cap = 11;
+		this->resize.step_height = NET_PRC__SIZE_OF_ROW;
 
-				DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline
-			} else { // show game info
-				uint16 y = 100;
-				const uint16 x = w->widget[NGWW_DETAILS].left + 5;
+		this->field = NGWW_PLAYER;
+		this->server = NULL;
 
-				DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
+		this->servers.sort_list = NULL;
+		this->servers.flags = VL_REBUILD | (_ng_sorting.order ? VL_DESC : VL_NONE);
+		this->servers.sort_type = _ng_sorting.criteria;
+
+		this->FindWindowPlacementAndResize(desc);
+	}
+
+	~NetworkGameWindow()
+	{
+		free(this->servers.sort_list);
+	}
+
+	/**
+	 * (Re)build the network game list as its amount has changed because
+	 * an item has been added or deleted for example
+	 */
+	void BuildNetworkGameList()
+	{
+		NetworkGameList *ngl_temp;
+		uint n = 0;
+
+		if (!(this->servers.flags & VL_REBUILD)) return;
+
+		/* Count the number of games in the list */
+		for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++;
+		if (n == 0) return;
+
+		/* Create temporary array of games to use for listing */
+		this->servers.sort_list = ReallocT(this->servers.sort_list, n);
+		this->servers.list_length = n;
+
+		for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) {
+			this->servers.sort_list[n++] = ngl_temp;
+		}
+
+		/* Force resort */
+		this->servers.flags &= ~VL_REBUILD;
+		this->servers.flags |= VL_RESORT;
+	}
+
+	void SortNetworkGameList()
+	{
+		static NGameNameSortFunction * const ngame_sorter[] = {
+			&NGameNameSorter,
+			&NGameClientSorter,
+			&NGameAllowedSorter
+		};
+
+		NetworkGameList *item;
+		uint i;
+
+		if (!(this->servers.flags & VL_RESORT)) return;
+		if (this->servers.list_length == 0) return;
+
+		_internal_sort_order = !!(this->servers.flags & VL_DESC);
+		qsort(this->servers.sort_list, this->servers.list_length, sizeof(this->servers.sort_list[0]), ngame_sorter[this->servers.sort_type]);
+
+		/* After sorting ngl->sort_list contains the sorted items. Put these back
+		 * into the original list. Basically nothing has changed, we are only
+		 * shuffling the ->next pointers */
+		_network_game_list = this->servers.sort_list[0];
+		for (item = _network_game_list, i = 1; i != this->servers.list_length; i++) {
+			item->next = this->servers.sort_list[i];
+			item = item->next;
+		}
+		item->next = NULL;
+
+		this->servers.flags &= ~VL_RESORT;
+	}
+
+	/**
+	 * Draw a single server line.
+	 * @param cur_item  the server to draw.
+	 * @param y         from where to draw?
+	 * @param highlight does the line need to be highlighted?
+	 */
+	void DrawServerLine(const NetworkGameList *cur_item, uint y, bool highlight)
+	{
+		/* show highlighted item with a different colour */
+		if (highlight) GfxFillRect(this->widget[NGWW_NAME].left + 1, y - 2, this->widget[NGWW_INFO].right - 1, y + 9, 10);
+
+		SetDParamStr(0, cur_item->info.server_name);
+		DrawStringTruncated(this->widget[NGWW_NAME].left + 5, y, STR_02BD, TC_BLACK, this->widget[NGWW_NAME].right - this->widget[NGWW_NAME].left - 5);
+
+		SetDParam(0, cur_item->info.clients_on);
+		SetDParam(1, cur_item->info.clients_max);
+		SetDParam(2, cur_item->info.companies_on);
+		SetDParam(3, cur_item->info.companies_max);
+		DrawStringCentered(this->widget[NGWW_CLIENTS].left + 39, y, STR_NETWORK_GENERAL_ONLINE, TC_GOLD);
+
+		/* only draw icons if the server is online */
+		if (cur_item->online) {
+			/* draw a lock if the server is password protected */
+			if (cur_item->info.use_password) DrawSprite(SPR_LOCK, PAL_NONE, this->widget[NGWW_INFO].left + 5, y - 1);
+
+			/* draw red or green icon, depending on compatibility with server */
+			DrawSprite(SPR_BLOT, (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), this->widget[NGWW_INFO].left + 15, y);
+
+			/* draw flag according to server language */
+			DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, PAL_NONE, this->widget[NGWW_INFO].left + 25, y);
+		}
+	}
+
+	virtual void OnPaint()
+	{
+		const NetworkGameList *sel = this->server;
+		const SortButtonState arrow = (this->servers.flags & VL_DESC) ? SBS_DOWN : SBS_UP;
+
+		if (this->servers.flags & VL_REBUILD) {
+			this->BuildNetworkGameList();
+			SetVScrollCount(this, this->servers.list_length);
+		}
+		if (this->servers.flags & VL_RESORT) this->SortNetworkGameList();
+
+		/* 'Refresh' button invisible if no server selected */
+		this->SetWidgetDisabledState(NGWW_REFRESH, sel == NULL);
+		/* 'Join' button disabling conditions */
+		this->SetWidgetDisabledState(NGWW_JOIN, sel == NULL || // no Selected Server
+				!sel->online || // Server offline
+				sel->info.clients_on >= sel->info.clients_max || // Server full
+				!sel->info.compatible); // Revision mismatch
+
+		/* 'NewGRF Settings' button invisible if no NewGRF is used */
+		this->SetWidgetHiddenState(NGWW_NEWGRF, sel == NULL ||
+				!sel->online ||
+				sel->info.grfconfig == NULL);
+
+		SetDParam(0, 0x00);
+		SetDParam(1, _lan_internet_types_dropdown[_network_lan_internet]);
+		DrawWindowWidgets(this);
+
+		/* Edit box to set player name */
+		this->DrawEditBox(NGWW_PLAYER);
+
+		DrawString(this->widget[NGWW_PLAYER].left - 100, 23, STR_NETWORK_PLAYER_NAME, TC_GOLD);
+
+		/* Sort based on widgets: name, clients, compatibility */
+		switch (this->servers.sort_type) {
+			case NGWW_NAME    - NGWW_NAME: DrawSortButtonState(this, NGWW_NAME,    arrow); break;
+			case NGWW_CLIENTS - NGWW_NAME: DrawSortButtonState(this, NGWW_CLIENTS, arrow); break;
+			case NGWW_INFO    - NGWW_NAME: DrawSortButtonState(this, NGWW_INFO,    arrow); break;
+		}
+
+		uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
+		int32 n = 0;
+		int32 pos = this->vscroll.pos;
+		const NetworkGameList *cur_item = _network_game_list;
+
+		while (pos > 0 && cur_item != NULL) {
+			pos--;
+			cur_item = cur_item->next;
+		}
+
+		while (cur_item != NULL) {
+			this->DrawServerLine(cur_item, y, cur_item == sel);
+
+			cur_item = cur_item->next;
+			y += NET_PRC__SIZE_OF_ROW;
+			if (++n == this->vscroll.cap) break; // max number of games in the window
+		}
+
+		const NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
+		/* Draw the last joined server, if any */
+		if (last_joined != NULL) this->DrawServerLine(last_joined, y = this->widget[NGWW_LASTJOINED].top + 3, last_joined == sel);
+
+		/* Draw the right menu */
+		GfxFillRect(this->widget[NGWW_DETAILS].left + 1, 43, this->widget[NGWW_DETAILS].right - 1, 92, 157);
+		if (sel == NULL) {
+			DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 58, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
+		} else if (!sel->online) {
+			SetDParamStr(0, sel->info.server_name);
+			DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 68, STR_ORANGE, TC_FROMSTRING); // game name
+
+			DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 132, STR_NETWORK_SERVER_OFFLINE, TC_FROMSTRING); // server offline
+		} else { // show game info
+			uint16 y = 100;
+			const uint16 x = this->widget[NGWW_DETAILS].left + 5;
+
+			DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, 48, STR_NETWORK_GAME_INFO, TC_FROMSTRING);
 
 
-				SetDParamStr(0, sel->info.server_name);
-				DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name
-
-				SetDParamStr(0, sel->info.map_name);
-				DrawStringCenteredTruncated(w->widget[NGWW_DETAILS].left, w->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name
-
-				SetDParam(0, sel->info.clients_on);
-				SetDParam(1, sel->info.clients_max);
-				SetDParam(2, sel->info.companies_on);
-				SetDParam(3, sel->info.companies_max);
-				DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD);
-				y += 10;
-
-				SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang);
-				DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language
-				y += 10;
-
-				SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set);
-				DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset
-				y += 10;
-
-				SetDParam(0, sel->info.map_width);
-				SetDParam(1, sel->info.map_height);
-				DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size
-				y += 10;
-
-				SetDParamStr(0, sel->info.server_revision);
-				DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version
-				y += 10;
-
-				SetDParamStr(0, sel->info.hostname);
-				SetDParam(1, sel->port);
-				DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address
-				y += 10;
-
-				SetDParam(0, sel->info.start_date);
-				DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date
-				y += 10;
-
-				SetDParam(0, sel->info.game_date);
-				DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date
-				y += 10;
-
-				y += 2;
-
-				if (!sel->info.compatible) {
-					DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch
-				} else if (sel->info.clients_on == sel->info.clients_max) {
-					/* Show: server full, when clients_on == clients_max */
-					DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full
-				} else if (sel->info.use_password) {
-					DrawStringCentered(w->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning
-				}
-
-				y += 10;
-			}
-		} break;
-
-		case WE_CLICK:
-			nd->field = e->we.click.widget;
-			switch (e->we.click.widget) {
-				case NGWW_PLAYER:
-					ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q,  NGWW_PLAYER, 0, 0);
-					break;
-
-				case NGWW_CANCEL: // Cancel button
-					DeleteWindowById(WC_NETWORK_WINDOW, 0);
-					break;
-
-				case NGWW_CONN_BTN: // 'Connection' droplist
-					ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN
-					break;
+			SetDParamStr(0, sel->info.server_name);
+			DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 62, STR_ORANGE, TC_BLACK); // game name
 
-				case NGWW_NAME: // Sort by name
-				case NGWW_CLIENTS: // Sort by connected clients
-				case NGWW_INFO: // Connectivity (green dot)
-					if (ld->sort_type == e->we.click.widget - NGWW_NAME) ld->flags ^= VL_DESC;
-					ld->flags |= VL_RESORT;
-					ld->sort_type = e->we.click.widget - NGWW_NAME;
-
-					_ng_sorting.order = !!(ld->flags & VL_DESC);
-					_ng_sorting.criteria = ld->sort_type;
-					SetWindowDirty(w);
-					break;
-
-				case NGWW_MATRIX: { // Matrix to show networkgames
-					NetworkGameList *cur_item;
-					uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW;
-
-					if (id_v >= w->vscroll.cap) return; // click out of bounds
-					id_v += w->vscroll.pos;
-
-					cur_item = _network_game_list;
-					for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next;
-
-					nd->server = cur_item;
-					SetWindowDirty(w);
-				} break;
-
-				case NGWW_LASTJOINED: {
-					NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
-					if (last_joined != NULL) {
-						nd->server = last_joined;
-						SetWindowDirty(w);
-					}
-				} break;
+			SetDParamStr(0, sel->info.map_name);
+			DrawStringCenteredTruncated(this->widget[NGWW_DETAILS].left, this->widget[NGWW_DETAILS].right, 74, STR_02BD, TC_BLACK); // map name
 
-				case NGWW_FIND: // Find server automatically
-					switch (_network_lan_internet) {
-						case 0: NetworkUDPSearchGame(); break;
-						case 1: NetworkUDPQueryMasterServer(); break;
-					}
-					break;
-
-				case NGWW_ADD: // Add a server
-					ShowQueryString(
-						BindCString(_network_default_ip),
-						STR_NETWORK_ENTER_IP,
-						31 | 0x1000,  // maximum number of characters OR
-						250, // characters up to this width pixels, whichever is satisfied first
-						w, CS_ALPHANUMERAL);
-					break;
-
-				case NGWW_START: // Start server
-					ShowNetworkStartServerWindow();
-					break;
+			SetDParam(0, sel->info.clients_on);
+			SetDParam(1, sel->info.clients_max);
+			SetDParam(2, sel->info.companies_on);
+			SetDParam(3, sel->info.companies_max);
+			DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD);
+			y += 10;
 
-				case NGWW_JOIN: // Join Game
-					if (nd->server != NULL) {
-						snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&nd->server->ip));
-						_network_last_port = nd->server->port;
-						ShowNetworkLobbyWindow(nd->server);
-					}
-					break;
-
-				case NGWW_REFRESH: // Refresh
-					if (nd->server != NULL) NetworkUDPQueryServer(nd->server->info.hostname, nd->server->port);
-					break;
+			SetDParam(0, STR_NETWORK_LANG_ANY + sel->info.server_lang);
+			DrawString(x, y, STR_NETWORK_LANGUAGE, TC_GOLD); // server language
+			y += 10;
 
-				case NGWW_NEWGRF: // NewGRF Settings
-					if (nd->server != NULL) ShowNewGRFSettings(false, false, false, &nd->server->info.grfconfig);
-					break;
-			}
-			break;
+			SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set);
+			DrawString(x, y, STR_NETWORK_TILESET, TC_GOLD); // tileset
+			y += 10;
 
-		case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
-			switch (e->we.dropdown.button) {
-				case NGWW_CONN_BTN:
-					_network_lan_internet = e->we.dropdown.index;
-					break;
+			SetDParam(0, sel->info.map_width);
+			SetDParam(1, sel->info.map_height);
+			DrawString(x, y, STR_NETWORK_MAP_SIZE, TC_GOLD); // map size
+			y += 10;
 
-				default:
-					NOT_REACHED();
+			SetDParamStr(0, sel->info.server_revision);
+			DrawString(x, y, STR_NETWORK_SERVER_VERSION, TC_GOLD); // server version
+			y += 10;
+
+			SetDParamStr(0, sel->info.hostname);
+			SetDParam(1, sel->port);
+			DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, TC_GOLD); // server address
+			y += 10;
+
+			SetDParam(0, sel->info.start_date);
+			DrawString(x, y, STR_NETWORK_START_DATE, TC_GOLD); // start date
+			y += 10;
+
+			SetDParam(0, sel->info.game_date);
+			DrawString(x, y, STR_NETWORK_CURRENT_DATE, TC_GOLD); // current date
+			y += 10;
+
+			y += 2;
+
+			if (!sel->info.compatible) {
+				DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, TC_FROMSTRING); // server mismatch
+			} else if (sel->info.clients_on == sel->info.clients_max) {
+				/* Show: server full, when clients_on == clients_max */
+				DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_SERVER_FULL, TC_FROMSTRING); // server full
+			} else if (sel->info.use_password) {
+				DrawStringCentered(this->widget[NGWW_DETAILS].left + 115, y, STR_NETWORK_PASSWORD, TC_FROMSTRING); // password warning
 			}
 
-			SetWindowDirty(w);
-			break;
-
-		case WE_MOUSELOOP:
-			if (nd->field == NGWW_PLAYER) HandleEditBox(w, &WP(w, network_ql_d).q, NGWW_PLAYER);
-			break;
+			y += 10;
+		}
+	}
 
-		case WE_MESSAGE:
-			if (e->we.message.msg != 0) nd->server = NULL;
-			ld->flags |= VL_REBUILD;
-			SetWindowDirty(w);
-			break;
+	virtual void OnClick(Point pt, int widget)
+	{
+		this->field = widget;
+		switch (widget) {
+			case NGWW_PLAYER:
+				ShowOnScreenKeyboard(this, NGWW_PLAYER, 0, 0);
+				break;
 
-		case WE_KEYPRESS:
-			if (nd->field != NGWW_PLAYER) {
-				if (nd->server != NULL) {
-					if (e->we.keypress.keycode == WKC_DELETE) { // Press 'delete' to remove servers
-						NetworkGameListRemoveItem(nd->server);
-						NetworkRebuildHostList();
-						nd->server = NULL;
-					}
+			case NGWW_CANCEL: // Cancel button
+				DeleteWindowById(WC_NETWORK_WINDOW, 0);
+				break;
+
+			case NGWW_CONN_BTN: // 'Connection' droplist
+				ShowDropDownMenu(this, _lan_internet_types_dropdown, _network_lan_internet, NGWW_CONN_BTN, 0, 0); // do it for widget NSSW_CONN_BTN
+				break;
+
+			case NGWW_NAME: // Sort by name
+			case NGWW_CLIENTS: // Sort by connected clients
+			case NGWW_INFO: // Connectivity (green dot)
+				if (this->servers.sort_type == widget - NGWW_NAME) this->servers.flags ^= VL_DESC;
+				this->servers.flags |= VL_RESORT;
+				this->servers.sort_type = widget - NGWW_NAME;
+
+				_ng_sorting.order = !!(this->servers.flags & VL_DESC);
+				_ng_sorting.criteria = this->servers.sort_type;
+				this->SetDirty();
+				break;
+
+			case NGWW_MATRIX: { // Matrix to show networkgames
+				NetworkGameList *cur_item;
+				uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW;
+
+				if (id_v >= this->vscroll.cap) return; // click out of bounds
+				id_v += this->vscroll.pos;
+
+				cur_item = _network_game_list;
+				for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next;
+
+				this->server = cur_item;
+				this->SetDirty();
+			} break;
+
+			case NGWW_LASTJOINED: {
+				NetworkGameList *last_joined = NetworkGameListAddItem(inet_addr(_network_last_host), _network_last_port);
+				if (last_joined != NULL) {
+					this->server = last_joined;
+					this->SetDirty();
+				}
+			} break;
+
+			case NGWW_FIND: // Find server automatically
+				switch (_network_lan_internet) {
+					case 0: NetworkUDPSearchGame(); break;
+					case 1: NetworkUDPQueryMasterServer(); break;
 				}
 				break;
-			}
-
-			if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NGWW_PLAYER, e) == 1) break; // enter pressed
-
-			/* The name is only allowed when it starts with a letter! */
-			if (_edit_str_net_buf[0] != '\0' && _edit_str_net_buf[0] != ' ') {
-				ttd_strlcpy(_network_player_name, _edit_str_net_buf, lengthof(_network_player_name));
-			} else {
-				ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name));
-			}
-
-			break;
-
-		case WE_ON_EDIT_TEXT:
-			NetworkAddServer(e->we.edittext.str);
-			NetworkRebuildHostList();
-			break;
-
-		case WE_RESIZE: {
-			w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
 
-			w->widget[NGWW_MATRIX].data = (w->vscroll.cap << 8) + 1;
-
-			SetVScrollCount(w, ld->list_length);
-
-			int widget_width = w->widget[NGWW_FIND].right - w->widget[NGWW_FIND].left;
-			int space = (w->width - 4 * widget_width - 25) / 3;
+			case NGWW_ADD: // Add a server
+				ShowQueryString(
+					BindCString(_network_default_ip),
+					STR_NETWORK_ENTER_IP,
+					31 | 0x1000,  // maximum number of characters OR
+					250, // characters up to this width pixels, whichever is satisfied first
+					this, CS_ALPHANUMERAL);
+				break;
 
-			int offset = 10;
-			for (uint i = 0; i < 4; i++) {
-				w->widget[NGWW_FIND + i].left  = offset;
-				offset += widget_width;
-				w->widget[NGWW_FIND + i].right = offset;
-				offset += space;
+			case NGWW_START: // Start server
+				ShowNetworkStartServerWindow();
+				break;
+
+			case NGWW_JOIN: // Join Game
+				if (this->server != NULL) {
+					snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&this->server->ip));
+					_network_last_port = this->server->port;
+					ShowNetworkLobbyWindow(this->server);
+				}
+				break;
+
+			case NGWW_REFRESH: // Refresh
+				if (this->server != NULL) NetworkUDPQueryServer(this->server->info.hostname, this->server->port);
+				break;
+
+			case NGWW_NEWGRF: // NewGRF Settings
+				if (this->server != NULL) ShowNewGRFSettings(false, false, false, &this->server->info.grfconfig);
+				break;
+		}
+	}
+
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		switch (widget) {
+			case NGWW_CONN_BTN:
+				_network_lan_internet = index;
+				break;
+
+			default:
+				NOT_REACHED();
+		}
+
+		this->SetDirty();
+	}
+
+	virtual void OnMouseLoop()
+	{
+		if (this->field == NGWW_PLAYER) this->HandleEditBox(NGWW_PLAYER);
+	}
+
+	virtual void OnInvalidateData(int data)
+	{
+		if (data != 0) this->server = NULL;
+		this->servers.flags |= VL_REBUILD;
+		this->SetDirty();
+	}
+
+	virtual bool OnKeyPress(uint16 key, uint16 keycode)
+	{
+		bool cont = true;
+		if (this->field != NGWW_PLAYER) {
+			if (this->server != NULL) {
+				if (keycode == WKC_DELETE) { // Press 'delete' to remove servers
+					NetworkGameListRemoveItem(this->server);
+					NetworkRebuildHostList();
+					this->server = NULL;
+				}
 			}
-		} break;
+			return cont;
+		}
 
-		case WE_DESTROY: // Nicely clean up the sort-list
-			free(WP(w, network_ql_d).sort_list);
-			break;
+		if (this->HandleEditBoxKey(NGWW_PLAYER, keycode, key, cont) == 1) return cont; // enter pressed
+
+		/* The name is only allowed when it starts with a letter! */
+		if (StrEmpty(this->edit_str_buf) && this->edit_str_buf[0] != ' ') {
+			ttd_strlcpy(_network_player_name, this->edit_str_buf, lengthof(_network_player_name));
+		} else {
+			ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name));
+		}
+		return cont;
 	}
-}
+
+	virtual void OnQueryTextFinished(char *str)
+	{
+		if (!StrEmpty(str)) {
+			NetworkAddServer(str);
+			NetworkRebuildHostList();
+		}
+	}
+
+	virtual void OnResize(Point new_size, Point delta)
+	{
+		this->vscroll.cap += delta.y / (int)this->resize.step_height;
+
+		this->widget[NGWW_MATRIX].data = (this->vscroll.cap << 8) + 1;
+
+		SetVScrollCount(this, this->servers.list_length);
+
+		int widget_width = this->widget[NGWW_FIND].right - this->widget[NGWW_FIND].left;
+		int space = (this->width - 4 * widget_width - 25) / 3;
+
+		int offset = 10;
+		for (uint i = 0; i < 4; i++) {
+			this->widget[NGWW_FIND + i].left  = offset;
+			offset += widget_width;
+			this->widget[NGWW_FIND + i].right = offset;
+			offset += space;
+		}
+	}
+};
 
 static const Widget _network_game_window_widgets[] = {
 /* TOP */
@@ -653,7 +641,7 @@
 	WC_NETWORK_WINDOW, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_STD_BTN | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE,
 	_network_game_window_widgets,
-	NetworkGameWindowWndProc,
+	NULL,
 };
 
 void ShowNetworkGameWindow()
@@ -675,16 +663,7 @@
 		_ng_sorting.order = 0;    // sort ascending by default
 	}
 
-	Window *w = AllocateWindowDesc(&_network_game_window_desc);
-	if (w != NULL) {
-		querystr_d *querystr = &WP(w, network_ql_d).q;
-
-		ttd_strlcpy(_edit_str_net_buf, _network_player_name, lengthof(_edit_str_net_buf));
-		querystr->afilter = CS_ALPHANUMERAL;
-		InitializeTextBuffer(&querystr->text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 120);
-
-		UpdateNetworkGameWindow(true);
-	}
+	new NetworkGameWindow(&_network_game_window_desc);
 }
 
 enum {
@@ -714,225 +693,237 @@
 	NSSW_CANCEL          = 27,   ///< 'Cancel' button
 };
 
-/**
- * Handler of actions done in the NetworkStartServer window
- *
- * @param w pointer to the Window structure
- * @param e pointer to window event
- * @note    Uses network_ql_d (network_d, querystr_d and list_d) WP macro
- * @see     struct _network_start_server_window_widgets
- * @see     enum NetworkStartServerWidgets
- */
-static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
-{
-	network_d *nd = &WP(w, network_ql_d).n;
-
-	switch (e->event) {
-		case WE_CREATE: // focus input box
-			nd->field = NSSW_GAMENAME;
-			_network_game_info.use_password = (_network_server_password[0] != '\0');
-			break;
-
-		case WE_PAINT: {
-			int y = NSSWND_START, pos;
-			const FiosItem *item;
-
-			/* draw basic widgets */
-			SetDParam(1, _connection_types_dropdown[_network_advertise]);
-			SetDParam(2, _network_game_info.clients_max);
-			SetDParam(3, _network_game_info.companies_max);
-			SetDParam(4, _network_game_info.spectators_max);
-			SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang);
-			DrawWindowWidgets(w);
-
-			/* editbox to set game name */
-			DrawEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME);
-
-			/* if password is set, draw red '*' next to 'Set password' button */
-			if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED);
-
-			/* draw list of maps */
-			GfxFillRect(11, 63, 258, 215, 0xD7);  // black background of maps list
-
-			pos = w->vscroll.pos;
-			while (pos < _fios_num + 1) {
-				item = _fios_list + pos - 1;
-				if (item == nd->map || (pos == 0 && nd->map == NULL))
-					GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour
-
-				if (pos == 0) {
-					DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN);
-				} else {
-					DoDrawString(item->title, 14, y, _fios_colors[item->type] );
-				}
-				pos++;
-				y += NSSWND_ROWSIZE;
-
-				if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break;
-			}
-		} break;
-
-		case WE_CLICK:
-			if (e->we.click.widget != NSSW_CONNTYPE_BTN && e->we.click.widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(w);
-			nd->field = e->we.click.widget;
-			switch (e->we.click.widget) {
-				case NSSW_CLOSE:  // Close 'X'
-				case NSSW_CANCEL: // Cancel button
-					ShowNetworkGameWindow();
-					break;
-
-				case NSSW_GAMENAME:
-					ShowOnScreenKeyboard(w, &WP(w, network_ql_d).q,  NSSW_GAMENAME, 0, 0);
-					break;
-
-				case NSSW_SETPWD: // Set password button
-					nd->widget_id = NSSW_SETPWD;
-					ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, w, CS_ALPHANUMERAL);
-					break;
-
-				case NSSW_SELMAP: { // Select map
-					int y = (e->we.click.pt.y - NSSWND_START) / NSSWND_ROWSIZE;
-
-					y += w->vscroll.pos;
-					if (y >= w->vscroll.count) return;
-
-					nd->map = (y == 0) ? NULL : _fios_list + y - 1;
-					SetWindowDirty(w);
-				} break;
-
-				case NSSW_CONNTYPE_BTN: // Connection type
-					ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN
-					break;
+struct NetworkStartServerWindow : public QueryStringBaseWindow {
+	byte field;                  ///< Selected text-field
+	FiosItem *map;               ///< Selected map
+	byte widget_id;              ///< The widget that has the pop-up input menu
 
-				case NSSW_CLIENTS_BTND:    case NSSW_CLIENTS_BTNU:    // Click on up/down button for number of clients
-				case NSSW_COMPANIES_BTND:  case NSSW_COMPANIES_BTNU:  // Click on up/down button for number of companies
-				case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators
-					/* Don't allow too fast scrolling */
-					if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
-						w->HandleButtonClick(e->we.click.widget);
-						SetWindowDirty(w);
-						switch (e->we.click.widget) {
-							default: NOT_REACHED();
-							case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU:
-								_network_game_info.clients_max    = Clamp(_network_game_info.clients_max    + e->we.click.widget - NSSW_CLIENTS_TXT,    2, MAX_CLIENTS);
-								break;
-							case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU:
-								_network_game_info.companies_max  = Clamp(_network_game_info.companies_max  + e->we.click.widget - NSSW_COMPANIES_TXT,  1, MAX_PLAYERS);
-								break;
-							case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU:
-								_network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + e->we.click.widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS);
-								break;
-						}
-					}
-					_left_button_clicked = false;
-					break;
-
-				case NSSW_CLIENTS_TXT:    // Click on number of players
-					nd->widget_id = NSSW_CLIENTS_TXT;
-					SetDParam(0, _network_game_info.clients_max);
-					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS,    3, 50, w, CS_NUMERAL);
-					break;
-
-				case NSSW_COMPANIES_TXT:  // Click on number of companies
-					nd->widget_id = NSSW_COMPANIES_TXT;
-					SetDParam(0, _network_game_info.companies_max);
-					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES,  3, 50, w, CS_NUMERAL);
-					break;
-
-				case NSSW_SPECTATORS_TXT: // Click on number of spectators
-					nd->widget_id = NSSW_SPECTATORS_TXT;
-					SetDParam(0, _network_game_info.spectators_max);
-					ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, w, CS_NUMERAL);
-					break;
-
-				case NSSW_LANGUAGE_BTN: { // Language
-					uint sel = 0;
-					for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) {
-						if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) {
-							sel = i;
-							break;
-						}
-					}
-					ShowDropDownMenu(w, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0);
-				} break;
-
-				case NSSW_START: // Start game
-					_is_network_server = true;
+	NetworkStartServerWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc)
+	{
+		ttd_strlcpy(this->edit_str_buf, _network_server_name, lengthof(this->edit_str_buf));
 
-					if (nd->map == NULL) { // start random new game
-						ShowGenerateLandscape();
-					} else { // load a scenario
-						char *name = FiosBrowseTo(nd->map);
-						if (name != NULL) {
-							SetFiosType(nd->map->type);
-							_file_to_saveload.filetype = FT_SCENARIO;
-							ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
-							ttd_strlcpy(_file_to_saveload.title, nd->map->title, sizeof(_file_to_saveload.title));
-
-							delete w;
-							SwitchMode(SM_START_SCENARIO);
-						}
-					}
-					break;
+		_saveload_mode = SLD_NEW_GAME;
+		BuildFileList();
+		this->vscroll.cap = 12;
+		this->vscroll.count = _fios_num + 1;
 
-				case NSSW_LOAD: // Load game
-					_is_network_server = true;
-					/* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets
-					* copied all the elements of 'load game' and upon closing that, it segfaults */
-					delete w;
-					ShowSaveLoadDialog(SLD_LOAD_GAME);
-					break;
-			}
-			break;
+		this->afilter = CS_ALPHANUMERAL;
+		InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 160);
 
-		case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
-			switch (e->we.dropdown.button) {
-				case NSSW_CONNTYPE_BTN:
-					_network_advertise = (e->we.dropdown.index != 0);
-					break;
-				case NSSW_LANGUAGE_BTN:
-					_network_game_info.server_lang = _language_dropdown[e->we.dropdown.index] - STR_NETWORK_LANG_ANY;
-					break;
-				default:
-					NOT_REACHED();
+		this->field = NSSW_GAMENAME;
+		_network_game_info.use_password = !StrEmpty(_network_server_password);
+
+		this->FindWindowPlacementAndResize(desc);
+	}
+
+	virtual void OnPaint()
+	{
+		int y = NSSWND_START, pos;
+		const FiosItem *item;
+
+		/* draw basic widgets */
+		SetDParam(1, _connection_types_dropdown[_network_advertise]);
+		SetDParam(2, _network_game_info.clients_max);
+		SetDParam(3, _network_game_info.companies_max);
+		SetDParam(4, _network_game_info.spectators_max);
+		SetDParam(5, STR_NETWORK_LANG_ANY + _network_game_info.server_lang);
+		DrawWindowWidgets(this);
+
+		/* editbox to set game name */
+		this->DrawEditBox(NSSW_GAMENAME);
+
+		/* if password is set, draw red '*' next to 'Set password' button */
+		if (_network_game_info.use_password) DoDrawString("*", 408, 23, TC_RED);
+
+		/* draw list of maps */
+		GfxFillRect(11, 63, 258, 215, 0xD7);  // black background of maps list
+
+		pos = this->vscroll.pos;
+		while (pos < _fios_num + 1) {
+			item = _fios_list + pos - 1;
+			if (item == this->map || (pos == 0 && this->map == NULL))
+				GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour
+
+			if (pos == 0) {
+				DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, TC_DARK_GREEN);
+			} else {
+				DoDrawString(item->title, 14, y, _fios_colors[item->type] );
 			}
-
-			SetWindowDirty(w);
-			break;
-
-		case WE_MOUSELOOP:
-			if (nd->field == NSSW_GAMENAME) HandleEditBox(w, &WP(w, network_ql_d).q, NSSW_GAMENAME);
-			break;
-
-		case WE_KEYPRESS:
-			if (nd->field == NSSW_GAMENAME) {
-				if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, NSSW_GAMENAME, e) == 1) break; // enter pressed
-
-				ttd_strlcpy(_network_server_name, WP(w, network_ql_d).q.text.buf, sizeof(_network_server_name));
-			}
-			break;
+			pos++;
+			y += NSSWND_ROWSIZE;
 
-		case WE_ON_EDIT_TEXT:
-			if (e->we.edittext.str == NULL) break;
+			if (y >= this->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break;
+		}
+	}
 
-			if (nd->widget_id == NSSW_SETPWD) {
-				ttd_strlcpy(_network_server_password, e->we.edittext.str, lengthof(_network_server_password));
-				_network_game_info.use_password = (_network_server_password[0] != '\0');
-			} else {
-				int32 value = atoi(e->we.edittext.str);
-				w->InvalidateWidget(nd->widget_id);
-				switch (nd->widget_id) {
-					default: NOT_REACHED();
-					case NSSW_CLIENTS_TXT:    _network_game_info.clients_max    = Clamp(value, 2, MAX_CLIENTS); break;
-					case NSSW_COMPANIES_TXT:  _network_game_info.companies_max  = Clamp(value, 1, MAX_PLAYERS); break;
-					case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break;
+	virtual void OnClick(Point pt, int widget)
+	{
+		if (widget != NSSW_CONNTYPE_BTN && widget != NSSW_LANGUAGE_BTN) HideDropDownMenu(this);
+		this->field = widget;
+		switch (widget) {
+			case NSSW_CLOSE:  // Close 'X'
+			case NSSW_CANCEL: // Cancel button
+				ShowNetworkGameWindow();
+				break;
+
+			case NSSW_GAMENAME:
+				ShowOnScreenKeyboard(this, NSSW_GAMENAME, 0, 0);
+				break;
+
+			case NSSW_SETPWD: // Set password button
+				this->widget_id = NSSW_SETPWD;
+				ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, this, CS_ALPHANUMERAL);
+				break;
+
+			case NSSW_SELMAP: { // Select map
+				int y = (pt.y - NSSWND_START) / NSSWND_ROWSIZE;
+
+				y += this->vscroll.pos;
+				if (y >= this->vscroll.count) return;
+
+				this->map = (y == 0) ? NULL : _fios_list + y - 1;
+				this->SetDirty();
+			} break;
+
+			case NSSW_CONNTYPE_BTN: // Connection type
+				ShowDropDownMenu(this, _connection_types_dropdown, _network_advertise, NSSW_CONNTYPE_BTN, 0, 0); // do it for widget NSSW_CONNTYPE_BTN
+				break;
+
+			case NSSW_CLIENTS_BTND:    case NSSW_CLIENTS_BTNU:    // Click on up/down button for number of clients
+			case NSSW_COMPANIES_BTND:  case NSSW_COMPANIES_BTNU:  // Click on up/down button for number of companies
+			case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU: // Click on up/down button for number of spectators
+				/* Don't allow too fast scrolling */
+				if ((this->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
+					this->HandleButtonClick(widget);
+					this->SetDirty();
+					switch (widget) {
+						default: NOT_REACHED();
+						case NSSW_CLIENTS_BTND: case NSSW_CLIENTS_BTNU:
+							_network_game_info.clients_max    = Clamp(_network_game_info.clients_max    + widget - NSSW_CLIENTS_TXT,    2, MAX_CLIENTS);
+							break;
+						case NSSW_COMPANIES_BTND: case NSSW_COMPANIES_BTNU:
+							_network_game_info.companies_max  = Clamp(_network_game_info.companies_max  + widget - NSSW_COMPANIES_TXT,  1, MAX_PLAYERS);
+							break;
+						case NSSW_SPECTATORS_BTND: case NSSW_SPECTATORS_BTNU:
+							_network_game_info.spectators_max = Clamp(_network_game_info.spectators_max + widget - NSSW_SPECTATORS_TXT, 0, MAX_CLIENTS);
+							break;
+					}
 				}
-			}
+				_left_button_clicked = false;
+				break;
 
-			SetWindowDirty(w);
-			break;
+			case NSSW_CLIENTS_TXT:    // Click on number of players
+				this->widget_id = NSSW_CLIENTS_TXT;
+				SetDParam(0, _network_game_info.clients_max);
+				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_CLIENTS,    3, 50, this, CS_NUMERAL);
+				break;
+
+			case NSSW_COMPANIES_TXT:  // Click on number of companies
+				this->widget_id = NSSW_COMPANIES_TXT;
+				SetDParam(0, _network_game_info.companies_max);
+				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_COMPANIES,  3, 50, this, CS_NUMERAL);
+				break;
+
+			case NSSW_SPECTATORS_TXT: // Click on number of spectators
+				this->widget_id = NSSW_SPECTATORS_TXT;
+				SetDParam(0, _network_game_info.spectators_max);
+				ShowQueryString(STR_CONFIG_PATCHES_INT32, STR_NETWORK_NUMBER_OF_SPECTATORS, 3, 50, this, CS_NUMERAL);
+				break;
+
+			case NSSW_LANGUAGE_BTN: { // Language
+				uint sel = 0;
+				for (uint i = 0; i < lengthof(_language_dropdown) - 1; i++) {
+					if (_language_dropdown[i] == STR_NETWORK_LANG_ANY + _network_game_info.server_lang) {
+						sel = i;
+						break;
+					}
+				}
+				ShowDropDownMenu(this, _language_dropdown, sel, NSSW_LANGUAGE_BTN, 0, 0);
+			} break;
+
+			case NSSW_START: // Start game
+				_is_network_server = true;
+
+				if (this->map == NULL) { // start random new game
+					ShowGenerateLandscape();
+				} else { // load a scenario
+					char *name = FiosBrowseTo(this->map);
+					if (name != NULL) {
+						SetFiosType(this->map->type);
+						_file_to_saveload.filetype = FT_SCENARIO;
+						ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
+						ttd_strlcpy(_file_to_saveload.title, this->map->title, sizeof(_file_to_saveload.title));
+
+						delete this;
+						SwitchMode(SM_START_SCENARIO);
+					}
+				}
+				break;
+
+			case NSSW_LOAD: // Load game
+				_is_network_server = true;
+				/* XXX - WC_NETWORK_WINDOW (this window) should stay, but if it stays, it gets
+				* copied all the elements of 'load game' and upon closing that, it segfaults */
+				delete this;
+				ShowSaveLoadDialog(SLD_LOAD_GAME);
+				break;
+		}
 	}
-}
+
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		switch (widget) {
+			case NSSW_CONNTYPE_BTN:
+				_network_advertise = (index != 0);
+				break;
+			case NSSW_LANGUAGE_BTN:
+				_network_game_info.server_lang = _language_dropdown[index] - STR_NETWORK_LANG_ANY;
+				break;
+			default:
+				NOT_REACHED();
+		}
+
+		this->SetDirty();
+	}
+
+	virtual void OnMouseLoop()
+	{
+		if (this->field == NSSW_GAMENAME) this->HandleEditBox(NSSW_GAMENAME);
+	}
+
+	virtual bool OnKeyPress(uint16 key, uint16 keycode)
+	{
+		bool cont = true;
+		if (this->field == NSSW_GAMENAME) {
+			if (this->HandleEditBoxKey(NSSW_GAMENAME, key, keycode, cont) == 1) return cont; // enter pressed
+
+			ttd_strlcpy(_network_server_name, this->text.buf, sizeof(_network_server_name));
+		}
+
+		return cont;
+	}
+
+	virtual void OnQueryTextFinished(char *str)
+	{
+		if (str == NULL) return;
+
+		if (this->widget_id == NSSW_SETPWD) {
+			ttd_strlcpy(_network_server_password, str, lengthof(_network_server_password));
+			_network_game_info.use_password = !StrEmpty(_network_server_password);
+		} else {
+			int32 value = atoi(str);
+			this->InvalidateWidget(this->widget_id);
+			switch (this->widget_id) {
+				default: NOT_REACHED();
+				case NSSW_CLIENTS_TXT:    _network_game_info.clients_max    = Clamp(value, 2, MAX_CLIENTS); break;
+				case NSSW_COMPANIES_TXT:  _network_game_info.companies_max  = Clamp(value, 1, MAX_PLAYERS); break;
+				case NSSW_SPECTATORS_TXT: _network_game_info.spectators_max = Clamp(value, 0, MAX_CLIENTS); break;
+			}
+		}
+
+		this->SetDirty();
+	}
+};
 
 static const Widget _network_start_server_window_widgets[] = {
 /* Window decoration and background panel */
@@ -985,23 +976,14 @@
 	WC_NETWORK_WINDOW, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 	_network_start_server_window_widgets,
-	NetworkStartServerWindowWndProc,
+	NULL,
 };
 
 static void ShowNetworkStartServerWindow()
 {
 	DeleteWindowById(WC_NETWORK_WINDOW, 0);
 
-	Window *w = AllocateWindowDesc(&_network_start_server_window_desc);
-	ttd_strlcpy(_edit_str_net_buf, _network_server_name, lengthof(_edit_str_net_buf));
-
-	_saveload_mode = SLD_NEW_GAME;
-	BuildFileList();
-	w->vscroll.cap = 12;
-	w->vscroll.count = _fios_num + 1;
-
-	WP(w, network_ql_d).q.afilter = CS_ALPHANUMERAL;
-	InitializeTextBuffer(&WP(w, network_ql_d).q.text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 160);
+	new NetworkStartServerWindow(&_network_start_server_window_desc);
 }
 
 static PlayerID NetworkLobbyFindCompanyIndex(byte pos)
@@ -1028,164 +1010,156 @@
 	NLWW_CANCEL   = 12, ///< 'Cancel' button
 };
 
-/**
- * Handler of actions done in the NetworkLobby window
- *
- * @param w pointer to the Window structure
- * @param e pointer to window event
- * @note    uses network_d WP macro
- * @see     struct _network_lobby_window_widgets
- * @see     enum NetworkLobbyWindowWidgets
- */
-static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e)
-{
-	network_d *nd = &WP(w, network_d);
-
-	switch (e->event) {
-		case WE_CREATE:
-			nd->company = INVALID_PLAYER;
-			break;
-
-		case WE_PAINT: {
-			const NetworkGameInfo *gi = &nd->server->info;
-			int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos;
+struct NetworkLobbyWindow : public Window {
+	PlayerID company;        ///< Select company
+	NetworkGameList *server; ///< Selected server
 
-			/* Join button is disabled when no company is selected */
-			w->SetWidgetDisabledState(NLWW_JOIN, nd->company == INVALID_PLAYER);
-			/* Cannot start new company if there are too many */
-			w->SetWidgetDisabledState(NLWW_NEW, gi->companies_on >= gi->companies_max);
-			/* Cannot spectate if there are too many spectators */
-			w->SetWidgetDisabledState(NLWW_SPECTATE, gi->spectators_on >= gi->spectators_max);
-
-			/* Draw window widgets */
-			SetDParamStr(0, gi->server_name);
-			DrawWindowWidgets(w);
+	NetworkLobbyWindow(const WindowDesc *desc, NetworkGameList *ngl) :
+			Window(desc), company(INVALID_PLAYER), server(ngl)
+	{
+		this->vscroll.cap = 10;
 
-			/* Draw company list */
-			pos = w->vscroll.pos;
-			while (pos < gi->companies_on) {
-				byte company = NetworkLobbyFindCompanyIndex(pos);
-				bool income = false;
-				if (nd->company == company)
-					GfxFillRect(11, y - 1, 154, y + 10, 10); // show highlighted item with a different colour
+		this->FindWindowPlacementAndResize(desc);
+	}
 
-				DoDrawStringTruncated(_network_player_info[company].company_name, 13, y, TC_BLACK, 135 - 13);
-				if (_network_player_info[company].use_password != 0) DrawSprite(SPR_LOCK, PAL_NONE, 135, y);
+	virtual void OnPaint()
+	{
+		const NetworkGameInfo *gi = &this->server->info;
+		int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos;
 
-				/* If the company's income was positive puts a green dot else a red dot */
-				if (_network_player_info[company].income >= 0) income = true;
-				DrawSprite(SPR_BLOT, income ? PALETTE_TO_GREEN : PALETTE_TO_RED, 145, y);
+		/* Join button is disabled when no company is selected */
+		this->SetWidgetDisabledState(NLWW_JOIN, this->company == INVALID_PLAYER);
+		/* Cannot start new company if there are too many */
+		this->SetWidgetDisabledState(NLWW_NEW, gi->companies_on >= gi->companies_max);
+		/* Cannot spectate if there are too many spectators */
+		this->SetWidgetDisabledState(NLWW_SPECTATE, gi->spectators_on >= gi->spectators_max);
 
-				pos++;
-				y += NET_PRC__SIZE_OF_ROW;
-				if (pos >= w->vscroll.cap) break;
+		/* Draw window widgets */
+		SetDParamStr(0, gi->server_name);
+		DrawWindowWidgets(this);
+
+		/* Draw company list */
+		pos = this->vscroll.pos;
+		while (pos < gi->companies_on) {
+			byte company = NetworkLobbyFindCompanyIndex(pos);
+			bool income = false;
+			if (this->company == company) {
+				GfxFillRect(11, y - 1, 154, y + 10, 10); // show highlighted item with a different colour
 			}
 
-			/* Draw info about selected company when it is selected in the left window */
-			GfxFillRect(174, 39, 403, 75, 157);
-			DrawStringCentered(290, 50, STR_NETWORK_COMPANY_INFO, TC_FROMSTRING);
-			if (nd->company != INVALID_PLAYER) {
-				const uint x = 183;
-				const uint trunc_width = w->widget[NLWW_DETAILS].right - x;
-				y = 80;
-
-				SetDParam(0, nd->server->info.clients_on);
-				SetDParam(1, nd->server->info.clients_max);
-				SetDParam(2, nd->server->info.companies_on);
-				SetDParam(3, nd->server->info.companies_max);
-				DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD);
-				y += 10;
-
-				SetDParamStr(0, _network_player_info[nd->company].company_name);
-				DrawStringTruncated(x, y, STR_NETWORK_COMPANY_NAME, TC_GOLD, trunc_width);
-				y += 10;
-
-				SetDParam(0, _network_player_info[nd->company].inaugurated_year);
-				DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, TC_GOLD); // inauguration year
-				y += 10;
-
-				SetDParam(0, _network_player_info[nd->company].company_value);
-				DrawString(x, y, STR_NETWORK_VALUE, TC_GOLD); // company value
-				y += 10;
-
-				SetDParam(0, _network_player_info[nd->company].money);
-				DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, TC_GOLD); // current balance
-				y += 10;
-
-				SetDParam(0, _network_player_info[nd->company].income);
-				DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, TC_GOLD); // last year's income
-				y += 10;
-
-				SetDParam(0, _network_player_info[nd->company].performance);
-				DrawString(x, y, STR_NETWORK_PERFORMANCE, TC_GOLD); // performance
-				y += 10;
-
-				SetDParam(0, _network_player_info[nd->company].num_vehicle[0]);
-				SetDParam(1, _network_player_info[nd->company].num_vehicle[1]);
-				SetDParam(2, _network_player_info[nd->company].num_vehicle[2]);
-				SetDParam(3, _network_player_info[nd->company].num_vehicle[3]);
-				SetDParam(4, _network_player_info[nd->company].num_vehicle[4]);
-				DrawString(x, y, STR_NETWORK_VEHICLES, TC_GOLD); // vehicles
-				y += 10;
+			DoDrawStringTruncated(_network_player_info[company].company_name, 13, y, TC_BLACK, 135 - 13);
+			if (_network_player_info[company].use_password != 0) DrawSprite(SPR_LOCK, PAL_NONE, 135, y);
 
-				SetDParam(0, _network_player_info[nd->company].num_station[0]);
-				SetDParam(1, _network_player_info[nd->company].num_station[1]);
-				SetDParam(2, _network_player_info[nd->company].num_station[2]);
-				SetDParam(3, _network_player_info[nd->company].num_station[3]);
-				SetDParam(4, _network_player_info[nd->company].num_station[4]);
-				DrawString(x, y, STR_NETWORK_STATIONS, TC_GOLD); // stations
-				y += 10;
-
-				SetDParamStr(0, _network_player_info[nd->company].players);
-				DrawStringTruncated(x, y, STR_NETWORK_PLAYERS, TC_GOLD, trunc_width); // players
-			}
-		} break;
-
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case NLWW_CLOSE:    // Close 'X'
-				case NLWW_CANCEL:   // Cancel button
-					ShowNetworkGameWindow();
-					break;
-
-				case NLWW_MATRIX: { // Company list
-					uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW;
-
-					if (id_v >= w->vscroll.cap) break;
+			/* If the company's income was positive puts a green dot else a red dot */
+			if (_network_player_info[company].income >= 0) income = true;
+			DrawSprite(SPR_BLOT, income ? PALETTE_TO_GREEN : PALETTE_TO_RED, 145, y);
 
-					id_v += w->vscroll.pos;
-					nd->company = (id_v >= nd->server->info.companies_on) ? INVALID_PLAYER : NetworkLobbyFindCompanyIndex(id_v);
-					SetWindowDirty(w);
-				} break;
-
-				case NLWW_JOIN:     // Join company
-					/* Button can be clicked only when it is enabled */
-					_network_playas = nd->company;
-					NetworkClientConnectGame(_network_last_host, _network_last_port);
-					break;
-
-				case NLWW_NEW:      // New company
-					_network_playas = PLAYER_NEW_COMPANY;
-					NetworkClientConnectGame(_network_last_host, _network_last_port);
-					break;
+			pos++;
+			y += NET_PRC__SIZE_OF_ROW;
+			if (pos >= this->vscroll.cap) break;
+		}
 
-				case NLWW_SPECTATE: // Spectate game
-					_network_playas = PLAYER_SPECTATOR;
-					NetworkClientConnectGame(_network_last_host, _network_last_port);
-					break;
+		/* Draw info about selected company when it is selected in the left window */
+		GfxFillRect(174, 39, 403, 75, 157);
+		DrawStringCentered(290, 50, STR_NETWORK_COMPANY_INFO, TC_FROMSTRING);
+		if (this->company != INVALID_PLAYER) {
+			const uint x = 183;
+			const uint trunc_width = this->widget[NLWW_DETAILS].right - x;
+			y = 80;
 
-				case NLWW_REFRESH:  // Refresh
-					NetworkTCPQueryServer(_network_last_host, _network_last_port); // company info
-					NetworkUDPQueryServer(_network_last_host, _network_last_port); // general data
-					break;
-			}
-			break;
+			SetDParam(0, gi->clients_on);
+			SetDParam(1, gi->clients_max);
+			SetDParam(2, gi->companies_on);
+			SetDParam(3, gi->companies_max);
+			DrawString(x, y, STR_NETWORK_CLIENTS, TC_GOLD);
+			y += 10;
 
-		case WE_MESSAGE:
-			SetWindowDirty(w);
-			break;
+			SetDParamStr(0, _network_player_info[this->company].company_name);
+			DrawStringTruncated(x, y, STR_NETWORK_COMPANY_NAME, TC_GOLD, trunc_width);
+			y += 10;
+
+			SetDParam(0, _network_player_info[this->company].inaugurated_year);
+			DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, TC_GOLD); // inauguration year
+			y += 10;
+
+			SetDParam(0, _network_player_info[this->company].company_value);
+			DrawString(x, y, STR_NETWORK_VALUE, TC_GOLD); // company value
+			y += 10;
+
+			SetDParam(0, _network_player_info[this->company].money);
+			DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, TC_GOLD); // current balance
+			y += 10;
+
+			SetDParam(0, _network_player_info[this->company].income);
+			DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, TC_GOLD); // last year's income
+			y += 10;
+
+			SetDParam(0, _network_player_info[this->company].performance);
+			DrawString(x, y, STR_NETWORK_PERFORMANCE, TC_GOLD); // performance
+			y += 10;
+
+			SetDParam(0, _network_player_info[this->company].num_vehicle[0]);
+			SetDParam(1, _network_player_info[this->company].num_vehicle[1]);
+			SetDParam(2, _network_player_info[this->company].num_vehicle[2]);
+			SetDParam(3, _network_player_info[this->company].num_vehicle[3]);
+			SetDParam(4, _network_player_info[this->company].num_vehicle[4]);
+			DrawString(x, y, STR_NETWORK_VEHICLES, TC_GOLD); // vehicles
+			y += 10;
+
+			SetDParam(0, _network_player_info[this->company].num_station[0]);
+			SetDParam(1, _network_player_info[this->company].num_station[1]);
+			SetDParam(2, _network_player_info[this->company].num_station[2]);
+			SetDParam(3, _network_player_info[this->company].num_station[3]);
+			SetDParam(4, _network_player_info[this->company].num_station[4]);
+			DrawString(x, y, STR_NETWORK_STATIONS, TC_GOLD); // stations
+			y += 10;
+
+			SetDParamStr(0, _network_player_info[this->company].players);
+			DrawStringTruncated(x, y, STR_NETWORK_PLAYERS, TC_GOLD, trunc_width); // players
+		}
 	}
-}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case NLWW_CLOSE:    // Close 'X'
+			case NLWW_CANCEL:   // Cancel button
+				ShowNetworkGameWindow();
+				break;
+
+			case NLWW_MATRIX: { // Company list
+				uint32 id_v = (pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW;
+
+				if (id_v >= this->vscroll.cap) break;
+
+				id_v += this->vscroll.pos;
+				this->company = (id_v >= this->server->info.companies_on) ? INVALID_PLAYER : NetworkLobbyFindCompanyIndex(id_v);
+				this->SetDirty();
+			} break;
+
+			case NLWW_JOIN:     // Join company
+				/* Button can be clicked only when it is enabled */
+				_network_playas = this->company;
+				NetworkClientConnectGame(_network_last_host, _network_last_port);
+				break;
+
+			case NLWW_NEW:      // New company
+				_network_playas = PLAYER_NEW_COMPANY;
+				NetworkClientConnectGame(_network_last_host, _network_last_port);
+				break;
+
+			case NLWW_SPECTATE: // Spectate game
+				_network_playas = PLAYER_SPECTATOR;
+				NetworkClientConnectGame(_network_last_host, _network_last_port);
+				break;
+
+			case NLWW_REFRESH:  // Refresh
+				NetworkTCPQueryServer(_network_last_host, _network_last_port); // company info
+				NetworkUDPQueryServer(_network_last_host, _network_last_port); // general data
+				break;
+		}
+	}
+};
 
 static const Widget _network_lobby_window_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                    STR_018B_CLOSE_WINDOW },           // NLWW_CLOSE
@@ -1216,7 +1190,7 @@
 	WC_NETWORK_WINDOW, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 	_network_lobby_window_widgets,
-	NetworkLobbyWindowWndProc,
+	NULL,
 };
 
 /* Show the networklobbywindow with the selected server
@@ -1228,12 +1202,7 @@
 	NetworkTCPQueryServer(_network_last_host, _network_last_port); // company info
 	NetworkUDPQueryServer(_network_last_host, _network_last_port); // general data
 
-	Window *w = AllocateWindowDesc(&_network_lobby_window_desc);
-	if (w != NULL) {
-		WP(w, network_ql_d).n.server = ngl;
-		strcpy(_edit_str_net_buf, "");
-		w->vscroll.cap = 10;
-	}
+	new NetworkLobbyWindow(&_network_lobby_window_desc, ngl);
 }
 
 // The window below gives information about the connected clients
@@ -1381,10 +1350,10 @@
 	/* If height is changed */
 	if (w->height != CLNWND_OFFSET + num + 1) {
 		// XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1)
-		SetWindowDirty(w);
+		w->SetDirty();
 		w->widget[3].bottom = w->widget[3].top + num + 2;
 		w->height = CLNWND_OFFSET + num + 1;
-		SetWindowDirty(w);
+		w->SetDirty();
 		return false;
 	}
 	return true;
@@ -1471,7 +1440,7 @@
 	int h = ClientListPopupHeight();
 
 	/* Allocate the popup */
-	w = AllocateWindow(x, y, 150, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets);
+	w = new Window(x, y, 150, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets);
 	w->widget[0].bottom = w->widget[0].top + h;
 	w->widget[0].right = w->widget[0].left + 150;
 
@@ -1480,8 +1449,6 @@
 	// Save our client
 	WP(w, menu_d).main_button = client_no;
 	WP(w, menu_d).sel_index = 0;
-	// We are a popup
-	_popup_menu_active = true;
 
 	return w;
 }
@@ -1513,25 +1480,22 @@
 			}
 		} break;
 
-		case WE_POPUPMENU_SELECT: {
+		case WE_MOUSELOOP: {
 			/* We selected an action */
-			int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
-
-			if (index >= 0 && e->we.popupmenu.pt.y >= w->top) {
-				HandleClientListPopupClick(index, WP(w, menu_d).main_button);
-			}
+			int index = (_cursor.pos.y - w->top) / CLNWND_ROWSIZE;
 
-			DeleteWindowById(WC_TOOLBAR_MENU, 0);
-		} break;
+			if (_left_button_down) {
+				if (index == -1 || index == WP(w, menu_d).sel_index) return;
 
-		case WE_POPUPMENU_OVER: {
-			/* Our mouse hoovers over an action? Select it! */
-			int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
+				WP(w, menu_d).sel_index = index;
+				w->SetDirty();
+			} else {
+				if (index >= 0 && _cursor.pos.y >= w->top) {
+					HandleClientListPopupClick(index, WP(w, menu_d).main_button);
+				}
 
-			if (index == -1 || index == WP(w, menu_d).sel_index) return;
-
-			WP(w, menu_d).sel_index = index;
-			SetWindowDirty(w);
+				DeleteWindowById(WC_TOOLBAR_MENU, 0);
+			}
 		} break;
 	}
 }
@@ -1589,7 +1553,7 @@
 			if (e->we.mouseover.pt.y == -1) {
 				_selected_clientlist_y = 0;
 				_selected_clientlist_item = 255;
-				SetWindowDirty(w);
+				w->SetDirty();
 				break;
 			}
 			/* It did not change.. no update! */
@@ -1604,7 +1568,7 @@
 			}
 
 			/* Repaint */
-			SetWindowDirty(w);
+			w->SetDirty();
 			break;
 
 		case WE_DESTROY: case WE_CREATE:
@@ -1617,7 +1581,7 @@
 
 void ShowClientList()
 {
-	AllocateWindowDescFront(&_client_list_desc, 0);
+	AllocateWindowDescFront<Window>(&_client_list_desc, 0);
 }
 
 
@@ -1672,21 +1636,19 @@
 		case WE_CLICK:
 			if (e->we.click.widget == 2) { //Disconnect button
 				NetworkDisconnect();
-				delete w;
 				SwitchMode(SM_MENU);
 				ShowNetworkGameWindow();
 			}
 			break;
 
-			/* If the server asks for a password, we need to fill it in */
-			case WE_ON_EDIT_TEXT_CANCEL:
+		case WE_ON_EDIT_TEXT:
+			if (StrEmpty(e->we.edittext.str)) {
 				NetworkDisconnect();
 				ShowNetworkGameWindow();
-				break;
-
-			case WE_ON_EDIT_TEXT:
+			} else {
 				SEND_COMMAND(PACKET_CLIENT_PASSWORD)(pw_type, e->we.edittext.str);
-				break;
+			}
+			break;
 	}
 }
 
@@ -1708,7 +1670,7 @@
 void ShowJoinStatusWindow()
 {
 	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
-	Window *w = AllocateWindowDesc(&_network_join_status_window_desc);
+	Window *w = new Window(&_network_join_status_window_desc);
 	/* Parent the status window to the lobby */
 	if (w != NULL) w->parent = FindWindowById(WC_NETWORK_WINDOW, 0);
 }
@@ -1723,202 +1685,217 @@
 	}
 }
 
-/**
- * Find the next item of the list of things that can be auto-completed.
- * @param item The current indexed item to return. This function can, and most
- *     likely will, alter item, to skip empty items in the arrays.
- * @return Returns the char that matched to the index.
- */
-static const char *ChatTabCompletionNextItem(uint *item)
-{
-	static char chat_tab_temp_buffer[64];
 
-	/* First, try clients */
-	if (*item < MAX_CLIENT_INFO) {
-		/* Skip inactive clients */
-		while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++;
-		if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name;
+struct NetworkChatWindow : public QueryStringBaseWindow {
+	DestType dtype;
+	int dest;
+
+	NetworkChatWindow (const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(desc)
+	{
+		this->LowerWidget(2);
+		this->dtype   = type;
+		this->dest    = dest;
+		this->afilter = CS_ALPHANUMERAL;
+		InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 0);
+
+		InvalidateWindowData(WC_NEWS_WINDOW, 0, this->height);
+		SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys
+
+		_chat_tab_completion_active = false;
+
+		this->FindWindowPlacementAndResize(desc);
 	}
 
-	/* Then, try townnames */
-	/* Not that the following assumes all town indices are adjacent, ie no
-	 * towns have been deleted. */
-	if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) {
-		const Town *t;
+	~NetworkChatWindow ()
+	{
+		InvalidateWindowData(WC_NEWS_WINDOW, 0, 0);
+		ClrBit(_no_scroll, SCROLL_CHAT);
+	}
 
-		FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
-			/* Get the town-name via the string-system */
-			SetDParam(0, t->index);
-			GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer));
-			return &chat_tab_temp_buffer[0];
+	/**
+	 * Find the next item of the list of things that can be auto-completed.
+	 * @param item The current indexed item to return. This function can, and most
+	 *     likely will, alter item, to skip empty items in the arrays.
+	 * @return Returns the char that matched to the index.
+	 */
+	const char *ChatTabCompletionNextItem(uint *item)
+	{
+		static char chat_tab_temp_buffer[64];
+
+		/* First, try clients */
+		if (*item < MAX_CLIENT_INFO) {
+			/* Skip inactive clients */
+			while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++;
+			if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name;
+		}
+
+		/* Then, try townnames */
+		/* Not that the following assumes all town indices are adjacent, ie no
+		* towns have been deleted. */
+		if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) {
+			const Town *t;
+
+			FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
+				/* Get the town-name via the string-system */
+				SetDParam(0, t->index);
+				GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer));
+				return &chat_tab_temp_buffer[0];
+			}
+		}
+
+		return NULL;
+	}
+
+	/**
+	 * Find what text to complete. It scans for a space from the left and marks
+	 *  the word right from that as to complete. It also writes a \0 at the
+	 *  position of the space (if any). If nothing found, buf is returned.
+	 */
+	static char *ChatTabCompletionFindText(char *buf)
+	{
+		char *p = strrchr(buf, ' ');
+		if (p == NULL) return buf;
+
+		*p = '\0';
+		return p + 1;
+	}
+
+	/**
+	 * See if we can auto-complete the current text of the user.
+	 */
+	void ChatTabCompletion()
+	{
+		static char _chat_tab_completion_buf[lengthof(this->edit_str_buf)];
+		Textbuf *tb = &this->text;
+		size_t len, tb_len;
+		uint item;
+		char *tb_buf, *pre_buf;
+		const char *cur_name;
+		bool second_scan = false;
+
+		item = 0;
+
+		/* Copy the buffer so we can modify it without damaging the real data */
+		pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf);
+
+		tb_buf  = ChatTabCompletionFindText(pre_buf);
+		tb_len  = strlen(tb_buf);
+
+		while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) {
+			item++;
+
+			if (_chat_tab_completion_active) {
+				/* We are pressing TAB again on the same name, is there an other name
+				*  that starts with this? */
+				if (!second_scan) {
+					size_t offset;
+					size_t length;
+
+					/* If we are completing at the begin of the line, skip the ': ' we added */
+					if (tb_buf == pre_buf) {
+						offset = 0;
+						length = tb->length - 2;
+					} else {
+						/* Else, find the place we are completing at */
+						offset = strlen(pre_buf) + 1;
+						length = tb->length - offset;
+					}
+
+					/* Compare if we have a match */
+					if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
+
+					continue;
+				}
+
+				/* Now any match we make on _chat_tab_completion_buf after this, is perfect */
+			}
+
+			len = strlen(cur_name);
+			if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
+				/* Save the data it was before completion */
+				if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf);
+				_chat_tab_completion_active = true;
+
+				/* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
+				if (pre_buf == tb_buf) {
+					snprintf(tb->buf, lengthof(this->edit_str_buf), "%s: ", cur_name);
+				} else {
+					snprintf(tb->buf, lengthof(this->edit_str_buf), "%s %s", pre_buf, cur_name);
+				}
+
+				/* Update the textbuffer */
+				UpdateTextBufferSize(&this->text);
+
+				this->SetDirty();
+				free(pre_buf);
+				return;
+			}
+		}
+
+		if (second_scan) {
+			/* We walked all posibilities, and the user presses tab again.. revert to original text */
+			strcpy(tb->buf, _chat_tab_completion_buf);
+			_chat_tab_completion_active = false;
+
+			/* Update the textbuffer */
+			UpdateTextBufferSize(&this->text);
+
+			this->SetDirty();
+		}
+		free(pre_buf);
+	}
+
+	virtual void OnPaint()
+	{
+		static const StringID chat_captions[] = {
+			STR_NETWORK_CHAT_ALL_CAPTION,
+			STR_NETWORK_CHAT_COMPANY_CAPTION,
+			STR_NETWORK_CHAT_CLIENT_CAPTION
+		};
+
+		DrawWindowWidgets(this);
+
+		assert((uint)this->dtype < lengthof(chat_captions));
+		DrawStringRightAligned(this->widget[2].left - 2, this->widget[2].top + 1, chat_captions[this->dtype], TC_BLACK);
+		this->DrawEditBox(2);
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case 2:
+				ShowOnScreenKeyboard(this, 2, 0, 3);
+				break;
+
+			case 3: /* Send */
+				SendChat(this->text.buf, this->dtype, this->dest);
+			/* FALLTHROUGH */
+			case 0: /* Cancel */ delete this; break;
 		}
 	}
 
-	return NULL;
-}
-
-/**
- * Find what text to complete. It scans for a space from the left and marks
- *  the word right from that as to complete. It also writes a \0 at the
- *  position of the space (if any). If nothing found, buf is returned.
- */
-static char *ChatTabCompletionFindText(char *buf)
-{
-	char *p = strrchr(buf, ' ');
-	if (p == NULL) return buf;
-
-	*p = '\0';
-	return p + 1;
-}
-
-/**
- * See if we can auto-complete the current text of the user.
- */
-static void ChatTabCompletion(Window *w)
-{
-	static char _chat_tab_completion_buf[lengthof(_edit_str_net_buf)];
-	Textbuf *tb = &WP(w, chatquerystr_d).text;
-	uint len, tb_len;
-	uint item;
-	char *tb_buf, *pre_buf;
-	const char *cur_name;
-	bool second_scan = false;
-
-	item = 0;
-
-	/* Copy the buffer so we can modify it without damaging the real data */
-	pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf);
-
-	tb_buf  = ChatTabCompletionFindText(pre_buf);
-	tb_len  = strlen(tb_buf);
-
-	while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) {
-		item++;
-
-		if (_chat_tab_completion_active) {
-			/* We are pressing TAB again on the same name, is there an other name
-			 *  that starts with this? */
-			if (!second_scan) {
-				uint offset;
-				uint length;
-
-				/* If we are completing at the begin of the line, skip the ': ' we added */
-				if (tb_buf == pre_buf) {
-					offset = 0;
-					length = tb->length - 2;
-				} else {
-					/* Else, find the place we are completing at */
-					offset = strlen(pre_buf) + 1;
-					length = tb->length - offset;
-				}
-
-				/* Compare if we have a match */
-				if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
-
-				continue;
-			}
-
-			/* Now any match we make on _chat_tab_completion_buf after this, is perfect */
-		}
-
-		len = strlen(cur_name);
-		if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
-			/* Save the data it was before completion */
-			if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf);
-			_chat_tab_completion_active = true;
-
-			/* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
-			if (pre_buf == tb_buf) {
-				snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s: ", cur_name);
-			} else {
-				snprintf(tb->buf, lengthof(_edit_str_net_buf), "%s %s", pre_buf, cur_name);
-			}
-
-			/* Update the textbuffer */
-			UpdateTextBufferSize(&WP(w, chatquerystr_d).text);
-
-			SetWindowDirty(w);
-			free(pre_buf);
-			return;
-		}
+	virtual void OnMouseLoop()
+	{
+		this->HandleEditBox(2);
 	}
 
-	if (second_scan) {
-		/* We walked all posibilities, and the user presses tab again.. revert to original text */
-		strcpy(tb->buf, _chat_tab_completion_buf);
-		_chat_tab_completion_active = false;
-
-		/* Update the textbuffer */
-		UpdateTextBufferSize(&WP(w, chatquerystr_d).text);
-
-		SetWindowDirty(w);
+	virtual bool OnKeyPress(uint16 key, uint16 keycode)
+	{
+		bool cont = true;
+		if (keycode == WKC_TAB) {
+			ChatTabCompletion();
+		} else {
+			_chat_tab_completion_active = false;
+			switch (this->HandleEditBoxKey(2, key, keycode, cont)) {
+				case 1: /* Return */
+					SendChat(this->text.buf, this->dtype, this->dest);
+				/* FALLTHROUGH */
+				case 2: /* Escape */ delete this; break;
+			}
+		}
+		return cont;
 	}
-	free(pre_buf);
-}
-
-/*
- * uses chatquerystr_d WP macro
- * uses chatquerystr_d->dtype to store type of chat message (Private/Team/All)
- */
-static void ChatWindowWndProc(Window *w, WindowEvent *e)
-{
-	switch (e->event) {
-		case WE_CREATE:
-			SendWindowMessage(WC_NEWS_WINDOW, 0, WE_CREATE, w->height, 0);
-			SetBit(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys
-			break;
-
-		case WE_PAINT: {
-			static const StringID chat_captions[] = {
-				STR_NETWORK_CHAT_ALL_CAPTION,
-				STR_NETWORK_CHAT_COMPANY_CAPTION,
-				STR_NETWORK_CHAT_CLIENT_CAPTION
-			};
-
-			DrawWindowWidgets(w);
-
-			assert((uint)WP(w, chatquerystr_d).dtype < lengthof(chat_captions));
-			DrawStringRightAligned(w->widget[2].left - 2, w->widget[2].top + 1, chat_captions[WP(w, chatquerystr_d).dtype], TC_BLACK);
-			DrawEditBox(w, &WP(w, chatquerystr_d), 2);
-		} break;
-
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case 2:
-					ShowOnScreenKeyboard(w, &WP(w, chatquerystr_d), 2, 0, 3);
-					break;
-
-				case 3: /* Send */
-					SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest);
-				/* FALLTHROUGH */
-				case 0: /* Cancel */ delete w; break;
-			}
-			break;
-
-		case WE_MOUSELOOP:
-			HandleEditBox(w, &WP(w, chatquerystr_d), 2);
-			break;
-
-		case WE_KEYPRESS:
-			if (e->we.keypress.keycode == WKC_TAB) {
-				ChatTabCompletion(w);
-			} else {
-				_chat_tab_completion_active = false;
-				switch (HandleEditBoxKey(w, &WP(w, chatquerystr_d), 2, e)) {
-					case 1: /* Return */
-						SendChat(WP(w, chatquerystr_d).text.buf, WP(w, chatquerystr_d).dtype, WP(w, chatquerystr_d).dest);
-					/* FALLTHROUGH */
-					case 2: /* Escape */ delete w; break;
-				}
-			}
-			break;
-
-		case WE_DESTROY:
-			SendWindowMessage(WC_NEWS_WINDOW, 0, WE_DESTROY, 0, 0);
-			ClrBit(_no_scroll, SCROLL_CHAT);
-			break;
-	}
-}
+};
 
 static const Widget _chat_window_widgets[] = {
 {   WWT_CLOSEBOX, RESIZE_NONE,  14,   0,  10,  0, 13, STR_00C5,                  STR_018B_CLOSE_WINDOW},
@@ -1933,23 +1910,13 @@
 	WC_SEND_NETWORK_MSG, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
 	_chat_window_widgets,
-	ChatWindowWndProc
+	NULL
 };
 
 void ShowNetworkChatQueryWindow(DestType type, int dest)
 {
 	DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
-
-	_edit_str_net_buf[0] = '\0';
-	_chat_tab_completion_active = false;
-
-	Window *w = AllocateWindowDesc(&_chat_window_desc);
-
-	w->LowerWidget(2);
-	WP(w, chatquerystr_d).dtype   = type;
-	WP(w, chatquerystr_d).dest    = dest;
-	WP(w, chatquerystr_d).afilter = CS_ALPHANUMERAL;
-	InitializeTextBuffer(&WP(w, chatquerystr_d).text, _edit_str_net_buf, lengthof(_edit_str_net_buf), 0);
+	new NetworkChatWindow (&_chat_window_desc, type, dest);
 }
 
 /** Enum for NetworkGameWindow, referring to _network_game_window_widgets */
@@ -1964,61 +1931,75 @@
 	NCPWW_OK,                       ///< Safe the password etc.
 };
 
-static void NetworkCompanyPasswordWindowWndProc(Window *w, WindowEvent *e)
-{
-	switch (e->event) {
-		case WE_PAINT:
-			DrawWindowWidgets(w);
-			DrawEditBox(w, &WP(w, chatquerystr_d), 4);
-			break;
-
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case NCPWW_OK: {
-					if (w->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) {
-						snprintf(_network_default_company_pass, lengthof(_network_default_company_pass), "%s", _edit_str_net_buf);
-					}
-
-					/* empty password is a '*' because of console argument */
-					if (StrEmpty(_edit_str_net_buf)) snprintf(_edit_str_net_buf, lengthof(_edit_str_net_buf), "*");
-					char *password = _edit_str_net_buf;
-					NetworkChangeCompanyPassword(1, &password);
-				}
-
-				/* FALL THROUGH */
-				case NCPWW_CANCEL:
-					delete w;
-					break;
+struct NetworkCompanyPasswordWindow : public QueryStringBaseWindow {
+	NetworkCompanyPasswordWindow(const WindowDesc *desc) : QueryStringBaseWindow(desc)
+	{
+		this->afilter = CS_ALPHANUMERAL;
+		InitializeTextBuffer(&this->text, this->edit_str_buf, min(lengthof(_network_default_company_pass), lengthof(this->edit_str_buf)), 0);
 
-				case NCPWW_SAVE_AS_DEFAULT_PASSWORD:
-					w->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD);
-					SetWindowDirty(w);
-					break;
-				case NCPWW_PASSWORD:
-					ShowOnScreenKeyboard(w, &WP(w, chatquerystr_d), NCPWW_PASSWORD, 2, 1);
-					break;
-			}
-			break;
-
-		case WE_MOUSELOOP:
-			HandleEditBox(w, &WP(w, chatquerystr_d), 4);
-			break;
+		this->FindWindowPlacementAndResize(desc);
+	}
 
-		case WE_KEYPRESS:
-			switch (HandleEditBoxKey(w, &WP(w, chatquerystr_d), 4, e)) {
-				case 1: // Return
-					e->event = WE_CLICK;
-					e->we.click.widget = NCPWW_OK;
-					NetworkCompanyPasswordWindowWndProc(w, e);
-					break;
+	void OnOk()
+	{
+		if (this->IsWidgetLowered(NCPWW_SAVE_AS_DEFAULT_PASSWORD)) {
+			snprintf(_network_default_company_pass, lengthof(_network_default_company_pass), "%s", this->edit_str_buf);
+		}
 
-				case 2: // Escape
-					delete w;
-					break;
-			}
-			break;
+		/* empty password is a '*' because of console argument */
+		if (StrEmpty(this->edit_str_buf)) snprintf(this->edit_str_buf, lengthof(this->edit_str_buf), "*");
+		char *password = this->edit_str_buf;
+		NetworkChangeCompanyPassword(1, &password);
 	}
-}
+
+	virtual void OnPaint()
+	{
+		DrawWindowWidgets(this);
+		this->DrawEditBox(4);
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case NCPWW_OK:
+				this->OnOk();
+
+			/* FALL THROUGH */
+			case NCPWW_CANCEL:
+				delete this;
+				break;
+
+			case NCPWW_SAVE_AS_DEFAULT_PASSWORD:
+				this->ToggleWidgetLoweredState(NCPWW_SAVE_AS_DEFAULT_PASSWORD);
+				this->SetDirty();
+				break;
+
+			case NCPWW_PASSWORD:
+				ShowOnScreenKeyboard(this, NCPWW_PASSWORD, 2, 1);
+				break;
+		}
+	}
+
+	virtual void OnMouseLoop()
+	{
+		this->HandleEditBox(4);
+	}
+
+	virtual bool OnKeyPress(uint16 key, uint16 keycode)
+	{
+		bool cont;
+		switch (this->HandleEditBoxKey(4, key, keycode, cont)) {
+			case 1: // Return
+				this->OnOk();
+				/* FALL THROUGH */
+
+			case 2: // Escape
+				delete this;
+				break;
+		}
+		return cont;
+	}
+};
 
 static const Widget _ncp_window_widgets[] = {
 {   WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,  0, 13, STR_00C5,                          STR_018B_CLOSE_WINDOW},
@@ -2037,17 +2018,14 @@
 	WC_COMPANY_PASSWORD_WINDOW, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
 	_ncp_window_widgets,
-	NetworkCompanyPasswordWindowWndProc
+	NULL
 };
 
 void ShowNetworkCompanyPasswordWindow()
 {
 	DeleteWindowById(WC_COMPANY_PASSWORD_WINDOW, 0);
 
-	_edit_str_net_buf[0] = '\0';
-	Window *w = AllocateWindowDesc(&_ncp_window_desc);
-	WP(w, chatquerystr_d).afilter = CS_ALPHANUMERAL;
-	InitializeTextBuffer(&WP(w, chatquerystr_d).text, _edit_str_net_buf, min(lengthof(_network_default_company_pass), lengthof(_edit_str_net_buf)), 0);
+	new NetworkCompanyPasswordWindow(&_ncp_window_desc);
 }
 
 #endif /* ENABLE_NETWORK */
--- a/src/network/network_udp.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/network/network_udp.cpp	Sun May 11 20:09:34 2008 +0000
@@ -205,7 +205,7 @@
 
 	const GRFConfig *in_reply[NETWORK_MAX_GRF_COUNT];
 	uint8 in_reply_count = 0;
-	uint packet_len = 0;
+	size_t packet_len = 0;
 
 	DEBUG(net, 6, "[udp] newgrf data request from %s:%d", inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port));
 
--- a/src/newgrf.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/newgrf.cpp	Sun May 11 20:09:34 2008 +0000
@@ -96,7 +96,7 @@
 static GrfDataType _grf_data_type;
 
 
-typedef void (*SpecialSpriteHandler)(byte *buf, int len);
+typedef void (*SpecialSpriteHandler)(byte *buf, size_t len);
 
 enum {
 	MAX_STATIONS = 256,
@@ -152,7 +152,7 @@
 	DEBUG(grf, severity, "[%s:%d] %s", _cur_grfconfig->filename, _nfo_line, buf);
 }
 
-static inline bool check_length(int real, int wanted, const char *str)
+static inline bool check_length(size_t real, size_t wanted, const char *str)
 {
 	if (real >= wanted) return true;
 	grfmsg(0, "%s: Invalid pseudo sprite length %d (expected %d)!", str, real, wanted);
@@ -2248,7 +2248,7 @@
 }
 
 /* Action 0x00 */
-static void FeatureChangeInfo(byte *buf, int len)
+static void FeatureChangeInfo(byte *buf, size_t len)
 {
 	byte *bufend = buf + len;
 
@@ -2363,7 +2363,7 @@
 }
 
 /* Action 0x00 (GLS_SAFETYSCAN) */
-static void SafeChangeInfo(byte *buf, int len)
+static void SafeChangeInfo(byte *buf, size_t len)
 {
 	if (!check_length(len, 6, "SafeChangeInfo")) return;
 	buf++;
@@ -2386,7 +2386,7 @@
 }
 
 /* Action 0x00 (GLS_RESERVE) */
-static void ReserveChangeInfo(byte *buf, int len)
+static void ReserveChangeInfo(byte *buf, size_t len)
 {
 	byte *bufend = buf + len;
 
@@ -2482,7 +2482,7 @@
 }
 
 /* Action 0x01 */
-static void NewSpriteSet(byte *buf, int len)
+static void NewSpriteSet(byte *buf, size_t len)
 {
 	/* <01> <feature> <num-sets> <num-ent>
 	 *
@@ -2518,7 +2518,7 @@
 }
 
 /* Action 0x01 (SKIP) */
-static void SkipAct1(byte *buf, int len)
+static void SkipAct1(byte *buf, size_t len)
 {
 	if (!check_length(len, 4, "SkipAct1")) return;
 	buf++;
@@ -2577,7 +2577,7 @@
 }
 
 /* Action 0x02 */
-static void NewSpriteGroup(byte *buf, int len)
+static void NewSpriteGroup(byte *buf, size_t len)
 {
 	/* <02> <feature> <set-id> <type/num-entries> <feature-specific-data...>
 	 *
@@ -3182,7 +3182,7 @@
 
 
 /* Action 0x03 */
-static void FeatureMapSpriteGroup(byte *buf, int len)
+static void FeatureMapSpriteGroup(byte *buf, size_t len)
 {
 	/* <03> <feature> <n-id> <ids>... <num-cid> [<cargo-type> <cid>]... <def-cid>
 	 * id-list    := [<id>] [id-list]
@@ -3267,7 +3267,7 @@
 }
 
 /* Action 0x04 */
-static void FeatureNewName(byte *buf, int len)
+static void FeatureNewName(byte *buf, size_t len)
 {
 	/* <04> <veh-type> <language-id> <num-veh> <offset> <data...>
 	 *
@@ -3421,7 +3421,7 @@
 }
 
 /* Action 0x05 */
-static void GraphicsNew(byte *buf, int len)
+static void GraphicsNew(byte *buf, size_t len)
 {
 	/* <05> <graphics-type> <num-sprites> <other data...>
 	 *
@@ -3535,7 +3535,7 @@
 }
 
 /* Action 0x05 (SKIP) */
-static void SkipAct5(byte *buf, int len)
+static void SkipAct5(byte *buf, size_t len)
 {
 	if (!check_length(len, 2, "SkipAct5")) return;
 	buf++;
@@ -3711,7 +3711,7 @@
 }
 
 /* Action 0x06 */
-static void CfgApply(byte *buf, int len)
+static void CfgApply(byte *buf, size_t len)
 {
 	/* <06> <param-num> <param-size> <offset> ... <FF>
 	 *
@@ -3799,7 +3799,7 @@
 
 /* Action 0x07 */
 /* Action 0x09 */
-static void SkipIf(byte *buf, int len)
+static void SkipIf(byte *buf, size_t len)
 {
 	/* <07/09> <param-num> <param-size> <condition-type> <value> <num-sprites>
 	 *
@@ -3954,7 +3954,7 @@
 
 
 /* Action 0x08 (GLS_FILESCAN) */
-static void ScanInfo(byte *buf, int len)
+static void ScanInfo(byte *buf, size_t len)
 {
 	if (!check_length(len, 8, "Info")) return;
 	buf++;
@@ -3981,7 +3981,7 @@
 }
 
 /* Action 0x08 */
-static void GRFInfo(byte *buf, int len)
+static void GRFInfo(byte *buf, size_t len)
 {
 	/* <08> <version> <grf-id> <name> <info>
 	 *
@@ -4005,7 +4005,7 @@
 }
 
 /* Action 0x0A */
-static void SpriteReplace(byte *buf, int len)
+static void SpriteReplace(byte *buf, size_t len)
 {
 	/* <0A> <num-sets> <set1> [<set2> ...]
 	 * <set>: <num-sprites> <first-sprite>
@@ -4041,7 +4041,7 @@
 }
 
 /* Action 0x0A (SKIP) */
-static void SkipActA(byte *buf, int len)
+static void SkipActA(byte *buf, size_t len)
 {
 	buf++;
 	uint8 num_sets = grf_load_byte(&buf);
@@ -4057,7 +4057,7 @@
 }
 
 /* Action 0x0B */
-static void GRFLoadError(byte *buf, int len)
+static void GRFLoadError(byte *buf, size_t len)
 {
 	/* <0B> <severity> <language-id> <message-id> [<message...> 00] [<data...>] 00 [<parnum>]
 	 *
@@ -4167,7 +4167,7 @@
 }
 
 /* Action 0x0C */
-static void GRFComment(byte *buf, int len)
+static void GRFComment(byte *buf, size_t len)
 {
 	/* <0C> [<ignored...>]
 	 *
@@ -4175,13 +4175,13 @@
 
 	if (len == 1) return;
 
-	int text_len = len - 1;
+	size_t text_len = len - 1;
 	const char *text = (const char*)(buf + 1);
 	grfmsg(2, "GRFComment: %.*s", text_len, text);
 }
 
 /* Action 0x0D (GLS_SAFETYSCAN) */
-static void SafeParamSet(byte *buf, int len)
+static void SafeParamSet(byte *buf, size_t len)
 {
 	if (!check_length(len, 5, "SafeParamSet")) return;
 	buf++;
@@ -4313,7 +4313,7 @@
 
 
 /* Action 0x0D */
-static void ParamSet(byte *buf, int len)
+static void ParamSet(byte *buf, size_t len)
 {
 	/* <0D> <target> <operation> <source1> <source2> [<data>]
 	 *
@@ -4604,7 +4604,7 @@
 }
 
 /* Action 0x0E (GLS_SAFETYSCAN) */
-static void SafeGRFInhibit(byte *buf, int len)
+static void SafeGRFInhibit(byte *buf, size_t len)
 {
 	/* <0E> <num> <grfids...>
 	 *
@@ -4632,7 +4632,7 @@
 }
 
 /* Action 0x0E */
-static void GRFInhibit(byte *buf, int len)
+static void GRFInhibit(byte *buf, size_t len)
 {
 	/* <0E> <num> <grfids...>
 	 *
@@ -4657,7 +4657,7 @@
 }
 
 /* Action 0x0F */
-static void FeatureTownName(byte *buf, int len)
+static void FeatureTownName(byte *buf, size_t len)
 {
 	/* <0F> <id> <style-name> <num-parts> <parts>
 	 *
@@ -4757,7 +4757,7 @@
 }
 
 /* Action 0x10 */
-static void DefineGotoLabel(byte *buf, int len)
+static void DefineGotoLabel(byte *buf, size_t len)
 {
 	/* <10> <label> [<comment>]
 	 *
@@ -4787,7 +4787,7 @@
 }
 
 /* Action 0x11 */
-static void GRFSound(byte *buf, int len)
+static void GRFSound(byte *buf, size_t len)
 {
 	/* <11> <num>
 	 *
@@ -4804,7 +4804,7 @@
 }
 
 /* Action 0x11 (SKIP) */
-static void SkipAct11(byte *buf, int len)
+static void SkipAct11(byte *buf, size_t len)
 {
 	/* <11> <num>
 	 *
@@ -4930,7 +4930,7 @@
 }
 
 /* Action 0x12 */
-static void LoadFontGlyph(byte *buf, int len)
+static void LoadFontGlyph(byte *buf, size_t len)
 {
 	/* <12> <num_def> <font_size> <num_char> <base_char>
 	 *
@@ -4962,7 +4962,7 @@
 }
 
 /* Action 0x12 (SKIP) */
-static void SkipAct12(byte *buf, int len)
+static void SkipAct12(byte *buf, size_t len)
 {
 	/* <12> <num_def> <font_size> <num_char> <base_char>
 	 *
@@ -4992,7 +4992,7 @@
 }
 
 /* Action 0x13 */
-static void TranslateGRFStrings(byte *buf, int len)
+static void TranslateGRFStrings(byte *buf, size_t len)
 {
 	/* <13> <grfid> <num-ent> <offset> <text...>
 	 *
@@ -5086,7 +5086,7 @@
 
 
 /* Used during safety scan on unsafe actions */
-static void GRFUnsafe(byte *buf, int len)
+static void GRFUnsafe(byte *buf, size_t len)
 {
 	SetBit(_cur_grfconfig->flags, GCF_UNSAFE);
 
--- a/src/newgrf_config.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/newgrf_config.cpp	Sun May 11 20:09:34 2008 +0000
@@ -309,7 +309,7 @@
 }
 
 /* Scan a path for NewGRFs */
-static uint ScanPath(const char *path, int basepath_length)
+static uint ScanPath(const char *path, size_t basepath_length)
 {
 	extern bool FiosIsValidFile(const char *path, const struct dirent *ent, struct stat *sb);
 
--- a/src/newgrf_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/newgrf_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -408,7 +408,7 @@
 					Window *w;
 
 					DeleteWindowByClass(WC_SAVELOAD);
-					w = AllocateWindowDesc(&_newgrf_add_dlg_desc);
+					w = new Window(&_newgrf_add_dlg_desc);
 					w->resize.step_height = 10;
 
 					WP(w, newgrf_add_d).list = list;
@@ -516,8 +516,9 @@
 
 				/* parse_intlist returns -1 on error */
 				if (c->num_params == (byte)-1) c->num_params = 0;
+
+				w->SetDirty();
 			}
-			w->SetDirty();
 			break;
 
 		case WE_DESTROY:
@@ -582,7 +583,7 @@
 	Window *w;
 
 	DeleteWindowByClass(WC_GAME_OPTIONS);
-	w = AllocateWindowDesc(&_newgrf_desc);
+	w = new Window(&_newgrf_desc);
 	if (w == NULL) return;
 
 	w->resize.step_height = 14;
--- a/src/news_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/news_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -60,12 +60,8 @@
 static NewsID _oldest_news = 0;             ///< points to first item in fifo queue
 static NewsID _latest_news = INVALID_NEWS;  ///< points to last item in fifo queue
 
-struct news_d : vp_d {
-	uint16 follow_vehicle;
-	int32 scrollpos_x;
-	int32 scrollpos_y;
-	int32 dest_scrollpos_x;
-	int32 dest_scrollpos_y;
+struct news_d {
+	uint16 chat_height;
 	NewsItem *ni;
 };
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(news_d));
@@ -131,7 +127,7 @@
 	switch (e->event) {
 		case WE_CREATE: { // If chatbar is open at creation time, we need to go above it
 			const Window *w1 = FindWindowById(WC_SEND_NETWORK_MSG, 0);
-			w->message.msg = (w1 != NULL) ? w1->height : 0;
+			WP(w, news_d).chat_height = (w1 != NULL) ? w1->height : 0;
 			break;
 		}
 
@@ -209,11 +205,11 @@
 					} else if (ni->flags & NF_TILE) {
 						if (_ctrl_pressed) {
 							ShowExtraViewPortWindow(ni->data_a);
-							if (ni->data_b != 0) {
+							if (ni->flags & NF_TILE2) {
 								ShowExtraViewPortWindow(ni->data_b);
 							}
 						} else {
-							if (!ScrollMainWindowToTile(ni->data_a) && ni->data_b != 0) {
+							if (!ScrollMainWindowToTile(ni->data_a) && ni->flags & NF_TILE2) {
 								ScrollMainWindowToTile(ni->data_b);
 							}
 						}
@@ -231,15 +227,12 @@
 			}
 			break;
 
-		case WE_MESSAGE: // The chatbar has notified us that is was either created or closed
-			switch (e->we.message.msg) {
-				case WE_CREATE: w->message.msg = e->we.message.wparam; break;
-				case WE_DESTROY: w->message.msg = 0; break;
-			}
+		case WE_INVALIDATE_DATA: // The chatbar has notified us that is was either created or closed
+			WP(w, news_d).chat_height = e->we.invalidate.data;
 			break;
 
 		case WE_TICK: { // Scroll up newsmessages from the bottom in steps of 4 pixels
-			int y = max(w->top - 4, _screen.height - w->height - 12 - w->message.msg);
+			int y = max(w->top - 4, _screen.height - w->height - 12 - WP(w, news_d).chat_height);
 			if (y == w->top) return;
 
 			if (w->viewport != NULL) {
@@ -432,7 +425,7 @@
 		case NM_NORMAL:
 		case NM_CALLBACK:
 			_news_type13_desc.top = top;
-			w = AllocateWindowDesc(&_news_type13_desc);
+			w = new Window(&_news_type13_desc);
 			if (ni->flags & NF_VIEWPORT) {
 				InitializeWindowViewport(w, 2, 58, 426, 110,
 					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
@@ -441,7 +434,7 @@
 
 		case NM_THIN:
 			_news_type2_desc.top = top;
-			w = AllocateWindowDesc(&_news_type2_desc);
+			w = new Window(&_news_type2_desc);
 			if (ni->flags & NF_VIEWPORT) {
 				InitializeWindowViewport(w, 2, 58, 426, 70,
 					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
@@ -450,7 +443,7 @@
 
 		default:
 			_news_type0_desc.top = top;
-			w = AllocateWindowDesc(&_news_type0_desc);
+			w = new Window(&_news_type0_desc);
 			if (ni->flags & NF_VIEWPORT) {
 				InitializeWindowViewport(w, 3, 17, 274, 47,
 					ni->data_a | (ni->flags & NF_VEHICLE ? 0x80000000 : 0), ZOOM_LVL_NEWS);
@@ -711,7 +704,7 @@
 void ShowMessageHistory()
 {
 	DeleteWindowById(WC_MESSAGE_HISTORY, 0);
-	Window *w = AllocateWindowDesc(&_message_history_desc);
+	Window *w = new Window(&_message_history_desc);
 
 	if (w == NULL) return;
 
@@ -933,7 +926,7 @@
 void ShowMessageOptions()
 {
 	DeleteWindowById(WC_GAME_OPTIONS, 0);
-	AllocateWindowDesc(&_message_options_desc);
+	new Window(&_message_options_desc);
 }
 
 
--- a/src/news_type.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/news_type.h	Sun May 11 20:09:34 2008 +0000
@@ -48,10 +48,11 @@
 enum NewsFlag {
 	NF_NONE      = 0,        ///< No flag is set.
 	NF_VIEWPORT  = (1 << 1), ///< Does the news message have a viewport? (ingame picture of happening)
-	NF_TILE      = (1 << 2), ///< When clicked on the news message scroll to a given tile? Tile is in data_a/data_b
+	NF_TILE      = (1 << 2), ///< When clicked on the news message scroll to a given tile? Tile is in data_a
 	NF_VEHICLE   = (1 << 3), ///< When clicked on the message scroll to the vehicle? VehicleID is in data_a
 	NF_FORCE_BIG = (1 << 4), ///< Force the appearance of a news message if it has already been shown (internal)
 	NF_INCOLOR   = (1 << 5), ///< Show the newsmessage in colour, otherwise it defaults to black & white
+	NF_TILE2     = (1 << 6), ///< There is a second tile to scroll to; tile is in data_b
 };
 DECLARE_ENUM_AS_BIT_SET(NewsFlag);
 
--- a/src/openttd.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/openttd.cpp	Sun May 11 20:09:34 2008 +0000
@@ -61,6 +61,7 @@
 #include "cheat_func.h"
 #include "animated_tile_func.h"
 #include "functions.h"
+#include "elrail_func.h"
 #include "rev.h"
 
 #include "newgrf.h"
@@ -98,6 +99,7 @@
 void ResetMusic();
 void ResetOldNames();
 void ProcessAsyncSaveFinish();
+void CallWindowTickEvent();
 
 extern void SetDifficultyLevel(int mode, GameOptions *gm_opt);
 extern Player* DoStartupNewPlayer(bool is_ai);
@@ -295,8 +297,20 @@
 }
 
 
-static void UnInitializeGame()
+/** Unitializes drivers, frees allocated memory, cleans pools, ...
+ * Generally, prepares the game for shutting down
+ */
+static void ShutdownGame()
 {
+	/* stop the AI */
+	AI_Uninitialize();
+
+	IConsoleFree();
+
+	if (_network_available) NetworkShutDown(); // Shut down the network and close any open connections
+
+	DriverFactoryBase::ShutdownDrivers();
+
 	UnInitWindowSystem();
 
 	/* Uninitialize airport state machines */
@@ -317,6 +331,9 @@
 	free((void*)_industry_sort);
 
 	free(_config_file);
+
+	/* Close all and any open filehandles */
+	FioCloseAll();
 }
 
 static void LoadIntroGame()
@@ -632,13 +649,6 @@
 	_video_driver->MainLoop();
 
 	WaitTillSaved();
-	IConsoleFree();
-
-	if (_network_available) NetworkShutDown(); // Shut down the network and close any open connections
-
-	_video_driver->Stop();
-	_music_driver->Stop();
-	_sound_driver->Stop();
 
 	/* only save config if we have to */
 	if (save_config) {
@@ -646,14 +656,8 @@
 		SaveToHighScore();
 	}
 
-	/* Reset windowing system and free config file */
-	UnInitializeGame();
-
-	/* stop the AI */
-	AI_Uninitialize();
-
-	/* Close all and any open filehandles */
-	FioCloseAll();
+	/* Reset windowing system, stop drivers, free used memory, ... */
+	ShutdownGame();
 
 	return 0;
 }
@@ -1098,8 +1102,8 @@
 		Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
 		assert(w);
 
-		WP(w, vp_d).dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
-		WP(w, vp_d).dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
+		w->viewport->dest_scrollpos_x += ScaleByZoom(x, w->viewport->zoom);
+		w->viewport->dest_scrollpos_y += ScaleByZoom(y, w->viewport->zoom);
 	}
 }
 
@@ -1209,8 +1213,8 @@
 	const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
 
 	if (w != NULL) {
-		_saved_scrollpos_x = WP(w, const vp_d).scrollpos_x;
-		_saved_scrollpos_y = WP(w, const vp_d).scrollpos_y;
+		_saved_scrollpos_x = w->viewport->scrollpos_x;
+		_saved_scrollpos_y = w->viewport->scrollpos_y;
 		_saved_scrollpos_zoom = w->viewport->zoom;
 	}
 }
@@ -1322,10 +1326,10 @@
 
 	Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
 
-	WP(w, vp_d).scrollpos_x = _saved_scrollpos_x;
-	WP(w, vp_d).scrollpos_y = _saved_scrollpos_y;
-	WP(w, vp_d).dest_scrollpos_x = _saved_scrollpos_x;
-	WP(w, vp_d).dest_scrollpos_y = _saved_scrollpos_y;
+	w->viewport->scrollpos_x = _saved_scrollpos_x;
+	w->viewport->scrollpos_y = _saved_scrollpos_y;
+	w->viewport->dest_scrollpos_x = _saved_scrollpos_x;
+	w->viewport->dest_scrollpos_y = _saved_scrollpos_y;
 
 	ViewPort *vp = w->viewport;
 	vp->zoom = min(_saved_scrollpos_zoom, ZOOM_LVL_MAX);
--- a/src/order_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/order_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -1185,9 +1185,9 @@
 	DeleteWindowById(WC_VEHICLE_DETAILS, veh);
 
 	if (v->owner != _local_player) {
-		w = AllocateWindowDescFront(&_other_orders_desc, veh);
+		w = AllocateWindowDescFront<Window>(&_other_orders_desc, veh);
 	} else {
-		w = AllocateWindowDescFront((v->type == VEH_TRAIN || v->type == VEH_ROAD) ? &_orders_train_desc : &_orders_desc, veh);
+		w = AllocateWindowDescFront<Window>((v->type == VEH_TRAIN || v->type == VEH_ROAD) ? &_orders_train_desc : &_orders_desc, veh);
 	}
 
 	if (w != NULL) {
--- a/src/osk_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/osk_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -12,20 +12,11 @@
 #include "debug.h"
 #include "window_func.h"
 #include "gfx_func.h"
+#include "querystring_gui.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
 
-struct osk_d {
-	querystr_d *qs; // text-input
-	int text_btn;   // widget number of parent's text field
-	int ok_btn;     // widget number of parent's ok button (=0 when ok shouldn't be passed on)
-	int cancel_btn; // widget number of parent's cancel button (=0 when cancel shouldn't be passed on; text will be reverted to original)
-	Textbuf *text;  // pointer to parent's textbuffer (to update caret position)
-	char *orig;     // the original text, in case we cancel
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(osk_d));
-
 enum OskWidgets {
 	OSK_WIDGET_TEXT = 3,
 	OSK_WIDGET_CANCEL = 5,
@@ -50,149 +41,162 @@
 };
 static byte _keystate = KEYS_NONE;
 
-/*
- * Only show valid characters; do not show characters that would
- * only insert a space when we have a spacebar to do that or
- * characters that are not allowed to be entered.
- */
-static void ChangeOskDiabledState(Window *w, const querystr_d *qs, bool shift)
-{
-	for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
-		w->SetWidgetDisabledState(OSK_WIDGET_LETTERS + i,
-				!IsValidChar(_keyboard[shift][i], qs->afilter) || _keyboard[shift][i] == ' ');
+struct OskWindow : public Window {
+	QueryString *qs;       ///< text-input
+	int text_btn;          ///< widget number of parent's text field
+	int ok_btn;            ///< widget number of parent's ok button (=0 when ok shouldn't be passed on)
+	int cancel_btn;        ///< widget number of parent's cancel button (=0 when cancel shouldn't be passed on; text will be reverted to original)
+	Textbuf *text;         ///< pointer to parent's textbuffer (to update caret position)
+	char orig_str_buf[64]; ///< Original string.
+
+	OskWindow(const WindowDesc *desc, QueryStringBaseWindow *parent, int button, int cancel, int ok) : Window(desc)
+	{
+		this->parent = parent;
+		assert(parent != NULL);
+
+		if (parent->widget[button].data != 0) parent->caption = parent->widget[button].data;
+
+		this->qs         = parent;
+		this->text_btn   = button;
+		this->cancel_btn = cancel;
+		this->ok_btn     = ok;
+		this->text       = &parent->text;
+
+		/* make a copy in case we need to reset later */
+		strcpy(this->orig_str_buf, this->qs->text.buf);
+
+		SetBit(_no_scroll, SCROLL_EDIT);
+		/* Not needed by default. */
+		this->DisableWidget(OSK_WIDGET_SPECIAL);
+
+		this->FindWindowPlacementAndResize(desc);
 	}
-	w->SetWidgetDisabledState(OSK_WIDGET_SPACE, !IsValidChar(' ', qs->afilter));
-}
-
-/* on screen keyboard */
-static void OskWndProc(Window *w, WindowEvent *e)
-{
-	querystr_d *qs = WP(w, osk_d).qs;
 
-	switch (e->event) {
-		case WE_CREATE:
-			SetBit(_no_scroll, SCROLL_EDIT);
-			/* Not needed by default. */
-			w->DisableWidget(OSK_WIDGET_SPECIAL);
-			break;
+	/**
+	 * Only show valid characters; do not show characters that would
+	 * only insert a space when we have a spacebar to do that or
+	 * characters that are not allowed to be entered.
+	 */
+	void ChangeOskDiabledState(bool shift)
+	{
+		for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
+			this->SetWidgetDisabledState(OSK_WIDGET_LETTERS + i,
+					!IsValidChar(_keyboard[shift][i], this->qs->afilter) || _keyboard[shift][i] == ' ');
+		}
+		this->SetWidgetDisabledState(OSK_WIDGET_SPACE, !IsValidChar(' ', this->qs->afilter));
+	}
 
-		case WE_PAINT: {
+	virtual void OnPaint()
+	{
+		bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT);
+
+		this->LowerWidget(OSK_WIDGET_TEXT);
+		this->SetWidgetLoweredState(OSK_WIDGET_SHIFT, HasBit(_keystate, KEYS_SHIFT));
+		this->SetWidgetLoweredState(OSK_WIDGET_CAPS, HasBit(_keystate, KEYS_CAPS));
+
+		this->ChangeOskDiabledState(shift);
+
+		SetDParam(0, this->qs->caption);
+		DrawWindowWidgets(this);
+
+		for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
+			DrawCharCentered(_keyboard[shift][i],
+				this->widget[OSK_WIDGET_LETTERS + i].left + 8,
+				this->widget[OSK_WIDGET_LETTERS + i].top + 3,
+				TC_BLACK);
+		}
+
+		this->qs->DrawEditBox(this, OSK_WIDGET_TEXT);
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		/* clicked a letter */
+		if (widget >= OSK_WIDGET_LETTERS) {
 			bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT);
 
-			w->LowerWidget(OSK_WIDGET_TEXT);
-			w->SetWidgetLoweredState(OSK_WIDGET_SHIFT, HasBit(_keystate, KEYS_SHIFT));
-			w->SetWidgetLoweredState(OSK_WIDGET_CAPS, HasBit(_keystate, KEYS_CAPS));
-
-			ChangeOskDiabledState(w, qs, shift);
-
-			SetDParam(0, qs->caption);
-			DrawWindowWidgets(w);
+			WChar c = _keyboard[shift][widget - OSK_WIDGET_LETTERS];
 
-			for (uint i = 0; i < OSK_KEYBOARD_ENTRIES; i++) {
-				DrawCharCentered(_keyboard[shift][i],
-					w->widget[OSK_WIDGET_LETTERS + i].left + 8,
-					w->widget[OSK_WIDGET_LETTERS + i].top + 3,
-					TC_BLACK);
+			if (!IsValidChar(c, this->qs->afilter)) return;
+
+			if (InsertTextBufferChar(&this->qs->text, c)) this->InvalidateWidget(OSK_WIDGET_TEXT);
+
+			if (HasBit(_keystate, KEYS_SHIFT)) {
+				ToggleBit(_keystate, KEYS_SHIFT);
+				this->widget[OSK_WIDGET_SHIFT].color = HasBit(_keystate, KEYS_SHIFT) ? 15 : 14;
+				this->SetDirty();
 			}
-
-			DrawEditBox(w, qs, OSK_WIDGET_TEXT);
-			break;
+			return;
 		}
 
-		case WE_CLICK:
-			/* clicked a letter */
-			if (e->we.click.widget >= OSK_WIDGET_LETTERS) {
-				bool shift = HasBit(_keystate, KEYS_CAPS) ^ HasBit(_keystate, KEYS_SHIFT);
-
-				WChar c = _keyboard[shift][e->we.click.widget - OSK_WIDGET_LETTERS];
+		switch (widget) {
+			case OSK_WIDGET_BACKSPACE:
+				if (DeleteTextBufferChar(&this->qs->text, WKC_BACKSPACE)) this->InvalidateWidget(OSK_WIDGET_TEXT);
+				break;
 
-				if (!IsValidChar(c, qs->afilter)) break;
-
-				if (InsertTextBufferChar(&qs->text, c)) w->InvalidateWidget(OSK_WIDGET_TEXT);
+			case OSK_WIDGET_SPECIAL:
+				/*
+				 * Anything device specific can go here.
+				 * The button itself is hidden by default, and when you need it you
+				 * can not hide it in the create event.
+				 */
+				break;
 
-				if (HasBit(_keystate, KEYS_SHIFT)) {
-					ToggleBit(_keystate, KEYS_SHIFT);
-					w->widget[OSK_WIDGET_SHIFT].color = HasBit(_keystate, KEYS_SHIFT) ? 15 : 14;
-					w->SetDirty();
+			case OSK_WIDGET_CAPS:
+				ToggleBit(_keystate, KEYS_CAPS);
+				this->SetDirty();
+				break;
+
+			case OSK_WIDGET_SHIFT:
+				ToggleBit(_keystate, KEYS_SHIFT);
+				this->SetDirty();
+				break;
+
+			case OSK_WIDGET_SPACE:
+				if (InsertTextBufferChar(&this->qs->text, ' ')) this->InvalidateWidget(OSK_WIDGET_TEXT);
+				break;
+
+			case OSK_WIDGET_LEFT:
+				if (MoveTextBufferPos(&this->qs->text, WKC_LEFT)) this->InvalidateWidget(OSK_WIDGET_TEXT);
+				break;
+
+			case OSK_WIDGET_RIGHT:
+				if (MoveTextBufferPos(&this->qs->text, WKC_RIGHT)) this->InvalidateWidget(OSK_WIDGET_TEXT);
+				break;
+
+			case OSK_WIDGET_OK:
+				if (this->qs->orig == NULL || strcmp(this->qs->text.buf, this->qs->orig) != 0) {
+					/* pass information by simulating a button press on parent window */
+					if (this->ok_btn != 0) {
+						this->parent->OnClick(pt, this->ok_btn);
+					}
+				}
+				delete this;
+				break;
+
+			case OSK_WIDGET_CANCEL:
+				if (this->cancel_btn != 0) { // pass a cancel event to the parent window
+					this->parent->OnClick(pt, this->cancel_btn);
+					/* Window gets deleted when the parent window removes itself. */
+				} else { // or reset to original string
+					strcpy(qs->text.buf, this->orig_str_buf);
+					UpdateTextBufferSize(&qs->text);
+					MoveTextBufferPos(&qs->text, WKC_END);
+					delete this;
 				}
 				break;
-			}
-
-			switch (e->we.click.widget) {
-				case OSK_WIDGET_BACKSPACE:
-					if (DeleteTextBufferChar(&qs->text, WKC_BACKSPACE)) w->InvalidateWidget(OSK_WIDGET_TEXT);
-					break;
-
-				case OSK_WIDGET_SPECIAL:
-					/*
-					 * Anything device specific can go here.
-					 * The button itself is hidden by default, and when you need it you
-					 * can not hide it in the create event.
-					 */
-					break;
-
-				case OSK_WIDGET_CAPS:
-					ToggleBit(_keystate, KEYS_CAPS);
-					w->SetDirty();
-					break;
-
-				case OSK_WIDGET_SHIFT:
-					ToggleBit(_keystate, KEYS_SHIFT);
-					w->SetDirty();
-					break;
-
-				case OSK_WIDGET_SPACE:
-					if (InsertTextBufferChar(&qs->text, ' ')) w->InvalidateWidget(OSK_WIDGET_TEXT);
-					break;
-
-				case OSK_WIDGET_LEFT:
-					if (MoveTextBufferPos(&qs->text, WKC_LEFT)) w->InvalidateWidget(OSK_WIDGET_TEXT);
-					break;
-
-				case OSK_WIDGET_RIGHT:
-					if (MoveTextBufferPos(&qs->text, WKC_RIGHT)) w->InvalidateWidget(OSK_WIDGET_TEXT);
-					break;
+		}
+		/* make sure that the parent window's textbox also gets updated */
+		if (this->parent != NULL) this->parent->InvalidateWidget(this->text_btn);
+	}
 
-				case OSK_WIDGET_OK:
-					if (qs->orig == NULL || strcmp(qs->text.buf, qs->orig) != 0) {
-						/* pass information by simulating a button press on parent window */
-						if (WP(w, osk_d).ok_btn != 0) {
-							Window *parent = w->parent;
-							WindowEvent e;
-							e.event = WE_CLICK;
-							e.we.click.widget = WP(w, osk_d).ok_btn;
-							parent->HandleWindowEvent(&e);
-						}
-					}
-					delete w;
-					break;
-
-				case OSK_WIDGET_CANCEL:
-					if (WP(w, osk_d).cancel_btn != 0) { // pass a cancel event to the parent window
-						Window *parent = w->parent;
-						WindowEvent e;
-						e.event = WE_CLICK;
-						e.we.click.widget = WP(w, osk_d).cancel_btn;
-						parent->HandleWindowEvent(&e);
-					} else { // or reset to original string
-						strcpy(qs->text.buf, WP(w, osk_d).orig);
-						UpdateTextBufferSize(&qs->text);
-						MoveTextBufferPos(&qs->text, WKC_END);
-					}
-					delete w;
-					break;
-			}
-			/* make sure that the parent window's textbox also gets updated */
-			if (w->parent != NULL) w->parent->InvalidateWidget(WP(w, osk_d).text_btn);
-			break;
-
-		case WE_MOUSELOOP:
-			HandleEditBox(w, qs, OSK_WIDGET_TEXT);
-			/* make the caret of the parent window also blink */
-			w->parent->InvalidateWidget(WP(w, osk_d).text_btn);
-			break;
+	virtual void OnMouseLoop()
+	{
+		this->qs->HandleEditBox(this, OSK_WIDGET_TEXT);
+		/* make the caret of the parent window also blink */
+		this->parent->InvalidateWidget(this->text_btn);
 	}
-}
+};
 
 static const Widget _osk_widgets[] = {
 {      WWT_EMPTY, RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,               STR_NULL},
@@ -277,7 +281,7 @@
 	WC_OSK, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 	_osk_widgets,
-	OskWndProc
+	NULL
 };
 
 /**
@@ -345,26 +349,10 @@
  * @param ok     widget number of parent's ok button  (0 if ok events should not
  *               be passed)
  */
-void ShowOnScreenKeyboard(Window *parent, querystr_d *q, int button, int cancel, int ok)
+void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok)
 {
 	DeleteWindowById(WC_OSK, 0);
 
-	Window *w = AllocateWindowDesc(&_osk_desc);
-
-	w->parent = parent;
-	assert(parent != NULL);
-
-	if (parent->widget[button].data != 0) q->caption = parent->widget[button].data;
-
-	WP(w, osk_d).qs         = q;
-	WP(w, osk_d).text_btn   = button;
-	WP(w, osk_d).cancel_btn = cancel;
-	WP(w, osk_d).ok_btn     = ok;
-	WP(w, osk_d).text       = &q->text;
-
 	GetKeyboardLayout();
-
-	/* make a copy in case we need to reset later */
-	strcpy(_orig_str_buf, WP(w, osk_d).qs->text.buf);
-	WP(w, osk_d).orig = _orig_str_buf;
+	new OskWindow(&_osk_desc, parent, button, cancel, ok);
 }
--- a/src/player_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/player_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -42,6 +42,12 @@
 };
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(facesel_d));
 
+struct highscore_d {
+	uint32 background_img;
+	int8 rank;
+};
+assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(highscore_d));
+
 enum {
 	FIRST_GUI_CALL = INT_MAX,  ///< default value to specify thuis is the first call of the resizable gui
 };
@@ -247,7 +253,7 @@
 {
 	if (!IsValidPlayer(player)) return;
 
-	Window *w = AllocateWindowDescFront(show_small ? &_player_finances_small_desc : &_player_finances_desc, player);
+	Window *w = AllocateWindowDescFront<Window>(show_small ? &_player_finances_small_desc : &_player_finances_desc, player);
 	if (w != NULL) {
 		w->caption_color = w->window_number;
 		WP(w, def_d).data_1 = show_small;
@@ -973,6 +979,7 @@
 			break;
 
 		case WE_ON_EDIT_TEXT:
+			if (e->we.edittext.str == NULL) break;
 			/* Set a new player face number */
 			if (!StrEmpty(e->we.edittext.str)) {
 				*pf = strtoul(e->we.edittext.str, NULL, 10);
@@ -1018,7 +1025,7 @@
 {
 	if (!IsValidPlayer(player)) return;
 
-	Window *w = AllocateWindowDescFront(adv ? &_select_player_face_adv_desc : &_select_player_face_desc, player); // simple or advanced window
+	Window *w = AllocateWindowDescFront<Window>(adv ? &_select_player_face_adv_desc : &_select_player_face_desc, player); // simple or advanced window
 
 	if (w != NULL) {
 		w->caption_color = w->window_number;
@@ -1235,7 +1242,7 @@
 				case PCW_WIDGET_NEW_FACE: DoSelectPlayerFace((PlayerID)w->window_number, false); break;
 
 				case PCW_WIDGET_COLOR_SCHEME: {
-					Window *wf = AllocateWindowDescFront(_loaded_newgrf_features.has_2CC ? &_select_player_livery_2cc_desc : &_select_player_livery_desc, w->window_number);
+					Window *wf = AllocateWindowDescFront<Window>(_loaded_newgrf_features.has_2CC ? &_select_player_livery_2cc_desc : &_select_player_livery_desc, w->window_number);
 					if (wf != NULL) {
 						wf->caption_color = wf->window_number;
 						WP(wf, livery_d).livery_class = LC_OTHER;
@@ -1303,7 +1310,7 @@
 			}
 			break;
 
-		case WE_MOUSELOOP:
+		case WE_TICK:
 			/* redraw the window every now and then */
 			if ((++w->vscroll.pos & 0x1F) == 0) w->SetDirty();
 			break;
@@ -1325,7 +1332,7 @@
 			break;
 
 		case WE_ON_EDIT_TEXT:
-			if (StrEmpty(e->we.edittext.str)) return;
+			if (StrEmpty(e->we.edittext.str)) break;
 
 			_cmd_text = e->we.edittext.str;
 			switch (WP(w, def_d).byte_1) {
@@ -1355,7 +1362,7 @@
 
 	if (!IsValidPlayer(player)) return;
 
-	w = AllocateWindowDescFront(&_player_company_desc, player);
+	w = AllocateWindowDescFront<Window>(&_player_company_desc, player);
 	if (w != NULL) w->caption_color = w->window_number;
 }
 
@@ -1411,7 +1418,7 @@
 
 void ShowBuyCompanyDialog(uint player)
 {
-	AllocateWindowDescFront(&_buy_company_desc, player);
+	AllocateWindowDescFront<Window>(&_buy_company_desc, player);
 }
 
 /********** HIGHSCORE and ENDGAME windows */
@@ -1552,7 +1559,7 @@
 	if (_game_mode != GM_MENU) HideVitalWindows();
 
 	DeleteWindowByClass(WC_HIGHSCORE);
-	w = AllocateWindowDesc(&_highscore_desc);
+	w = new Window(&_highscore_desc);
 
 	if (w != NULL) {
 		MarkWholeScreenDirty();
@@ -1575,7 +1582,7 @@
 
 	HideVitalWindows();
 	DeleteWindowByClass(WC_ENDSCREEN);
-	w = AllocateWindowDesc(&_endgame_desc);
+	w = new Window(&_endgame_desc);
 
 	if (w != NULL) {
 		MarkWholeScreenDirty();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/querystring_gui.h	Sun May 11 20:09:34 2008 +0000
@@ -0,0 +1,38 @@
+/* $Id$ */
+
+/** @file querystring_gui.h Base for the GUIs that have an edit box in them. */
+
+#ifndef QUERYSTRING_GUI_H
+#define QUERYSTRING_GUI_H
+
+#include "textbuf_gui.h"
+#include "window_gui.h"
+
+struct QueryString {
+	StringID caption;
+	Textbuf text;
+	const char *orig;
+	CharSetFilter afilter;
+	bool handled;
+
+	void DrawEditBox(Window *w, int wid);
+	void HandleEditBox(Window *w, int wid);
+	int HandleEditBoxKey(Window *w, int wid, uint16 key, uint16 keycode, bool &cont);
+};
+
+struct QueryStringBaseWindow : public Window, public QueryString {
+	char edit_str_buf[64];
+	char orig_str_buf[64];
+
+	QueryStringBaseWindow(const WindowDesc *desc, void *data = NULL, WindowNumber window_number = 0) : Window(desc, data, window_number)
+	{
+	}
+
+	void DrawEditBox(int wid);
+	void HandleEditBox(int wid);
+	int HandleEditBoxKey(int wid, uint16 key, uint16 keycode, bool &cont);
+};
+
+void ShowOnScreenKeyboard(QueryStringBaseWindow *parent, int button, int cancel, int ok);
+
+#endif /* QUERYSTRING_GUI_H */
--- a/src/rail.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/rail.h	Sun May 11 20:09:34 2008 +0000
@@ -11,7 +11,7 @@
 #include "gfx_type.h"
 #include "core/bitmath_func.hpp"
 #include "economy_func.h"
-#include "tile_cmd.h"
+#include "slope_type.h"
 
 enum RailTypeFlag {
 	RTF_CATENARY = 0,  ///< Set if the rail type should have catenary drawn
@@ -198,29 +198,8 @@
 void *EnsureNoTrainOnTrackProc(Vehicle *v, void *data);
 int TicksToLeaveDepot(const Vehicle *v);
 
-
-/**
- * Test if a rail type has catenary
- * @param rt Rail type to test
- */
-static inline bool HasCatenary(RailType rt)
-{
-	return HasBit(GetRailTypeInfo(rt)->flags, RTF_CATENARY);
-}
-
-
-/**
- * Draws overhead wires and pylons for electric railways.
- * @param ti The TileInfo struct of the tile being drawn
- * @see DrawCatenaryRailway
- */
-void DrawCatenary(const TileInfo *ti);
-void DrawCatenaryOnTunnel(const TileInfo *ti);
-void DrawCatenaryOnBridge(const TileInfo *ti);
-
 Foundation GetRailFoundation(Slope tileh, TrackBits bits);
 
-int32 SettingsDisableElrail(int32 p1); ///< _patches.disable_elrail callback
 
 /**
  * Finds out if a Player has a certain railtype available
--- a/src/rail_cmd.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/rail_cmd.cpp	Sun May 11 20:09:34 2008 +0000
@@ -44,6 +44,7 @@
 #include "station_map.h"
 #include "water_map.h"
 #include "functions.h"
+#include "elrail_func.h"
 #include "oldpool_func.h"
 
 #include "table/sprites.h"
@@ -1761,7 +1762,7 @@
 
 		if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti);
 
-		if (HasCatenary(GetRailType(ti->tile))) DrawCatenary(ti);
+		if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti);
 
 		if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails);
 	} else {
@@ -1840,7 +1841,7 @@
 
 		DrawGroundSprite(image, PAL_NONE);
 
-		if (HasCatenary(GetRailType(ti->tile))) DrawCatenary(ti);
+		if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti);
 
 		/* End now if buildings are invisible */
 		if (IsInvisibilitySet(TO_BUILDINGS)) return;
--- a/src/rail_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/rail_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -251,11 +251,6 @@
 		CMD_BUILD_TUNNEL | CMD_MSG(STR_5016_CAN_T_BUILD_TUNNEL_HERE));
 }
 
-void PlaceProc_BuyLand(TileIndex tile)
-{
-	DoCommandP(tile, 0, 0, CcPlaySound1E, CMD_PURCHASE_LAND_AREA | CMD_NO_WATER | CMD_MSG(STR_5806_CAN_T_PURCHASE_THIS_LAND));
-}
-
 static void PlaceRail_ConvertRail(TileIndex tile)
 {
 	VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CONVERT_RAIL);
@@ -752,7 +747,7 @@
 	if (button < 0 || !(w = FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_RAIL))) {
 		DeleteWindowByClass(WC_BUILD_TOOLBAR);
 		_cur_railtype = railtype;
-		w = AllocateWindowDescFront(&_build_rail_desc, TRANSPORT_RAIL);
+		w = AllocateWindowDescFront<Window>(&_build_rail_desc, TRANSPORT_RAIL);
 		SetupRailToolbar(railtype, w);
 	}
 
@@ -1127,7 +1122,7 @@
 		w->SetDirty();
 		break;
 
-	case WE_MOUSELOOP:
+	case WE_TICK:
 		if (WP(w, def_d).close) {
 			delete w;
 			return;
@@ -1226,10 +1221,10 @@
 {
 	Window *w;
 	if (GetNumStationClasses() <= 2 && GetNumCustomStations(STAT_CLASS_DFLT) == 1) {
-		w = AllocateWindowDesc(&_station_builder_desc);
+		w = new Window(&_station_builder_desc);
 		_railstation.newstations = false;
 	} else {
-		w = AllocateWindowDesc(&_newstation_builder_desc);
+		w = new Window(&_newstation_builder_desc);
 		_railstation.newstations = true;
 		_railstation.station_count = GetNumCustomStations(_railstation.station_class);
 
@@ -1350,7 +1345,7 @@
 			w->SetDirty();
 			break;
 
-		case WE_MOUSELOOP:
+		case WE_TICK:
 			if (WP(w, def_d).close) delete w;
 			return;
 
@@ -1397,7 +1392,7 @@
  */
 static void ShowSignalBuilder()
 {
-	AllocateWindowDesc(&_signal_builder_desc);
+	new Window(&_signal_builder_desc);
 }
 
 /** Enum referring to the widgets of the build rail depot window */
@@ -1441,7 +1436,7 @@
 		}
 		break;
 
-	case WE_MOUSELOOP:
+	case WE_TICK:
 		if (WP(w, def_d).close) delete w;
 		return;
 
@@ -1473,7 +1468,7 @@
 
 static void ShowBuildTrainDepotPicker()
 {
-	AllocateWindowDesc(&_build_depot_desc);
+	new Window(&_build_depot_desc);
 }
 
 /** Enum referring to the widgets of the build NewGRF rail waypoint window */
@@ -1540,7 +1535,7 @@
 		break;
 	}
 
-	case WE_MOUSELOOP:
+	case WE_TICK:
 		if (WP(w, def_d).close) delete w;
 		break;
 
@@ -1576,7 +1571,7 @@
 
 static void ShowBuildWaypointPicker()
 {
-	Window *w = AllocateWindowDesc(&_build_waypoint_desc);
+	Window *w = new Window(&_build_waypoint_desc);
 	w->hscroll.cap = 5;
 	w->hscroll.count = _waypoint_count;
 }
--- a/src/road_cmd.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/road_cmd.cpp	Sun May 11 20:09:34 2008 +0000
@@ -37,6 +37,7 @@
 #include "cheat_func.h"
 #include "functions.h"
 #include "effectvehicle_func.h"
+#include "elrail_func.h"
 #include "oldpool_func.h"
 
 #include "table/sprites.h"
@@ -1138,7 +1139,7 @@
 				DrawGroundSprite(SPR_TRAMWAY_OVERLAY + (GetCrossingRoadAxis(ti->tile) ^ 1), pal);
 				DrawTramCatenary(ti, GetCrossingRoadBits(ti->tile));
 			}
-			if (HasCatenary(GetRailType(ti->tile))) DrawCatenary(ti);
+			if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti);
 			break;
 		}
 
--- a/src/road_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/road_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -574,7 +574,7 @@
 						break;
 
 					case DDSP_DEMOLISH_AREA:
-						DoCommandP(end_tile, start_tile, 0, CcPlaySound10, CMD_CLEAR_AREA | CMD_MSG(STR_00B5_CAN_T_CLEAR_THIS_AREA));
+						GUIPlaceProcDragXY(e);
 						break;
 
 					case DDSP_PLACE_ROAD_X_DIR:
@@ -679,7 +679,7 @@
 	_cur_roadtype = roadtype;
 
 	DeleteWindowByClass(WC_BUILD_TOOLBAR);
-	Window *w = AllocateWindowDescFront(roadtype == ROADTYPE_ROAD ? &_build_road_desc : &_build_tramway_desc, TRANSPORT_ROAD);
+	Window *w = AllocateWindowDescFront<Window>(roadtype == ROADTYPE_ROAD ? &_build_road_desc : &_build_tramway_desc, TRANSPORT_ROAD);
 	if (_patches.link_terraform_toolbar) ShowTerraformToolbar(w);
 }
 
@@ -714,7 +714,7 @@
 void ShowBuildRoadScenToolbar()
 {
 	_cur_roadtype = ROADTYPE_ROAD;
-	AllocateWindowDescFront(&_build_road_scen_desc, 0);
+	AllocateWindowDescFront<Window>(&_build_road_scen_desc, 0);
 }
 
 /** Enum referring to the widgets of the build road depot window */
@@ -762,7 +762,7 @@
 			}
 			break;
 
-		case WE_MOUSELOOP:
+		case WE_TICK:
 			if (WP(w, def_d).close) delete w;
 			break;
 
@@ -817,7 +817,7 @@
 
 static void ShowRoadDepotPicker()
 {
-	AllocateWindowDesc(_cur_roadtype == ROADTYPE_ROAD ? &_build_road_depot_desc : &_build_tram_depot_desc);
+	new Window(_cur_roadtype == ROADTYPE_ROAD ? &_build_road_depot_desc : &_build_tram_depot_desc);
 }
 
 /** Enum referring to the widgets of the build road station window */
@@ -919,7 +919,7 @@
 			}
 			break;
 
-		case WE_MOUSELOOP:
+		case WE_TICK:
 			if (WP(w, def_d).close) {
 				delete w;
 				return;
@@ -966,7 +966,7 @@
 
 static void ShowRVStationPicker(RoadStopType rs)
 {
-	Window *w = AllocateWindowDesc(&_rv_station_picker_desc);
+	Window *w = new Window(&_rv_station_picker_desc);
 	if (w == NULL) return;
 
 	w->window_class = (rs == ROADSTOP_BUS) ? WC_BUS_STATION : WC_TRUCK_STATION;
--- a/src/saveload.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/saveload.cpp	Sun May 11 20:09:34 2008 +0000
@@ -531,7 +531,7 @@
 static inline size_t SlCalcNetStringLen(const char *ptr, size_t length)
 {
 	if (ptr == NULL) return 0;
-	return minu(strlen(ptr), length - 1);
+	return min(strlen(ptr), length - 1);
 }
 
 /** Calculate the gross length of the string that it
@@ -911,7 +911,7 @@
 static void SlLoadChunk(const ChunkHandler *ch)
 {
 	byte m = SlReadByte();
-	size_t len;
+	uint32 len;
 	uint32 endoffs;
 
 	_sl.block_mode = m;
@@ -1204,9 +1204,9 @@
 	_z.avail_out = 4096;
 
 	do {
-		/* read more bytes from the file?*/
+		/* read more bytes from the file? */
 		if (_z.avail_in == 0) {
-			_z.avail_in = fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh);
+			_z.avail_in = (uint)fread(_z.next_in = _sl.buf + 4096, 1, 4096, _sl.fh);
 		}
 
 		/* inflate the data */
@@ -1493,7 +1493,7 @@
 	_fast_forward = 0;
 	if (_cursor.sprite == SPR_CURSOR_MOUSE) SetMouseCursor(SPR_CURSOR_ZZZ, PAL_NONE);
 
-	SendWindowMessage(WC_STATUS_BAR, 0, true, 0, 0);
+	InvalidateWindowData(WC_STATUS_BAR, 0, true);
 	_ts.saveinprogress = true;
 }
 
@@ -1504,7 +1504,7 @@
 	_fast_forward = _ts.ff_state;
 	if (_cursor.sprite == SPR_CURSOR_ZZZ) SetMouseCursor(SPR_CURSOR_MOUSE, PAL_NONE);
 
-	SendWindowMessage(WC_STATUS_BAR, 0, false, 0, 0);
+	InvalidateWindowData(WC_STATUS_BAR, 0, false);
 	_ts.saveinprogress = false;
 }
 
--- a/src/settings.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/settings.cpp	Sun May 11 20:09:34 2008 +0000
@@ -53,6 +53,7 @@
 #include "textbuf_gui.h"
 #include "string_func.h"
 #include "rail_gui.h"
+#include "elrail_func.h"
 #include "gui.h"
 #include "town.h"
 #include "video/video_driver.hpp"
@@ -78,18 +79,18 @@
 typedef void SettingDescProcList(IniFile *ini, const char *grpname, char **list, uint len, SettingListCallbackProc proc);
 
 static void pool_init(SettingsMemoryPool **pool);
-static void *pool_alloc(SettingsMemoryPool **pool, uint size);
-static void *pool_strdup(SettingsMemoryPool **pool, const char *mem, uint size);
+static void *pool_alloc(SettingsMemoryPool **pool, size_t size);
+static void *pool_strdup(SettingsMemoryPool **pool, const char *mem, size_t size);
 static void pool_free(SettingsMemoryPool **pool);
 static bool IsSignedVarMemType(VarType vt);
 
 struct SettingsMemoryPool {
-	uint pos, size;
+	size_t pos, size;
 	SettingsMemoryPool *next;
 	byte mem[1];
 };
 
-static SettingsMemoryPool *pool_new(uint minsize)
+static SettingsMemoryPool *pool_new(size_t minsize)
 {
 	SettingsMemoryPool *p;
 	if (minsize < 4096 - 12) minsize = 4096 - 12;
@@ -106,9 +107,9 @@
 	*pool = pool_new(0);
 }
 
-static void *pool_alloc(SettingsMemoryPool **pool, uint size)
+static void *pool_alloc(SettingsMemoryPool **pool, size_t size)
 {
-	uint pos;
+	size_t pos;
 	SettingsMemoryPool *p = *pool;
 
 	size = Align(size, sizeof(void*));
@@ -129,7 +130,7 @@
 	return p->mem + pos;
 }
 
-static void *pool_strdup(SettingsMemoryPool **pool, const char *mem, uint size)
+static void *pool_strdup(SettingsMemoryPool **pool, const char *mem, size_t size)
 {
 	byte *p = (byte*)pool_alloc(pool, size + 1);
 	p[size] = 0;
@@ -186,7 +187,7 @@
 }
 
 /** allocate an ini group object */
-static IniGroup *ini_group_alloc(IniFile *ini, const char *grpt, int len)
+static IniGroup *ini_group_alloc(IniFile *ini, const char *grpt, size_t len)
 {
 	IniGroup *grp = (IniGroup*)pool_alloc(&ini->pool, sizeof(IniGroup));
 	grp->ini = ini;
@@ -205,7 +206,7 @@
 	return grp;
 }
 
-static IniItem *ini_item_alloc(IniGroup *group, const char *name, int len)
+static IniItem *ini_item_alloc(IniGroup *group, const char *name, size_t len)
 {
 	IniItem *item = (IniItem*)pool_alloc(&group->ini->pool, sizeof(IniItem));
 	item->name = (char*)pool_strdup(&group->ini->pool, name, len);
@@ -324,11 +325,11 @@
 }
 
 /** lookup a group or make a new one */
-static IniGroup *ini_getgroup(IniFile *ini, const char *name, int len)
+static IniGroup *ini_getgroup(IniFile *ini, const char *name, size_t len = 0)
 {
 	IniGroup *group;
 
-	if (len == -1) len = strlen(name);
+	if (len == 0) len = strlen(name);
 
 	/* does it exist already? */
 	for (group = ini->group; group; group = group->next)
@@ -345,7 +346,7 @@
 static IniItem *ini_getitem(IniGroup *group, const char *name, bool create)
 {
 	IniItem *item;
-	uint len = strlen(name);
+	size_t len = strlen(name);
 
 	for (item = group->item; item; item = item->next)
 		if (strcmp(item->name, name) == 0) return item;
@@ -404,12 +405,12 @@
  * @param one the current value of the setting for which a value needs found
  * @param onelen force calculation of the *one parameter
  * @return the integer index of the full-list, or -1 if not found */
-static int lookup_oneofmany(const char *many, const char *one, int onelen)
+static int lookup_oneofmany(const char *many, const char *one, size_t onelen = 0)
 {
 	const char *s;
 	int idx;
 
-	if (onelen == -1) onelen = strlen(one);
+	if (onelen == 0) onelen = strlen(one);
 
 	/* check if it's an integer */
 	if (*one >= '0' && *one <= '9')
@@ -420,7 +421,7 @@
 		/* find end of item */
 		s = many;
 		while (*s != '|' && *s != 0) s++;
-		if (s - many == onelen && !memcmp(one, many, onelen)) return idx;
+		if ((size_t)(s - many) == onelen && !memcmp(one, many, onelen)) return idx;
 		if (*s == 0) return -1;
 		many = s + 1;
 		idx++;
@@ -615,7 +616,7 @@
 		return (void*)val;
 	}
 	case SDT_ONEOFMANY: {
-		long r = lookup_oneofmany(desc->many, str, -1);
+		long r = lookup_oneofmany(desc->many, str);
 		/* if the first attempt of conversion from string to the appropriate value fails,
 		 * look if we have defined a converter from old value to new value. */
 		if (r == -1 && desc->proc_cnvt != NULL) r = desc->proc_cnvt(str);
@@ -706,7 +707,7 @@
 static void ini_load_settings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object)
 {
 	IniGroup *group;
-	IniGroup *group_def = ini_getgroup(ini, grpname, -1);
+	IniGroup *group_def = ini_getgroup(ini, grpname);
 	IniItem *item;
 	const void *p;
 	void *ptr;
@@ -801,7 +802,7 @@
 			group = ini_getgroup(ini, sdb->name, s - sdb->name);
 			s++;
 		} else {
-			if (group_def == NULL) group_def = ini_getgroup(ini, grpname, -1);
+			if (group_def == NULL) group_def = ini_getgroup(ini, grpname);
 			s = sdb->name;
 			group = group_def;
 		}
@@ -896,7 +897,7 @@
  * inside the list */
 static void ini_load_setting_list(IniFile *ini, const char *grpname, char **list, uint len, SettingListCallbackProc proc)
 {
-	IniGroup *group = ini_getgroup(ini, grpname, -1);
+	IniGroup *group = ini_getgroup(ini, grpname);
 	IniItem *item;
 	const char *entry;
 	uint i, j;
@@ -924,7 +925,7 @@
  * @param proc callback function that can will provide the source data if defined */
 static void ini_save_setting_list(IniFile *ini, const char *grpname, char **list, uint len, SettingListCallbackProc proc)
 {
-	IniGroup *group = ini_getgroup(ini, grpname, -1);
+	IniGroup *group = ini_getgroup(ini, grpname);
 	IniItem *item = NULL;
 	const char *entry;
 	uint i;
@@ -1140,6 +1141,14 @@
 	return 0;
 }
 
+static int32 CloseSignalGUI(int32 p1)
+{
+	if (p1 == 0) {
+		DeleteWindowByClass(WC_BUILD_SIGNAL);
+	}
+	return 0;
+}
+
 static int32 UpdateConsists(int32 p1)
 {
 	Vehicle *v;
@@ -1236,7 +1245,7 @@
 static int32 ConvertLandscape(const char *value)
 {
 	/* try with the old values */
-	return lookup_oneofmany("normal|hilly|desert|candy", value, -1);
+	return lookup_oneofmany("normal|hilly|desert|candy", value);
 }
 
 /* End - Callback Functions */
@@ -1407,7 +1416,7 @@
 	SDT_BOOL(Patches, longbridges,                   0,NN,  true,        STR_CONFIG_PATCHES_LONGBRIDGES,         NULL),
 	SDT_BOOL(Patches, signal_side,                   N,NN,  true,        STR_CONFIG_PATCHES_SIGNALSIDE,          RedrawScreen),
 	SDT_BOOL(Patches, always_small_airport,          0,NN, false,        STR_CONFIG_PATCHES_SMALL_AIRPORTS,      NULL),
-	SDT_BOOL(Patches, enable_signal_gui,             S, 0, false,        STR_CONFIG_PATCHES_ENABLE_SIGNAL_GUI,   NULL),
+	SDT_BOOL(Patches, enable_signal_gui,             S, 0, false,        STR_CONFIG_PATCHES_ENABLE_SIGNAL_GUI,   CloseSignalGUI),
 	 SDT_VAR(Patches, drag_signals_density,SLE_UINT8,S, 0,  4, 1, 20, 0, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY,DragSignalsDensityChanged),
 	 SDT_VAR(Patches, semaphore_build_before,SLE_INT32, S, NC, 1975, MIN_YEAR, MAX_YEAR, 1, STR_CONFIG_PATCHES_SEMAPHORE_BUILD_BEFORE_DATE, ResetSignalVariant),
 	SDT_CONDVAR(Patches, town_layout, SLE_UINT8, 59, SL_MAX_VERSION, 0, MS, TL_ORIGINAL, TL_NO_ROADS, NUM_TLS - 1, 1, STR_CONFIG_PATCHES_TOWN_LAYOUT, CheckTownLayout),
@@ -1650,7 +1659,7 @@
 
 static void NewsDisplayLoadConfig(IniFile *ini, const char *grpname)
 {
-	IniGroup *group = ini_getgroup(ini, grpname, -1);
+	IniGroup *group = ini_getgroup(ini, grpname);
 	IniItem *item;
 
 	/* If no group exists, return */
@@ -1685,7 +1694,7 @@
 /* Load a GRF configuration from the given group name */
 static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_static)
 {
-	IniGroup *group = ini_getgroup(ini, grpname, -1);
+	IniGroup *group = ini_getgroup(ini, grpname);
 	IniItem *item;
 	GRFConfig *first = NULL;
 	GRFConfig **curr = &first;
@@ -1737,7 +1746,7 @@
 
 static void NewsDisplaySaveConfig(IniFile *ini, const char *grpname)
 {
-	IniGroup *group = ini_getgroup(ini, grpname, -1);
+	IniGroup *group = ini_getgroup(ini, grpname);
 	IniItem **item;
 
 	if (group == NULL) return;
@@ -1762,7 +1771,7 @@
  */
 static void SaveVersionInConfig(IniFile *ini)
 {
-	IniGroup *group = ini_getgroup(ini, "version", -1);
+	IniGroup *group = ini_getgroup(ini, "version");
 
 	if (group == NULL) return;
 	group->item = NULL;
@@ -1786,7 +1795,7 @@
 /* Save a GRF configuration to the given group name */
 static void GRFSaveConfig(IniFile *ini, const char *grpname, const GRFConfig *list)
 {
-	IniGroup *group = ini_getgroup(ini, grpname, -1);
+	IniGroup *group = ini_getgroup(ini, grpname);
 	IniItem **item;
 	const GRFConfig *c;
 
--- a/src/settings_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/settings_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -389,7 +389,7 @@
 void ShowGameOptions()
 {
 	DeleteWindowById(WC_GAME_OPTIONS, 0);
-	AllocateWindowDesc(&_game_options_desc);
+	new Window(&_game_options_desc);
 }
 
 struct GameSettingData {
@@ -655,7 +655,7 @@
 					break;
 			} break;
 
-		case WE_MOUSELOOP: /* Handle the visual 'clicking' of the buttons */
+		case WE_TICK: /* Handle the visual 'clicking' of the buttons */
 			if (diffic_d->timeout != 0) {
 				diffic_d->timeout--;
 				if (diffic_d->timeout == 0) diffic_d->clicked_button = NO_SETTINGS_BUTTON;
@@ -698,7 +698,7 @@
 	/* Copy current settings (ingame or in intro) to temporary holding place
 	 * change that when setting stuff, copy back on clicking 'OK' */
 	_opt_mod_temp = *_opt_ptr;
-	AllocateWindowDesc(&_game_difficulty_desc);
+	new Window(&_game_difficulty_desc);
 }
 
 static const char *_patches_ui[] = {
@@ -1058,7 +1058,7 @@
 			break;
 
 		case WE_ON_EDIT_TEXT:
-			if (e->we.edittext.str != NULL) {
+			if (!StrEmpty(e->we.edittext.str)) {
 				const PatchEntry *pe = &_patches_page[WP(w, def_d).data_1].entries[WP(w, def_d).data_3];
 				const SettingDesc *sd = pe->setting;
 				int32 value = atoi(e->we.edittext.str);
@@ -1103,7 +1103,7 @@
 void ShowPatchesSelection()
 {
 	DeleteWindowById(WC_GAME_OPTIONS, 0);
-	AllocateWindowDesc(&_patches_selection_desc);
+	new Window(&_patches_selection_desc);
 }
 
 
@@ -1270,6 +1270,8 @@
 		} break;
 
 		case WE_ON_EDIT_TEXT: {
+			if (e->we.edittext.str == NULL) break;
+
 			const char *b = e->we.edittext.str;
 
 			switch (WP(w, def_d).data_2) {
@@ -1278,7 +1280,7 @@
 					break;
 
 				case CUSTCURR_SEPARATOR: /* Thousands seperator */
-					_custom_currency.separator = (b[0] == '\0') ? ' ' : b[0];
+					_custom_currency.separator = StrEmpty(b) ? ' ' : b[0];
 					ttd_strlcpy(_str_separator, b, lengthof(_str_separator));
 					break;
 
@@ -1333,5 +1335,5 @@
 	_str_separator[1] = '\0';
 
 	DeleteWindowById(WC_CUSTOM_CURRENCY, 0);
-	AllocateWindowDesc(&_cust_currency_desc);
+	new Window(&_cust_currency_desc);
 }
--- a/src/signs.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/signs.cpp	Sun May 11 20:09:34 2008 +0000
@@ -13,6 +13,7 @@
 #include "variables.h"
 #include "strings_func.h"
 #include "viewport_func.h"
+#include "tilehighlight_func.h"
 #include "zoom_func.h"
 #include "functions.h"
 #include "window_func.h"
--- a/src/signs_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/signs_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -19,6 +19,7 @@
 #include "map_func.h"
 #include "gfx_func.h"
 #include "viewport_func.h"
+#include "querystring_gui.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -135,7 +136,7 @@
 
 void ShowSignList()
 {
-	Window *w = AllocateWindowDescFront(&_sign_list_desc, 0);
+	Window *w = AllocateWindowDescFront<Window>(&_sign_list_desc, 0);
 	if (w != NULL) {
 		w->vscroll.cap = 12;
 		w->resize.step_height = 10;
@@ -143,11 +144,11 @@
 	}
 }
 
-/** Edit sign window stuff */
-struct editsign_d : querystr_d {
-	SignID cur_sign;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(editsign_d));
+static void RenameSign(SignID index, const char *text)
+{
+	_cmd_text = text;
+	DoCommandP(0, index, 0, NULL, CMD_RENAME_SIGN | CMD_MSG(STR_280C_CAN_T_CHANGE_SIGN_NAME));
+}
 
 enum QueryEditSignWidgets {
 	QUERY_EDIT_SIGN_WIDGET_TEXT = 3,
@@ -158,120 +159,122 @@
 	QUERY_EDIT_SIGN_WIDGET_NEXT,
 };
 
-static void UpdateSignEditWindow(Window *w, const Sign *si)
-{
-	/* Display an empty string when the sign hasnt been edited yet */
-	if (si->name != NULL) {
-		SetDParam(0, si->index);
-		GetString(_edit_str_buf, STR_SIGN_NAME, lastof(_edit_str_buf));
-	} else {
-		GetString(_edit_str_buf, STR_EMPTY, lastof(_edit_str_buf));
-	}
-	_edit_str_buf[lengthof(_edit_str_buf) - 1] = '\0';
-
-	WP(w, editsign_d).cur_sign = si->index;
-	InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, 31, 255); // Allow 31 characters (including \0)
-
-	w->InvalidateWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
-}
-
-static void RenameSign(SignID index, const char *text)
-{
-	_cmd_text = text;
-	DoCommandP(0, index, 0, NULL, CMD_RENAME_SIGN | CMD_MSG(STR_280C_CAN_T_CHANGE_SIGN_NAME));
-}
-
-static void QuerySignEditWndProc(Window *w, WindowEvent *e)
-{
-	editsign_d *qs = &WP(w, editsign_d);
-	Sign       *si;
-	uint       sign_index = 0;
-
-	switch (e->event) {
-		case WE_CREATE:
-			SetBit(_no_scroll, SCROLL_EDIT);
-			break;
-
-		case WE_PAINT:
-			SetDParam(0, qs->caption);
-			DrawWindowWidgets(w);
-			DrawEditBox(w, qs, QUERY_EDIT_SIGN_WIDGET_TEXT);
-			break;
-
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case QUERY_EDIT_SIGN_WIDGET_PREVIOUS:
-					if (_sign_sort_dirty) GlobalSortSignList();
-					sign_index = _sign_sort[_num_sign_sort - 1]->index;
-					for (uint i = 1; i < _num_sign_sort; i++) {
-						if (qs->cur_sign == _sign_sort[i]->index) {
-							sign_index = _sign_sort[i - 1]->index;
-							break;
-						}
-					}
-					si = GetSign(sign_index);
+struct SignWindow : QueryStringBaseWindow {
+	SignID cur_sign;
 
-					/* Scroll to sign and reopen window */
-					ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
-					UpdateSignEditWindow(w, si);
-					break;
-
-				case QUERY_EDIT_SIGN_WIDGET_NEXT:
-					if (_sign_sort_dirty) GlobalSortSignList();
-					sign_index = _sign_sort[0]->index;
-					for (uint i = 0; i < _num_sign_sort - 1; i++) {
-						if (qs->cur_sign == _sign_sort[i]->index) {
-							sign_index = _sign_sort[i + 1]->index;
-							break;
-						}
-					}
-					si = GetSign(sign_index);
-
-					/* Scroll to sign and reopen window */
-					ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
-					UpdateSignEditWindow(w, si);
-					break;
-
-				case QUERY_EDIT_SIGN_WIDGET_TEXT:
-					ShowOnScreenKeyboard(w, qs, e->we.click.widget, QUERY_EDIT_SIGN_WIDGET_CANCEL, QUERY_EDIT_SIGN_WIDGET_OK);
-					break;
-
-				case QUERY_EDIT_SIGN_WIDGET_DELETE:
-					/* Only need to set the buffer to null, the rest is handled as the OK button */
-					DeleteTextBufferAll(&qs->text);
-					/* FALL THROUGH */
+	SignWindow(const WindowDesc *desc, const Sign *si) : QueryStringBaseWindow(desc)
+	{
+		SetBit(_no_scroll, SCROLL_EDIT);
+		this->caption = STR_280B_EDIT_SIGN_TEXT;
+		this->afilter = CS_ALPHANUMERAL;
+		this->LowerWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
 
-				case QUERY_EDIT_SIGN_WIDGET_OK:
-					RenameSign(qs->cur_sign, qs->text.buf);
-					/* FALL THROUGH */
-
-				case QUERY_EDIT_SIGN_WIDGET_CANCEL:
-					delete w;
-					break;
-			}
-			break;
+		UpdateSignEditWindow(si);
+	}
 
-		case WE_KEYPRESS:
-			switch (HandleEditBoxKey(w, qs, QUERY_EDIT_SIGN_WIDGET_TEXT, e)) {
-				case 1: // Enter pressed, confirms change
-					RenameSign(qs->cur_sign, qs->text.buf);
-					/* FALL THROUGH */
+	~SignWindow()
+	{
+		ClrBit(_no_scroll, SCROLL_EDIT);
+	}
 
-				case 2: // ESC pressed, closes window, abandons changes
-					delete w;
-					break;
-			}
-			break;
+	void UpdateSignEditWindow(const Sign *si)
+	{
+		/* Display an empty string when the sign hasnt been edited yet */
+		if (si->name != NULL) {
+			SetDParam(0, si->index);
+			GetString(this->edit_str_buf, STR_SIGN_NAME, lastof(this->edit_str_buf));
+		} else {
+			GetString(this->edit_str_buf, STR_EMPTY, lastof(this->edit_str_buf));
+		}
+		this->edit_str_buf[lengthof(this->edit_str_buf) - 1] = '\0';
 
-		case WE_MOUSELOOP:
-			HandleEditBox(w, qs, QUERY_EDIT_SIGN_WIDGET_TEXT);
-			break;
+		this->cur_sign = si->index;
+		InitializeTextBuffer(&this->text, this->edit_str_buf, 31, 255); // Allow 31 characters (including \0)
 
-		case WE_DESTROY:
-			ClrBit(_no_scroll, SCROLL_EDIT);
-			break;
+		this->InvalidateWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
 	}
-}
+
+	virtual void OnPaint()
+	{
+		SetDParam(0, this->caption);
+		DrawWindowWidgets(this);
+		this->DrawEditBox(QUERY_EDIT_SIGN_WIDGET_TEXT);
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case QUERY_EDIT_SIGN_WIDGET_PREVIOUS: {
+				if (_sign_sort_dirty) GlobalSortSignList();
+				SignID sign_index = _sign_sort[_num_sign_sort - 1]->index;
+				for (uint i = 1; i < _num_sign_sort; i++) {
+					if (this->cur_sign == _sign_sort[i]->index) {
+						sign_index = _sign_sort[i - 1]->index;
+						break;
+					}
+				}
+				const Sign *si = GetSign(sign_index);
+
+				/* Scroll to sign and reopen window */
+				ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
+				UpdateSignEditWindow(si);
+			} break;
+
+			case QUERY_EDIT_SIGN_WIDGET_NEXT: {
+				if (_sign_sort_dirty) GlobalSortSignList();
+				SignID sign_index = _sign_sort[0]->index;
+				for (uint i = 0; i < _num_sign_sort - 1; i++) {
+					if (this->cur_sign == _sign_sort[i]->index) {
+						sign_index = _sign_sort[i + 1]->index;
+						break;
+					}
+				}
+				const Sign *si = GetSign(sign_index);
+
+				/* Scroll to sign and reopen window */
+				ScrollMainWindowToTile(TileVirtXY(si->x, si->y));
+				UpdateSignEditWindow(si);
+			} break;
+
+			case QUERY_EDIT_SIGN_WIDGET_TEXT:
+				ShowOnScreenKeyboard(this, widget, QUERY_EDIT_SIGN_WIDGET_CANCEL, QUERY_EDIT_SIGN_WIDGET_OK);
+				break;
+
+			case QUERY_EDIT_SIGN_WIDGET_DELETE:
+				/* Only need to set the buffer to null, the rest is handled as the OK button */
+				DeleteTextBufferAll(&this->text);
+				/* FALL THROUGH */
+
+			case QUERY_EDIT_SIGN_WIDGET_OK:
+				RenameSign(this->cur_sign, this->text.buf);
+				/* FALL THROUGH */
+
+			case QUERY_EDIT_SIGN_WIDGET_CANCEL:
+				delete this;
+				break;
+		}
+	}
+
+	virtual bool OnKeyPress(uint16 key, uint16 keycode)
+	{
+		bool cont = true;
+		switch (this->HandleEditBoxKey(QUERY_EDIT_SIGN_WIDGET_TEXT, key, keycode, cont)) {
+			case 1: // Enter pressed, confirms change
+				RenameSign(this->cur_sign, this->text.buf);
+				/* FALL THROUGH */
+
+			case 2: // ESC pressed, closes window, abandons changes
+				delete this;
+				break;
+		}
+		return cont;
+	}
+
+	virtual void OnMouseLoop()
+	{
+		this->HandleEditBox(QUERY_EDIT_SIGN_WIDGET_TEXT);
+	}
+};
 
 static const Widget _query_sign_edit_widgets[] = {
 { WWT_CLOSEBOX, RESIZE_NONE,  14,   0,  10,   0,  13, STR_00C5,          STR_018B_CLOSE_WINDOW},
@@ -292,7 +295,7 @@
 	WC_QUERY_STRING, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 	_query_sign_edit_widgets,
-	QuerySignEditWndProc
+	NULL
 };
 
 void ShowRenameSignWindow(const Sign *si)
@@ -301,11 +304,5 @@
 	DeleteWindowById(WC_QUERY_STRING, 0);
 	DeleteWindowById(WC_SAVELOAD, 0);
 
-	Window *w = AllocateWindowDesc(&_query_sign_edit_desc);
-
-	WP(w, editsign_d).caption = STR_280B_EDIT_SIGN_TEXT;
-	WP(w, editsign_d).afilter = CS_ALPHANUMERAL;
-	w->LowerWidget(QUERY_EDIT_SIGN_WIDGET_TEXT);
-
-	UpdateSignEditWindow(w, si);
+	new SignWindow(&_query_sign_edit_desc, si);
 }
--- a/src/smallmap_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/smallmap_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -554,539 +554,536 @@
 	SM_WIDGET_RESIZEBOX,
 };
 
-enum SmallMapType {
-	SMT_CONTOUR,
-	SMT_VEHICLES,
-	SMT_INDUSTRY,
-	SMT_OWNER = 5,
-};
-
-/**
- * Draws the small map.
- *
- * Basically, the small map is draw column of pixels by column of pixels. The pixels
- * are drawn directly into the screen buffer. The final map is drawn in multiple passes.
- * The passes are:
- * <ol><li>The colors of tiles in the different modes.</li>
- * <li>Town names (optional)</li></ol>
- *
- * @param dpi pointer to pixel to write onto
- * @param w pointer to Window struct
- * @param type type of map requested (vegetation, owners, routes, etc)
- * @param show_towns true if the town names should be displayed, false if not.
- */
-static void DrawSmallMap(DrawPixelInfo *dpi, Window *w, int type, bool show_towns)
+class SmallMapWindow : public Window
 {
-	Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
-	DrawPixelInfo *old_dpi;
-	int dx,dy, x, y, x2, y2;
-	void *ptr;
-	int tile_x;
-	int tile_y;
-	ViewPort *vp;
-
-	old_dpi = _cur_dpi;
-	_cur_dpi = dpi;
-
-	/* clear it */
-	GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
-
-	/* setup owner table */
-	if (type == SMT_OWNER) {
-		const Player *p;
+	enum SmallMapType {
+		SMT_CONTOUR,
+		SMT_VEHICLES,
+		SMT_INDUSTRY,
+		SMT_OWNER = 5,
+	};
 
-		/* fill with some special colors */
-		_owner_colors[OWNER_TOWN] = MKCOLOR(0xB4B4B4B4);
-		_owner_colors[OWNER_NONE] = MKCOLOR(0x54545454);
-		_owner_colors[OWNER_WATER] = MKCOLOR(0xCACACACA);
-		_owner_colors[OWNER_END]   = MKCOLOR(0x20202020); /* industry */
-
-		/* now fill with the player colors */
-		FOR_ALL_PLAYERS(p) {
-			if (p->is_active) {
-				_owner_colors[p->index] =
-					_colour_gradient[p->player_color][5] * 0x01010101;
-			}
-		}
-	}
-
-	tile_x = WP(w, smallmap_d).scroll_x / TILE_SIZE;
-	tile_y = WP(w, smallmap_d).scroll_y / TILE_SIZE;
-
-	dx = dpi->left + WP(w, smallmap_d).subscroll;
-	tile_x -= dx / 4;
-	tile_y += dx / 4;
-	dx &= 3;
+	enum {
+		BASE_NB_PER_COLUMN = 6,
+	};
 
-	dy = dpi->top;
-	tile_x += dy / 2;
-	tile_y += dy / 2;
-
-	if (dy & 1) {
-		tile_x++;
-		dx += 2;
-		if (dx > 3) {
-			dx -= 4;
-			tile_x--;
-			tile_y++;
-		}
-	}
+	int32 scroll_x;
+	int32 scroll_y;
+	int32 subscroll;
 
-	ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
-	x = - dx - 4;
-	y = 0;
+public:
+	/**
+	 * Draws the small map.
+	 *
+	 * Basically, the small map is draw column of pixels by column of pixels. The pixels
+	 * are drawn directly into the screen buffer. The final map is drawn in multiple passes.
+	 * The passes are:
+	 * <ol><li>The colors of tiles in the different modes.</li>
+	 * <li>Town names (optional)</li></ol>
+	 *
+	 * @param dpi pointer to pixel to write onto
+	 * @param w pointer to Window struct
+	 * @param type type of map requested (vegetation, owners, routes, etc)
+	 * @param show_towns true if the town names should be displayed, false if not.
+	 */
+	void DrawSmallMap(DrawPixelInfo *dpi, int type, bool show_towns)
+	{
+		Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
+		DrawPixelInfo *old_dpi;
+		int dx,dy, x, y, x2, y2;
+		void *ptr;
+		int tile_x;
+		int tile_y;
+		ViewPort *vp;
 
-	for (;;) {
-		uint32 mask = 0xFFFFFFFF;
-		int reps;
-		int t;
+		old_dpi = _cur_dpi;
+		_cur_dpi = dpi;
 
-		/* distance from left edge */
-		if (x < 0) {
-			if (x < -3) goto skip_column;
-			/* mask to use at the left edge */
-			mask = _smallmap_mask_left[x + 3];
+		/* clear it */
+		GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
+
+		/* setup owner table */
+		if (type == SMT_OWNER) {
+			const Player *p;
+
+			/* fill with some special colors */
+			_owner_colors[OWNER_TOWN] = MKCOLOR(0xB4B4B4B4);
+			_owner_colors[OWNER_NONE] = MKCOLOR(0x54545454);
+			_owner_colors[OWNER_WATER] = MKCOLOR(0xCACACACA);
+			_owner_colors[OWNER_END]   = MKCOLOR(0x20202020); /* industry */
+
+			/* now fill with the player colors */
+			FOR_ALL_PLAYERS(p) {
+				if (p->is_active) {
+					_owner_colors[p->index] =
+						_colour_gradient[p->player_color][5] * 0x01010101;
+				}
+			}
 		}
 
-		/* distance from right edge */
-		t = dpi->width - x;
-		if (t < 4) {
-			if (t <= 0) break; /* exit loop */
-			/* mask to use at the right edge */
-			mask &= _smallmap_mask_right[t - 1];
-		}
+		tile_x = this->scroll_x / TILE_SIZE;
+		tile_y = this->scroll_y / TILE_SIZE;
 
-		/* number of lines */
-		reps = (dpi->height - y + 1) / 2;
-		if (reps > 0) {
-			DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, _smallmap_draw_procs[type]);
+		dx = dpi->left + this->subscroll;
+		tile_x -= dx / 4;
+		tile_y += dx / 4;
+		dx &= 3;
+
+		dy = dpi->top;
+		tile_x += dy / 2;
+		tile_y += dy / 2;
+
+		if (dy & 1) {
+			tile_x++;
+			dx += 2;
+			if (dx > 3) {
+				dx -= 4;
+				tile_x--;
+				tile_y++;
+			}
 		}
 
-skip_column:
-		if (y == 0) {
-			tile_y++;
-			y++;
-			ptr = blitter->MoveTo(ptr, 0, 1);
-		} else {
-			tile_x--;
-			y--;
-			ptr = blitter->MoveTo(ptr, 0, -1);
-		}
-		ptr = blitter->MoveTo(ptr, 2, 0);
-		x += 2;
-	}
+		ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
+		x = - dx - 4;
+		y = 0;
 
-	/* draw vehicles? */
-	if (type == SMT_CONTOUR || type == SMT_VEHICLES) {
-		Vehicle *v;
-		bool skip;
-		byte color;
+		for (;;) {
+			uint32 mask = 0xFFFFFFFF;
+			int reps;
+			int t;
 
-		FOR_ALL_VEHICLES(v) {
-			if (v->type != VEH_EFFECT &&
-					(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0) {
-				/* Remap into flat coordinates. */
+			/* distance from left edge */
+			if (x < 0) {
+				if (x < -3) goto skip_column;
+				/* mask to use at the left edge */
+				mask = _smallmap_mask_left[x + 3];
+			}
+
+			/* distance from right edge */
+			t = dpi->width - x;
+			if (t < 4) {
+				if (t <= 0) break; /* exit loop */
+				/* mask to use at the right edge */
+				mask &= _smallmap_mask_right[t - 1];
+			}
+
+			/* number of lines */
+			reps = (dpi->height - y + 1) / 2;
+			if (reps > 0) {
+				DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, _smallmap_draw_procs[type]);
+			}
+
+	skip_column:
+			if (y == 0) {
+				tile_y++;
+				y++;
+				ptr = blitter->MoveTo(ptr, 0, 1);
+			} else {
+				tile_x--;
+				y--;
+				ptr = blitter->MoveTo(ptr, 0, -1);
+			}
+			ptr = blitter->MoveTo(ptr, 2, 0);
+			x += 2;
+		}
+
+		/* draw vehicles? */
+		if (type == SMT_CONTOUR || type == SMT_VEHICLES) {
+			Vehicle *v;
+			bool skip;
+			byte color;
+
+			FOR_ALL_VEHICLES(v) {
+				if (v->type != VEH_EFFECT &&
+						(v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) == 0) {
+					/* Remap into flat coordinates. */
+					Point pt = RemapCoords(
+						v->x_pos / TILE_SIZE - this->scroll_x / TILE_SIZE, // divide each one separately because (a-b)/c != a/c-b/c in integer world
+						v->y_pos / TILE_SIZE - this->scroll_y / TILE_SIZE, //    dtto
+						0);
+					x = pt.x;
+					y = pt.y;
+
+					/* Check if y is out of bounds? */
+					y -= dpi->top;
+					if (!IsInsideMM(y, 0, dpi->height)) continue;
+
+					/* Default is to draw both pixels. */
+					skip = false;
+
+					/* Offset X coordinate */
+					x -= this->subscroll + 3 + dpi->left;
+
+					if (x < 0) {
+						/* if x+1 is 0, that means we're on the very left edge,
+						 *  and should thus only draw a single pixel */
+						if (++x != 0) continue;
+						skip = true;
+					} else if (x >= dpi->width - 1) {
+						/* Check if we're at the very right edge, and if so draw only a single pixel */
+						if (x != dpi->width - 1) continue;
+						skip = true;
+					}
+
+					/* Calculate pointer to pixel and the color */
+					color = (type == SMT_VEHICLES) ? _vehicle_type_colors[v->type] : 0xF;
+
+					/* And draw either one or two pixels depending on clipping */
+					blitter->SetPixel(dpi->dst_ptr, x, y, color);
+					if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, color);
+				}
+			}
+		}
+
+		if (show_towns) {
+			const Town *t;
+
+			FOR_ALL_TOWNS(t) {
+				/* Remap the town coordinate */
 				Point pt = RemapCoords(
-					v->x_pos / TILE_SIZE - WP(w, smallmap_d).scroll_x / TILE_SIZE, // divide each one separately because (a-b)/c != a/c-b/c in integer world
-					v->y_pos / TILE_SIZE - WP(w, smallmap_d).scroll_y / TILE_SIZE, //    dtto
+					(int)(TileX(t->xy) * TILE_SIZE - this->scroll_x) / TILE_SIZE,
+					(int)(TileY(t->xy) * TILE_SIZE - this->scroll_y) / TILE_SIZE,
 					0);
-				x = pt.x;
+				x = pt.x - this->subscroll + 3 - (t->sign.width_2 >> 1);
 				y = pt.y;
 
-				/* Check if y is out of bounds? */
-				y -= dpi->top;
-				if (!IsInsideMM(y, 0, dpi->height)) continue;
-
-				/* Default is to draw both pixels. */
-				skip = false;
-
-				/* Offset X coordinate */
-				x -= WP(w, smallmap_d).subscroll + 3 + dpi->left;
-
-				if (x < 0) {
-					/* if x+1 is 0, that means we're on the very left edge,
-					 *  and should thus only draw a single pixel */
-					if (++x != 0) continue;
-					skip = true;
-				} else if (x >= dpi->width - 1) {
-					/* Check if we're at the very right edge, and if so draw only a single pixel */
-					if (x != dpi->width - 1) continue;
-					skip = true;
+				/* Check if the town sign is within bounds */
+				if (x + t->sign.width_2 > dpi->left &&
+						x < dpi->left + dpi->width &&
+						y + 6 > dpi->top &&
+						y < dpi->top + dpi->height) {
+					/* And draw it. */
+					SetDParam(0, t->index);
+					DrawString(x, y, STR_2056, TC_WHITE);
 				}
-
-				/* Calculate pointer to pixel and the color */
-				color = (type == SMT_VEHICLES) ? _vehicle_type_colors[v->type] : 0xF;
-
-				/* And draw either one or two pixels depending on clipping */
-				blitter->SetPixel(dpi->dst_ptr, x, y, color);
-				if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, color);
 			}
 		}
+
+		/* Draw map indicators */
+		{
+			Point pt;
+
+			/* Find main viewport. */
+			vp = FindWindowById(WC_MAIN_WINDOW,0)->viewport;
+
+			pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
+
+			x = vp->virtual_left - pt.x;
+			y = vp->virtual_top - pt.y;
+			x2 = (x + vp->virtual_width) / TILE_SIZE;
+			y2 = (y + vp->virtual_height) / TILE_SIZE;
+			x /= TILE_SIZE;
+			y /= TILE_SIZE;
+
+			x -= this->subscroll;
+			x2 -= this->subscroll;
+
+			DrawVertMapIndicator(x, y, x, y2);
+			DrawVertMapIndicator(x2, y, x2, y2);
+
+			DrawHorizMapIndicator(x, y, x2, y);
+			DrawHorizMapIndicator(x, y2, x2, y2);
+		}
+		_cur_dpi = old_dpi;
 	}
 
-	if (show_towns) {
-		const Town *t;
+	void SmallMapCenterOnCurrentPos()
+	{
+		int x, y;
+		ViewPort *vp;
+		vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
 
-		FOR_ALL_TOWNS(t) {
-			/* Remap the town coordinate */
-			Point pt = RemapCoords(
-				(int)(TileX(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_x) / TILE_SIZE,
-				(int)(TileY(t->xy) * TILE_SIZE - WP(w, smallmap_d).scroll_y) / TILE_SIZE,
-				0);
-			x = pt.x - WP(w, smallmap_d).subscroll + 3 - (t->sign.width_2 >> 1);
-			y = pt.y;
+		x  = ((vp->virtual_width  - (this->widget[SM_WIDGET_MAP].right  - this->widget[SM_WIDGET_MAP].left) * TILE_SIZE) / 2 + vp->virtual_left) / 4;
+		y  = ((vp->virtual_height - (this->widget[SM_WIDGET_MAP].bottom - this->widget[SM_WIDGET_MAP].top ) * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
+		this->scroll_x = (y - x) & ~0xF;
+		this->scroll_y = (x + y) & ~0xF;
+		this->SetDirty();
+	}
 
-			/* Check if the town sign is within bounds */
-			if (x + t->sign.width_2 > dpi->left &&
-					x < dpi->left + dpi->width &&
-					y + 6 > dpi->top &&
-					y < dpi->top + dpi->height) {
-				/* And draw it. */
-				SetDParam(0, t->index);
-				DrawString(x, y, STR_2056, TC_WHITE);
+	SmallMapWindow(const WindowDesc *desc, void *data, int window_number) : Window(desc, data, window_number)
+	{
+		/* Resize the window to fit industries list */
+		if (_industries_per_column > BASE_NB_PER_COLUMN) {
+			uint diff = ((_industries_per_column - BASE_NB_PER_COLUMN) * BASE_NB_PER_COLUMN) + 1;
+
+			this->height = this->height + diff;
+
+			Widget *wi = &this->widget[SM_WIDGET_LEGEND]; // label panel
+			wi->bottom = wi->bottom + diff;
+
+			wi = &this->widget[SM_WIDGET_BUTTONSPANEL]; // filler panel under smallmap buttons
+			wi->bottom = wi->bottom + diff - 1;
+
+			/* Change widget position
+			 * - footer panel
+			 * - enable all industry
+			 * - disable all industry
+			 * - resize window button
+			 */
+			for (uint i = SM_WIDGET_BOTTOMPANEL; i <= SM_WIDGET_RESIZEBOX; i++) {
+				wi           = &this->widget[i];
+				wi->top      = wi->top + diff;
+				wi->bottom   = wi->bottom + diff;
 			}
 		}
+
+		this->LowerWidget(_smallmap_type + SMT_OWNER);
+		this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, _smallmap_show_towns);
+
+		this->SmallMapCenterOnCurrentPos();
+		this->FindWindowPlacementAndResize(desc);
+	}
+
+	virtual void OnPaint()
+	{
+		const LegendAndColour *tbl;
+		int x, y, y_org;
+		uint diff;
+		DrawPixelInfo new_dpi;
+
+		/* Hide Enable all/Disable all buttons if is not industry type small map*/
+		this->SetWidgetHiddenState(SM_WIDGET_ENABLEINDUSTRIES, _smallmap_type != SMT_INDUSTRY);
+		this->SetWidgetHiddenState(SM_WIDGET_DISABLEINDUSTRIES, _smallmap_type != SMT_INDUSTRY);
+
+		/* draw the window */
+		SetDParam(0, STR_00E5_CONTOURS + _smallmap_type);
+		DrawWindowWidgets(this);
+
+		tbl = _legend_table[_smallmap_type];
+
+		/* difference in window size */
+		diff = (_industries_per_column > BASE_NB_PER_COLUMN) ? ((_industries_per_column - BASE_NB_PER_COLUMN) * BASE_NB_PER_COLUMN) + 1 : 0;
+
+		x = 4;
+		y_org = this->height - 44 - 11 - diff;
+		y = y_org;
+
+		for (;;) {
+			if (_smallmap_type == SMT_INDUSTRY) {
+				/* Industry name must be formated, since it's not in tiny font in the specs.
+				 * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font.*/
+				SetDParam(0, tbl->legend);
+				assert(tbl->type < NUM_INDUSTRYTYPES);
+				SetDParam(1, _industry_counts[tbl->type]);
+				if (!tbl->show_on_map) {
+					/* Simply draw the string, not the black border of the legend color.
+					 * This will enforce the idea of the disabled item */
+					DrawString(x + 11, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
+				} else {
+					DrawString(x + 11, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
+					GfxFillRect(x, y + 1, x + 8, y + 5, 0); // outer border of the legend color
+				}
+			} else {
+				/* Anything that is not an industry is using normal process */
+				GfxFillRect(x, y + 1, x + 8, y + 5, 0);
+				DrawString(x + 11, y, tbl->legend, TC_FROMSTRING);
+			}
+			GfxFillRect(x + 1, y + 2, x + 7, y + 4, tbl->colour); // legend color
+
+			tbl += 1;
+			y += 6;
+
+			if (tbl->end) { // end of the list
+				break;
+			} else if (tbl->col_break) {
+				/*  break asked, continue at top, 123 pixels (one "row") to the right */
+				x += 123;
+				y = y_org;
+			}
+		}
+
+		if (!FillDrawPixelInfo(&new_dpi, 3, 17, this->width - 28 + 22, this->height - 64 - 11 - diff)) return;
+
+		this->DrawSmallMap(&new_dpi, _smallmap_type, _smallmap_show_towns);
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case SM_WIDGET_MAP: { // Map window
+				/*
+				 * XXX: scrolling with the left mouse button is done by subsequently
+				 * clicking with the left mouse button; clicking once centers the
+				 * large map at the selected point. So by unclicking the left mouse
+				 * button here, it gets reclicked during the next inputloop, which
+				 * would make it look like the mouse is being dragged, while it is
+				 * actually being (virtually) clicked every inputloop.
+				 */
+				_left_button_clicked = false;
+
+				Point pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
+				Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
+				w->viewport->dest_scrollpos_x = pt.x + ((_cursor.pos.x - this->left + 2) << 4) - (w->viewport->virtual_width >> 1);
+				w->viewport->dest_scrollpos_y = pt.y + ((_cursor.pos.y - this->top - 16) << 4) - (w->viewport->virtual_height >> 1);
+
+				this->SetDirty();
+			} break;
+
+			case SM_WIDGET_CONTOUR:    // Show land contours
+			case SM_WIDGET_VEHICLES:   // Show vehicles
+			case SM_WIDGET_INDUSTRIES: // Show industries
+			case SM_WIDGET_ROUTES:     // Show transport routes
+			case SM_WIDGET_VEGETATION: // Show vegetation
+			case SM_WIDGET_OWNERS:     // Show land owners
+				this->RaiseWidget(_smallmap_type + SM_WIDGET_CONTOUR);
+				_smallmap_type = widget - SM_WIDGET_CONTOUR;
+				this->LowerWidget(_smallmap_type + SM_WIDGET_CONTOUR);
+
+				this->SetDirty();
+				SndPlayFx(SND_15_BEEP);
+				break;
+
+			case SM_WIDGET_CENTERMAP: // Center the smallmap again
+				this->SmallMapCenterOnCurrentPos();
+
+				this->SetDirty();
+				SndPlayFx(SND_15_BEEP);
+				break;
+
+			case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
+				this->ToggleWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME);
+				_smallmap_show_towns = this->IsWidgetLowered(SM_WIDGET_TOGGLETOWNNAME);
+
+				this->SetDirty();
+				SndPlayFx(SND_15_BEEP);
+				break;
+
+			case SM_WIDGET_LEGEND: // Legend
+				/* if industry type small map*/
+				if (_smallmap_type == SMT_INDUSTRY) {
+					/* if click on industries label, find right industry type and enable/disable it */
+					Widget *wi = &this->widget[SM_WIDGET_LEGEND]; // label panel
+					uint column = (pt.x - 4) / 123;
+					uint line = (pt.y - wi->top - 2) / 6;
+					uint free = _smallmap_industry_count % 3;
+
+					if (column <= 3) {
+						/* check if click is on industry label*/
+						uint industry_pos = 0;
+						for (uint i = 0; i <= column; i++) {
+							uint diff = (free > 0) ? 1 : 0;
+							uint max_column_lines = _industries_per_column + diff;
+
+							if (i < column) industry_pos = industry_pos + _industries_per_column + diff;
+
+							if (i == column && line <= max_column_lines - 1) {
+								industry_pos = industry_pos + line;
+								_legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
+							}
+							if( free > 0) free--;
+						}
+					}
+					/* Raise the two buttons "all", as we have done a specific choice */
+					this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
+					this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
+					this->SetDirty();
+				}
+				break;
+
+			case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
+				for (int i = 0; i != _smallmap_industry_count; i++) {
+					_legend_from_industries[i].show_on_map = true;
+				}
+				/* toggle appeareance indicating the choice */
+				this->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
+				this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
+				this->SetDirty();
+				break;
+
+			case SM_WIDGET_DISABLEINDUSTRIES: // disable all industries
+				for (int i = 0; i != _smallmap_industry_count; i++) {
+					_legend_from_industries[i].show_on_map = false;
+				}
+				/* toggle appeareance indicating the choice */
+				this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
+				this->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
+				this->SetDirty();
+				break;
+		}
 	}
 
-	/* Draw map indicators */
+	virtual void OnRightClick(Point pt, int widget)
 	{
-		Point pt;
-
-		/* Find main viewport. */
-		vp = FindWindowById(WC_MAIN_WINDOW,0)->viewport;
-
-		pt = RemapCoords(WP(w, smallmap_d).scroll_x, WP(w, smallmap_d).scroll_y, 0);
-
-		x = vp->virtual_left - pt.x;
-		y = vp->virtual_top - pt.y;
-		x2 = (x + vp->virtual_width) / TILE_SIZE;
-		y2 = (y + vp->virtual_height) / TILE_SIZE;
-		x /= TILE_SIZE;
-		y /= TILE_SIZE;
-
-		x -= WP(w, smallmap_d).subscroll;
-		x2 -= WP(w, smallmap_d).subscroll;
-
-		DrawVertMapIndicator(x, y, x, y2);
-		DrawVertMapIndicator(x2, y, x2, y2);
-
-		DrawHorizMapIndicator(x, y, x2, y);
-		DrawHorizMapIndicator(x, y2, x2, y2);
+		if (widget == SM_WIDGET_MAP) {
+			if (_scrolling_viewport) return;
+			_scrolling_viewport = true;
+			_cursor.delta.x = 0;
+			_cursor.delta.y = 0;
+		}
 	}
-	_cur_dpi = old_dpi;
-}
-
-void SmallMapCenterOnCurrentPos(Window *w)
-{
-	int x, y;
-	ViewPort *vp;
-	vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
-
-	x  = ((vp->virtual_width  - (w->widget[SM_WIDGET_MAP].right  - w->widget[SM_WIDGET_MAP].left) * TILE_SIZE) / 2 + vp->virtual_left) / 4;
-	y  = ((vp->virtual_height - (w->widget[SM_WIDGET_MAP].bottom - w->widget[SM_WIDGET_MAP].top ) * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
-	WP(w, smallmap_d).scroll_x = (y - x) & ~0xF;
-	WP(w, smallmap_d).scroll_y = (x + y) & ~0xF;
-	w->SetDirty();
-}
-
-enum {
-	BASE_NB_PER_COLUMN = 6,
-};
-
-static void SmallMapWindowProc(Window *w, WindowEvent *e)
-{
-	switch (e->event) {
-		case WE_PAINT: {
-			const LegendAndColour *tbl;
-			int x, y, y_org;
-			uint diff;
-			DrawPixelInfo new_dpi;
-
-			/* Hide Enable all/Disable all buttons if is not industry type small map*/
-			w->SetWidgetHiddenState(SM_WIDGET_ENABLEINDUSTRIES, _smallmap_type != SMT_INDUSTRY);
-			w->SetWidgetHiddenState(SM_WIDGET_DISABLEINDUSTRIES, _smallmap_type != SMT_INDUSTRY);
-
-			/* draw the window */
-			SetDParam(0, STR_00E5_CONTOURS + _smallmap_type);
-			DrawWindowWidgets(w);
-
-			tbl = _legend_table[_smallmap_type];
-
-			/* difference in window size */
-			diff = (_industries_per_column > BASE_NB_PER_COLUMN) ? ((_industries_per_column - BASE_NB_PER_COLUMN) * BASE_NB_PER_COLUMN) + 1 : 0;
-
-			x = 4;
-			y_org = w->height - 44 - 11 - diff;
-			y = y_org;
-
-			for (;;) {
-
-				if (_smallmap_type == SMT_INDUSTRY) {
-					/* Industry name must be formated, since it's not in tiny font in the specs.
-					 * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font.*/
-					SetDParam(0, tbl->legend);
-					assert(tbl->type < NUM_INDUSTRYTYPES);
-					SetDParam(1, _industry_counts[tbl->type]);
-					if (!tbl->show_on_map) {
-						/* Simply draw the string, not the black border of the legend color.
-						 * This will enforce the idea of the disabled item */
-						DrawString(x + 11, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
-					} else {
-						DrawString(x + 11, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
-						GfxFillRect(x, y + 1, x + 8, y + 5, 0); // outer border of the legend color
-					}
-				} else {
-					/* Anything that is not an industry is using normal process */
-					GfxFillRect(x, y + 1, x + 8, y + 5, 0);
-					DrawString(x + 11, y, tbl->legend, TC_FROMSTRING);
-				}
-				GfxFillRect(x + 1, y + 2, x + 7, y + 4, tbl->colour); // legend color
-
-				tbl += 1;
-				y += 6;
-
-				if (tbl->end) { // end of the list
-					break;
-				} else if (tbl->col_break) {
-					/*  break asked, continue at top, 123 pixels (one "row") to the right */
-					x += 123;
-					y = y_org;
-				}
-			}
-
-			if (!FillDrawPixelInfo(&new_dpi, 3, 17, w->width - 28 + 22, w->height - 64 - 11 - diff))
-				return;
-
-			DrawSmallMap(&new_dpi, w, _smallmap_type, _smallmap_show_towns);
-		} break;
-
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case SM_WIDGET_MAP: { // Map window
-					Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
-					Point pt;
-
-					/*
-					 * XXX: scrolling with the left mouse button is done by subsequently
-					 * clicking with the left mouse button; clicking once centers the
-					 * large map at the selected point. So by unclicking the left mouse
-					 * button here, it gets reclicked during the next inputloop, which
-					 * would make it look like the mouse is being dragged, while it is
-					 * actually being (virtually) clicked every inputloop.
-					 */
-					_left_button_clicked = false;
-
-					pt = RemapCoords(WP(w, smallmap_d).scroll_x, WP(w,smallmap_d).scroll_y, 0);
-					WP(w2, vp_d).dest_scrollpos_x = pt.x + ((_cursor.pos.x - w->left + 2) << 4) - (w2->viewport->virtual_width >> 1);
-					WP(w2, vp_d).dest_scrollpos_y = pt.y + ((_cursor.pos.y - w->top - 16) << 4) - (w2->viewport->virtual_height >> 1);
-
-					w->SetDirty();
-				} break;
-
-				case SM_WIDGET_CONTOUR:    // Show land contours
-				case SM_WIDGET_VEHICLES:   // Show vehicles
-				case SM_WIDGET_INDUSTRIES: // Show industries
-				case SM_WIDGET_ROUTES:     // Show transport routes
-				case SM_WIDGET_VEGETATION: // Show vegetation
-				case SM_WIDGET_OWNERS:     // Show land owners
-					w->RaiseWidget(_smallmap_type + SM_WIDGET_CONTOUR);
-					_smallmap_type = e->we.click.widget - SM_WIDGET_CONTOUR;
-					w->LowerWidget(_smallmap_type + SM_WIDGET_CONTOUR);
 
-					w->SetDirty();
-					SndPlayFx(SND_15_BEEP);
-					break;
-
-				case SM_WIDGET_CENTERMAP: // Center the smallmap again
-					SmallMapCenterOnCurrentPos(w);
-
-					w->SetDirty();
-					SndPlayFx(SND_15_BEEP);
-					break;
-
-				case SM_WIDGET_TOGGLETOWNNAME: // Toggle town names
-					w->ToggleWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME);
-					_smallmap_show_towns = w->IsWidgetLowered(SM_WIDGET_TOGGLETOWNNAME);
-
-					w->SetDirty();
-					SndPlayFx(SND_15_BEEP);
-					break;
-
-				case SM_WIDGET_LEGEND: // Legend
-					/* if industry type small map*/
-					if (_smallmap_type == SMT_INDUSTRY) {
-						/* if click on industries label, find right industry type and enable/disable it */
-						Widget *wi = &w->widget[SM_WIDGET_LEGEND]; // label panel
-						uint column = (e->we.click.pt.x - 4) / 123;
-						uint line = (e->we.click.pt.y - wi->top - 2) / 6;
-						uint free = _smallmap_industry_count % 3;
-
-						if (column <= 3) {
-							/* check if click is on industry label*/
-							uint industry_pos = 0;
-							for (uint i = 0; i <= column; i++) {
-								uint diff = (free > 0) ? 1 : 0;
-								uint max_column_lines = _industries_per_column + diff;
-
-								if (i < column) industry_pos = industry_pos + _industries_per_column + diff;
-
-								if (i == column && line <= max_column_lines - 1) {
-									industry_pos = industry_pos + line;
-									_legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
-								}
-								if( free > 0) free--;
-							}
-						}
-						/* Raise the two buttons "all", as we have done a specific choice */
-						w->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
-						w->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
-						w->SetDirty();
-					}
-					break;
-
-				case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
-					for (int i = 0; i != _smallmap_industry_count; i++) {
-						_legend_from_industries[i].show_on_map = true;
-					}
-					/* toggle appeareance indicating the choice */
-					w->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
-					w->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
-					w->SetDirty();
-					break;
-
-				case SM_WIDGET_DISABLEINDUSTRIES: // disable all industries
-					for (int i = 0; i != _smallmap_industry_count; i++) {
-						_legend_from_industries[i].show_on_map = false;
-					}
-					/* toggle appeareance indicating the choice */
-					w->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
-					w->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
-					w->SetDirty();
-					break;
-				}
-			break;
+	virtual void OnTick()
+	{
+		/* update the window every now and then */
+		if ((++this->vscroll.pos & 0x1F) == 0) this->SetDirty();
+	}
 
-		case WE_RCLICK:
-			if (e->we.click.widget == SM_WIDGET_MAP) {
-				if (_scrolling_viewport) return;
-				_scrolling_viewport = true;
-				_cursor.delta.x = 0;
-				_cursor.delta.y = 0;
-			}
-			break;
-
-		case WE_MOUSELOOP:
-			/* update the window every now and then */
-			if ((++w->vscroll.pos & 0x1F) == 0) w->SetDirty();
-			break;
-
-		case WE_SCROLL: {
-			int x;
-			int y;
-			int sub;
-			int hx;
-			int hy;
-			int hvx;
-			int hvy;
-
-			_cursor.fix_at = true;
-
-			x = WP(w, smallmap_d).scroll_x;
-			y = WP(w, smallmap_d).scroll_y;
-
-			sub = WP(w, smallmap_d).subscroll + e->we.scroll.delta.x;
-
-			x -= (sub >> 2) << 4;
-			y += (sub >> 2) << 4;
-			sub &= 3;
-
-			x += (e->we.scroll.delta.y >> 1) << 4;
-			y += (e->we.scroll.delta.y >> 1) << 4;
+	virtual void OnScroll(Point delta)
+	{
+		_cursor.fix_at = true;
 
-			if (e->we.scroll.delta.y & 1) {
-				x += TILE_SIZE;
-				sub += 2;
-				if (sub > 3) {
-					sub -= 4;
-					x -= TILE_SIZE;
-					y += TILE_SIZE;
-				}
-			}
+		int x = this->scroll_x;
+		int y = this->scroll_y;
 
-			hx = (w->widget[SM_WIDGET_MAP].right  - w->widget[SM_WIDGET_MAP].left) / 2;
-			hy = (w->widget[SM_WIDGET_MAP].bottom - w->widget[SM_WIDGET_MAP].top ) / 2;
-			hvx = hx * -4 + hy * 8;
-			hvy = hx *  4 + hy * 8;
-			if (x < -hvx) {
-				x = -hvx;
-				sub = 0;
+		int sub = this->subscroll + delta.x;
+
+		x -= (sub >> 2) << 4;
+		y += (sub >> 2) << 4;
+		sub &= 3;
+
+		x += (delta.y >> 1) << 4;
+		y += (delta.y >> 1) << 4;
+
+		if (delta.y & 1) {
+			x += TILE_SIZE;
+			sub += 2;
+			if (sub > 3) {
+				sub -= 4;
+				x -= TILE_SIZE;
+				y += TILE_SIZE;
 			}
-			if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
-				x = MapMaxX() * TILE_SIZE - hvx;
-				sub = 0;
-			}
-			if (y < -hvy) {
-				y = -hvy;
-				sub = 0;
-			}
-			if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
-				y = MapMaxY() * TILE_SIZE - hvy;
-				sub = 0;
-			}
+		}
 
-			WP(w, smallmap_d).scroll_x = x;
-			WP(w, smallmap_d).scroll_y = y;
-			WP(w, smallmap_d).subscroll = sub;
+		int hx = (this->widget[SM_WIDGET_MAP].right  - this->widget[SM_WIDGET_MAP].left) / 2;
+		int hy = (this->widget[SM_WIDGET_MAP].bottom - this->widget[SM_WIDGET_MAP].top ) / 2;
+		int hvx = hx * -4 + hy * 8;
+		int hvy = hx *  4 + hy * 8;
+		if (x < -hvx) {
+			x = -hvx;
+			sub = 0;
+		}
+		if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
+			x = MapMaxX() * TILE_SIZE - hvx;
+			sub = 0;
+		}
+		if (y < -hvy) {
+			y = -hvy;
+			sub = 0;
+		}
+		if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
+			y = MapMaxY() * TILE_SIZE - hvy;
+			sub = 0;
+		}
 
-			w->SetDirty();
-		} break;
+		this->scroll_x = x;
+		this->scroll_y = y;
+		this->subscroll = sub;
+
+		this->SetDirty();
 	}
-}
+};
 
 static const WindowDesc _smallmap_desc = {
 	WDP_AUTO, WDP_AUTO, 350, 214, 446, 314,
 	WC_SMALLMAP, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_smallmap_widgets,
-	SmallMapWindowProc
+	NULL
 };
 
 void ShowSmallMap()
 {
-	Window *w;
-
-	w = AllocateWindowDescFront(&_smallmap_desc, 0);
-	if (w == NULL) return;
-
-	/* Resize the window to fit industries list */
-	if (_industries_per_column > BASE_NB_PER_COLUMN) {
-		uint diff = ((_industries_per_column - BASE_NB_PER_COLUMN) * BASE_NB_PER_COLUMN) + 1;
-
-		w->height = w->height + diff;
-
-		Widget *wi = &w->widget[SM_WIDGET_LEGEND]; // label panel
-		wi->bottom = wi->bottom + diff;
-
-		wi = &w->widget[SM_WIDGET_BUTTONSPANEL]; // filler panel under smallmap buttons
-		wi->bottom = wi->bottom + diff - 1;
-
-		/* Change widget position
-		 * - footer panel
-		 * - enable all industry
-		 * - disable all industry
-		 * - resize window button
-		 */
-		for (uint i = SM_WIDGET_BOTTOMPANEL; i <= SM_WIDGET_RESIZEBOX; i++) {
-			wi           = &w->widget[i];
-			wi->top      = wi->top + diff;
-			wi->bottom   = wi->bottom + diff;
-		}
-	}
-
-	w->LowerWidget(_smallmap_type + SMT_OWNER);
-	w->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, _smallmap_show_towns);
-
-	SmallMapCenterOnCurrentPos(w);
+	AllocateWindowDescFront<SmallMapWindow>(&_smallmap_desc, 0);
 }
 
 /* Extra ViewPort Window Stuff */
@@ -1131,21 +1128,21 @@
 
 		case 7: { // location button (move main view to same spot as this view) 'Paste Location'
 			Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
-			int x = WP(w, vp_d).scrollpos_x; // Where is the main looking at
-			int y = WP(w, vp_d).scrollpos_y;
+			int x = w->viewport->scrollpos_x; // Where is the main looking at
+			int y = w->viewport->scrollpos_y;
 
 			/* set this view to same location. Based on the center, adjusting for zoom */
-			WP(w2, vp_d).dest_scrollpos_x =  x - (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
-			WP(w2, vp_d).dest_scrollpos_y =  y - (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
+			w2->viewport->dest_scrollpos_x =  x - (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
+			w2->viewport->dest_scrollpos_y =  y - (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
 		} break;
 
 		case 8: { // inverse location button (move this view to same spot as main view) 'Copy Location'
 			const Window *w2 = FindWindowById(WC_MAIN_WINDOW, 0);
-			int x = WP(w2, const vp_d).scrollpos_x;
-			int y = WP(w2, const vp_d).scrollpos_y;
+			int x = w2->viewport->scrollpos_x;
+			int y = w2->viewport->scrollpos_y;
 
-			WP(w, vp_d).dest_scrollpos_x =  x + (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
-			WP(w, vp_d).dest_scrollpos_y =  y + (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
+			w->viewport->dest_scrollpos_x =  x + (w2->viewport->virtual_width -  w->viewport->virtual_width) / 2;
+			w->viewport->dest_scrollpos_y =  y + (w2->viewport->virtual_height - w->viewport->virtual_height) / 2;
 		} break;
 		}
 		break;
@@ -1165,20 +1162,18 @@
 				_scrolling_viewport = false;
 			}
 
-			WP(w, vp_d).scrollpos_x += ScaleByZoom(e->we.scroll.delta.x, vp->zoom);
-			WP(w, vp_d).scrollpos_y += ScaleByZoom(e->we.scroll.delta.y, vp->zoom);
-			WP(w, vp_d).dest_scrollpos_x = WP(w, vp_d).scrollpos_x;
-			WP(w, vp_d).dest_scrollpos_y = WP(w, vp_d).scrollpos_y;
+			w->viewport->scrollpos_x += ScaleByZoom(e->we.scroll.delta.x, vp->zoom);
+			w->viewport->scrollpos_y += ScaleByZoom(e->we.scroll.delta.y, vp->zoom);
+			w->viewport->dest_scrollpos_x = w->viewport->scrollpos_x;
+			w->viewport->dest_scrollpos_y = w->viewport->scrollpos_y;
 		} break;
 
 		case WE_MOUSEWHEEL:
 			ZoomInOrOutToCursorWindow(e->we.wheel.wheel < 0, w);
 			break;
 
-
-		case WE_MESSAGE:
+		case WE_INVALIDATE_DATA:
 			/* Only handle zoom message if intended for us (msg ZOOM_IN/ZOOM_OUT) */
-			if (e->we.message.wparam != w->window_number) break;
 			HandleZoomMessage(w, w->viewport, 5, 6);
 			break;
 	}
@@ -1199,7 +1194,7 @@
 	/* find next free window number for extra viewport */
 	while (FindWindowById(WC_EXTRA_VIEW_PORT, i) != NULL) i++;
 
-	Window *w = AllocateWindowDescFront(&_extra_view_port_desc, i);
+	Window *w = AllocateWindowDescFront<Window>(&_extra_view_port_desc, i);
 	if (w != NULL) {
 		Point pt;
 
@@ -1208,15 +1203,31 @@
 			const Window *v = FindWindowById(WC_MAIN_WINDOW, 0);
 
 			/* center on same place as main window (zoom is maximum, no adjustment needed) */
-			pt.x = WP(v, vp_d).scrollpos_x + v->viewport->virtual_height / 2;
-			pt.y = WP(v, vp_d).scrollpos_y + v->viewport->virtual_height / 2;
+			pt.x = v->viewport->scrollpos_x + v->viewport->virtual_height / 2;
+			pt.y = v->viewport->scrollpos_y + v->viewport->virtual_height / 2;
 		} else {
 			pt = RemapCoords(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, TileHeight(tile));
 		}
 
-		WP(w, vp_d).scrollpos_x = pt.x - ((w->widget[4].right - w->widget[4].left) - 1) / 2;
-		WP(w, vp_d).scrollpos_y = pt.y - ((w->widget[4].bottom - w->widget[4].top) - 1) / 2;
-		WP(w, vp_d).dest_scrollpos_x = WP(w, vp_d).scrollpos_x;
-		WP(w, vp_d).dest_scrollpos_y = WP(w, vp_d).scrollpos_y;
+		w->viewport->scrollpos_x = pt.x - ((w->widget[4].right - w->widget[4].left) - 1) / 2;
+		w->viewport->scrollpos_y = pt.y - ((w->widget[4].bottom - w->widget[4].top) - 1) / 2;
+		w->viewport->dest_scrollpos_x = w->viewport->scrollpos_x;
+		w->viewport->dest_scrollpos_y = w->viewport->scrollpos_y;
 	}
 }
+
+bool ScrollMainWindowTo(int x, int y, bool instant)
+{
+	bool res = ScrollWindowTo(x, y, FindWindowById(WC_MAIN_WINDOW, 0), instant);
+
+	/* If a user scrolls to a tile (via what way what so ever) and already is on
+	 *  that tile (e.g.: pressed twice), move the smallmap to that location,
+	 *  so you directly see where you are on the smallmap. */
+
+	if (res) return res;
+
+	SmallMapWindow *w = dynamic_cast<SmallMapWindow*>(FindWindowById(WC_SMALLMAP, 0));
+	if (w != NULL) w->SmallMapCenterOnCurrentPos();
+
+	return res;
+}
--- a/src/spritecache.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/spritecache.cpp	Sun May 11 20:09:34 2008 +0000
@@ -61,7 +61,7 @@
 
 
 struct MemBlock {
-	uint32 size;
+	size_t size;
 	byte data[VARARRAY_SIZE];
 };
 
--- a/src/spriteloader/png.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/spriteloader/png.cpp	Sun May 11 20:09:34 2008 +0000
@@ -44,7 +44,7 @@
 	return false;
 }
 
-static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 id, bool mask)
+static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 id, volatile bool mask)
 {
 	png_byte header[8];
 	png_structp png_ptr;
--- a/src/station_cmd.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/station_cmd.cpp	Sun May 11 20:09:34 2008 +0000
@@ -42,6 +42,7 @@
 #include "signal_func.h"
 #include "oldpool_func.h"
 #include "animated_tile_func.h"
+#include "elrail_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -2106,7 +2107,7 @@
 		DrawGroundSprite(image, HasBit(image, PALETTE_MODIFIER_COLOR) ? palette : PAL_NONE);
 	}
 
-	if (IsRailwayStation(ti->tile) && HasCatenary(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
+	if (IsRailwayStation(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
 
 	if (HasBit(roadtypes, ROADTYPE_TRAM)) {
 		Axis axis = GetRoadStopDir(ti->tile) == DIAGDIR_NE ? AXIS_X : AXIS_Y;
--- a/src/station_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/station_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -25,6 +25,7 @@
 #include "gfx_func.h"
 #include "widgets/dropdown_func.h"
 #include "newgrf_cargo.h"
+#include "string_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -164,60 +165,43 @@
 	return (_internal_sort_order & 1) ? maxr2 - maxr1 : maxr1 - maxr2;
 }
 
-/** Flags for station list */
-enum StationListFlags {
-	SL_ORDER   = 1 << 0, ///< Order - ascending (=0), descending (=1)
-	SL_RESORT  = 1 << 1, ///< Resort the list
-	SL_REBUILD = 1 << 2, ///< Rebuild the list
-};
-
-DECLARE_ENUM_AS_BIT_SET(StationListFlags);
-
-/** Information about station list */
-struct plstations_d {
-	const Station **sort_list; ///< Pointer to list of stations
-	uint16 list_length;        ///< Number of stations in list
-	uint16 resort_timer;       ///< Tick counter to resort the list
-	byte sort_type;            ///< Sort type - name, waiting, ...
-	byte flags;                ///< Flags - SL_ORDER, SL_RESORT, SL_REBUILD
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(plstations_d));
+typedef GUIList<const Station*> GUIStationList;
 
 /**
  * Set the station sort flag for all station-list windows.
  * @param sl_flag Sort list flag to set for all station-list windows
  */
-static void SetStationListsFlag(StationListFlags sl_flag)
+static void SetStationListsFlag(SortListFlags sl_flag)
 {
 	Window *const *wz;
 
 	FOR_ALL_WINDOWS(wz) {
 		Window *w = *wz;
 		if (w->window_class == WC_STATION_LIST) {
-			WP(w, plstations_d).flags |= sl_flag;
+			dynamic_cast<GUIStationList*>(w)->flags |= sl_flag;
 			w->SetDirty();
 		}
 	}
 }
 
 /**
- * Set the 'SL_REBUILD' flag for all station lists
+ * Set the 'VL_REBUILD' flag for all station lists
  */
 void RebuildStationLists()
 {
-	SetStationListsFlag(SL_REBUILD);
+	SetStationListsFlag(VL_REBUILD);
 }
 
 /**
- * Set the 'SL_RESORT' flag for all station lists
+ * Set the 'VL_RESORT' flag for all station lists
  */
 void ResortStationLists()
 {
-	SetStationListsFlag(SL_RESORT);
+	SetStationListsFlag(VL_RESORT);
 }
 
 /**
- * Rebuild station list if the SL_REBUILD flag is set
+ * Rebuild station list if the VL_REBUILD flag is set
  *
  * @param sl pointer to plstations_d (station list and flags)
  * @param owner player whose stations are to be in list
@@ -225,12 +209,12 @@
  * @param cargo_filter bitmap of cargo types to include
  * @param include_empty whether we should include stations without waiting cargo
  */
-static void BuildStationsList(plstations_d *sl, PlayerID owner, byte facilities, uint32 cargo_filter, bool include_empty)
+static void BuildStationsList(GUIStationList *sl, PlayerID owner, byte facilities, uint32 cargo_filter, bool include_empty)
 {
 	uint n = 0;
 	const Station *st;
 
-	if (!(sl->flags & SL_REBUILD)) return;
+	if (!(sl->flags & VL_REBUILD)) return;
 
 	/* Create array for sorting */
 	const Station **station_sort = MallocT<const Station*>(GetMaxStationIndex() + 1);
@@ -264,18 +248,18 @@
 
 	for (uint i = 0; i < n; ++i) sl->sort_list[i] = station_sort[i];
 
-	sl->flags &= ~SL_REBUILD;
-	sl->flags |= SL_RESORT;
+	sl->flags &= ~VL_REBUILD;
+	sl->flags |= VL_RESORT;
 	free((void*)station_sort);
 }
 
 
 /**
- * Sort station list if the SL_RESORT flag is set
+ * Sort station list if the VL_RESORT flag is set
  *
  * @param sl pointer to plstations_d (station list and flags)
  */
-static void SortStationsList(plstations_d *sl)
+static void SortStationsList(GUIStationList *sl)
 {
 	static StationSortListingTypeFunction *const _station_sorter[] = {
 		&StationNameSorter,
@@ -284,298 +268,351 @@
 		&StationRatingMaxSorter
 	};
 
-	if (!(sl->flags & SL_RESORT)) return;
+	if (!(sl->flags & VL_RESORT)) return;
 
-	_internal_sort_order = sl->flags & SL_ORDER;
+	_internal_sort_order = sl->flags & VL_DESC;
 	_last_station = NULL; // used for "cache" in namesorting
 	qsort((void*)sl->sort_list, sl->list_length, sizeof(sl->sort_list[0]), _station_sorter[sl->sort_type]);
 
 	sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
-	sl->flags &= ~SL_RESORT;
+	sl->flags &= ~VL_RESORT;
 }
 
 /**
- * Fuction called when any WindowEvent occurs for PlayerStations window
- *
- * @param w pointer to the PlayerStations window
- * @param e pointer to window event
+ * The list of stations per player.
  */
-static void PlayerStationsWndProc(Window *w, WindowEvent *e)
+struct PlayerStationsWindow : public Window, public GUIStationList
 {
-	const PlayerID owner = (PlayerID)w->window_number;
-	static byte facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
-	static Listing station_sort = {0, 0};
-	static bool include_empty = true;
-
-	plstations_d *sl = &WP(w, plstations_d);
-
-	switch (e->event) {
-		case WE_CREATE:
-			if (_cargo_filter == _cargo_filter_max) _cargo_filter = _cargo_mask;
-
-			for (uint i = 0; i < 5; i++) {
-				if (HasBit(facilities, i)) w->LowerWidget(i + SLW_TRAIN);
-			}
-			w->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
-			w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
-			w->SetWidgetLoweredState(SLW_NOCARGOWAITING, include_empty);
-
-			sl->sort_list = NULL;
-			sl->flags = SL_REBUILD;
-			sl->sort_type = station_sort.criteria;
-			if (station_sort.order) sl->flags |= SL_ORDER;
+	static Listing station_sort;
+	static byte facilities;
+	static bool include_empty;
 
-			/* set up resort timer */
-			sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
-			break;
-
-		case WE_PAINT: {
-			BuildStationsList(sl, owner, facilities, _cargo_filter, include_empty);
-			SortStationsList(sl);
-
-			SetVScrollCount(w, sl->list_length);
-
-			/* draw widgets, with player's name in the caption */
-			SetDParam(0, owner);
-			SetDParam(1, w->vscroll.count);
-
-			/* Set text of sort by dropdown */
-			w->widget[SLW_SORTDROPBTN].data = _station_sort_listing[sl->sort_type];
+	PlayerStationsWindow(const WindowDesc *desc, void *data, WindowNumber window_number) : Window(desc, data, window_number)
+	{
+		this->caption_color = (byte)this->window_number;
+		this->vscroll.cap = 12;
+		this->resize.step_height = 10;
+		this->resize.height = this->height - 10 * 7; // minimum if 5 in the list
 
-			DrawWindowWidgets(w);
-
-			/* draw arrow pointing up/down for ascending/descending sorting */
-			DrawSortButtonState(w, SLW_SORTBY, sl->flags & SL_ORDER ? SBS_DOWN : SBS_UP);
-
-			int cg_ofst;
-			int x = 89;
-			int y = 14;
-			int xb = 2; ///< offset from left of widget
+		/* Add cargo filter buttons */
+		uint num_active = 0;
+		for (CargoID c = 0; c < NUM_CARGO; c++) {
+			if (GetCargo(c)->IsValid()) num_active++;
+		}
 
-			uint i = 0;
-			for (CargoID c = 0; c < NUM_CARGO; c++) {
-				const CargoSpec *cs = GetCargo(c);
-				if (!cs->IsValid()) continue;
+		this->widget_count += num_active;
+		this->widget = ReallocT(this->widget, this->widget_count + 1);
+		this->widget[this->widget_count].type = WWT_LAST;
 
-				cg_ofst = HasBit(_cargo_filter, c) ? 2 : 1;
-				GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, cs->rating_colour);
-				DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, cs->abbrev, TC_BLACK);
-				x += 14;
-				i++;
+		uint i = 0;
+		for (CargoID c = 0; c < NUM_CARGO; c++) {
+			if (!GetCargo(c)->IsValid()) continue;
+
+			Widget *wi = &this->widget[SLW_CARGOSTART + i];
+			wi->type     = WWT_PANEL;
+			wi->display_flags = RESIZE_NONE;
+			wi->color    = 14;
+			wi->left     = 89 + i * 14;
+			wi->right    = wi->left + 13;
+			wi->top      = 14;
+			wi->bottom   = 24;
+			wi->data     = 0;
+			wi->tooltips = STR_USE_CTRL_TO_SELECT_MORE;
+
+			if (HasBit(_cargo_filter, c)) this->LowerWidget(SLW_CARGOSTART + i);
+			i++;
+		}
+
+		this->widget[SLW_NOCARGOWAITING].left += num_active * 14;
+		this->widget[SLW_NOCARGOWAITING].right += num_active * 14;
+		this->widget[SLW_CARGOALL].left += num_active * 14;
+		this->widget[SLW_CARGOALL].right += num_active * 14;
+		this->widget[SLW_PAN_RIGHT].left += num_active * 14;
+
+		if (num_active > 15) {
+			/* Resize and fix the minimum width, if necessary */
+			ResizeWindow(this, (num_active - 15) * 14, 0);
+			this->resize.width = this->width;
+		}
+
+		if (_cargo_filter == _cargo_filter_max) _cargo_filter = _cargo_mask;
+
+		for (uint i = 0; i < 5; i++) {
+			if (HasBit(facilities, i)) this->LowerWidget(i + SLW_TRAIN);
+		}
+		this->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
+		this->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
+		this->SetWidgetLoweredState(SLW_NOCARGOWAITING, include_empty);
+
+		this->sort_list = NULL;
+		this->flags = VL_REBUILD;
+		this->sort_type = station_sort.criteria;
+		if (station_sort.order) this->flags |= VL_DESC;
+
+		/* set up resort timer */
+		this->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+	}
+
+	virtual void OnPaint()
+	{
+		PlayerID owner = (PlayerID)this->window_number;
+
+		BuildStationsList(this, owner, facilities, _cargo_filter, include_empty);
+		SortStationsList(this);
+
+		SetVScrollCount(this, this->list_length);
+
+		/* draw widgets, with player's name in the caption */
+		SetDParam(0, owner);
+		SetDParam(1, this->vscroll.count);
+
+		/* Set text of sort by dropdown */
+		this->widget[SLW_SORTDROPBTN].data = _station_sort_listing[this->sort_type];
+
+		DrawWindowWidgets(this);
+
+		/* draw arrow pointing up/down for ascending/descending sorting */
+		DrawSortButtonState(this, SLW_SORTBY, this->flags & VL_DESC ? SBS_DOWN : SBS_UP);
+
+		int cg_ofst;
+		int x = 89;
+		int y = 14;
+		int xb = 2; ///< offset from left of widget
+
+		uint i = 0;
+		for (CargoID c = 0; c < NUM_CARGO; c++) {
+			const CargoSpec *cs = GetCargo(c);
+			if (!cs->IsValid()) continue;
+
+			cg_ofst = HasBit(_cargo_filter, c) ? 2 : 1;
+			GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, cs->rating_colour);
+			DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, cs->abbrev, TC_BLACK);
+			x += 14;
+			i++;
+		}
+
+		x += 6;
+		cg_ofst = this->IsWidgetLowered(SLW_NOCARGOWAITING) ? 2 : 1;
+		DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, TC_BLACK);
+		x += 14;
+		cg_ofst = this->IsWidgetLowered(SLW_CARGOALL) ? 2 : 1;
+		DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
+
+		cg_ofst = this->IsWidgetLowered(SLW_FACILALL) ? 2 : 1;
+		DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
+
+		if (this->vscroll.count == 0) { // player has no stations
+			DrawString(xb, 40, STR_304A_NONE, TC_FROMSTRING);
+			return;
+		}
+
+		int max = min(this->vscroll.pos + this->vscroll.cap, this->list_length);
+		y = 40; // start of the list-widget
+
+		for (int i = this->vscroll.pos; i < max; ++i) { // do until max number of stations of owner
+			const Station *st = this->sort_list[i];
+			int x;
+
+			assert(st->xy != 0);
+
+			/* Do not do the complex check HasStationInUse here, it may be even false
+				* when the order had been removed and the station list hasn't been removed yet */
+			assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
+
+			SetDParam(0, st->index);
+			SetDParam(1, st->facilities);
+			x = DrawString(xb, y, STR_3049_0, TC_FROMSTRING) + 5;
+
+			/* show cargo waiting and station ratings */
+			for (CargoID j = 0; j < NUM_CARGO; j++) {
+				if (!st->goods[j].cargo.Empty()) {
+					StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
+					x += 20;
+				}
+			}
+			y += 10;
+		}
+	}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		PlayerID owner = (PlayerID)this->window_number;
+
+		switch (widget) {
+			case SLW_LIST: {
+				uint32 id_v = (pt.y - 41) / 10;
+
+				if (id_v >= this->vscroll.cap) return; // click out of bounds
+
+				id_v += this->vscroll.pos;
+
+				if (id_v >= this->list_length) return; // click out of list bound
+
+				const Station *st = this->sort_list[id_v];
+				/* do not check HasStationInUse - it is slow and may be invalid */
+				assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
+
+				if (_ctrl_pressed) {
+					ShowExtraViewPortWindow(st->xy);
+				} else {
+					ScrollMainWindowToTile(st->xy);
+				}
+				break;
 			}
 
-			x += 6;
-			cg_ofst = w->IsWidgetLowered(SLW_NOCARGOWAITING) ? 2 : 1;
-			DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, TC_BLACK);
-			x += 14;
-			cg_ofst = w->IsWidgetLowered(SLW_CARGOALL) ? 2 : 1;
-			DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
+			case SLW_TRAIN:
+			case SLW_TRUCK:
+			case SLW_BUS:
+			case SLW_AIRPLANE:
+			case SLW_SHIP:
+				if (_ctrl_pressed) {
+					ToggleBit(facilities, widget - SLW_TRAIN);
+					this->ToggleWidgetLoweredState(widget);
+				} else {
+					uint i;
+					FOR_EACH_SET_BIT(i, facilities) {
+						this->RaiseWidget(i + SLW_TRAIN);
+					}
+					SetBit(facilities, widget - SLW_TRAIN);
+					this->LowerWidget(widget);
+				}
+				this->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
+				this->flags |= VL_REBUILD;
+				this->SetDirty();
+				break;
 
-			cg_ofst = w->IsWidgetLowered(SLW_FACILALL) ? 2 : 1;
-			DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
+			case SLW_FACILALL:
+				for (uint i = 0; i < 5; i++) {
+					this->LowerWidget(i + SLW_TRAIN);
+				}
+				this->LowerWidget(SLW_FACILALL);
 
-			if (w->vscroll.count == 0) { // player has no stations
-				DrawString(xb, 40, STR_304A_NONE, TC_FROMSTRING);
-				return;
+				facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
+				this->flags |= VL_REBUILD;
+				this->SetDirty();
+				break;
+
+			case SLW_CARGOALL: {
+				uint i = 0;
+				for (CargoID c = 0; c < NUM_CARGO; c++) {
+					if (!GetCargo(c)->IsValid()) continue;
+					this->LowerWidget(i + SLW_CARGOSTART);
+					i++;
+				}
+				this->LowerWidget(SLW_NOCARGOWAITING);
+				this->LowerWidget(SLW_CARGOALL);
+
+				_cargo_filter = _cargo_mask;
+				include_empty = true;
+				this->flags |= VL_REBUILD;
+				this->SetDirty();
+				break;
 			}
 
-			int max = min(w->vscroll.pos + w->vscroll.cap, sl->list_length);
-			y = 40; // start of the list-widget
-
-			for (int i = w->vscroll.pos; i < max; ++i) { // do until max number of stations of owner
-				const Station *st = sl->sort_list[i];
-				int x;
-
-				assert(st->xy != 0);
-
-				/* Do not do the complex check HasStationInUse here, it may be even false
-				 * when the order had been removed and the station list hasn't been removed yet */
-				assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
-
-				SetDParam(0, st->index);
-				SetDParam(1, st->facilities);
-				x = DrawString(xb, y, STR_3049_0, TC_FROMSTRING) + 5;
+			case SLW_SORTBY: // flip sorting method asc/desc
+				this->flags ^= VL_DESC; //DESC-flag
+				station_sort.order = HasBit(this->flags, 0);
+				this->flags |= VL_RESORT;
+				this->flags4 |= 5 << WF_TIMEOUT_SHL;
+				this->LowerWidget(SLW_SORTBY);
+				this->SetDirty();
+				break;
 
-				/* show cargo waiting and station ratings */
-				for (CargoID j = 0; j < NUM_CARGO; j++) {
-					if (!st->goods[j].cargo.Empty()) {
-						StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
-						x += 20;
-					}
-				}
-				y += 10;
-			}
-			break;
-		}
+			case SLW_SORTDROPBTN: // select sorting criteria dropdown menu
+				ShowDropDownMenu(this, _station_sort_listing, this->sort_type, SLW_SORTDROPBTN, 0, 0);
+				break;
 
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case SLW_LIST: {
-					uint32 id_v = (e->we.click.pt.y - 41) / 10;
-
-					if (id_v >= w->vscroll.cap) return; // click out of bounds
+			case SLW_NOCARGOWAITING:
+				if (_ctrl_pressed) {
+					include_empty = !include_empty;
+					this->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
+				} else {
+					for (uint i = SLW_CARGOSTART; i < this->widget_count; i++) {
+						this->RaiseWidget(i);
+					}
 
-					id_v += w->vscroll.pos;
+					_cargo_filter = 0;
+					include_empty = true;
 
-					if (id_v >= sl->list_length) return; // click out of list bound
+					this->LowerWidget(SLW_NOCARGOWAITING);
+				}
+				this->flags |= VL_REBUILD;
+				this->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
+				this->SetDirty();
+				break;
 
-					const Station *st = sl->sort_list[id_v];
-					/* do not check HasStationInUse - it is slow and may be invalid */
-					assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
+			default:
+				if (widget >= SLW_CARGOSTART) { // change cargo_filter
+					/* Determine the selected cargo type */
+					CargoID c;
+					int i = 0;
+					for (c = 0; c < NUM_CARGO; c++) {
+						if (!GetCargo(c)->IsValid()) continue;
+						if (widget - SLW_CARGOSTART == i) break;
+						i++;
+					}
 
 					if (_ctrl_pressed) {
-						ShowExtraViewPortWindow(st->xy);
+						ToggleBit(_cargo_filter, c);
+						this->ToggleWidgetLoweredState(widget);
 					} else {
-						ScrollMainWindowToTile(st->xy);
-					}
-					break;
-				}
-
-				case SLW_TRAIN:
-				case SLW_TRUCK:
-				case SLW_BUS:
-				case SLW_AIRPLANE:
-				case SLW_SHIP:
-					if (_ctrl_pressed) {
-						ToggleBit(facilities, e->we.click.widget - SLW_TRAIN);
-						w->ToggleWidgetLoweredState(e->we.click.widget);
-					} else {
-						uint i;
-						FOR_EACH_SET_BIT(i, facilities) {
-							w->RaiseWidget(i + SLW_TRAIN);
+						for (uint i = SLW_CARGOSTART; i < this->widget_count; i++) {
+							this->RaiseWidget(i);
 						}
-						SetBit(facilities, e->we.click.widget - SLW_TRAIN);
-						w->LowerWidget(e->we.click.widget);
-					}
-					w->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
-					sl->flags |= SL_REBUILD;
-					w->SetDirty();
-					break;
-
-				case SLW_FACILALL:
-					for (uint i = 0; i < 5; i++) {
-						w->LowerWidget(i + SLW_TRAIN);
-					}
-					w->LowerWidget(SLW_FACILALL);
-
-					facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
-					sl->flags |= SL_REBUILD;
-					w->SetDirty();
-					break;
-
-				case SLW_CARGOALL: {
-					uint i = 0;
-					for (CargoID c = 0; c < NUM_CARGO; c++) {
-						if (!GetCargo(c)->IsValid()) continue;
-						w->LowerWidget(i + SLW_CARGOSTART);
-						i++;
-					}
-					w->LowerWidget(SLW_NOCARGOWAITING);
-					w->LowerWidget(SLW_CARGOALL);
-
-					_cargo_filter = _cargo_mask;
-					include_empty = true;
-					sl->flags |= SL_REBUILD;
-					w->SetDirty();
-					break;
-				}
-
-				case SLW_SORTBY: // flip sorting method asc/desc
-					sl->flags ^= SL_ORDER; //DESC-flag
-					station_sort.order = HasBit(sl->flags, 0);
-					sl->flags |= SL_RESORT;
-					w->flags4 |= 5 << WF_TIMEOUT_SHL;
-					w->LowerWidget(SLW_SORTBY);
-					w->SetDirty();
-					break;
-
-				case SLW_SORTDROPBTN: // select sorting criteria dropdown menu
-					ShowDropDownMenu(w, _station_sort_listing, sl->sort_type, SLW_SORTDROPBTN, 0, 0);
-					break;
-
-				case SLW_NOCARGOWAITING:
-					if (_ctrl_pressed) {
-						include_empty = !include_empty;
-						w->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
-					} else {
-						for (uint i = SLW_CARGOSTART; i < w->widget_count; i++) {
-							w->RaiseWidget(i);
-						}
+						this->RaiseWidget(SLW_NOCARGOWAITING);
 
 						_cargo_filter = 0;
-						include_empty = true;
-
-						w->LowerWidget(SLW_NOCARGOWAITING);
-					}
-					sl->flags |= SL_REBUILD;
-					w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
-					w->SetDirty();
-					break;
-
-				default:
-					if (e->we.click.widget >= SLW_CARGOSTART) { // change cargo_filter
-						/* Determine the selected cargo type */
-						CargoID c;
-						int i = 0;
-						for (c = 0; c < NUM_CARGO; c++) {
-							if (!GetCargo(c)->IsValid()) continue;
-							if (e->we.click.widget - SLW_CARGOSTART == i) break;
-							i++;
-						}
-
-						if (_ctrl_pressed) {
-							ToggleBit(_cargo_filter, c);
-							w->ToggleWidgetLoweredState(e->we.click.widget);
-						} else {
-							for (uint i = SLW_CARGOSTART; i < w->widget_count; i++) {
-								w->RaiseWidget(i);
-							}
-							w->RaiseWidget(SLW_NOCARGOWAITING);
-
-							_cargo_filter = 0;
-							include_empty = false;
+						include_empty = false;
 
-							SetBit(_cargo_filter, c);
-							w->LowerWidget(e->we.click.widget);
-						}
-						sl->flags |= SL_REBUILD;
-						w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
-						w->SetDirty();
+						SetBit(_cargo_filter, c);
+						this->LowerWidget(widget);
 					}
-					break;
-			}
-			break;
+					this->flags |= VL_REBUILD;
+					this->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
+					this->SetDirty();
+				}
+				break;
+		}
+	}
 
-		case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
-			if (sl->sort_type != e->we.dropdown.index) {
-				/* value has changed -> resort */
-				sl->sort_type = e->we.dropdown.index;
-				station_sort.criteria = sl->sort_type;
-				sl->flags |= SL_RESORT;
-			}
-			w->SetDirty();
-			break;
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		if (this->sort_type != index) {
+			/* value has changed -> resort */
+			this->sort_type = index;
+			station_sort.criteria = this->sort_type;
+			this->flags |= VL_RESORT;
+		}
+		this->SetDirty();
+	}
 
-		case WE_TICK:
-			if (_pause_game != 0) break;
-			if (--sl->resort_timer == 0) {
-				DEBUG(misc, 3, "Periodic rebuild station list player %d", owner);
-				sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
-				sl->flags |= SL_REBUILD;
-				w->SetDirty();
-			}
-			break;
+	virtual void OnTick()
+	{
+		if (_pause_game != 0) return;
+		if (--this->resort_timer == 0) {
+			DEBUG(misc, 3, "Periodic rebuild station list player %d", this->window_number);
+			this->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+			this->flags |= VL_REBUILD;
+			this->SetDirty();
+		}
+	}
 
-		case WE_TIMEOUT:
-			w->RaiseWidget(SLW_SORTBY);
-			w->SetDirty();
-			break;
+	virtual void OnTimeout()
+	{
+		this->RaiseWidget(SLW_SORTBY);
+		this->SetDirty();
+	}
 
-		case WE_RESIZE:
-			w->vscroll.cap += e->we.sizing.diff.y / 10;
-			break;
+	virtual void OnResize(Point new_size, Point delta)
+	{
+		this->vscroll.cap += delta.y / 10;
 	}
-}
+};
+
+Listing PlayerStationsWindow::station_sort = {0, 0};
+byte PlayerStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
+bool PlayerStationsWindow::include_empty = true;
+
 
 static const Widget _player_stations_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},            // SLW_CLOSEBOX
@@ -608,7 +645,7 @@
 	WC_STATION_LIST, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_player_stations_widgets,
-	PlayerStationsWndProc
+	NULL
 };
 
 /**
@@ -620,54 +657,7 @@
 {
 	if (!IsValidPlayer(player)) return;
 
-	Window *w = AllocateWindowDescFront(&_player_stations_desc, player);
-	if (w == NULL) return;
-
-	w->caption_color = (byte)w->window_number;
-	w->vscroll.cap = 12;
-	w->resize.step_height = 10;
-	w->resize.height = w->height - 10 * 7; // minimum if 5 in the list
-
-	/* Add cargo filter buttons */
-	uint num_active = 0;
-	for (CargoID c = 0; c < NUM_CARGO; c++) {
-		if (GetCargo(c)->IsValid()) num_active++;
-	}
-
-	w->widget_count += num_active;
-	w->widget = ReallocT(w->widget, w->widget_count + 1);
-	w->widget[w->widget_count].type = WWT_LAST;
-
-	uint i = 0;
-	for (CargoID c = 0; c < NUM_CARGO; c++) {
-		if (!GetCargo(c)->IsValid()) continue;
-
-		Widget *wi = &w->widget[SLW_CARGOSTART + i];
-		wi->type     = WWT_PANEL;
-		wi->display_flags = RESIZE_NONE;
-		wi->color    = 14;
-		wi->left     = 89 + i * 14;
-		wi->right    = wi->left + 13;
-		wi->top      = 14;
-		wi->bottom   = 24;
-		wi->data     = 0;
-		wi->tooltips = STR_USE_CTRL_TO_SELECT_MORE;
-
-		if (HasBit(_cargo_filter, c)) w->LowerWidget(SLW_CARGOSTART + i);
-		i++;
-	}
-
-	w->widget[SLW_NOCARGOWAITING].left += num_active * 14;
-	w->widget[SLW_NOCARGOWAITING].right += num_active * 14;
-	w->widget[SLW_CARGOALL].left += num_active * 14;
-	w->widget[SLW_CARGOALL].right += num_active * 14;
-	w->widget[SLW_PAN_RIGHT].left += num_active * 14;
-
-	if (num_active > 15) {
-		/* Resize and fix the minimum width, if necessary */
-		ResizeWindow(w, (num_active - 15) * 14, 0);
-		w->resize.width = w->width;
-	}
+	AllocateWindowDescFront<PlayerStationsWindow>(&_player_stations_desc, player);
 }
 
 static const Widget _station_view_widgets[] = {
@@ -726,12 +716,6 @@
 	} while (--num);
 }
 
-struct stationview_d {
-	uint32 cargo;                 ///< Bitmask of cargo types to expand
-	uint16 cargo_rows[NUM_CARGO]; ///< Header row for each cargo type
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(stationview_d));
-
 struct CargoData {
 	CargoID cargo;
 	StationID source;
@@ -747,285 +731,285 @@
 typedef std::list<CargoData> CargoDataList;
 
 /**
- * Redraws whole StationView window
- *
- * @param w pointer to window
+ * The StationView window
  */
-static void DrawStationViewWindow(Window *w)
-{
-	StationID station_id = w->window_number;
-	const Station *st = GetStation(station_id);
-	CargoDataList cargolist;
-	uint32 transfers = 0;
-
-	/* count types of cargos waiting in station */
-	for (CargoID i = 0; i < NUM_CARGO; i++) {
-		if (st->goods[i].cargo.Empty()) {
-			WP(w, stationview_d).cargo_rows[i] = 0;
-		} else {
-			/* Add an entry for total amount of cargo of this type waiting. */
-			cargolist.push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
-
-			/* Set the row for this cargo entry for the expand/hide button */
-			WP(w, stationview_d).cargo_rows[i] = cargolist.size();
+struct StationViewWindow : public Window {
+	uint32 cargo;                 ///< Bitmask of cargo types to expand
+	uint16 cargo_rows[NUM_CARGO]; ///< Header row for each cargo type
 
-			/* Add an entry for each distinct cargo source. */
-			const CargoList::List *packets = st->goods[i].cargo.Packets();
-			for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
-				const CargoPacket *cp = *it;
-				if (cp->source != station_id) {
-					bool added = false;
-
-					/* Enable the expand/hide button for this cargo type */
-					SetBit(transfers, i);
+	StationViewWindow(const WindowDesc *desc, void *data, WindowNumber window_number) : Window(desc, data, window_number)
+	{
+		PlayerID owner = GetStation(window_number)->owner;
+		if (owner != OWNER_NONE) this->caption_color = owner;
+		this->vscroll.cap = 5;
+		this->resize.step_height = 10;
 
-					/* Don't add cargo lines if not expanded */
-					if (!HasBit(WP(w, stationview_d).cargo, i)) break;
+		this->FindWindowPlacementAndResize(desc);
+	}
 
-					/* Check if we already have this source in the list */
-					for (CargoDataList::iterator jt = cargolist.begin(); jt != cargolist.end(); jt++) {
-						CargoData *cd = &(*jt);
-						if (cd->cargo == i && cd->source == cp->source) {
-							cd->count += cp->count;
-							added = true;
-							break;
+	~StationViewWindow()
+	{
+		WindowNumber wno =
+			(this->window_number << 16) | VLW_STATION_LIST | GetStation(this->window_number)->owner;
+
+		DeleteWindowById(WC_TRAINS_LIST, wno);
+		DeleteWindowById(WC_ROADVEH_LIST, wno);
+		DeleteWindowById(WC_SHIPS_LIST, wno);
+		DeleteWindowById(WC_AIRCRAFT_LIST, wno);
+	}
+
+	virtual void OnPaint()
+	{
+		StationID station_id = this->window_number;
+		const Station *st = GetStation(station_id);
+		CargoDataList cargolist;
+		uint32 transfers = 0;
+
+		/* count types of cargos waiting in station */
+		for (CargoID i = 0; i < NUM_CARGO; i++) {
+			if (st->goods[i].cargo.Empty()) {
+				this->cargo_rows[i] = 0;
+			} else {
+				/* Add an entry for total amount of cargo of this type waiting. */
+				cargolist.push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
+
+				/* Set the row for this cargo entry for the expand/hide button */
+				this->cargo_rows[i] = cargolist.size();
+
+				/* Add an entry for each distinct cargo source. */
+				const CargoList::List *packets = st->goods[i].cargo.Packets();
+				for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
+					const CargoPacket *cp = *it;
+					if (cp->source != station_id) {
+						bool added = false;
+
+						/* Enable the expand/hide button for this cargo type */
+						SetBit(transfers, i);
+
+						/* Don't add cargo lines if not expanded */
+						if (!HasBit(this->cargo, i)) break;
+
+						/* Check if we already have this source in the list */
+						for (CargoDataList::iterator jt = cargolist.begin(); jt != cargolist.end(); jt++) {
+							CargoData *cd = &(*jt);
+							if (cd->cargo == i && cd->source == cp->source) {
+								cd->count += cp->count;
+								added = true;
+								break;
+							}
 						}
+
+						if (!added) cargolist.push_back(CargoData(i, cp->source, cp->count));
 					}
-
-					if (!added) cargolist.push_back(CargoData(i, cp->source, cp->count));
 				}
 			}
 		}
-	}
-	SetVScrollCount(w, cargolist.size() + 1); // update scrollbar
-
-	/* disable some buttons */
-	w->SetWidgetDisabledState(SVW_RENAME,   st->owner != _local_player);
-	w->SetWidgetDisabledState(SVW_TRAINS,   !(st->facilities & FACIL_TRAIN));
-	w->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
-	w->SetWidgetDisabledState(SVW_PLANES,   !(st->facilities & FACIL_AIRPORT));
-	w->SetWidgetDisabledState(SVW_SHIPS,    !(st->facilities & FACIL_DOCK));
-
-	SetDParam(0, st->index);
-	SetDParam(1, st->facilities);
-	DrawWindowWidgets(w);
-
-	int x = 2;  ///< coordinates used for printing waiting/accepted/rating of cargo
-	int y = 15;
-	int pos = w->vscroll.pos; ///< = w->vscroll.pos
-
-	uint width = w->widget[SVW_WAITING].right - w->widget[SVW_WAITING].left - 4;
-	int maxrows = w->vscroll.cap;
-
-	StringID str;
+		SetVScrollCount(this, cargolist.size() + 1); // update scrollbar
 
-	if (--pos < 0) {
-		str = STR_00D0_NOTHING;
-		for (CargoID i = 0; i < NUM_CARGO; i++) {
-			if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
-		}
-		SetDParam(0, str);
-		DrawString(x, y, STR_0008_WAITING, TC_FROMSTRING);
-		y += 10;
-	}
+		/* disable some buttons */
+		this->SetWidgetDisabledState(SVW_RENAME,   st->owner != _local_player);
+		this->SetWidgetDisabledState(SVW_TRAINS,   !(st->facilities & FACIL_TRAIN));
+		this->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
+		this->SetWidgetDisabledState(SVW_PLANES,   !(st->facilities & FACIL_AIRPORT));
+		this->SetWidgetDisabledState(SVW_SHIPS,    !(st->facilities & FACIL_DOCK));
 
-	for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
+		SetDParam(0, st->index);
+		SetDParam(1, st->facilities);
+		DrawWindowWidgets(this);
+
+		int x = 2;  ///< coordinates used for printing waiting/accepted/rating of cargo
+		int y = 15;
+		int pos = this->vscroll.pos; ///< = this->vscroll.pos
+
+		uint width = this->widget[SVW_WAITING].right - this->widget[SVW_WAITING].left - 4;
+		int maxrows = this->vscroll.cap;
+
+		StringID str;
+
 		if (--pos < 0) {
-			const CargoData *cd = &(*it);
-			if (cd->source == INVALID_STATION) {
-				/* Heading */
-				DrawCargoIcons(cd->cargo, cd->count, x, y, width);
-				SetDParam(0, cd->cargo);
-				SetDParam(1, cd->count);
-				if (HasBit(transfers, cd->cargo)) {
-					/* This cargo has transfers waiting so show the expand or shrink 'button' */
-					const char *sym = HasBit(WP(w, stationview_d).cargo, cd->cargo) ? "-" : "+";
-					DrawStringRightAligned(x + width - 8, y, STR_0009, TC_FROMSTRING);
-					DoDrawString(sym, x + width - 6, y, TC_YELLOW);
-				} else {
-					DrawStringRightAligned(x + width, y, STR_0009, TC_FROMSTRING);
-				}
-			} else {
-				SetDParam(0, cd->cargo);
-				SetDParam(1, cd->count);
-				SetDParam(2, cd->source);
-				DrawStringRightAlignedTruncated(x + width, y, STR_EN_ROUTE_FROM, TC_FROMSTRING, width);
+			str = STR_00D0_NOTHING;
+			for (CargoID i = 0; i < NUM_CARGO; i++) {
+				if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
 			}
-
+			SetDParam(0, str);
+			DrawString(x, y, STR_0008_WAITING, TC_FROMSTRING);
 			y += 10;
 		}
-	}
-
-	if (w->widget[SVW_ACCEPTS].data == STR_3032_RATINGS) { // small window with list of accepted cargo
-		char *b = _userstring;
-		bool first = true;
-
-		b = InlineString(b, STR_000C_ACCEPTS);
 
-		for (CargoID i = 0; i < NUM_CARGO; i++) {
-			if (b >= lastof(_userstring) - (1 + 2 * 4)) break; // ',' or ' ' and two calls to Utf8Encode()
-			if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
-				if (first) {
-					first = false;
+		for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
+			if (--pos < 0) {
+				const CargoData *cd = &(*it);
+				if (cd->source == INVALID_STATION) {
+					/* Heading */
+					DrawCargoIcons(cd->cargo, cd->count, x, y, width);
+					SetDParam(0, cd->cargo);
+					SetDParam(1, cd->count);
+					if (HasBit(transfers, cd->cargo)) {
+						/* This cargo has transfers waiting so show the expand or shrink 'button' */
+						const char *sym = HasBit(this->cargo, cd->cargo) ? "-" : "+";
+						DrawStringRightAligned(x + width - 8, y, STR_0009, TC_FROMSTRING);
+						DoDrawString(sym, x + width - 6, y, TC_YELLOW);
+					} else {
+						DrawStringRightAligned(x + width, y, STR_0009, TC_FROMSTRING);
+					}
 				} else {
-					/* Add a comma if this is not the first item */
-					*b++ = ',';
-					*b++ = ' ';
+					SetDParam(0, cd->cargo);
+					SetDParam(1, cd->count);
+					SetDParam(2, cd->source);
+					DrawStringRightAlignedTruncated(x + width, y, STR_EN_ROUTE_FROM, TC_FROMSTRING, width);
 				}
-				b = InlineString(b, GetCargo(i)->name);
+
+				y += 10;
 			}
 		}
 
-		/* If first is still true then no cargo is accepted */
-		if (first) b = InlineString(b, STR_00D0_NOTHING);
-
-		*b = '\0';
-
-		/* Make sure we detect any buffer overflow */
-		assert(b < endof(_userstring));
-
-		DrawStringMultiLine(2, w->widget[SVW_ACCEPTLIST].top + 1, STR_SPEC_USERSTRING, w->widget[SVW_ACCEPTLIST].right - w->widget[SVW_ACCEPTLIST].left);
-	} else { // extended window with list of cargo ratings
-		y = w->widget[SVW_RATINGLIST].top + 1;
-
-		DrawString(2, y, STR_3034_LOCAL_RATING_OF_TRANSPORT, TC_FROMSTRING);
-		y += 10;
-
-		for (CargoID i = 0; i < NUM_CARGO; i++) {
-			const CargoSpec *cs = GetCargo(i);
-			if (!cs->IsValid()) continue;
+		if (this->widget[SVW_ACCEPTS].data == STR_3032_RATINGS) { // small window with list of accepted cargo
+			char *b = _userstring;
+			bool first = true;
 
-			const GoodsEntry *ge = &st->goods[i];
-			if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
+			b = InlineString(b, STR_000C_ACCEPTS);
 
-			SetDParam(0, cs->name);
-			SetDParam(2, ge->rating * 101 >> 8);
-			SetDParam(1, STR_3035_APPALLING + (ge->rating >> 5));
-			DrawString(8, y, STR_303D, TC_FROMSTRING);
+			for (CargoID i = 0; i < NUM_CARGO; i++) {
+				if (b >= lastof(_userstring) - (1 + 2 * 4)) break; // ',' or ' ' and two calls to Utf8Encode()
+				if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
+					if (first) {
+						first = false;
+					} else {
+						/* Add a comma if this is not the first item */
+						*b++ = ',';
+						*b++ = ' ';
+					}
+					b = InlineString(b, GetCargo(i)->name);
+				}
+			}
+
+			/* If first is still true then no cargo is accepted */
+			if (first) b = InlineString(b, STR_00D0_NOTHING);
+
+			*b = '\0';
+
+			/* Make sure we detect any buffer overflow */
+			assert(b < endof(_userstring));
+
+			DrawStringMultiLine(2, this->widget[SVW_ACCEPTLIST].top + 1, STR_SPEC_USERSTRING, this->widget[SVW_ACCEPTLIST].right - this->widget[SVW_ACCEPTLIST].left);
+		} else { // extended window with list of cargo ratings
+			y = this->widget[SVW_RATINGLIST].top + 1;
+
+			DrawString(2, y, STR_3034_LOCAL_RATING_OF_TRANSPORT, TC_FROMSTRING);
 			y += 10;
-		}
-	}
-}
 
-static void HandleCargoWaitingClick(Window *w, int row)
-{
-	if (row == 0) return;
+			for (CargoID i = 0; i < NUM_CARGO; i++) {
+				const CargoSpec *cs = GetCargo(i);
+				if (!cs->IsValid()) continue;
 
-	for (CargoID c = 0; c < NUM_CARGO; c++) {
-		if (WP(w, stationview_d).cargo_rows[c] == row) {
-			ToggleBit(WP(w, stationview_d).cargo, c);
-			w->InvalidateWidget(SVW_WAITING);
-			break;
+				const GoodsEntry *ge = &st->goods[i];
+				if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
+
+				SetDParam(0, cs->name);
+				SetDParam(2, ge->rating * 101 >> 8);
+				SetDParam(1, STR_3035_APPALLING + (ge->rating >> 5));
+				DrawString(8, y, STR_303D, TC_FROMSTRING);
+				y += 10;
+			}
 		}
 	}
-}
-
-
-/**
- * Fuction called when any WindowEvent occurs for any StationView window
- *
- * @param w pointer to the StationView window
- * @param e pointer to window event
- */
-static void StationViewWndProc(Window *w, WindowEvent *e)
-{
-	switch (e->event) {
-		case WE_PAINT:
-			DrawStationViewWindow(w);
-			break;
-
-		case WE_CLICK:
-			switch (e->we.click.widget) {
-				case SVW_WAITING:
-					HandleCargoWaitingClick(w, (e->we.click.pt.y - w->widget[SVW_WAITING].top) / 10 + w->vscroll.pos);
-					break;
 
-				case SVW_LOCATION:
-					if (_ctrl_pressed) {
-						ShowExtraViewPortWindow(GetStation(w->window_number)->xy);
-					} else {
-						ScrollMainWindowToTile(GetStation(w->window_number)->xy);
-					}
-					break;
-
-				case SVW_RATINGS:
-					w->SetDirty();
+	void HandleCargoWaitingClick(int row)
+	{
+		if (row == 0) return;
 
-					if (w->widget[SVW_RATINGS].data == STR_3032_RATINGS) {
-						/* Switch to ratings view */
-						w->widget[SVW_RATINGS].data = STR_3033_ACCEPTS;
-						w->widget[SVW_RATINGS].tooltips = STR_3056_SHOW_LIST_OF_ACCEPTED_CARGO;
-						ResizeWindowForWidget(w, SVW_ACCEPTLIST, 0, 100);
-					} else {
-						/* Switch to accepts view */
-						w->widget[SVW_RATINGS].data = STR_3032_RATINGS;
-						w->widget[SVW_RATINGS].tooltips = STR_3054_SHOW_STATION_RATINGS;
-						ResizeWindowForWidget(w, SVW_ACCEPTLIST, 0, -100);
-					}
+		for (CargoID c = 0; c < NUM_CARGO; c++) {
+			if (this->cargo_rows[c] == row) {
+				ToggleBit(this->cargo, c);
+				this->InvalidateWidget(SVW_WAITING);
+				break;
+			}
+		}
+	}
 
-					w->SetDirty();
-					break;
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case SVW_WAITING:
+				this->HandleCargoWaitingClick((pt.y - this->widget[SVW_WAITING].top) / 10 + this->vscroll.pos);
+				break;
 
-				case SVW_RENAME:
-					SetDParam(0, w->window_number);
-					ShowQueryString(STR_STATION, STR_3030_RENAME_STATION_LOADING, 31, 180, w, CS_ALPHANUMERAL);
-					break;
+			case SVW_LOCATION:
+				if (_ctrl_pressed) {
+					ShowExtraViewPortWindow(GetStation(this->window_number)->xy);
+				} else {
+					ScrollMainWindowToTile(GetStation(this->window_number)->xy);
+				}
+				break;
 
-				case SVW_TRAINS: { // Show a list of scheduled trains to this station
-					const Station *st = GetStation(w->window_number);
-					ShowVehicleListWindow(st->owner, VEH_TRAIN, (StationID)w->window_number);
-					break;
+			case SVW_RATINGS:
+				this->SetDirty();
+
+				if (this->widget[SVW_RATINGS].data == STR_3032_RATINGS) {
+					/* Switch to ratings view */
+					this->widget[SVW_RATINGS].data = STR_3033_ACCEPTS;
+					this->widget[SVW_RATINGS].tooltips = STR_3056_SHOW_LIST_OF_ACCEPTED_CARGO;
+					ResizeWindowForWidget(this, SVW_ACCEPTLIST, 0, 100);
+				} else {
+					/* Switch to accepts view */
+					this->widget[SVW_RATINGS].data = STR_3032_RATINGS;
+					this->widget[SVW_RATINGS].tooltips = STR_3054_SHOW_STATION_RATINGS;
+					ResizeWindowForWidget(this, SVW_ACCEPTLIST, 0, -100);
 				}
 
-				case SVW_ROADVEHS: { // Show a list of scheduled road-vehicles to this station
-					const Station *st = GetStation(w->window_number);
-					ShowVehicleListWindow(st->owner, VEH_ROAD, (StationID)w->window_number);
-					break;
-				}
-
-				case SVW_PLANES: { // Show a list of scheduled aircraft to this station
-					const Station *st = GetStation(w->window_number);
-					/* Since oilrigs have no owners, show the scheduled aircraft of current player */
-					PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
-					ShowVehicleListWindow(owner, VEH_AIRCRAFT, (StationID)w->window_number);
-					break;
-				}
-
-				case SVW_SHIPS: { // Show a list of scheduled ships to this station
-					const Station *st = GetStation(w->window_number);
-					/* Since oilrigs/bouys have no owners, show the scheduled ships of current player */
-					PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
-					ShowVehicleListWindow(owner, VEH_SHIP, (StationID)w->window_number);
-					break;
-				}
-			}
-			break;
+				this->SetDirty();
+				break;
 
-		case WE_ON_EDIT_TEXT:
-			if (e->we.edittext.str[0] != '\0') {
-				_cmd_text = e->we.edittext.str;
-				DoCommandP(0, w->window_number, 0, NULL,
-					CMD_RENAME_STATION | CMD_MSG(STR_3031_CAN_T_RENAME_STATION));
-			}
-			break;
-
-		case WE_DESTROY: {
-			WindowNumber wno =
-				(w->window_number << 16) | VLW_STATION_LIST | GetStation(w->window_number)->owner;
+			case SVW_RENAME:
+				SetDParam(0, this->window_number);
+				ShowQueryString(STR_STATION, STR_3030_RENAME_STATION_LOADING, 31, 180, this, CS_ALPHANUMERAL);
+				break;
 
-			DeleteWindowById(WC_TRAINS_LIST, wno);
-			DeleteWindowById(WC_ROADVEH_LIST, wno);
-			DeleteWindowById(WC_SHIPS_LIST, wno);
-			DeleteWindowById(WC_AIRCRAFT_LIST, wno);
-			break;
+			case SVW_TRAINS: { // Show a list of scheduled trains to this station
+				const Station *st = GetStation(this->window_number);
+				ShowVehicleListWindow(st->owner, VEH_TRAIN, (StationID)this->window_number);
+				break;
+			}
+
+			case SVW_ROADVEHS: { // Show a list of scheduled road-vehicles to this station
+				const Station *st = GetStation(this->window_number);
+				ShowVehicleListWindow(st->owner, VEH_ROAD, (StationID)this->window_number);
+				break;
+			}
+
+			case SVW_PLANES: { // Show a list of scheduled aircraft to this station
+				const Station *st = GetStation(this->window_number);
+				/* Since oilrigs have no owners, show the scheduled aircraft of current player */
+				PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
+				ShowVehicleListWindow(owner, VEH_AIRCRAFT, (StationID)this->window_number);
+				break;
+			}
+
+			case SVW_SHIPS: { // Show a list of scheduled ships to this station
+				const Station *st = GetStation(this->window_number);
+				/* Since oilrigs/bouys have no owners, show the scheduled ships of current player */
+				PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
+				ShowVehicleListWindow(owner, VEH_SHIP, (StationID)this->window_number);
+				break;
+			}
 		}
+	}
 
-		case WE_RESIZE:
-			if (e->we.sizing.diff.x != 0) ResizeButtons(w, SVW_LOCATION, SVW_RENAME);
-			w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
-			break;
+	virtual void OnQueryTextFinished(char *str)
+	{
+		if (!StrEmpty(str)) {
+			_cmd_text = str;
+			DoCommandP(0, this->window_number, 0, NULL,
+				CMD_RENAME_STATION | CMD_MSG(STR_3031_CAN_T_RENAME_STATION));
+		}
 	}
-}
+
+	virtual void OnResize(Point new_size, Point delta)
+	{
+		if (delta.x != 0) ResizeButtons(this, SVW_LOCATION, SVW_RENAME);
+		this->vscroll.cap += delta.y / (int)this->resize.step_height;
+	}
+};
 
 
 static const WindowDesc _station_view_desc = {
@@ -1033,7 +1017,7 @@
 	WC_STATION_VIEW, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_station_view_widgets,
-	StationViewWndProc
+	NULL
 };
 
 /**
@@ -1043,11 +1027,5 @@
  */
 void ShowStationViewWindow(StationID station)
 {
-	Window *w = AllocateWindowDescFront(&_station_view_desc, station);
-	if (w == NULL) return;
-
-	PlayerID owner = GetStation(w->window_number)->owner;
-	if (owner != OWNER_NONE) w->caption_color = owner;
-	w->vscroll.cap = 5;
-	w->resize.step_height = 10;
+	AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station);
 }
--- a/src/statusbar_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/statusbar_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -12,7 +12,7 @@
 #include "string_func.h"
 #include "strings_func.h"
 #include "player_base.h"
-#include "viewport_func.h"
+#include "tilehighlight_func.h"
 #include "news_gui.h"
 #include "player_gui.h"
 #include "window_gui.h"
@@ -86,7 +86,7 @@
 			}
 
 			/* Draw status bar */
-			if (w->message.msg) { // true when saving is active
+			if (WP(w, def_d).data_3) { // true when saving is active
 				DrawStringCenteredTruncated(w->widget[1].left + 1, w->widget[1].right - 1, 1, STR_SAVING_GAME, TC_FROMSTRING);
 			} else if (_do_autosave) {
 				DrawStringCenteredTruncated(w->widget[1].left + 1, w->widget[1].right - 1, 1, STR_032F_AUTOSAVE, TC_FROMSTRING);
@@ -113,9 +113,8 @@
 			if (WP(w, def_d).data_2 > 0) DrawSprite(SPR_BLOT, PALETTE_TO_RED, w->widget[1].right - 11, 2);
 		} break;
 
-		case WE_MESSAGE:
-			w->message.msg = e->we.message.msg;
-			w->SetDirty();
+		case WE_INVALIDATE_DATA:
+			WP(w, def_d).data_3 = e->we.invalidate.data;
 			break;
 
 		case WE_CLICK:
@@ -163,7 +162,7 @@
 void ShowStatusBar()
 {
 	_main_status_desc.top = _screen.height - 12;
-	Window *w = AllocateWindowDesc(&_main_status_desc);
+	Window *w = new Window(&_main_status_desc);
 	if (w != NULL) {
 		CLRBITS(w->flags4, WF_WHITE_BORDER_MASK);
 		WP(w, def_d).data_1 = -1280;
--- a/src/string_func.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/string_func.h	Sun May 11 20:09:34 2008 +0000
@@ -45,10 +45,10 @@
 
 
 /** Get the length of a string, within a limited buffer */
-static inline int ttd_strnlen(const char *str, int maxlen)
+static inline size_t ttd_strnlen(const char *str, size_t maxlen)
 {
 	const char *t;
-	for (t = str; *t != '\0' && t - str < maxlen; t++) {}
+	for (t = str; *t != '\0' && (size_t)(t - str) < maxlen; t++) {}
 	return t - str;
 }
 
--- a/src/subsidy_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/subsidy_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -197,5 +197,5 @@
 
 void ShowSubsidiesList()
 {
-	AllocateWindowDescFront(&_subsidies_list_desc, 0);
+	AllocateWindowDescFront<Window>(&_subsidies_list_desc, 0);
 }
--- a/src/table/engines.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/table/engines.h	Sun May 11 20:09:34 2008 +0000
@@ -387,8 +387,8 @@
 	RVI( 0, G,  13, 112,    1000, 131,   120, RC_S,  0, 0              , 20, R, S), //   8
 	RVI( 1, G,  19, 128,    1200, 162,   140, RC_S,  0, 0              , 21, R, S), //   9
 	RVI( 0, G,  22, 144,    1600, 170,   130, RC_S,  0, 0              , 22, R, S), //  10
-	RVI( 8, M,  11, 112,   600/2,32/2,  85/2, RC_D, 38, CT_PASSENGERS  , 10, R, D), //  11
-	RVI(10, M,  14, 120,   700/2,38/2,  70/2, RC_D, 40, CT_PASSENGERS  , 11, R, D), //  12
+	RVI( 8, M,  11, 112,   600/2,  32,  85/2, RC_D, 38, CT_PASSENGERS  , 10, R, D), //  11
+	RVI(10, M,  14, 120,   700/2,  38,  70/2, RC_D, 40, CT_PASSENGERS  , 11, R, D), //  12
 	RVI( 4, G,  15, 128,    1250,  72,    95, RC_D,  0, 0              , 30, R, D), //  13
 	RVI( 5, G,  17, 144,    1750, 101,   120, RC_D,  0, 0              , 31, R, D), //  14
 	RVI( 4, G,  18, 160,    2580, 112,   140, RC_D,  0, 0              , 32, R, D), //  15
@@ -396,13 +396,13 @@
 	RVI(12, G,  16, 112,    2400, 120,   105, RC_D,  0, 0              , 34, R, D), //  17
 	RVI(13, G,  30, 112,    6600, 207,   155, RC_D,  0, 0              , 35, R, D), //  18
 	RVI(15, G,  18, 104,    1500, 110,   105, RC_D,  0, 0              , 29, R, D), //  19
-	RVI(16, M,  35, 160,  3500/2,95/2, 205/2, RC_D,  0, 0              , 45, R, D), //  20
+	RVI(16, M,  35, 160,  3500/2,  95, 205/2, RC_D,  0, 0              , 45, R, D), //  20
 	RVI(18, G,  21, 104,    2200, 120,   145, RC_D,  0, 0              , 32, R, D), //  21
-	RVI( 6, M,  20, 200,  4500/2,70/2, 190/2, RC_D,  4, CT_MAIL        , 50, R, D), //  22
+	RVI( 6, M,  20, 200,  4500/2,  70, 190/2, RC_D,  4, CT_MAIL        , 50, R, D), //  22
 	RVI(20, G,  26, 160,    3600,  84,   180, RC_E,  0, 0              , 40, C, E), //  23
 	RVI(20, G,  30, 176,    5000,  82,   205, RC_E,  0, 0              , 41, C, E), //  24
-	RVI(21, M,  40, 240,  7000/2,90/2, 240/2, RC_E,  0, 0              , 51, C, E), //  25
-	RVI(23, M,  43, 264,  8000/2,95/2, 250/2, RC_E,  0, 0              , 52, C, E), //  26
+	RVI(21, M,  40, 240,  7000/2,  90, 240/2, RC_E,  0, 0              , 51, C, E), //  25
+	RVI(23, M,  43, 264,  8000/2,  95, 250/2, RC_E,  0, 0              , 52, C, E), //  26
 	RVI(33, W, 247,   0,       0,  25,     0, RC_W, 40, CT_PASSENGERS  ,  0, R, A), //  27
 	RVI(35, W, 228,   0,       0,  21,     0, RC_W, 30, CT_MAIL        ,  0, R, A), //  28
 	RVI(34, W, 176,   0,       0,  18,     0, RC_W, 30, CT_COAL        ,  0, R, A), //  29
@@ -431,7 +431,7 @@
 	RVI(58, W, 193,   0,       0,  18,     0, RC_W, 25, CT_FIZZY_DRINKS,  0, R, A), //  52
 	RVI(59, W, 191,   0,       0,  18,     0, RC_W, 30, CT_PLASTIC     ,  0, R, A), //  53
 	RVI(25, G,  52, 304,    9000,  95,   230, RC_E,  0, 0              , 60, O, N), //  54
-	RVI(26, M,  60, 336, 10000/2,85/2, 240/2, RC_E, 25, CT_PASSENGERS  , 62, O, N), //  55
+	RVI(26, M,  60, 336, 10000/2,  85, 240/2, RC_E, 25, CT_PASSENGERS  , 62, O, N), //  55
 	RVI(26, G,  53, 320,    5000,  95,   230, RC_E,  0, 0              , 63, O, N), //  56
 	RVI(60, W, 247,   0,       0,  25,     0, RC_W, 45, CT_PASSENGERS  ,  0, O, A), //  57
 	RVI(62, W, 228,   0,       0,  21,     0, RC_W, 35, CT_MAIL        ,  0, O, A), //  58
@@ -463,7 +463,7 @@
 	RVI(28, G,  70, 400,   10000, 105,   250, RC_E,  0, 0              , 70, L, V), //  84
 	RVI(29, G,  74, 448,   12000, 120,   253, RC_E,  0, 0              , 71, L, V), //  85
 	RVI(30, G,  82, 480,   15000, 130,   254, RC_E,  0, 0              , 72, L, V), //  86
-	RVI(31, M,  95, 640, 20000/2,150/2,255/2, RC_E,  0, 0              , 73, L, V), //  87
+	RVI(31, M,  95, 640, 20000/2, 150, 255/2, RC_E,  0, 0              , 73, L, V), //  87
 	RVI(28, G,  70, 480,   10000, 120,   250, RC_E,  0, 0              , 74, L, V), //  88
 	RVI(60, W, 247,   0,       0,  25,     0, RC_W, 47, CT_PASSENGERS  ,  0, L, A), //  89
 	RVI(62, W, 228,   0,       0,  21,     0, RC_W, 37, CT_MAIL        ,  0, L, A), //  90
--- a/src/terraform_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/terraform_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -159,6 +159,13 @@
 	'O',
 };
 
+void CcPlaySound1E(bool success, TileIndex tile, uint32 p1, uint32 p2);
+
+static void PlaceProc_BuyLand(TileIndex tile)
+{
+	DoCommandP(tile, 0, 0, CcPlaySound1E, CMD_PURCHASE_LAND_AREA | CMD_NO_WATER | CMD_MSG(STR_5806_CAN_T_PURCHASE_THIS_LAND));
+}
+
 void PlaceProc_DemolishArea(TileIndex tile)
 {
 	VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_DEMOLISH_AREA);
@@ -174,7 +181,7 @@
 	VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_LOWER_AND_LEVEL_AREA);
 }
 
-void PlaceProc_LevelLand(TileIndex tile)
+static void PlaceProc_LevelLand(TileIndex tile)
 {
 	VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_LEVEL_AREA);
 }
@@ -304,7 +311,7 @@
 void ShowTerraformToolbar(Window *link)
 {
 	if (!IsValidPlayer(_current_player)) return;
-	Window *w = AllocateWindowDescFront(&_terraform_desc, 0);
+	Window *w = AllocateWindowDescFront<Window>(&_terraform_desc, 0);
 	if (w != NULL && link != NULL) {
 		/* Align the terraform toolbar under the main toolbar and put the linked
 		 * toolbar to left of it
@@ -679,5 +686,5 @@
 
 void ShowEditorTerraformToolbar()
 {
-	AllocateWindowDescFront(&_scen_edit_land_gen_desc, 0);
+	AllocateWindowDescFront<Window>(&_scen_edit_land_gen_desc, 0);
 }
--- a/src/textbuf_gui.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/textbuf_gui.h	Sun May 11 20:09:34 2008 +0000
@@ -18,21 +18,6 @@
 	uint16 caretxoffs;          ///< the current position of the caret in pixels
 };
 
-struct querystr_d {
-	StringID caption;
-	Textbuf text;
-	const char *orig;
-	CharSetFilter afilter;
-	bool handled;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(querystr_d));
-
-extern char _edit_str_buf[64];
-extern char _orig_str_buf[lengthof(_edit_str_buf)];
-
-void DrawEditBox(Window *w, querystr_d *string, int wid);
-void HandleEditBox(Window *w, querystr_d *string, int wid);
-int HandleEditBoxKey(Window *w, querystr_d *string, int wid, WindowEvent *we);
 bool HandleCaret(Textbuf *tb);
 
 void DeleteTextBufferAll(Textbuf *tb);
@@ -56,6 +41,4 @@
  */
 extern char _keyboard_opt[2][OSK_KEYBOARD_ENTRIES * 4 + 1];
 
-void ShowOnScreenKeyboard(Window *parent, querystr_d *q, int button, int cancel, int ok);
-
 #endif /* TEXTBUF_GUI_H */
--- a/src/tilehighlight_func.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/tilehighlight_func.h	Sun May 11 20:09:34 2008 +0000
@@ -12,13 +12,17 @@
 
 typedef void PlaceProc(TileIndex tile);
 void PlaceProc_DemolishArea(TileIndex tile);
-void PlaceProc_LevelLand(TileIndex tile);
-void PlaceProc_BuyLand(TileIndex tile);
 bool GUIPlaceProcDragXY(const WindowEvent *e);
 
 bool HandlePlacePushButton(Window *w, int widget, CursorID cursor, ViewportHighlightMode mode, PlaceProc *placeproc);
 void SetObjectToPlaceWnd(CursorID icon, SpriteID pal, ViewportHighlightMode mode, Window *w);
 void SetObjectToPlace(CursorID icon, SpriteID pal, ViewportHighlightMode mode, WindowClass window_class, WindowNumber window_num);
+void ResetObjectToPlace();
+
+void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method);
+void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process);
+void VpSetPresizeRange(TileIndex from, TileIndex to);
+void VpSetPlaceSizingLimit(int limit);
 
 extern PlaceProc *_place_proc;
 extern TileHighlightData _thd;
--- a/src/tilehighlight_type.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/tilehighlight_type.h	Sun May 11 20:09:34 2008 +0000
@@ -69,7 +69,7 @@
 	WindowNumber window_number;
 
 	ViewportPlaceMethod select_method;
-	byte select_proc;
+	ViewportDragDropSelectionProcess select_proc;
 
 	TileIndex redsq;
 };
--- a/src/timetable_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/timetable_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -249,6 +249,8 @@
 		} break;
 
 		case WE_ON_EDIT_TEXT: {
+			if (we->we.edittext.str == NULL) break;
+
 			const Vehicle *v = GetVehicle(w->window_number);
 
 			uint32 p1 = PackTimetableArgs(v, WP(w, timetable_d).sel);
@@ -301,7 +303,7 @@
 
 void ShowTimetableWindow(const Vehicle *v)
 {
-	Window *w = AllocateWindowDescFront(&_timetable_desc, v->index);
+	Window *w = AllocateWindowDescFront<Window>(&_timetable_desc, v->index);
 
 	if (w != NULL) {
 		w->caption_color = v->owner;
--- a/src/toolbar_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/toolbar_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -78,6 +78,29 @@
 	return pos;
 }
 
+/**
+ * In a window with menu_d custom extension, retrieve the menu item number from a position
+ * @param w Window holding the menu items
+ * @param x X coordinate of the position
+ * @param y Y coordinate of the position
+ * @return Index number of the menu item, or \c -1 if no valid selection under position
+ */
+static int GetMenuItemIndex(const Window *w)
+{
+	int x = _cursor.pos.x;
+	int y = _cursor.pos.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;
+}
+
 /* --- Pausing --- */
 
 static void ToolbarPauseClick(Window *w)
@@ -727,7 +750,7 @@
 			w->SetDirty();
 			break;
 
-		case WE_MOUSELOOP:
+		case WE_TICK:
 			if (w->IsWidgetLowered(0) != !!_pause_game) {
 				w->ToggleWidgetLoweredState(0);
 				w->InvalidateWidget(0);
@@ -775,7 +798,7 @@
 			}
 			break;
 
-		case WE_MESSAGE:
+		case WE_INVALIDATE_DATA:
 			if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) HandleZoomMessage(w, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, 17, 18);
 			break;
 	}
@@ -999,7 +1022,7 @@
 			}
 		} break;
 
-		case WE_MOUSELOOP:
+		case WE_TICK:
 			if (w->IsWidgetLowered(0) != !!_pause_game) {
 				w->ToggleWidgetLoweredState(0);
 				w->SetDirty();
@@ -1011,7 +1034,7 @@
 			}
 			break;
 
-		case WE_MESSAGE:
+		case WE_INVALIDATE_DATA:
 			HandleZoomMessage(w, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, 9, 10);
 			break;
 	}
@@ -1135,34 +1158,30 @@
 				return;
 			}
 
-		case WE_POPUPMENU_SELECT: {
-			int index = GetMenuItemIndex(w, e->we.popupmenu.pt.x, e->we.popupmenu.pt.y);
-
-			if (index < 0) {
-				Window *w2 = FindWindowById(WC_MAIN_TOOLBAR,0);
-				if (GetWidgetFromPos(w2, e->we.popupmenu.pt.x - w2->left, e->we.popupmenu.pt.y - w2->top) == WP(w, menu_d).main_button)
-					index = WP(w, menu_d).sel_index;
-			}
-
-			int action_id = WP(w, menu_d).action_id;
-			delete w;
+		case WE_MOUSELOOP: {
+			int index = GetMenuItemIndex(w);
 
-			if (index >= 0) {
-				assert((uint)index <= lengthof(_menu_clicked_procs));
-				_menu_clicked_procs[action_id](index);
-			}
-
-		} break;
+			if (_left_button_down) {
+				if (index == -1 || index == WP(w, menu_d).sel_index) return;
 
-		case WE_POPUPMENU_OVER: {
-			int index = GetMenuItemIndex(w, e->we.popupmenu.pt.x, e->we.popupmenu.pt.y);
+				WP(w, menu_d).sel_index = index;
+				w->SetDirty();
+			} else {
+				if (index < 0) {
+					Window *w2 = FindWindowById(WC_MAIN_TOOLBAR,0);
+					if (GetWidgetFromPos(w2, _cursor.pos.x - w2->left, _cursor.pos.y - w2->top) == WP(w, menu_d).main_button)
+						index = WP(w, menu_d).sel_index;
+				}
 
-			if (index == -1 || index == WP(w, menu_d).sel_index) return;
+				int action_id = WP(w, menu_d).action_id;
+				delete w;
 
-			WP(w, menu_d).sel_index = index;
-			w->SetDirty();
-			return;
-		}
+				if (index >= 0) {
+					assert((uint)index <= lengthof(_menu_clicked_procs));
+					_menu_clicked_procs[action_id](index);
+				}
+			}
+		} break;
 	}
 }
 
@@ -1232,7 +1251,7 @@
 
 	Point pos = GetToolbarDropdownPos(parent_button, width, height);
 
-	w = AllocateWindow(pos.x, pos.y, width, height, MenuWndProc, WC_TOOLBAR_MENU, _menu_widgets);
+	w = new Window(pos.x, pos.y, width, height, MenuWndProc, WC_TOOLBAR_MENU, _menu_widgets);
 	w->widget[0].bottom = item_count * 10 + 1;
 	w->flags4 &= ~WF_WHITE_BORDER_MASK;
 
@@ -1244,8 +1263,6 @@
 	WP(w, menu_d).checked_items = 0;
 	WP(w, menu_d).disabled_items = disabled_mask;
 
-	_popup_menu_active = true;
-
 	SndPlayFx(SND_15_BEEP);
 	return w;
 }
@@ -1332,51 +1349,48 @@
 			return;
 		}
 
-		case WE_POPUPMENU_SELECT: {
-			int index = GetMenuItemIndex(w, e->we.popupmenu.pt.x, e->we.popupmenu.pt.y);
-			int action_id = WP(w, menu_d).action_id;
+		case WE_MOUSELOOP: {
+			int index = GetMenuItemIndex(w);
 
-			/* We have a new entry at the top of the list of menu 9 when networking
-			*  so keep that in count */
-			if (_networking && WP(w, menu_d).main_button == 9) {
-				if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1;
+			if (_left_button_down) {
+				UpdatePlayerMenuHeight(w);
+				/* We have a new entry at the top of the list of menu 9 when networking
+				 * so keep that in count */
+				if (_networking && WP(w, menu_d).main_button == 9) {
+					if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1;
+				} else {
+					index = GetPlayerIndexFromMenu(index);
+				}
+
+				if (index == -1 || index == WP(w, menu_d).sel_index) return;
+
+				WP(w, menu_d).sel_index = index;
+				w->SetDirty();
 			} else {
-				index = GetPlayerIndexFromMenu(index);
-			}
+				int action_id = WP(w, menu_d).action_id;
 
-			if (index < 0) {
-				Window *w2 = FindWindowById(WC_MAIN_TOOLBAR,0);
-				if (GetWidgetFromPos(w2, e->we.popupmenu.pt.x - w2->left, e->we.popupmenu.pt.y - w2->top) == WP(w, menu_d).main_button)
-					index = WP(w, menu_d).sel_index;
-			}
+				/* We have a new entry at the top of the list of menu 9 when networking
+				 * so keep that in count */
+				if (_networking && WP(w, menu_d).main_button == 9) {
+					if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1;
+				} else {
+					index = GetPlayerIndexFromMenu(index);
+				}
 
-			delete w;
+				if (index < 0) {
+					Window *w2 = FindWindowById(WC_MAIN_TOOLBAR,0);
+					if (GetWidgetFromPos(w2, _cursor.pos.x - w2->left, _cursor.pos.y - w2->top) == WP(w, menu_d).main_button)
+						index = WP(w, menu_d).sel_index;
+				}
 
-			if (index >= 0) {
-				assert(index >= 0 && index < 30);
-				_menu_clicked_procs[action_id](index);
+				delete w;
+
+				if (index >= 0) {
+					assert(index >= 0 && index < 30);
+					_menu_clicked_procs[action_id](index);
+				}
 			}
 		} break;
-
-		case WE_POPUPMENU_OVER: {
-			int index;
-			UpdatePlayerMenuHeight(w);
-			index = GetMenuItemIndex(w, e->we.popupmenu.pt.x, e->we.popupmenu.pt.y);
-
-			/* We have a new entry at the top of the list of menu 9 when networking
-			* so keep that in count */
-			if (_networking && WP(w, menu_d).main_button == 9) {
-				if (index > 0) index = GetPlayerIndexFromMenu(index - 1) + 1;
-			} else {
-				index = GetPlayerIndexFromMenu(index);
-			}
-
-			if (index == -1 || index == WP(w, menu_d).sel_index) return;
-
-			WP(w, menu_d).sel_index = index;
-			w->SetDirty();
-			return;
-		}
 	}
 }
 
@@ -1392,7 +1406,7 @@
 
 	DeleteWindowById(WC_TOOLBAR_MENU, 0);
 	Point pos = GetToolbarDropdownPos(main_button, 241, 82);
-	w = AllocateWindow(pos.x, pos.y, 241, 82, PlayerMenuWndProc, WC_TOOLBAR_MENU, _player_menu_widgets);
+	w = new Window(pos.x, pos.y, 241, 82, PlayerMenuWndProc, WC_TOOLBAR_MENU, _player_menu_widgets);
 	w->flags4 &= ~WF_WHITE_BORDER_MASK;
 	WP(w, menu_d).item_count = 0;
 	WP(w, menu_d).sel_index = (_local_player != PLAYER_SPECTATOR) ? _local_player : GetPlayerIndexFromMenu(0);
@@ -1408,7 +1422,7 @@
 	WP(w, menu_d).main_button = main_button;
 	WP(w, menu_d).checked_items = gray;
 	WP(w, menu_d).disabled_items = 0;
-	_popup_menu_active = true;
+
 	SndPlayFx(SND_15_BEEP);
 	return w;
 }
@@ -1420,7 +1434,7 @@
 	/* Clean old GUI values; railtype is (re)set by rail_gui.cpp */
 	_last_built_roadtype = ROADTYPE_ROAD;
 
-	Window *w = AllocateWindowDesc((_game_mode != GM_EDITOR) ? &_toolb_normal_desc : &_toolb_scen_desc);
+	Window *w = new Window((_game_mode != GM_EDITOR) ? &_toolb_normal_desc : &_toolb_scen_desc);
 	if (w == NULL) return NULL;
 
 	CLRBITS(w->flags4, WF_WHITE_BORDER_MASK);
--- a/src/town_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/town_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -23,6 +23,7 @@
 #include "core/alloc_func.hpp"
 #include "settings_type.h"
 #include "tilehighlight_func.h"
+#include "string_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -252,7 +253,7 @@
 			}
 			break;
 
-		case WE_4:
+		case WE_100_TICKS:
 			w->SetDirty();
 			break;
 	}
@@ -268,7 +269,7 @@
 
 static void ShowTownAuthorityWindow(uint town)
 {
-	Window *w = AllocateWindowDescFront(&_town_authority_desc, town);
+	Window *w = AllocateWindowDescFront<Window>(&_town_authority_desc, town);
 
 	if (w != NULL) {
 		w->vscroll.cap = 5;
@@ -360,7 +361,7 @@
 			} break;
 
 		case WE_ON_EDIT_TEXT:
-			if (e->we.edittext.str[0] != '\0') {
+			if (!StrEmpty(e->we.edittext.str)) {
 				_cmd_text = e->we.edittext.str;
 				DoCommandP(0, w->window_number, 0, NULL,
 					CMD_RENAME_TOWN | CMD_MSG(STR_2008_CAN_T_RENAME_TOWN));
@@ -397,7 +398,7 @@
 {
 	Window *w;
 
-	w = AllocateWindowDescFront(&_town_view_desc, town);
+	w = AllocateWindowDescFront<Window>(&_town_view_desc, town);
 
 	if (w != NULL) {
 		w->flags4 |= WF_DISABLE_VP_SCROLL;
@@ -555,7 +556,7 @@
 			}
 			break;
 
-		case WE_4:
+		case WE_100_TICKS:
 			w->SetDirty();
 			break;
 
@@ -576,7 +577,7 @@
 
 void ShowTownDirectory()
 {
-	Window *w = AllocateWindowDescFront(&_town_directory_desc, 0);
+	Window *w = AllocateWindowDescFront<Window>(&_town_directory_desc, 0);
 
 	if (w != NULL) {
 		w->vscroll.cap = 16;
@@ -705,6 +706,6 @@
 void ShowBuildTownWindow()
 {
 	if (_game_mode != GM_EDITOR && !IsValidPlayer(_current_player)) return;
-	AllocateWindowDescFront(&_scen_edit_town_gen_desc, 0);
+	AllocateWindowDescFront<Window>(&_scen_edit_town_gen_desc, 0);
 }
 
--- a/src/transparency_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/transparency_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -126,5 +126,5 @@
 
 void ShowTransparencyToolbar(void)
 {
-	AllocateWindowDescFront(&_transparency_desc, 0);
+	AllocateWindowDescFront<Window>(&_transparency_desc, 0);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tree_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -0,0 +1,193 @@
+/* $Id$ */
+
+/** @file tree_gui.cpp GUIs for building trees. */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "window_gui.h"
+#include "gfx_func.h"
+#include "tilehighlight_func.h"
+#include "player_func.h"
+#include "command_func.h"
+#include "sound_func.h"
+#include "settings_type.h"
+
+#include "table/sprites.h"
+#include "table/strings.h"
+#include "table/tree_land.h"
+
+struct tree_d {
+	uint16 base;
+	uint16 count;
+};
+assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(tree_d));
+
+static int _tree_to_plant;
+void PlaceTreesRandomly();
+
+static const PalSpriteID _tree_sprites[] = {
+	{ 0x655, PAL_NONE }, { 0x663, PAL_NONE }, { 0x678, PAL_NONE }, { 0x62B, PAL_NONE },
+	{ 0x647, PAL_NONE }, { 0x639, PAL_NONE }, { 0x64E, PAL_NONE }, { 0x632, PAL_NONE },
+	{ 0x67F, PAL_NONE }, { 0x68D, PAL_NONE }, { 0x69B, PAL_NONE }, { 0x6A9, PAL_NONE },
+	{ 0x6AF, PAL_NONE }, { 0x6D2, PAL_NONE }, { 0x6D9, PAL_NONE }, { 0x6C4, PAL_NONE },
+	{ 0x6CB, PAL_NONE }, { 0x6B6, PAL_NONE }, { 0x6BD, PAL_NONE }, { 0x6E0, PAL_NONE },
+	{ 0x72E, PAL_NONE }, { 0x734, PAL_NONE }, { 0x74A, PAL_NONE }, { 0x74F, PAL_NONE },
+	{ 0x76B, PAL_NONE }, { 0x78F, PAL_NONE }, { 0x788, PAL_NONE }, { 0x77B, PAL_NONE },
+	{ 0x75F, PAL_NONE }, { 0x774, PAL_NONE }, { 0x720, PAL_NONE }, { 0x797, PAL_NONE },
+	{ 0x79E, PAL_NONE }, { 0x7A5, PALETTE_TO_GREEN }, { 0x7AC, PALETTE_TO_RED }, { 0x7B3, PAL_NONE },
+	{ 0x7BA, PAL_NONE }, { 0x7C1, PALETTE_TO_RED, }, { 0x7C8, PALETTE_TO_PALE_GREEN }, { 0x7CF, PALETTE_TO_YELLOW }, { 0x7D6, PALETTE_TO_RED }
+};
+
+static void BuildTreesWndProc(Window *w, WindowEvent *e)
+{
+	switch (e->event) {
+		case WE_CREATE:
+			ResetObjectToPlace();
+			break;
+
+		case WE_PAINT: {
+			DrawWindowWidgets(w);
+
+			int i = WP(w, tree_d).base = _tree_base_by_landscape[_opt.landscape];
+			int count = WP(w, tree_d).count = _tree_count_by_landscape[_opt.landscape];
+
+			int x = 18;
+			int y = 54;
+			do {
+				DrawSprite(_tree_sprites[i].sprite, _tree_sprites[i].pal, x, y);
+				x += 35;
+				if (!(++i & 3)) {
+					x -= 35 * 4;
+					y += 47;
+				}
+			} while (--count);
+		} break;
+
+		case WE_CLICK: {
+			int wid = e->we.click.widget;
+
+			switch (wid) {
+				case 0:
+					ResetObjectToPlace();
+					break;
+
+				case 3: case 4: case 5: case 6:
+				case 7: case 8: case 9: case 10:
+				case 11:case 12: case 13: case 14:
+					if (wid - 3 >= WP(w, tree_d).count) break;
+
+					if (HandlePlacePushButton(w, wid, SPR_CURSOR_TREE, VHM_RECT, NULL)) {
+						_tree_to_plant = WP(w, tree_d).base + wid - 3;
+					}
+					break;
+
+				case 15: // tree of random type.
+					if (HandlePlacePushButton(w, 15, SPR_CURSOR_TREE, VHM_RECT, NULL)) {
+						_tree_to_plant = -1;
+					}
+					break;
+
+				case 16: // place trees randomly over the landscape
+					w->LowerWidget(16);
+					w->flags4 |= 5 << WF_TIMEOUT_SHL;
+					SndPlayFx(SND_15_BEEP);
+					PlaceTreesRandomly();
+					MarkWholeScreenDirty();
+					break;
+			}
+		} break;
+
+		case WE_PLACE_OBJ:
+			VpStartPlaceSizing(e->we.place.tile, VPM_X_AND_Y_LIMITED, DDSP_PLANT_TREES);
+			VpSetPlaceSizingLimit(20);
+			break;
+
+		case WE_PLACE_DRAG:
+			VpSelectTilesWithMethod(e->we.place.pt.x, e->we.place.pt.y, e->we.place.select_method);
+			return;
+
+		case WE_PLACE_MOUSEUP:
+			if (e->we.place.pt.x != -1 && e->we.place.select_proc == DDSP_PLANT_TREES) {
+				DoCommandP(e->we.place.tile, _tree_to_plant, e->we.place.starttile, NULL,
+					CMD_PLANT_TREE | CMD_MSG(STR_2805_CAN_T_PLANT_TREE_HERE));
+			}
+			break;
+
+		case WE_TIMEOUT:
+			w->RaiseWidget(16);
+			break;
+
+		case WE_ABORT_PLACE_OBJ:
+			w->RaiseButtons();
+			break;
+	}
+}
+
+static const Widget _build_trees_widgets[] = {
+{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   142,     0,    13, STR_2802_TREES,        STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,   RESIZE_NONE,     7,     0,   142,    14,   170, 0x0,                   STR_NULL},
+{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   140,   157,   168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP},
+{    WIDGETS_END},
+};
+
+static const WindowDesc _build_trees_desc = {
+	497, 22, 143, 171, 143, 171,
+	WC_BUILD_TREES, WC_SCEN_LAND_GEN,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
+	_build_trees_widgets,
+	BuildTreesWndProc
+};
+
+static const Widget _build_trees_scen_widgets[] = {
+{   WWT_CLOSEBOX,   RESIZE_NONE,     7,     0,    10,     0,    13, STR_00C5,              STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,   RESIZE_NONE,     7,    11,   142,     0,    13, STR_2802_TREES,        STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,   RESIZE_NONE,     7,     0,   142,    14,   183, 0x0,                   STR_NULL},
+{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    16,    61, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,    63,   108, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,     2,    35,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    37,    70,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,    72,   105,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{      WWT_PANEL,   RESIZE_NONE,    14,   107,   140,   110,   155, 0x0,                   STR_280D_SELECT_TREE_TYPE_TO_PLANT},
+{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   140,   157,   168, STR_TREES_RANDOM_TYPE, STR_TREES_RANDOM_TYPE_TIP},
+{    WWT_TEXTBTN,   RESIZE_NONE,    14,     2,   140,   170,   181, STR_028A_RANDOM_TREES, STR_028B_PLANT_TREES_RANDOMLY_OVER},
+{    WIDGETS_END},
+};
+
+static const WindowDesc _build_trees_scen_desc = {
+	WDP_AUTO, WDP_AUTO, 143, 184, 143, 184,
+	WC_BUILD_TREES, WC_NONE,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
+	_build_trees_scen_widgets,
+	BuildTreesWndProc
+};
+
+
+void ShowBuildTreesToolbar()
+{
+	if (!IsValidPlayer(_current_player)) return;
+	AllocateWindowDescFront<Window>(&_build_trees_desc, 0);
+}
+
+void ShowBuildTreesScenToolbar()
+{
+	AllocateWindowDescFront<Window>(&_build_trees_scen_desc, 0);
+}
--- a/src/tunnelbridge_cmd.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/tunnelbridge_cmd.cpp	Sun May 11 20:09:34 2008 +0000
@@ -39,6 +39,7 @@
 #include "economy_func.h"
 #include "rail.h"
 #include "cheat_func.h"
+#include "elrail_func.h"
 #include "landscape_type.h"
 
 #include "table/sprites.h"
@@ -887,7 +888,7 @@
 					AddSortableSpriteToDraw(SPR_TRAMWAY_TUNNEL_WIRES + tunnelbridge_direction, PAL_NONE, ti->x, ti->y, BB_data[10], BB_data[11], TILE_HEIGHT, ti->z, IsTransparencySet(TO_CATENARY), BB_data[8], BB_data[9], BB_Z_SEPARATOR);
 				}
 			}
-		} else if (!IsInvisibilitySet(TO_CATENARY) && HasCatenary(GetRailType(ti->tile))) {
+		} else if (HasCatenaryDrawn(GetRailType(ti->tile))) {
 			catenary = true;
 			StartSpriteCombine();
 			DrawCatenaryOnTunnel(ti);
@@ -960,7 +961,7 @@
 				DrawBridgeTramBits(ti->x, ti->y, z, offset, HasBit(rts, ROADTYPE_ROAD), true);
 			}
 			EndSpriteCombine();
-		} else if (HasCatenary(GetRailType(ti->tile))) {
+		} else if (HasCatenaryDrawn(GetRailType(ti->tile))) {
 			DrawCatenary(ti);
 		}
 
@@ -1088,7 +1089,7 @@
 			EndSpriteCombine();
 			StartSpriteCombine();
 		}
-	} else if (HasCatenary(GetRailType(rampsouth))) {
+	} else if (HasCatenaryDrawn(GetRailType(rampsouth))) {
 		DrawCatenaryOnBridge(ti);
 	}
 
--- a/src/vehicle.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/vehicle.cpp	Sun May 11 20:09:34 2008 +0000
@@ -592,9 +592,9 @@
 	}
 
 	Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
-	if (w != NULL && WP(w, vp_d).follow_vehicle == this->index) {
+	if (w != NULL && w->viewport->follow_vehicle == this->index) {
 		ScrollMainWindowTo(this->x_pos, this->y_pos, true); // lock the main view on the vehicle's last position
-		WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
+		w->viewport->follow_vehicle = INVALID_VEHICLE;
 	}
 }
 
@@ -2693,7 +2693,7 @@
 {
 	int length = CountVehiclesInChain(v);
 
-	uint cargo_packages_count = 1;
+	size_t cargo_packages_count = 1;
 	for (const Vehicle *v_count = v; v_count != NULL; v_count=v_count->Next()) {
 		/* Now we count how many cargo packets we need to store.
 		 * We started with an offset by one because we also need an end of array marker. */
--- a/src/vehicle_gui.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/vehicle_gui.cpp	Sun May 11 20:09:34 2008 +0000
@@ -40,6 +40,11 @@
 #include "table/sprites.h"
 #include "table/strings.h"
 
+struct vehicledetails_d {
+	byte tab;
+};
+assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehicledetails_d));
+
 struct refit_d {
 	int sel;
 	struct RefitOption *cargo;
@@ -49,15 +54,7 @@
 };
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(refit_d));
 
-struct Sorting {
-	Listing aircraft;
-	Listing roadveh;
-	Listing ship;
-	Listing train;
-};
-
-static Sorting _sorting;
-
+Sorting _sorting;
 static bool   _internal_sort_order;     // descending/ascending
 
 typedef int CDECL VehicleSortListingTypeFunction(const void*, const void*);
@@ -119,7 +116,7 @@
 			case WC_ROADVEH_LIST:
 			case WC_SHIPS_LIST:
 			case WC_AIRCRAFT_LIST:
-				WP(w, vehiclelist_d).l.flags |= sl_flag;
+				dynamic_cast<VehicleListBase*>(w)->vehicles.flags |= sl_flag;
 				w->SetDirty();
 				break;
 
@@ -144,35 +141,35 @@
 	SetVehicleListsFlag(VL_RESORT);
 }
 
-void BuildVehicleList(vehiclelist_d *vl, PlayerID owner, uint16 index, uint16 window_type)
+void BuildVehicleList(VehicleListBase *vl, PlayerID owner, uint16 index, uint16 window_type)
 {
-	if (!(vl->l.flags & VL_REBUILD)) return;
+	if (!(vl->vehicles.flags & VL_REBUILD)) return;
 
 	DEBUG(misc, 3, "Building vehicle list for player %d at station %d", owner, index);
 
-	vl->l.list_length = GenerateVehicleSortList(&vl->sort_list, &vl->length_of_sort_list, vl->vehicle_type, owner, index, window_type);
+	vl->vehicles.list_length = GenerateVehicleSortList(&vl->vehicles.sort_list, &vl->vehicles.list_length, vl->vehicle_type, owner, index, window_type);
 
-	vl->l.flags &= ~VL_REBUILD;
-	vl->l.flags |= VL_RESORT;
+	vl->vehicles.flags &= ~VL_REBUILD;
+	vl->vehicles.flags |= VL_RESORT;
 }
 
 /* cached values for VehicleNameSorter to spare many GetString() calls */
 static const Vehicle *_last_vehicle[2] = { NULL, NULL };
 static char           _last_name[2][64] = { "", "" };
 
-void SortVehicleList(vehiclelist_d *vl)
+void SortVehicleList(VehicleListBase *vl)
 {
-	if (!(vl->l.flags & VL_RESORT)) return;
+	if (!(vl->vehicles.flags & VL_RESORT)) return;
 
 	/* invalidate cached values for name sorter - vehicle names could change */
 	_last_vehicle[0] = _last_vehicle[1] = NULL;
 
-	_internal_sort_order = (vl->l.flags & VL_DESC) != 0;
-	qsort((void*)vl->sort_list, vl->l.list_length, sizeof(vl->sort_list[0]),
-		_vehicle_sorter[vl->l.sort_type]);
+	_internal_sort_order = (vl->vehicles.flags & VL_DESC) != 0;
+	qsort((void*)vl->vehicles.sort_list, vl->vehicles.list_length, sizeof(vl->vehicles.sort_list[0]),
+		_vehicle_sorter[vl->vehicles.sort_type]);
 
-	vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
-	vl->l.flags &= ~VL_RESORT;
+	vl->vehicles.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+	vl->vehicles.flags &= ~VL_RESORT;
 }
 
 void DepotSortList(Vehicle **v, uint16 length)
@@ -451,7 +448,7 @@
 
 	DeleteWindowById(WC_VEHICLE_REFIT, v->index);
 
-	w = AllocateWindowDescFront(&_vehicle_refit_desc, v->index);
+	w = AllocateWindowDescFront<Window>(&_vehicle_refit_desc, v->index);
 	WP(w, refit_d).order = order;
 
 	if (w != NULL) {
@@ -741,7 +738,7 @@
 
 	if (w != NULL) {
 		w->window_number = to_index;
-		WP(w, vp_d).follow_vehicle = to_index;
+		w->viewport->follow_vehicle = to_index;
 		w->SetDirty();
 
 		w = FindWindowById(WC_VEHICLE_ORDERS, from_index);
@@ -810,128 +807,6 @@
 	{   WIDGETS_END},
 };
 
-static void CreateVehicleListWindow(Window *w)
-{
-	vehiclelist_d *vl = &WP(w, vehiclelist_d);
-	uint16 window_type = w->window_number & VLW_MASK;
-	PlayerID player = (PlayerID)GB(w->window_number, 0, 8);
-
-	vl->vehicle_type = (VehicleType)GB(w->window_number, 11, 5);
-	vl->length_of_sort_list = 0;
-	vl->sort_list = NULL;
-	w->caption_color = player;
-
-	/* Hide the widgets that we will not use in this window
-	 * Some windows contains actions only fit for the owner */
-	if (player == _local_player) {
-		w->HideWidget(VLW_WIDGET_OTHER_PLAYER_FILLER);
-		w->SetWidgetDisabledState(VLW_WIDGET_AVAILABLE_VEHICLES, window_type != VLW_STANDARD);
-	} else {
-		w->SetWidgetsHiddenState(true,
-			VLW_WIDGET_AVAILABLE_VEHICLES,
-			VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
-			VLW_WIDGET_STOP_ALL,
-			VLW_WIDGET_START_ALL,
-			VLW_WIDGET_EMPTY_BOTTOM_RIGHT,
-			WIDGET_LIST_END);
-	}
-
-	/* Set up the window widgets */
-	switch (vl->vehicle_type) {
-		case VEH_TRAIN:
-			w->widget[VLW_WIDGET_LIST].tooltips          = STR_883D_TRAINS_CLICK_ON_TRAIN_FOR;
-			w->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_TRAINS;
-			break;
-
-		case VEH_ROAD:
-			w->widget[VLW_WIDGET_LIST].tooltips          = STR_901A_ROAD_VEHICLES_CLICK_ON;
-			w->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_ROAD_VEHICLES;
-			break;
-
-		case VEH_SHIP:
-			w->widget[VLW_WIDGET_LIST].tooltips          = STR_9823_SHIPS_CLICK_ON_SHIP_FOR;
-			w->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_SHIPS;
-			break;
-
-		case VEH_AIRCRAFT:
-			w->widget[VLW_WIDGET_LIST].tooltips          = STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT;
-			w->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_AIRCRAFT;
-			break;
-
-		default: NOT_REACHED();
-	}
-
-	switch (window_type) {
-		case VLW_SHARED_ORDERS:
-			w->widget[VLW_WIDGET_CAPTION].data  = STR_VEH_WITH_SHARED_ORDERS_LIST;
-			break;
-		case VLW_STANDARD: /* Company Name - standard widget setup */
-			switch (vl->vehicle_type) {
-				case VEH_TRAIN:    w->widget[VLW_WIDGET_CAPTION].data = STR_881B_TRAINS;        break;
-				case VEH_ROAD:     w->widget[VLW_WIDGET_CAPTION].data = STR_9001_ROAD_VEHICLES; break;
-				case VEH_SHIP:     w->widget[VLW_WIDGET_CAPTION].data = STR_9805_SHIPS;         break;
-				case VEH_AIRCRAFT: w->widget[VLW_WIDGET_CAPTION].data = STR_A009_AIRCRAFT;      break;
-				default: NOT_REACHED(); break;
-			}
-			break;
-		case VLW_STATION_LIST: /* Station Name */
-			switch (vl->vehicle_type) {
-				case VEH_TRAIN:    w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_TRAINS;        break;
-				case VEH_ROAD:     w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_ROAD_VEHICLES; break;
-				case VEH_SHIP:     w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_SHIPS;         break;
-				case VEH_AIRCRAFT: w->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_AIRCRAFT;      break;
-				default: NOT_REACHED(); break;
-			}
-			break;
-
-		case VLW_DEPOT_LIST:
-			switch (vl->vehicle_type) {
-				case VEH_TRAIN:    w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_TRAIN_DEPOT;    break;
-				case VEH_ROAD:     w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_ROADVEH_DEPOT;  break;
-				case VEH_SHIP:     w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_SHIP_DEPOT;     break;
-				case VEH_AIRCRAFT: w->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_AIRCRAFT_DEPOT; break;
-				default: NOT_REACHED(); break;
-			}
-			break;
-		default: NOT_REACHED(); break;
-	}
-
-	switch (vl->vehicle_type) {
-		case VEH_TRAIN:
-			w->resize.step_width = 1;
-			/* Fallthrough */
-		case VEH_ROAD:
-			w->vscroll.cap = 7;
-			w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
-			w->resize.height = 220 - (PLY_WND_PRC__SIZE_OF_ROW_SMALL * 3); // Minimum of 4 vehicles
-			break;
-		case VEH_SHIP:
-		case VEH_AIRCRAFT:
-			w->vscroll.cap = 4;
-			w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_BIG;
-			break;
-		default: NOT_REACHED();
-	}
-
-	w->widget[VLW_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
-
-	/* Set up sorting. Make the window-specific _sorting variable
-		* point to the correct global _sorting struct so we are freed
-		* from having conditionals during window operation */
-	switch (vl->vehicle_type) {
-		case VEH_TRAIN:    vl->_sorting = &_sorting.train; break;
-		case VEH_ROAD:     vl->_sorting = &_sorting.roadveh; break;
-		case VEH_SHIP:     vl->_sorting = &_sorting.ship; break;
-		case VEH_AIRCRAFT: vl->_sorting = &_sorting.aircraft; break;
-		default: NOT_REACHED(); break;
-	}
-
-	vl->l.flags = VL_REBUILD | (vl->_sorting->order ? VL_DESC : VL_NONE);
-	vl->l.sort_type = vl->_sorting->criteria;
-	vl->sort_list = NULL;
-	vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
-}
-
 void DrawSmallOrderList(const Vehicle *v, int x, int y)
 {
 	const Order *order;
@@ -955,254 +830,371 @@
 	}
 }
 
-static void DrawVehicleListWindow(Window *w)
-{
-	vehiclelist_d *vl = &WP(w, vehiclelist_d);
-	int x = 2;
-	int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;
-	int max;
-	int i;
-	const PlayerID owner = (PlayerID)w->caption_color;
-	const uint16 window_type = w->window_number & VLW_MASK;
-	const uint16 index = GB(w->window_number, 16, 16);
-
-	BuildVehicleList(vl, owner, index, window_type);
-	SortVehicleList(vl);
-	SetVScrollCount(w, vl->l.list_length);
-
-	/* draw the widgets */
-	switch (window_type) {
-		case VLW_SHARED_ORDERS: /* Shared Orders */
-			if (vl->l.list_length == 0) {
-				/* We can't open this window without vehicles using this order
-				 * and we should close the window when deleting the order      */
-				NOT_REACHED();
-			}
-			SetDParam(0, w->vscroll.count);
-			break;
-
-		case VLW_STANDARD: /* Company Name */
-			SetDParam(0, owner);
-			SetDParam(1, w->vscroll.count);
-			break;
-
-		case VLW_STATION_LIST: /* Station Name */
-			SetDParam(0, index);
-			SetDParam(1, w->vscroll.count);
-			break;
-
-		case VLW_DEPOT_LIST:
-			switch (vl->vehicle_type) {
-				case VEH_TRAIN:    SetDParam(0, STR_8800_TRAIN_DEPOT);        break;
-				case VEH_ROAD:     SetDParam(0, STR_9003_ROAD_VEHICLE_DEPOT); break;
-				case VEH_SHIP:     SetDParam(0, STR_9803_SHIP_DEPOT);         break;
-				case VEH_AIRCRAFT: SetDParam(0, STR_A002_AIRCRAFT_HANGAR);    break;
-				default: NOT_REACHED(); break;
-			}
-			if (vl->vehicle_type == VEH_AIRCRAFT) {
-				SetDParam(1, index); // Airport name
-			} else {
-				SetDParam(1, GetDepot(index)->town_index);
-			}
-			SetDParam(2, w->vscroll.count);
-			break;
-		default: NOT_REACHED(); break;
-	}
-
-	w->SetWidgetsDisabledState(vl->l.list_length == 0,
-		VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
-		VLW_WIDGET_STOP_ALL,
-		VLW_WIDGET_START_ALL,
-		WIDGET_LIST_END);
-
-	DrawWindowWidgets(w);
-
-	/* draw sorting criteria string */
-	DrawString(85, 15, _vehicle_sort_listing[vl->l.sort_type], TC_BLACK);
-	/* draw arrow pointing up/down for ascending/descending sorting */
-	DrawSortButtonState(w, VLW_WIDGET_SORT_ORDER, vl->l.flags & VL_DESC ? SBS_DOWN : SBS_UP);
-
-	max = min(w->vscroll.pos + w->vscroll.cap, vl->l.list_length);
-	for (i = w->vscroll.pos; i < max; ++i) {
-		const Vehicle *v = vl->sort_list[i];
-		StringID str;
-
-		SetDParam(0, v->GetDisplayProfitThisYear());
-		SetDParam(1, v->GetDisplayProfitLastYear());
-
-		DrawVehicleImage(v, x + 19, y + 6, INVALID_VEHICLE, w->widget[VLW_WIDGET_LIST].right - w->widget[VLW_WIDGET_LIST].left - 20, 0);
-		DrawString(x + 19, y + w->resize.step_height - 8, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, TC_FROMSTRING);
-
-		if (v->name != NULL) {
-			/* The vehicle got a name so we will print it */
-			SetDParam(0, v->index);
-			DrawString(x + 19, y, STR_01AB, TC_FROMSTRING);
-		}
-
-		if (w->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG) DrawSmallOrderList(v, x + 138, y);
-
-		if (v->IsInDepot()) {
-			str = STR_021F;
-		} else {
-			str = (v->age > v->max_age - 366) ? STR_00E3 : STR_00E2;
-		}
-
-		SetDParam(0, v->unitnumber);
-		DrawString(x, y + 2, str, TC_FROMSTRING);
-
-		DrawVehicleProfitButton(v, x, y + 13);
-
-		y += w->resize.step_height;
-	}
-}
-
-/*
+/**
+ * Window for the (old) vehicle listing.
+ *
  * bitmask for w->window_number
  * 0-7 PlayerID (owner)
  * 8-10 window type (use flags in vehicle_gui.h)
  * 11-15 vehicle type (using VEH_, but can be compressed to fewer bytes if needed)
  * 16-31 StationID or OrderID depending on window type (bit 8-10)
- **/
-void PlayerVehWndProc(Window *w, WindowEvent *e)
-{
-	vehiclelist_d *vl = &WP(w, vehiclelist_d);
-
-	switch (e->event) {
-		case WE_CREATE:
-			CreateVehicleListWindow(w);
-			break;
-
-		case WE_PAINT:
-			DrawVehicleListWindow(w);
-			break;
-
-		case WE_CLICK: {
-			switch (e->we.click.widget) {
-				case VLW_WIDGET_SORT_ORDER: /* Flip sorting method ascending/descending */
-					vl->l.flags ^= VL_DESC;
-					vl->l.flags |= VL_RESORT;
-
-					vl->_sorting->order = !!(vl->l.flags & VL_DESC);
-					w->SetDirty();
-					break;
-				case VLW_WIDGET_SORT_BY_PULLDOWN:/* Select sorting criteria dropdown menu */
-					ShowDropDownMenu(w, _vehicle_sort_listing, vl->l.sort_type, VLW_WIDGET_SORT_BY_PULLDOWN, 0, (vl->vehicle_type == VEH_TRAIN || vl->vehicle_type == VEH_ROAD) ? 0 : (1 << 10));
-					return;
-				case VLW_WIDGET_LIST: { /* Matrix to show vehicles */
-					uint32 id_v = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / w->resize.step_height;
-					const Vehicle *v;
-
-					if (id_v >= w->vscroll.cap) return; // click out of bounds
-
-					id_v += w->vscroll.pos;
-
-					if (id_v >= vl->l.list_length) return; // click out of list bound
-
-					v = vl->sort_list[id_v];
-
-					ShowVehicleViewWindow(v);
-				} break;
-
-				case VLW_WIDGET_AVAILABLE_VEHICLES:
-					ShowBuildVehicleWindow(0, vl->vehicle_type);
-					break;
-
-				case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
-					static StringID action_str[] = {
-						STR_REPLACE_VEHICLES,
-						STR_SEND_FOR_SERVICING,
-						STR_NULL,
-						INVALID_STRING_ID
-					};
-
-					static const StringID depot_name[] = {
-						STR_SEND_TRAIN_TO_DEPOT,
-						STR_SEND_ROAD_VEHICLE_TO_DEPOT,
-						STR_SEND_SHIP_TO_DEPOT,
-						STR_SEND_AIRCRAFT_TO_HANGAR
-					};
-
-					/* XXX - Substite string since the dropdown cannot handle dynamic strings */
-					action_str[2] = depot_name[vl->vehicle_type];
-					ShowDropDownMenu(w, action_str, 0, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN, 0, (w->window_number & VLW_MASK) == VLW_STANDARD ? 0 : 1);
-					break;
-				}
+ */
+struct VehicleListWindow : public Window, public VehicleListBase {
 
-				case VLW_WIDGET_STOP_ALL:
-				case VLW_WIDGET_START_ALL:
-					DoCommandP(0, GB(w->window_number, 16, 16), (w->window_number & VLW_MASK) | (1 << 6) | (e->we.click.widget == VLW_WIDGET_START_ALL ? (1 << 5) : 0) | vl->vehicle_type, NULL, CMD_MASS_START_STOP);
-					break;
-			}
-		} break;
-
-		case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
-			switch (e->we.dropdown.button) {
-				case VLW_WIDGET_SORT_BY_PULLDOWN:
-					if (vl->l.sort_type != e->we.dropdown.index) {
-						/* value has changed -> resort */
-						vl->l.flags |= VL_RESORT;
-						vl->l.sort_type = e->we.dropdown.index;
-						vl->_sorting->criteria = vl->l.sort_type;
-					}
-					break;
-				case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN:
-					assert(vl->l.list_length != 0);
+	VehicleListWindow(const WindowDesc *desc, void *data, WindowNumber window_number) : Window(desc, data, window_number)
+	{
+		uint16 window_type = this->window_number & VLW_MASK;
+		PlayerID player = (PlayerID)GB(this->window_number, 0, 8);
 
-					switch (e->we.dropdown.index) {
-						case 0: /* Replace window */
-							ShowReplaceGroupVehicleWindow(DEFAULT_GROUP, vl->vehicle_type);
-							break;
-						case 1: /* Send for servicing */
-							DoCommandP(0, GB(w->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
-								(w->window_number & VLW_MASK) | DEPOT_MASS_SEND | DEPOT_SERVICE,
-								NULL,
-								GetCmdSendToDepot(vl->vehicle_type));
-							break;
-						case 2: /* Send to Depots */
-							DoCommandP(0, GB(w->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
-								(w->window_number & VLW_MASK) | DEPOT_MASS_SEND,
-								NULL,
-								GetCmdSendToDepot(vl->vehicle_type));
-							break;
+		this->vehicle_type = (VehicleType)GB(this->window_number, 11, 5);
+		this->vehicles.list_length = 0;
+		this->vehicles.sort_list = NULL;
+		this->caption_color = player;
 
-						default: NOT_REACHED();
-					}
-					break;
-				default: NOT_REACHED();
+		/* Hide the widgets that we will not use in this window
+		* Some windows contains actions only fit for the owner */
+		if (player == _local_player) {
+			this->HideWidget(VLW_WIDGET_OTHER_PLAYER_FILLER);
+			this->SetWidgetDisabledState(VLW_WIDGET_AVAILABLE_VEHICLES, window_type != VLW_STANDARD);
+		} else {
+			this->SetWidgetsHiddenState(true,
+				VLW_WIDGET_AVAILABLE_VEHICLES,
+				VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
+				VLW_WIDGET_STOP_ALL,
+				VLW_WIDGET_START_ALL,
+				VLW_WIDGET_EMPTY_BOTTOM_RIGHT,
+				WIDGET_LIST_END);
+		}
+
+		/* Set up the window widgets */
+		switch (this->vehicle_type) {
+			case VEH_TRAIN:
+				this->widget[VLW_WIDGET_LIST].tooltips          = STR_883D_TRAINS_CLICK_ON_TRAIN_FOR;
+				this->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_TRAINS;
+				break;
+
+			case VEH_ROAD:
+				this->widget[VLW_WIDGET_LIST].tooltips          = STR_901A_ROAD_VEHICLES_CLICK_ON;
+				this->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_ROAD_VEHICLES;
+				break;
+
+			case VEH_SHIP:
+				this->widget[VLW_WIDGET_LIST].tooltips          = STR_9823_SHIPS_CLICK_ON_SHIP_FOR;
+				this->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_SHIPS;
+				break;
+
+			case VEH_AIRCRAFT:
+				this->widget[VLW_WIDGET_LIST].tooltips          = STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT;
+				this->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_AIRCRAFT;
+				break;
+
+			default: NOT_REACHED();
+		}
+
+		switch (window_type) {
+			case VLW_SHARED_ORDERS:
+				this->widget[VLW_WIDGET_CAPTION].data  = STR_VEH_WITH_SHARED_ORDERS_LIST;
+				break;
+			case VLW_STANDARD: /* Company Name - standard widget setup */
+				switch (this->vehicle_type) {
+					case VEH_TRAIN:    this->widget[VLW_WIDGET_CAPTION].data = STR_881B_TRAINS;        break;
+					case VEH_ROAD:     this->widget[VLW_WIDGET_CAPTION].data = STR_9001_ROAD_VEHICLES; break;
+					case VEH_SHIP:     this->widget[VLW_WIDGET_CAPTION].data = STR_9805_SHIPS;         break;
+					case VEH_AIRCRAFT: this->widget[VLW_WIDGET_CAPTION].data = STR_A009_AIRCRAFT;      break;
+					default: NOT_REACHED(); break;
+				}
+				break;
+			case VLW_STATION_LIST: /* Station Name */
+				switch (this->vehicle_type) {
+					case VEH_TRAIN:    this->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_TRAINS;        break;
+					case VEH_ROAD:     this->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_ROAD_VEHICLES; break;
+					case VEH_SHIP:     this->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_SHIPS;         break;
+					case VEH_AIRCRAFT: this->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_AIRCRAFT;      break;
+					default: NOT_REACHED(); break;
+				}
+				break;
+
+			case VLW_DEPOT_LIST:
+				switch (this->vehicle_type) {
+					case VEH_TRAIN:    this->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_TRAIN_DEPOT;    break;
+					case VEH_ROAD:     this->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_ROADVEH_DEPOT;  break;
+					case VEH_SHIP:     this->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_SHIP_DEPOT;     break;
+					case VEH_AIRCRAFT: this->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_AIRCRAFT_DEPOT; break;
+					default: NOT_REACHED(); break;
+				}
+				break;
+			default: NOT_REACHED(); break;
+		}
+
+		switch (this->vehicle_type) {
+			case VEH_TRAIN:
+				this->resize.step_width = 1;
+				/* Fallthrough */
+			case VEH_ROAD:
+				this->vscroll.cap = 7;
+				this->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
+				this->resize.height = 220 - (PLY_WND_PRC__SIZE_OF_ROW_SMALL * 3); // Minimum of 4 vehicles
+				break;
+			case VEH_SHIP:
+			case VEH_AIRCRAFT:
+				this->vscroll.cap = 4;
+				this->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_BIG;
+				break;
+			default: NOT_REACHED();
+		}
+
+
+		this->widget[VLW_WIDGET_LIST].data = (this->vscroll.cap << 8) + 1;
+
+		/* Set up sorting. Make the window-specific _sorting variable
+			* point to the correct global _sorting struct so we are freed
+			* from having conditionals during window operation */
+		switch (this->vehicle_type) {
+			case VEH_TRAIN:    this->sorting = &_sorting.train; break;
+			case VEH_ROAD:     this->sorting = &_sorting.roadveh; break;
+			case VEH_SHIP:     this->sorting = &_sorting.ship; break;
+			case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break;
+			default: NOT_REACHED(); break;
+		}
+
+		this->vehicles.flags = VL_REBUILD | (this->sorting->order ? VL_DESC : VL_NONE);
+		this->vehicles.sort_type = this->sorting->criteria;
+		this->vehicles.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
+
+		this->FindWindowPlacementAndResize(desc);
+	}
+
+	~VehicleListWindow()
+	{
+		free((void*)this->vehicles.sort_list);
+	}
+
+	virtual void OnPaint()
+	{
+		int x = 2;
+		int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;
+		int max;
+		int i;
+		const PlayerID owner = (PlayerID)this->caption_color;
+		const uint16 window_type = this->window_number & VLW_MASK;
+		const uint16 index = GB(this->window_number, 16, 16);
+
+		BuildVehicleList(this, owner, index, window_type);
+		SortVehicleList(this);
+		SetVScrollCount(this, this->vehicles.list_length);
+
+		/* draw the widgets */
+		switch (window_type) {
+			case VLW_SHARED_ORDERS: /* Shared Orders */
+				if (this->vehicles.list_length == 0) {
+					/* We can't open this window without vehicles using this order
+					* and we should close the window when deleting the order      */
+					NOT_REACHED();
+				}
+				SetDParam(0, this->vscroll.count);
+				break;
+
+			case VLW_STANDARD: /* Company Name */
+				SetDParam(0, owner);
+				SetDParam(1, this->vscroll.count);
+				break;
+
+			case VLW_STATION_LIST: /* Station Name */
+				SetDParam(0, index);
+				SetDParam(1, this->vscroll.count);
+				break;
+
+			case VLW_DEPOT_LIST:
+				switch (this->vehicle_type) {
+					case VEH_TRAIN:    SetDParam(0, STR_8800_TRAIN_DEPOT);        break;
+					case VEH_ROAD:     SetDParam(0, STR_9003_ROAD_VEHICLE_DEPOT); break;
+					case VEH_SHIP:     SetDParam(0, STR_9803_SHIP_DEPOT);         break;
+					case VEH_AIRCRAFT: SetDParam(0, STR_A002_AIRCRAFT_HANGAR);    break;
+					default: NOT_REACHED(); break;
+				}
+				if (this->vehicle_type == VEH_AIRCRAFT) {
+					SetDParam(1, index); // Airport name
+				} else {
+					SetDParam(1, GetDepot(index)->town_index);
+				}
+				SetDParam(2, this->vscroll.count);
+				break;
+			default: NOT_REACHED(); break;
+		}
+
+		this->SetWidgetsDisabledState(this->vehicles.list_length == 0,
+			VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
+			VLW_WIDGET_STOP_ALL,
+			VLW_WIDGET_START_ALL,
+			WIDGET_LIST_END);
+
+		DrawWindowWidgets(this);
+
+		/* draw sorting criteria string */
+		DrawString(85, 15, _vehicle_sort_listing[this->vehicles.sort_type], TC_BLACK);
+		/* draw arrow pointing up/down for ascending/descending sorting */
+		DrawSortButtonState(this, VLW_WIDGET_SORT_ORDER, this->vehicles.flags & VL_DESC ? SBS_DOWN : SBS_UP);
+
+		max = min(this->vscroll.pos + this->vscroll.cap, this->vehicles.list_length);
+		for (i = this->vscroll.pos; i < max; ++i) {
+			const Vehicle *v = this->vehicles.sort_list[i];
+			StringID str;
+
+			SetDParam(0, v->GetDisplayProfitThisYear());
+			SetDParam(1, v->GetDisplayProfitLastYear());
+
+			DrawVehicleImage(v, x + 19, y + 6, INVALID_VEHICLE, this->widget[VLW_WIDGET_LIST].right - this->widget[VLW_WIDGET_LIST].left - 20, 0);
+			DrawString(x + 19, y + this->resize.step_height - 8, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, TC_FROMSTRING);
+
+			if (v->name != NULL) {
+				/* The vehicle got a name so we will print it */
+				SetDParam(0, v->index);
+				DrawString(x + 19, y, STR_01AB, TC_FROMSTRING);
 			}
-			w->SetDirty();
-			break;
-
-		case WE_DESTROY:
-			free((void*)vl->sort_list);
-			break;
-
-		case WE_TICK: /* resort the list every 20 seconds orso (10 days) */
-			if (_pause_game != 0) break;
-			if (--vl->l.resort_timer == 0) {
-				StationID station = ((w->window_number & VLW_MASK) == VLW_STATION_LIST) ? GB(w->window_number, 16, 16) : INVALID_STATION;
-				PlayerID owner = (PlayerID)w->caption_color;
 
-				DEBUG(misc, 3, "Periodic resort %d list player %d at station %d", vl->vehicle_type, owner, station);
-				vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
-				vl->l.flags |= VL_RESORT;
-				w->SetDirty();
+			if (this->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG) DrawSmallOrderList(v, x + 138, y);
+
+			if (v->IsInDepot()) {
+				str = STR_021F;
+			} else {
+				str = (v->age > v->max_age - 366) ? STR_00E3 : STR_00E2;
 			}
-			break;
 
-		case WE_RESIZE: /* Update the scroll + matrix */
-			w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
-			w->widget[VLW_WIDGET_LIST].data = (w->vscroll.cap << 8) + 1;
-			break;
+			SetDParam(0, v->unitnumber);
+			DrawString(x, y + 2, str, TC_FROMSTRING);
+
+			DrawVehicleProfitButton(v, x, y + 13);
+
+			y += this->resize.step_height;
+		}
 	}
-}
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		switch (widget) {
+			case VLW_WIDGET_SORT_ORDER: /* Flip sorting method ascending/descending */
+				this->vehicles.flags ^= VL_DESC;
+				this->vehicles.flags |= VL_RESORT;
+
+				this->sorting->order = !!(this->vehicles.flags & VL_DESC);
+				this->SetDirty();
+				break;
+			case VLW_WIDGET_SORT_BY_PULLDOWN:/* Select sorting criteria dropdown menu */
+				ShowDropDownMenu(this, _vehicle_sort_listing, this->vehicles.sort_type, VLW_WIDGET_SORT_BY_PULLDOWN, 0, (this->vehicle_type == VEH_TRAIN || this->vehicle_type == VEH_ROAD) ? 0 : (1 << 10));
+				return;
+			case VLW_WIDGET_LIST: { /* Matrix to show vehicles */
+				uint32 id_v = (pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / this->resize.step_height;
+				const Vehicle *v;
+
+				if (id_v >= this->vscroll.cap) return; // click out of bounds
+
+				id_v += this->vscroll.pos;
+
+				if (id_v >= this->vehicles.list_length) return; // click out of list bound
+
+				v = this->vehicles.sort_list[id_v];
+
+				ShowVehicleViewWindow(v);
+			} break;
+
+			case VLW_WIDGET_AVAILABLE_VEHICLES:
+				ShowBuildVehicleWindow(0, this->vehicle_type);
+				break;
+
+			case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
+				static StringID action_str[] = {
+					STR_REPLACE_VEHICLES,
+					STR_SEND_FOR_SERVICING,
+					STR_NULL,
+					INVALID_STRING_ID
+				};
+
+				static const StringID depot_name[] = {
+					STR_SEND_TRAIN_TO_DEPOT,
+					STR_SEND_ROAD_VEHICLE_TO_DEPOT,
+					STR_SEND_SHIP_TO_DEPOT,
+					STR_SEND_AIRCRAFT_TO_HANGAR
+				};
+
+				/* XXX - Substite string since the dropdown cannot handle dynamic strings */
+				action_str[2] = depot_name[this->vehicle_type];
+				ShowDropDownMenu(this, action_str, 0, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN, 0, (this->window_number & VLW_MASK) == VLW_STANDARD ? 0 : 1);
+				break;
+			}
+
+			case VLW_WIDGET_STOP_ALL:
+			case VLW_WIDGET_START_ALL:
+				DoCommandP(0, GB(this->window_number, 16, 16), (this->window_number & VLW_MASK) | (1 << 6) | (widget == VLW_WIDGET_START_ALL ? (1 << 5) : 0) | this->vehicle_type, NULL, CMD_MASS_START_STOP);
+				break;
+		}
+	}
+
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		switch (widget) {
+			case VLW_WIDGET_SORT_BY_PULLDOWN:
+				if (this->vehicles.sort_type != index) {
+					/* value has changed -> resort */
+					this->vehicles.flags |= VL_RESORT;
+					this->vehicles.sort_type = index;
+					this->sorting->criteria = this->vehicles.sort_type;
+				}
+				break;
+			case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN:
+				assert(this->vehicles.list_length != 0);
+
+				switch (index) {
+					case 0: /* Replace window */
+						ShowReplaceGroupVehicleWindow(DEFAULT_GROUP, this->vehicle_type);
+						break;
+					case 1: /* Send for servicing */
+						DoCommandP(0, GB(this->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
+							(this->window_number & VLW_MASK) | DEPOT_MASS_SEND | DEPOT_SERVICE,
+							NULL,
+							GetCmdSendToDepot(this->vehicle_type));
+						break;
+					case 2: /* Send to Depots */
+						DoCommandP(0, GB(this->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
+							(this->window_number & VLW_MASK) | DEPOT_MASS_SEND,
+							NULL,
+							GetCmdSendToDepot(this->vehicle_type));
+						break;
+
+					default: NOT_REACHED();
+				}
+				break;
+			default: NOT_REACHED();
+		}
+		this->SetDirty();
+	}
+
+	virtual void OnTick()
+	{
+		if (_pause_game != 0) return;
+		if (--this->vehicles.resort_timer == 0) {
+			StationID station = ((this->window_number & VLW_MASK) == VLW_STATION_LIST) ? GB(this->window_number, 16, 16) : INVALID_STATION;
+			PlayerID owner = (PlayerID)this->caption_color;
+
+			DEBUG(misc, 3, "Periodic resort %d list player %d at station %d", this->vehicle_type, owner, station);
+			this->vehicles.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+			this->vehicles.flags |= VL_RESORT;
+			this->SetDirty();
+		}
+	}
+
+	virtual void OnResize(Point new_size, Point delta)
+	{
+		this->vscroll.cap += delta.y / (int)this->resize.step_height;
+		this->widget[VLW_WIDGET_LIST].data = (this->vscroll.cap << 8) + 1;
+	}
+};
 
 static const WindowDesc _player_vehicle_list_train_desc = {
 	WDP_AUTO, WDP_AUTO, 260, 182, 260, 182,
 	WC_TRAINS_LIST, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_vehicle_list_widgets,
-	PlayerVehWndProc
+	NULL
 };
 
 static const WindowDesc _player_vehicle_list_road_veh_desc = {
@@ -1210,7 +1202,7 @@
 	WC_ROADVEH_LIST, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_vehicle_list_widgets,
-	PlayerVehWndProc
+	NULL
 };
 
 static const WindowDesc _player_vehicle_list_ship_desc = {
@@ -1218,7 +1210,7 @@
 	WC_SHIPS_LIST, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_vehicle_list_widgets,
-	PlayerVehWndProc
+	NULL
 };
 
 static const WindowDesc _player_vehicle_list_aircraft_desc = {
@@ -1226,12 +1218,12 @@
 	WC_AIRCRAFT_LIST, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
 	_vehicle_list_widgets,
-	PlayerVehWndProc
+	NULL
 };
 
 static void ShowVehicleListWindowLocal(PlayerID player, uint16 VLW_flag, VehicleType vehicle_type, uint16 unique_number)
 {
-	Window *w;
+	VehicleListWindow *w;
 	WindowNumber num;
 
 	if (!IsValidPlayer(player)) return;
@@ -1244,18 +1236,18 @@
 	switch (vehicle_type) {
 		default: NOT_REACHED();
 		case VEH_TRAIN:
-			w = AllocateWindowDescFront(&_player_vehicle_list_train_desc, num);
+			w = AllocateWindowDescFront<VehicleListWindow>(&_player_vehicle_list_train_desc, num);
 			if (w != NULL) ResizeWindow(w, 65, 38);
 			break;
 		case VEH_ROAD:
-			w = AllocateWindowDescFront(&_player_vehicle_list_road_veh_desc, num);
+			w = AllocateWindowDescFront<VehicleListWindow>(&_player_vehicle_list_road_veh_desc, num);
 			if (w != NULL) ResizeWindow(w, 0, 38);
 			break;
 		case VEH_SHIP:
-			w = AllocateWindowDescFront(&_player_vehicle_list_ship_desc, num);
+			w = AllocateWindowDescFront<VehicleListWindow>(&_player_vehicle_list_ship_desc, num);
 			break;
 		case VEH_AIRCRAFT:
-			w = AllocateWindowDescFront(&_player_vehicle_list_aircraft_desc, num);
+			w = AllocateWindowDescFront<VehicleListWindow>(&_player_vehicle_list_aircraft_desc, num);
 			break;
 	}
 
@@ -1671,7 +1663,7 @@
 {
 	DeleteWindowById(WC_VEHICLE_ORDERS, v->index);
 	DeleteWindowById(WC_VEHICLE_DETAILS, v->index);
-	AllocateWindowDescFront(&_vehicle_details_desc, v->index);
+	AllocateWindowDescFront<Window>(&_vehicle_details_desc, v->index);
 }
 
 
@@ -1747,7 +1739,7 @@
 /** Shows the vehicle view window of the given vehicle. */
 void ShowVehicleViewWindow(const Vehicle *v)
 {
-	Window *w = AllocateWindowDescFront((v->type == VEH_TRAIN) ? &_train_view_desc : &_vehicle_view_desc, v->index);
+	Window *w = AllocateWindowDescFront<Window>((v->type == VEH_TRAIN) ? &_train_view_desc : &_vehicle_view_desc, v->index);
 
 	if (w != NULL) {
 		w->caption_color = v->owner;
@@ -2099,7 +2091,7 @@
 					const Window *mainwindow = FindWindowById(WC_MAIN_WINDOW, 0);
 					/* code to allow the main window to 'follow' the vehicle if the ctrl key is pressed */
 					if (_ctrl_pressed && mainwindow->viewport->zoom == ZOOM_LVL_NORMAL) {
-						WP(mainwindow, vp_d).follow_vehicle = v->index;
+						mainwindow->viewport->follow_vehicle = v->index;
 					} else {
 						ScrollMainWindowTo(v->x_pos, v->y_pos);
 					}
@@ -2152,7 +2144,7 @@
 			DeleteWindowById(WC_VEHICLE_TIMETABLE, w->window_number);
 			break;
 
-		case WE_MOUSELOOP: {
+		case WE_TICK: {
 			const Vehicle *v = GetVehicle(w->window_number);
 			bool veh_stopped = v->IsStoppedInDepot();
 
--- a/src/vehicle_gui.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/vehicle_gui.h	Sun May 11 20:09:34 2008 +0000
@@ -14,13 +14,6 @@
 
 void DrawVehicleProfitButton(const Vehicle *v, int x, int y);
 void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order);
-void InitializeVehiclesGuiList();
-
-/* sorter stuff */
-void RebuildVehicleLists();
-void ResortVehicleLists();
-void SortVehicleList(vehiclelist_d *vl);
-void BuildVehicleList(vehiclelist_d *vl, PlayerID owner, uint16 index, uint16 window_type);
 
 #define PERIODIC_RESORT_DAYS 10
 
@@ -70,8 +63,6 @@
 	return (flags == VLW_STANDARD || flags == VLW_SHARED_ORDERS || flags == VLW_STATION_LIST || flags == VLW_DEPOT_LIST || flags == VLW_GROUP_LIST);
 }
 
-void PlayerVehWndProc(Window *w, WindowEvent *e);
-
 int DrawVehiclePurchaseInfo(int x, int y, uint w, EngineID engine_number);
 
 void DrawTrainImage(const Vehicle *v, int x, int y, VehicleID selection, int count, int skip);
@@ -137,6 +128,29 @@
 /* Unified window procedure */
 void ShowVehicleViewWindow(const Vehicle *v);
 
-Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y);
+Vehicle *CheckClickOnVehicle(const struct ViewPort *vp, int x, int y);
+
+typedef GUIList<const Vehicle*> GUIVehicleList;
+
+struct VehicleListBase {
+	GUIVehicleList vehicles;  ///< The list of vehicles
+	Listing *sorting;         ///< Pointer to the vehicle type related sorting.
+	VehicleType vehicle_type; ///< The vehicle type that is sorted
+};
+
+struct Sorting {
+	Listing aircraft;
+	Listing roadveh;
+	Listing ship;
+	Listing train;
+};
+
+extern Sorting _sorting;
+
+/* sorter stuff */
+void RebuildVehicleLists();
+void ResortVehicleLists();
+void SortVehicleList(VehicleListBase *vl);
+void BuildVehicleList(VehicleListBase *vl, PlayerID owner, uint16 index, uint16 window_type);
 
 #endif /* VEHICLE_GUI_H */
--- a/src/viewport.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/viewport.cpp	Sun May 11 20:09:34 2008 +0000
@@ -144,8 +144,6 @@
 static TileInfo *_cur_ti;
 bool _draw_bounding_boxes = false;
 
-extern void SmallMapCenterOnCurrentPos(Window *w);
-
 static Point MapXYZToViewport(const ViewPort *vp, uint x, uint y, uint z)
 {
 	Point p = RemapCoords(x, y, z);
@@ -156,7 +154,7 @@
 
 void DeleteWindowViewport(Window *w)
 {
-	w->viewport->width = 0;
+	free(w->viewport);
 	w->viewport = NULL;
 }
 
@@ -177,7 +175,7 @@
 {
 	assert(w->viewport == NULL);
 
-	ViewPort *vp = &(WP(w, vp_d).vp_data);
+	ViewportData *vp = CallocT<ViewportData>(1);
 
 	vp->left = x + w->left;
 	vp->top = y + w->top;
@@ -194,21 +192,21 @@
 	if (follow_flags & 0x80000000) {
 		const Vehicle *veh;
 
-		WP(w, vp_d).follow_vehicle = (VehicleID)(follow_flags & 0xFFFF);
-		veh = GetVehicle(WP(w, vp_d).follow_vehicle);
+		vp->follow_vehicle = (VehicleID)(follow_flags & 0xFFFF);
+		veh = GetVehicle(vp->follow_vehicle);
 		pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
 	} else {
 		uint x = TileX(follow_flags) * TILE_SIZE;
 		uint y = TileY(follow_flags) * TILE_SIZE;
 
-		WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
+		vp->follow_vehicle = INVALID_VEHICLE;
 		pt = MapXYZToViewport(vp, x, y, GetSlopeZ(x, y));
 	}
 
-	WP(w, vp_d).scrollpos_x = pt.x;
-	WP(w, vp_d).scrollpos_y = pt.y;
-	WP(w, vp_d).dest_scrollpos_x = pt.x;
-	WP(w, vp_d).dest_scrollpos_y = pt.y;
+	vp->scrollpos_x = pt.x;
+	vp->scrollpos_y = pt.y;
+	vp->dest_scrollpos_x = pt.x;
+	vp->dest_scrollpos_y = pt.y;
 
 	w->viewport = vp;
 	vp->virtual_left = 0;//pt.x;
@@ -426,9 +424,7 @@
 Point GetTileZoomCenterWindow(bool in, Window * w)
 {
 	int x, y;
-	ViewPort * vp;
-
-	vp = w->viewport;
+	ViewPort *vp = w->viewport;
 
 	if (in) {
 		x = ((_cursor.pos.x - vp->left) >> 1) + (vp->width >> 2);
@@ -1581,33 +1577,33 @@
 {
 	const ViewPort *vp = w->viewport;
 
-	if (WP(w, vp_d).follow_vehicle != INVALID_VEHICLE) {
-		const Vehicle* veh = GetVehicle(WP(w, vp_d).follow_vehicle);
+	if (w->viewport->follow_vehicle != INVALID_VEHICLE) {
+		const Vehicle* veh = GetVehicle(w->viewport->follow_vehicle);
 		Point pt = MapXYZToViewport(vp, veh->x_pos, veh->y_pos, veh->z_pos);
 
 		SetViewportPosition(w, pt.x, pt.y);
 	} else {
 		/* Ensure the destination location is within the map */
-		ClampViewportToMap(vp, WP(w, vp_d).dest_scrollpos_x, WP(w, vp_d).dest_scrollpos_y);
-
-		int delta_x = WP(w, vp_d).dest_scrollpos_x - WP(w, vp_d).scrollpos_x;
-		int delta_y = WP(w, vp_d).dest_scrollpos_y - WP(w, vp_d).scrollpos_y;
+		ClampViewportToMap(vp, w->viewport->dest_scrollpos_x, w->viewport->dest_scrollpos_y);
+
+		int delta_x = w->viewport->dest_scrollpos_x - w->viewport->scrollpos_x;
+		int delta_y = w->viewport->dest_scrollpos_y - w->viewport->scrollpos_y;
 
 		if (delta_x != 0 || delta_y != 0) {
 			if (_patches.smooth_scroll) {
 				int max_scroll = ScaleByMapSize1D(512);
 				/* Not at our desired positon yet... */
-				WP(w, vp_d).scrollpos_x += Clamp(delta_x / 4, -max_scroll, max_scroll);
-				WP(w, vp_d).scrollpos_y += Clamp(delta_y / 4, -max_scroll, max_scroll);
+				w->viewport->scrollpos_x += Clamp(delta_x / 4, -max_scroll, max_scroll);
+				w->viewport->scrollpos_y += Clamp(delta_y / 4, -max_scroll, max_scroll);
 			} else {
-				WP(w, vp_d).scrollpos_x = WP(w, vp_d).dest_scrollpos_x;
-				WP(w, vp_d).scrollpos_y = WP(w, vp_d).dest_scrollpos_y;
+				w->viewport->scrollpos_x = w->viewport->dest_scrollpos_x;
+				w->viewport->scrollpos_y = w->viewport->dest_scrollpos_y;
 			}
 		}
 
-		ClampViewportToMap(vp, WP(w, vp_d).scrollpos_x, WP(w, vp_d).scrollpos_y);
-
-		SetViewportPosition(w, WP(w, vp_d).scrollpos_x, WP(w, vp_d).scrollpos_y);
+		ClampViewportToMap(vp, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
+
+		SetViewportPosition(w, w->viewport->scrollpos_x, w->viewport->scrollpos_y);
 	}
 }
 
@@ -2057,14 +2053,7 @@
 	_tile_fract_coords.y = pt.y & 0xF;
 
 	w = GetCallbackWnd();
-	if (w != NULL) {
-		WindowEvent e;
-
-		e.event = WE_PLACE_OBJ;
-		e.we.place.pt = pt;
-		e.we.place.tile = TileVirtXY(pt.x, pt.y);
-		w->HandleWindowEvent(&e);
-	}
+	if (w != NULL) w->OnPlaceObject(pt, TileVirtXY(pt.x, pt.y));
 }
 
 
@@ -2073,42 +2062,21 @@
 {
 	/* The slope cannot be acquired outside of the map, so make sure we are always within the map. */
 	Point pt = MapXYZToViewport(w->viewport, x, y, GetSlopeZ(Clamp(x, 0, MapSizeX()), Clamp(y, 0, MapSizeY())));
-	WP(w, vp_d).follow_vehicle = INVALID_VEHICLE;
-
-	if (WP(w, vp_d).dest_scrollpos_x == pt.x && WP(w, vp_d).dest_scrollpos_y == pt.y)
+	w->viewport->follow_vehicle = INVALID_VEHICLE;
+
+	if (w->viewport->dest_scrollpos_x == pt.x && w->viewport->dest_scrollpos_y == pt.y)
 		return false;
 
 	if (instant) {
-		WP(w, vp_d).scrollpos_x = pt.x;
-		WP(w, vp_d).scrollpos_y = pt.y;
+		w->viewport->scrollpos_x = pt.x;
+		w->viewport->scrollpos_y = pt.y;
 	}
 
-	WP(w, vp_d).dest_scrollpos_x = pt.x;
-	WP(w, vp_d).dest_scrollpos_y = pt.y;
+	w->viewport->dest_scrollpos_x = pt.x;
+	w->viewport->dest_scrollpos_y = pt.y;
 	return true;
 }
 
-
-bool ScrollMainWindowTo(int x, int y, bool instant)
-{
-	Window *w;
-	bool res = ScrollWindowTo(x, y, FindWindowById(WC_MAIN_WINDOW, 0), instant);
-
-	/* If a user scrolls to a tile (via what way what so ever) and already is on
-	 *  that tile (e.g.: pressed twice), move the smallmap to that location,
-	 *  so you directly see where you are on the smallmap. */
-
-	if (res) return res;
-
-	w = FindWindowById(WC_SMALLMAP, 0);
-	if (w == NULL) return res;
-
-	SmallMapCenterOnCurrentPos(w);
-
-	return res;
-}
-
-
 bool ScrollMainWindowToTile(TileIndex tile, bool instant)
 {
 	return ScrollMainWindowTo(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, instant);
@@ -2228,7 +2196,7 @@
 }
 
 /** highlighting tiles while only going over them with the mouse */
-void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, byte process)
+void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, ViewportDragDropSelectionProcess process)
 {
 	_thd.select_method = method;
 	_thd.select_proc   = process;
@@ -2695,16 +2663,10 @@
 /** while dragging */
 bool VpHandlePlaceSizingDrag()
 {
-	Window *w;
-	WindowEvent e;
-
 	if (_special_mouse_mode != WSM_SIZING) return true;
 
-	e.we.place.select_method = _thd.select_method;
-	e.we.place.select_proc   = _thd.select_proc;
-
 	/* stop drag mode if the window has been closed */
-	w = FindWindowById(_thd.window_class, _thd.window_number);
+	Window *w = FindWindowById(_thd.window_class, _thd.window_number);
 	if (w == NULL) {
 		ResetObjectToPlace();
 		return false;
@@ -2712,9 +2674,7 @@
 
 	/* while dragging execute the drag procedure of the corresponding window (mostly VpSelectTilesWithMethod() ) */
 	if (_left_button_down) {
-		e.event = WE_PLACE_DRAG;
-		e.we.place.pt = GetTileBelowCursor();
-		w->HandleWindowEvent(&e);
+		w->OnPlaceDrag(_thd.select_method, _thd.select_proc, GetTileBelowCursor());
 		return false;
 	}
 
@@ -2723,7 +2683,7 @@
 	_special_mouse_mode = WSM_NONE;
 	if (_thd.next_drawstyle == HT_RECT) {
 		_thd.place_mode = VHM_RECT;
-	} else if (e.we.place.select_method == VPM_SIGNALDIRS) { // some might call this a hack... -- Dominik
+	} else if (_thd.select_method == VPM_SIGNALDIRS) { // some might call this a hack... -- Dominik
 		_thd.place_mode = VHM_RECT;
 	} else if (_thd.next_drawstyle & HT_LINE) {
 		_thd.place_mode = VHM_RAIL;
@@ -2734,12 +2694,7 @@
 	}
 	SetTileSelectSize(1, 1);
 
-	/* and call the mouseup event. */
-	e.event = WE_PLACE_MOUSEUP;
-	e.we.place.pt = _thd.selend;
-	e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
-	e.we.place.starttile = TileVirtXY(_thd.selstart.x, _thd.selstart.y);
-	w->HandleWindowEvent(&e);
+	w->OnPlaceMouseUp(_thd.select_method, _thd.select_proc, _thd.selend, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
 
 	return false;
 }
@@ -2758,7 +2713,7 @@
 	/* undo clicking on button and drag & drop */
 	if (_thd.place_mode != VHM_NONE || _special_mouse_mode == WSM_DRAGDROP) {
 		w = FindWindowById(_thd.window_class, _thd.window_number);
-		if (w != NULL) CallWindowEventNP(w, WE_ABORT_PLACE_OBJ);
+		if (w != NULL) w->OnPlaceObjectAbort();
 	}
 
 	SetTileSelectSize(1, 1);
--- a/src/viewport_func.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/viewport_func.h	Sun May 11 20:09:34 2008 +0000
@@ -51,15 +51,8 @@
 
 Vehicle *CheckMouseOverVehicle();
 
-void VpSelectTilesWithMethod(int x, int y, ViewportPlaceMethod method);
-void VpStartPlaceSizing(TileIndex tile, ViewportPlaceMethod method, byte process);
-void VpSetPresizeRange(TileIndex from, TileIndex to);
-void VpSetPlaceSizingLimit(int limit);
-
 void ViewportDoDraw(const ViewPort *vp, int left, int top, int right, int bottom);
 
-void ResetObjectToPlace();
-
 bool ScrollWindowTo(int x, int y, Window *w, bool instant = false);
 
 bool ScrollMainWindowToTile(TileIndex tile, bool instant = false);
--- a/src/viewport_type.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/viewport_type.h	Sun May 11 20:09:34 2008 +0000
@@ -58,4 +58,33 @@
 	VPM_SIGNALDIRS      = 6, ///< similiar to VMP_RAILDIRS, but with different cursor
 };
 
+/** Drag and drop selection process, or, what to do with an area of land when
+ * you've selected it. */
+enum ViewportDragDropSelectionProcess {
+	DDSP_DEMOLISH_AREA,
+	DDSP_RAISE_AND_LEVEL_AREA,
+	DDSP_LOWER_AND_LEVEL_AREA,
+	DDSP_LEVEL_AREA,
+	DDSP_CREATE_DESERT,
+	DDSP_CREATE_ROCKS,
+	DDSP_CREATE_WATER,
+	DDSP_CREATE_RIVER,
+	DDSP_PLANT_TREES,
+	DDSP_BUILD_BRIDGE,
+
+	/* Rail specific actions */
+	DDSP_PLACE_RAIL_NE,
+	DDSP_PLACE_RAIL_NW,
+	DDSP_PLACE_AUTORAIL,
+	DDSP_BUILD_SIGNALS,
+	DDSP_BUILD_STATION,
+	DDSP_REMOVE_STATION,
+	DDSP_CONVERT_RAIL,
+
+	/* Road specific actions */
+	DDSP_PLACE_ROAD_X_DIR,
+	DDSP_PLACE_ROAD_Y_DIR,
+	DDSP_PLACE_AUTOROAD,
+};
+
 #endif /* VIEWPORT_TYPE_H */
--- a/src/widgets/dropdown.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/widgets/dropdown.cpp	Sun May 11 20:09:34 2008 +0000
@@ -56,7 +56,13 @@
 	delete list;
 }
 
-struct dropdown_d {
+static const Widget _dropdown_menu_widgets[] = {
+{      WWT_PANEL,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_NULL},
+{  WWT_SCROLLBAR,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{   WIDGETS_END},
+};
+
+struct DropdownWindow : Window {
 	WindowClass parent_wnd_class;
 	WindowNumber parent_wnd_num;
 	byte parent_button;
@@ -65,164 +71,157 @@
 	byte click_delay;
 	bool drag_mode;
 	int scrolling;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(dropdown_d));
-
-static const Widget _dropdown_menu_widgets[] = {
-{      WWT_PANEL,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_NULL},
-{  WWT_SCROLLBAR,   RESIZE_NONE,     0,     0, 0,     0, 0, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
-{   WIDGETS_END},
-};
-
-static int GetDropDownItem(const Window *w)
-{
-	if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0) return -1;
 
-	int y     = _cursor.pos.y - w->top - 2;
-	int width = w->widget[0].right - 3;
-	int pos   = w->vscroll.pos;
-
-	const DropDownList *list = WP(w, dropdown_d).list;
-
-	for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
-		/* Skip items that are scrolled up */
-		if (--pos >= 0) continue;
-
-		const DropDownListItem *item = *it;
-		int item_height = item->Height(width);
-
-		if (y < item_height) {
-			if (item->masked || item->String() == STR_NULL) return -1;
-			return item->result;
-		}
-
-		y -= item_height;
+	DropdownWindow(int x, int y, int width, int height, const Widget *widget) : Window(x, y, width, height, NULL, WC_DROPDOWN_MENU, widget)
+	{
 	}
 
-	return -1;
-}
-
-static void DropDownMenuWndProc(Window *w, WindowEvent *e)
-{
-	switch (e->event) {
-		case WE_PAINT: {
-			DrawWindowWidgets(w);
-
-			int x = 1;
-			int y = 2;
-
-			int sel    = WP(w, dropdown_d).selected_index;
-			int width  = w->widget[0].right - 3;
-			int height = w->widget[0].bottom;
-			int pos    = w->vscroll.pos;
-
-			DropDownList *list = WP(w, dropdown_d).list;
-
-			for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
-				const DropDownListItem *item = *it;
-				int item_height = item->Height(width);
-
-				/* Skip items that are scrolled up */
-				if (--pos >= 0) continue;
-
-				if (y + item_height < height) {
-					if (item->String() != STR_NULL) {
-						if (sel == item->result) GfxFillRect(x + 1, y, x + width, y + item_height - 1, 0);
-
-						item->Draw(x, y, width, 10, sel == item->result);
+	~DropdownWindow()
+	{
+		Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
+		if (w2 != NULL) {
+			w2->RaiseWidget(this->parent_button);
+			w2->InvalidateWidget(this->parent_button);
+		}
 
-						if (item->masked) {
-							GfxFillRect(x, y, x + width, y + item_height - 1,
-								(1 << PALETTE_MODIFIER_GREYOUT) | _colour_gradient[w->widget[0].color][5]
-							);
-						}
-					} else {
-						int c1 = _colour_gradient[w->widget[0].color][3];
-						int c2 = _colour_gradient[w->widget[0].color][7];
-
-						GfxFillRect(x + 1, y + 3, x + w->width - 5, y + 3, c1);
-						GfxFillRect(x + 1, y + 4, x + w->width - 5, y + 4, c2);
-					}
-				}
-				y += item_height;
-			}
-		} break;
+		DeleteDropDownList(this->list);
+	}
 
-		case WE_CLICK: {
-			if (e->we.click.widget != 0) break;
-			int item = GetDropDownItem(w);
-			if (item >= 0) {
-				WP(w, dropdown_d).click_delay = 4;
-				WP(w, dropdown_d).selected_index = item;
-				SetWindowDirty(w);
-			}
-		} break;
+	int GetDropDownItem()
+	{
+		if (GetWidgetFromPos(this, _cursor.pos.x - this->left, _cursor.pos.y - this->top) < 0) return -1;
 
-		case WE_TICK:
-			if (WP(w, dropdown_d).scrolling == -1) {
-				w->vscroll.pos = max(0, w->vscroll.pos - 1);
-				SetWindowDirty(w);
-			} else if (WP(w, dropdown_d).scrolling == 1) {
-				w->vscroll.pos = min(w->vscroll.count - w->vscroll.cap, w->vscroll.pos + 1);
-				SetWindowDirty(w);
-			}
-			WP(w, dropdown_d).scrolling = 0;
-			break;
+		int y     = _cursor.pos.y - this->top - 2;
+		int width = this->widget[0].right - 3;
+		int pos   = this->vscroll.pos;
 
-		case WE_MOUSELOOP: {
-			Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
-			if (w2 == NULL) {
-				delete w;
-				return;
+		const DropDownList *list = this->list;
+
+		for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
+			/* Skip items that are scrolled up */
+			if (--pos >= 0) continue;
+
+			const DropDownListItem *item = *it;
+			int item_height = item->Height(width);
+
+			if (y < item_height) {
+				if (item->masked || item->String() == STR_NULL) return -1;
+				return item->result;
 			}
 
-			if (WP(w, dropdown_d).click_delay != 0 && --WP(w,dropdown_d).click_delay == 0) {
-				WindowEvent e;
-				e.event = WE_DROPDOWN_SELECT;
-				e.we.dropdown.button = WP(w, dropdown_d).parent_button;
-				e.we.dropdown.index  = WP(w, dropdown_d).selected_index;
-				w2->HandleWindowEvent(&e);
-				delete w;
-				return;
+			y -= item_height;
+		}
+
+		return -1;
+	}
+
+	virtual void OnPaint()
+	{
+		DrawWindowWidgets(this);
+
+		int x = 1;
+		int y = 2;
+
+		int sel    = this->selected_index;
+		int width  = this->widget[0].right - 3;
+		int height = this->widget[0].bottom;
+		int pos    = this->vscroll.pos;
+
+		DropDownList *list = this->list;
+
+		for (DropDownList::const_iterator it = list->begin(); it != list->end(); ++it) {
+			const DropDownListItem *item = *it;
+			int item_height = item->Height(width);
+
+			/* Skip items that are scrolled up */
+			if (--pos >= 0) continue;
+
+			if (y + item_height < height) {
+				if (item->String() != STR_NULL) {
+					if (sel == item->result) GfxFillRect(x + 1, y, x + width, y + item_height - 1, 0);
+
+					item->Draw(x, y, width, 10, sel == item->result);
+
+					if (item->masked) {
+						GfxFillRect(x, y, x + width, y + item_height - 1,
+							(1 << PALETTE_MODIFIER_GREYOUT) | _colour_gradient[this->widget[0].color][5]
+						);
+					}
+				} else {
+					int c1 = _colour_gradient[this->widget[0].color][3];
+					int c2 = _colour_gradient[this->widget[0].color][7];
+
+					GfxFillRect(x + 1, y + 3, x + this->width - 5, y + 3, c1);
+					GfxFillRect(x + 1, y + 4, x + this->width - 5, y + 4, c2);
+				}
+			}
+			y += item_height;
+		}
+	};
+
+	virtual void OnClick(Point pt, int widget)
+	{
+		if (widget != 0) return;
+		int item = GetDropDownItem();
+		if (item >= 0) {
+			this->click_delay = 4;
+			this->selected_index = item;
+			this->SetDirty();
+		}
+	}
+
+	virtual void OnTick()
+	{
+		if (this->scrolling == -1) {
+			this->vscroll.pos = max(0, this->vscroll.pos - 1);
+			this->SetDirty();
+		} else if (this->scrolling == 1) {
+			this->vscroll.pos = min(this->vscroll.count - this->vscroll.cap, this->vscroll.pos + 1);
+			this->SetDirty();
+		}
+		this->scrolling = 0;
+	}
+
+	virtual void OnMouseLoop()
+	{
+		Window *w2 = FindWindowById(this->parent_wnd_class, this->parent_wnd_num);
+		if (w2 == NULL) {
+			delete this;
+			return;
+		}
+
+		if (this->click_delay != 0 && --this->click_delay == 0) {
+			w2->OnDropdownSelect(this->parent_button, this->selected_index);
+			delete this;
+			return;
+		}
+
+		if (this->drag_mode) {
+			int item = GetDropDownItem();
+
+			if (!_left_button_clicked) {
+				this->drag_mode = false;
+				if (item < 0) return;
+				this->click_delay = 2;
+			} else {
+				if (_cursor.pos.y <= this->top + 2) {
+					/* Cursor is above the list, set scroll up */
+					this->scrolling = -1;
+					return;
+				} else if (_cursor.pos.y >= this->top + this->height - 2) {
+					/* Cursor is below list, set scroll down */
+					this->scrolling = 1;
+					return;
+				}
+
+				if (item < 0) return;
 			}
 
-			if (WP(w, dropdown_d).drag_mode) {
-				int item = GetDropDownItem(w);
-
-				if (!_left_button_clicked) {
-					WP(w, dropdown_d).drag_mode = false;
-					if (item < 0) return;
-					WP(w, dropdown_d).click_delay = 2;
-				} else {
-					if (_cursor.pos.y <= w->top + 2) {
-						/* Cursor is above the list, set scroll up */
-						WP(w, dropdown_d).scrolling = -1;
-						return;
-					} else if (_cursor.pos.y >= w->top + w->height - 2) {
-						/* Cursor is below list, set scroll down */
-						WP(w, dropdown_d).scrolling = 1;
-						return;
-					}
-
-					if (item < 0) return;
-				}
-
-				WP(w, dropdown_d).selected_index = item;
-				SetWindowDirty(w);
-			}
-		} break;
-
-		case WE_DESTROY: {
-			Window *w2 = FindWindowById(WP(w, dropdown_d).parent_wnd_class, WP(w,dropdown_d).parent_wnd_num);
-			if (w2 != NULL) {
-				w2->RaiseWidget(WP(w, dropdown_d).parent_button);
-				w2->InvalidateWidget(WP(w, dropdown_d).parent_button);
-			}
-
-			DeleteDropDownList(WP(w, dropdown_d).list);
-		} break;
+			this->selected_index = item;
+			this->SetDirty();
+		}
 	}
-}
+};
 
 void ShowDropDownList(Window *w, DropDownList *list, int selected, int button, uint width)
 {
@@ -282,13 +281,11 @@
 
 	if (width == 0) width = wi->right - wi->left + 1;
 
-	Window *dw = AllocateWindow(
+	DropdownWindow *dw = new DropdownWindow(
 		w->left + wi->left,
 		top,
 		width,
 		height + 4,
-		DropDownMenuWndProc,
-		WC_DROPDOWN_MENU,
 		_dropdown_menu_widgets);
 
 	dw->widget[0].color = wi->color;
@@ -314,13 +311,13 @@
 	dw->desc_flags = WDF_DEF_WIDGET;
 	dw->flags4 &= ~WF_WHITE_BORDER_MASK;
 
-	WP(dw, dropdown_d).parent_wnd_class = w->window_class;
-	WP(dw, dropdown_d).parent_wnd_num   = w->window_number;
-	WP(dw, dropdown_d).parent_button    = button;
-	WP(dw, dropdown_d).list             = list;
-	WP(dw, dropdown_d).selected_index   = selected;
-	WP(dw, dropdown_d).click_delay      = 0;
-	WP(dw, dropdown_d).drag_mode        = true;
+	dw->parent_wnd_class = w->window_class;
+	dw->parent_wnd_num   = w->window_number;
+	dw->parent_button    = button;
+	dw->list             = list;
+	dw->selected_index   = selected;
+	dw->click_delay      = 0;
+	dw->drag_mode        = true;
 }
 
 void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask, uint32 hidden_mask, uint width)
@@ -360,9 +357,10 @@
 	FOR_ALL_WINDOWS(wz) {
 		if ((*wz)->window_class != WC_DROPDOWN_MENU) continue;
 
-		if (pw->window_class == WP(*wz, dropdown_d).parent_wnd_class &&
-				pw->window_number == WP(*wz, dropdown_d).parent_wnd_num) {
-			delete *wz;
+		DropdownWindow *dw = dynamic_cast<DropdownWindow*>(*wz);
+		if (pw->window_class == dw->parent_wnd_class &&
+				pw->window_number == dw->parent_wnd_num) {
+			delete dw;
 			break;
 		}
 	}
--- a/src/win32.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/win32.cpp	Sun May 11 20:09:34 2008 +0000
@@ -206,16 +206,15 @@
 		HMODULE modules[100];
 		DWORD needed;
 		BOOL res;
-		int count, i;
 
 		HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
 		if (proc != NULL) {
 			res = EnumProcessModules(proc, modules, sizeof(modules), &needed);
 			CloseHandle(proc);
 			if (res) {
-				count = min(needed / sizeof(HMODULE), lengthof(modules));
+				size_t count = min(needed / sizeof(HMODULE), lengthof(modules));
 
-				for (i = 0; i != count; i++) output = PrintModuleInfo(output, modules[i]);
+				for (size_t i = 0; i != count; i++) output = PrintModuleInfo(output, modules[i]);
 				return output;
 			}
 		}
@@ -1091,7 +1090,7 @@
 	const char *ptr;
 
 	WChar c;
-	uint16 width, length;
+	size_t width, length;
 
 	if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
 		OpenClipboard(NULL);
@@ -1124,7 +1123,7 @@
 		if (!IsPrintable(c)) break;
 
 		size_t len = Utf8CharLen(c);
-		if (tb->length + length >= tb->maxlength - (uint16)len) break;
+		if (tb->length + length >= tb->maxlength - len) break;
 
 		byte charwidth = GetCharacterWidth(FS_NORMAL, c);
 		if (tb->maxwidth != 0 && width + tb->width + charwidth > tb->maxwidth) break;
@@ -1254,7 +1253,7 @@
  * @return pointer to utf8_buf. If conversion fails the string is of zero-length */
 char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen)
 {
-	int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, buflen, NULL, NULL);
+	int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (int)buflen, NULL, NULL);
 	if (len == 0) {
 		DEBUG(misc, 0, "[utf8] W2M error converting wide-string. Errno %d", GetLastError());
 		utf8_buf[0] = '\0';
@@ -1273,7 +1272,7 @@
  * @return pointer to utf16_buf. If conversion fails the string is of zero-length */
 wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen)
 {
-	int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, utf16_buf, buflen);
+	int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, utf16_buf, (int)buflen);
 	if (len == 0) {
 		DEBUG(misc, 0, "[utf8] M2W error converting '%s'. Errno %d", name, GetLastError());
 		utf16_buf[0] = '\0';
--- a/src/window.cpp	Wed May 07 21:09:51 2008 +0000
+++ b/src/window.cpp	Sun May 11 20:09:34 2008 +0000
@@ -44,13 +44,16 @@
 
 bool _scrolling_scrollbar;
 bool _scrolling_viewport;
-bool _popup_menu_active;
 
 byte _special_mouse_mode;
 
 
 /**
- * Call the window event handler for handling event \a e
+ * Call the window event handler for handling event \a e.
+ * This is a temporary helper functions that will be removed
+ * once all windows that still rely on WindowEvent and
+ * WindowEventCodes have been rewritten to use the 'OnXXX'
+ * event handlers.
  * @param e Window event to handle
  */
 void Window::HandleWindowEvent(WindowEvent *e)
@@ -58,6 +61,208 @@
 	if (wndproc != NULL) wndproc(this, e);
 }
 
+void Window::OnPaint()
+{
+	WindowEvent e;
+	e.event = WE_PAINT;
+	this->HandleWindowEvent(&e);
+}
+
+bool Window::OnKeyPress(uint16 key, uint16 keycode)
+{
+	WindowEvent e;
+	e.event = WE_KEYPRESS;
+	e.we.keypress.key     = key;
+	e.we.keypress.keycode = keycode;
+	e.we.keypress.cont    = true;
+	this->HandleWindowEvent(&e);
+
+	return e.we.keypress.cont;
+}
+
+bool Window::OnCTRLStateChange()
+{
+	WindowEvent e;
+	e.event = WE_CTRL_CHANGED;
+	e.we.ctrl.cont = true;
+	this->HandleWindowEvent(&e);
+
+	return e.we.ctrl.cont;
+}
+
+void Window::OnClick(Point pt, int widget)
+{
+	WindowEvent e;
+	e.event = WE_CLICK;
+	e.we.click.pt     = pt;
+	e.we.click.widget = widget;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnDoubleClick(Point pt, int widget)
+{
+	WindowEvent e;
+	e.event = WE_DOUBLE_CLICK;
+	e.we.click.pt     = pt;
+	e.we.click.widget = widget;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnRightClick(Point pt, int widget)
+{
+	WindowEvent e;
+	e.event = WE_RCLICK;
+	e.we.click.pt     = pt;
+	e.we.click.widget = widget;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnDragDrop(Point pt, int widget)
+{
+	WindowEvent e;
+	e.event = WE_DRAGDROP;
+	e.we.click.pt     = pt;
+	e.we.click.widget = widget;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnScroll(Point delta)
+{
+	WindowEvent e;
+	e.event = WE_SCROLL;
+	e.we.scroll.delta = delta;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnMouseOver(Point pt, int widget)
+{
+	WindowEvent e;
+	e.event = WE_MOUSEOVER;
+	e.we.click.pt     = pt;
+	e.we.click.widget = widget;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnMouseWheel(int wheel)
+{
+	WindowEvent e;
+	e.event = WE_MOUSEWHEEL;
+	e.we.wheel.wheel = wheel;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnMouseLoop()
+{
+	WindowEvent e;
+	e.event = WE_MOUSELOOP;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnTick()
+{
+	WindowEvent e;
+	e.event = WE_TICK;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnHundredthTick()
+{
+	WindowEvent e;
+	e.event = WE_100_TICKS;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnTimeout()
+{
+	WindowEvent e;
+	e.event = WE_TIMEOUT;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnResize(Point new_size, Point delta)
+{
+	WindowEvent e;
+	e.event = WE_RESIZE;
+	e.we.sizing.size = new_size;
+	e.we.sizing.diff = delta;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnDropdownSelect(int widget, int index)
+{
+	WindowEvent e;
+	e.event = WE_DROPDOWN_SELECT;
+	e.we.dropdown.button = widget;
+	e.we.dropdown.index  = index;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnQueryTextFinished(char *str)
+{
+	WindowEvent e;
+	e.event = WE_ON_EDIT_TEXT;
+	e.we.edittext.str = str;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnInvalidateData(int data)
+{
+	WindowEvent e;
+	e.event = WE_INVALIDATE_DATA;
+	e.we.invalidate.data = data;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnPlaceObject(Point pt, TileIndex tile)
+{
+	WindowEvent e;
+	e.event = WE_PLACE_OBJ;
+	e.we.place.pt   = pt;
+	e.we.place.tile = tile;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnPlaceObjectAbort()
+{
+	WindowEvent e;
+	e.event = WE_ABORT_PLACE_OBJ;
+	this->HandleWindowEvent(&e);
+}
+
+
+void Window::OnPlaceDrag(ViewportPlaceMethod select_method, byte select_proc, Point pt)
+{
+	WindowEvent e;
+	e.event = WE_PLACE_DRAG;
+	e.we.place.select_method = select_method;
+	e.we.place.select_proc   = select_proc;
+	e.we.place.pt            = pt;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnPlaceMouseUp(ViewportPlaceMethod select_method, byte select_proc, Point pt, TileIndex start_tile, TileIndex end_tile)
+{
+	WindowEvent e;
+	e.event = WE_PLACE_MOUSEUP;
+	e.we.place.select_method = select_method;
+	e.we.place.select_proc   = select_proc;
+	e.we.place.pt            = pt;
+	e.we.place.tile          = end_tile;
+	e.we.place.starttile     = start_tile;
+	this->HandleWindowEvent(&e);
+}
+
+void Window::OnPlacePresize(Point pt, TileIndex tile)
+{
+	WindowEvent e;
+	e.event = WE_PLACE_PRESIZE;
+	e.we.place.pt   = pt;
+	e.we.place.tile = tile;
+	this->HandleWindowEvent(&e);
+}
+
+
+
 void CDECL Window::SetWidgetsDisabledState(bool disab_stat, int widgets, ...)
 {
 	va_list wdg_list;
@@ -139,19 +344,15 @@
  */
 static void DispatchLeftClickEvent(Window *w, int x, int y, bool double_click)
 {
-	WindowEvent e;
-	e.we.click.pt.x = x;
-	e.we.click.pt.y = y;
-	e.event = double_click ? WE_DOUBLE_CLICK : WE_CLICK;
-
+	int widget = 0;
 	if (w->desc_flags & WDF_DEF_WIDGET) {
-		e.we.click.widget = GetWidgetFromPos(w, x, y);
-		if (e.we.click.widget < 0) return; // exit if clicked outside of widgets
+		widget = GetWidgetFromPos(w, x, y);
+		if (widget < 0) return; // exit if clicked outside of widgets
 
 		/* don't allow any interaction if the button has been disabled */
-		if (w->IsWidgetDisabled(e.we.click.widget)) return;
+		if (w->IsWidgetDisabled(widget)) return;
 
-		const Widget *wi = &w->widget[e.we.click.widget];
+		const Widget *wi = &w->widget[widget];
 
 		if (wi->type & WWB_MASK) {
 			/* special widget handling for buttons*/
@@ -159,20 +360,20 @@
 				case WWT_PANEL   | WWB_PUSHBUTTON: /* WWT_PUSHBTN */
 				case WWT_IMGBTN  | WWB_PUSHBUTTON: /* WWT_PUSHIMGBTN */
 				case WWT_TEXTBTN | WWB_PUSHBUTTON: /* WWT_PUSHTXTBTN */
-					w->HandleButtonClick(e.we.click.widget);
+					w->HandleButtonClick(widget);
 					break;
 			}
 		} else if (wi->type == WWT_SCROLLBAR || wi->type == WWT_SCROLL2BAR || wi->type == WWT_HSCROLLBAR) {
-			ScrollbarClickHandler(w, wi, e.we.click.pt.x, e.we.click.pt.y);
+			ScrollbarClickHandler(w, wi, x, y);
 		}
 
 		if (w->desc_flags & WDF_STD_BTN) {
-			if (e.we.click.widget == 0) { /* 'X' */
+			if (widget == 0) { /* 'X' */
 				delete w;
 				return;
 			}
 
-			if (e.we.click.widget == 1) { /* 'Title bar' */
+			if (widget == 1) { /* 'Title bar' */
 				StartWindowDrag(w);
 				return;
 			}
@@ -180,18 +381,24 @@
 
 		if (w->desc_flags & WDF_RESIZABLE && wi->type == WWT_RESIZEBOX) {
 			StartWindowSizing(w);
-			w->InvalidateWidget(e.we.click.widget);
+			w->InvalidateWidget(widget);
 			return;
 		}
 
 		if (w->desc_flags & WDF_STICKY_BUTTON && wi->type == WWT_STICKYBOX) {
 			w->flags4 ^= WF_STICKY;
-			w->InvalidateWidget(e.we.click.widget);
+			w->InvalidateWidget(widget);
 			return;
 		}
 	}
 
-	w->HandleWindowEvent(&e);
+	Point pt = { x, y };
+
+	if (double_click) {
+		w->OnDoubleClick(pt, widget);
+	} else {
+		w->OnClick(pt, widget);
+	}
 }
 
 /**
@@ -202,23 +409,21 @@
  */
 static void DispatchRightClickEvent(Window *w, int x, int y)
 {
-	WindowEvent e;
+	int widget = 0;
 
 	/* default tooltips handler? */
 	if (w->desc_flags & WDF_STD_TOOLTIPS) {
-		e.we.click.widget = GetWidgetFromPos(w, x, y);
-		if (e.we.click.widget < 0) return; // exit if clicked outside of widgets
+		widget = GetWidgetFromPos(w, x, y);
+		if (widget < 0) return; // exit if clicked outside of widgets
 
-		if (w->widget[e.we.click.widget].tooltips != 0) {
-			GuiShowTooltips(w->widget[e.we.click.widget].tooltips);
+		if (w->widget[widget].tooltips != 0) {
+			GuiShowTooltips(w->widget[widget].tooltips);
 			return;
 		}
 	}
 
-	e.event = WE_RCLICK;
-	e.we.click.pt.x = x;
-	e.we.click.pt.y = y;
-	w->HandleWindowEvent(&e);
+	Point pt = { x, y };
+	w->OnRightClick(pt, widget);
 }
 
 /**
@@ -316,7 +521,7 @@
 	dp->pitch = _screen.pitch;
 	dp->dst_ptr = BlitterFactoryBase::GetCurrentBlitter()->MoveTo(_screen.dst_ptr, left, top);
 	dp->zoom = ZOOM_LVL_NORMAL;
-	CallWindowEventNP(*wz, WE_PAINT);
+	(*wz)->OnPaint();
 }
 
 /**
@@ -346,20 +551,6 @@
 }
 
 /**
- * Dispatch an event to a possibly non-existing window.
- * If the window pointer w is \c NULL, the event is not dispatched
- * @param w Window to dispatch the event to, may be \c NULL
- * @param event Event to dispatch
- */
-void CallWindowEventNP(Window *w, int event)
-{
-	WindowEvent e;
-
-	e.event = event;
-	w->HandleWindowEvent(&e);
-}
-
-/**
  * Mark entire window as dirty (in need of re-paint)
  * @param w Window to redraw
  * @ingroup dirty
@@ -438,7 +629,9 @@
 	/* Delete any children a window might have in a head-recursive manner */
 	delete FindChildWindow(this);
 
-	CallWindowEventNP(this, WE_DESTROY);
+	WindowEvent e;
+	e.event = WE_DESTROY;
+	this->HandleWindowEvent(&e);
 	if (this->viewport != NULL) DeleteWindowViewport(this);
 
 	this->SetDirty();
@@ -656,10 +849,18 @@
 	return (w->original_widget == widget);
 }
 
-/** Copies 'widget' to 'w->widget' to allow for resizable windows
+/**
+ * Assign widgets to a new window by initialising its widget pointers, and by
+ * copying the widget array \a widget to \c w->widget to allow for resizable
+ * windows.
  * @param w Window on which to attach the widget array
- * @param widget pointer of widget array to fill the window with */
-void AssignWidgetToWindow(Window *w, const Widget *widget)
+ * @param widget pointer of widget array to fill the window with
+ *
+ * @post \c w->widget points to allocated memory and contains the copied widget array except for the terminating widget,
+ *       \c w->original_widget points to the original widgets,
+ *       \c w->widget_count contains number of widgets in the allocated memory.
+ */
+static void AssignWidgetToWindow(Window *w, const Widget *widget)
 {
 	w->original_widget = widget;
 
@@ -668,7 +869,7 @@
 
 		for (const Widget *wi = widget; wi->type != WWT_LAST; wi++) index++;
 
-		w->widget = ReallocT(w->widget, index);
+		w->widget = MallocT<Widget>(index);
 		memcpy(w->widget, widget, sizeof(*w->widget) * index);
 		w->widget_count = index - 1;
 	} else {
@@ -677,88 +878,88 @@
 	}
 }
 
-/** Open a new window.
- * This function is called from AllocateWindow() or AllocateWindowDesc()
+/**
+ * Initializes a new Window.
+ * This function is called the constructors.
  * See descriptions for those functions for usage
- * See AllocateWindow() for description of arguments.
  * Only addition here is window_number, which is the window_number being assigned to the new window
  * @param x offset in pixels from the left of the screen
  * @param y offset in pixels from the top of the screen
  * @param min_width minimum width in pixels of the window
  * @param min_height minimum height in pixels of the window
- * @param def_width default width in pixels of the window
- * @param def_height default height in pixels of the window
  * @param *proc see WindowProc function to call when any messages/updates happen to the window
  * @param cls see WindowClass class of the window, used for identification and grouping
  * @param *widget see Widget pointer to the window layout and various elements
  * @param window_number number being assigned to the new window
  * @param data the data to be given during the WE_CREATE message
  * @return Window pointer of the newly created window */
-static Window *LocalAllocateWindow(int x, int y, int min_width, int min_height, int def_width, int def_height,
+void Window::Initialize(int x, int y, int min_width, int min_height,
 				WindowProc *proc, WindowClass cls, const Widget *widget, int window_number, void *data)
 {
-	Window *w;
-
 	/* We have run out of windows, close one and use that as the place for our new one */
 	if (_last_z_window == endof(_z_windows)) {
-		w = FindDeletableWindow();
+		Window *w = FindDeletableWindow();
 		if (w == NULL) w = ForceFindDeletableWindow();
 		delete w;
 	}
 
-	w = new Window(proc);
-
 	/* Set up window properties */
-	w->window_class = cls;
-	w->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border
-	w->caption_color = 0xFF;
-	w->left = x;
-	w->top = y;
-	w->width = min_width;
-	w->height = min_height;
-	AssignWidgetToWindow(w, widget);
-	w->resize.width = min_width;
-	w->resize.height = min_height;
-	w->resize.step_width = 1;
-	w->resize.step_height = 1;
-	w->window_number = window_number;
-
-	{
-		Window **wz = _last_z_window;
+	this->window_class = cls;
+	this->flags4 = WF_WHITE_BORDER_MASK; // just opened windows have a white border
+	this->caption_color = 0xFF;
+	this->left = x;
+	this->top = y;
+	this->width = min_width;
+	this->height = min_height;
+	this->wndproc = proc;
+	AssignWidgetToWindow(this, widget);
+	this->resize.width = min_width;
+	this->resize.height = min_height;
+	this->resize.step_width = 1;
+	this->resize.step_height = 1;
+	this->window_number = window_number;
 
-		/* Hacky way of specifying always-on-top windows. These windows are
-		 * always above other windows because they are moved below them.
-		 * status-bar is above news-window because it has been created earlier.
-		 * Also, as the chat-window is excluded from this, it will always be
-		 * the last window, thus always on top.
-		 * XXX - Yes, ugly, probably needs something like w->always_on_top flag
-		 * to implement correctly, but even then you need some kind of distinction
-		 * between on-top of chat/news and status windows, because these conflict */
-		if (wz != _z_windows && w->window_class != WC_SEND_NETWORK_MSG && w->window_class != WC_HIGHSCORE && w->window_class != WC_ENDSCREEN) {
-			if (FindWindowById(WC_MAIN_TOOLBAR, 0)     != NULL) wz--;
-			if (FindWindowById(WC_STATUS_BAR, 0)       != NULL) wz--;
-			if (FindWindowById(WC_NEWS_WINDOW, 0)      != NULL) wz--;
-			if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--;
+	/* Hacky way of specifying always-on-top windows. These windows are
+		* always above other windows because they are moved below them.
+		* status-bar is above news-window because it has been created earlier.
+		* Also, as the chat-window is excluded from this, it will always be
+		* the last window, thus always on top.
+		* XXX - Yes, ugly, probably needs something like w->always_on_top flag
+		* to implement correctly, but even then you need some kind of distinction
+		* between on-top of chat/news and status windows, because these conflict */
+	Window **wz = _last_z_window;
+	if (wz != _z_windows && this->window_class != WC_SEND_NETWORK_MSG && this->window_class != WC_HIGHSCORE && this->window_class != WC_ENDSCREEN) {
+		if (FindWindowById(WC_MAIN_TOOLBAR, 0)     != NULL) wz--;
+		if (FindWindowById(WC_STATUS_BAR, 0)       != NULL) wz--;
+		if (FindWindowById(WC_NEWS_WINDOW, 0)      != NULL) wz--;
+		if (FindWindowById(WC_SEND_NETWORK_MSG, 0) != NULL) wz--;
 
-			assert(wz >= _z_windows);
-			if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz);
-		}
+		assert(wz >= _z_windows);
+		if (wz != _last_z_window) memmove(wz + 1, wz, (byte*)_last_z_window - (byte*)wz);
+	}
 
-		*wz = w;
-		_last_z_window++;
-	}
+	*wz = this;
+	_last_z_window++;
 
 	WindowEvent e;
 	e.event = WE_CREATE;
 	e.we.create.data = data;
-	w->HandleWindowEvent(&e);
+	this->HandleWindowEvent(&e);
+}
 
+/**
+ * Find a nice spot for this window and resize it towards the default size.
+ * @param def_width default width in pixels of the window
+ * @param def_height default height in pixels of the window
+ */
+void Window::FindWindowPlacementAndResize(int def_width, int def_height)
+{
 	/* Try to make windows smaller when our window is too small.
 	 * w->(width|height) is normally the same as min_(width|height),
 	 * but this way the GUIs can be made a little more dynamic;
 	 * one can use the same spec for multiple windows and those
 	 * can then determine the real minimum size of the window. */
-	if (w->width != def_width || w->height != def_height) {
+	if (this->width != def_width || this->height != def_height) {
 		/* Think about the overlapping toolbars when determining the minimum window size */
 		int free_height = _screen.height;
 		const Window *wt = FindWindowById(WC_STATUS_BAR, 0);
@@ -766,45 +967,48 @@
 		wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
 		if (wt != NULL) free_height -= wt->height;
 
-		int enlarge_x = max(min(def_width  - w->width,  _screen.width - w->width),  0);
-		int enlarge_y = max(min(def_height - w->height, free_height   - w->height), 0);
+		int enlarge_x = max(min(def_width  - this->width,  _screen.width - this->width),  0);
+		int enlarge_y = max(min(def_height - this->height, free_height   - this->height), 0);
 
 		/* X and Y has to go by step.. calculate it.
 		 * The cast to int is necessary else x/y are implicitly casted to
 		 * unsigned int, which won't work. */
-		if (w->resize.step_width  > 1) enlarge_x -= enlarge_x % (int)w->resize.step_width;
-		if (w->resize.step_height > 1) enlarge_y -= enlarge_y % (int)w->resize.step_height;
-
-		ResizeWindow(w, enlarge_x, enlarge_y);
+		if (this->resize.step_width  > 1) enlarge_x -= enlarge_x % (int)this->resize.step_width;
+		if (this->resize.step_height > 1) enlarge_y -= enlarge_y % (int)this->resize.step_height;
 
-		WindowEvent e;
-		e.event = WE_RESIZE;
-		e.we.sizing.size.x = w->width;
-		e.we.sizing.size.y = w->height;
-		e.we.sizing.diff.x = enlarge_x;
-		e.we.sizing.diff.y = enlarge_y;
-		w->HandleWindowEvent(&e);
+		ResizeWindow(this, enlarge_x, enlarge_y);
+
+		Point size;
+		Point diff;
+		size.x = this->width;
+		size.y = this->height;
+		diff.x = enlarge_x;
+		diff.y = enlarge_y;
+		this->OnResize(size, diff);
 	}
 
-	int nx = w->left;
-	int ny = w->top;
+	int nx = this->left;
+	int ny = this->top;
 
-	if (nx + w->width > _screen.width) nx -= (nx + w->width - _screen.width);
+	if (nx + this->width > _screen.width) nx -= (nx + this->width - _screen.width);
 
 	const Window *wt = FindWindowById(WC_MAIN_TOOLBAR, 0);
-	ny = max(ny, (wt == NULL || w == wt || y == 0) ? 0 : wt->height);
+	ny = max(ny, (wt == NULL || this == wt || this->top == 0) ? 0 : wt->height);
 	nx = max(nx, 0);
 
-	if (w->viewport != NULL) {
-		w->viewport->left += nx - w->left;
-		w->viewport->top  += ny - w->top;
+	if (this->viewport != NULL) {
+		this->viewport->left += nx - this->left;
+		this->viewport->top  += ny - this->top;
 	}
-	w->left = nx;
-	w->top = ny;
+	this->left = nx;
+	this->top = ny;
 
-	w->SetDirty();
+	this->SetDirty();
+}
 
-	return w;
+void Window::FindWindowPlacementAndResize(const WindowDesc *desc)
+{
+	this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
 }
 
 /**
@@ -821,10 +1025,11 @@
  * @param *widget see Widget pointer to the window layout and various elements
  * @return Window pointer of the newly created window
  */
-Window *AllocateWindow(int x, int y, int width, int height,
-			WindowProc *proc, WindowClass cls, const Widget *widget, void *data)
+Window::Window(int x, int y, int width, int height, WindowProc *proc, WindowClass cls, const Widget *widget, void *data)
 {
-	return LocalAllocateWindow(x, y, width, height, width, height, proc, cls, widget, 0, data);
+	this->Initialize(x, y, width, height, proc, cls, widget, 0, data);
+
+	if (proc != NULL) this->FindWindowPlacementAndResize(width, height);
 }
 
 
@@ -1014,37 +1219,13 @@
  *
  * @return Window pointer of the newly created window
  */
-static Window *LocalAllocateWindowDesc(const WindowDesc *desc, int window_number, void *data)
+Window::Window(const WindowDesc *desc, void *data, WindowNumber window_number)
 {
 	Point pt = LocalGetWindowPlacement(desc, window_number);
-	Window *w = LocalAllocateWindow(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->default_width, desc->default_height, desc->proc, desc->cls, desc->widgets, window_number, data);
-	w->desc_flags = desc->flags;
-
-	return w;
-}
+	this->Initialize(pt.x, pt.y, desc->minimum_width, desc->minimum_height, desc->proc, desc->cls, desc->widgets, window_number, data);
+	this->desc_flags = desc->flags;
 
-/**
- * Open a new window.
- * @param *desc The pointer to the WindowDesc to be created
- * @param data arbitrary data that is send with the WE_CREATE message
- * @return Window pointer of the newly created window
- */
-Window *AllocateWindowDesc(const WindowDesc *desc, void *data)
-{
-	return LocalAllocateWindowDesc(desc, 0, data);
-}
-
-/**
- * Open a new window.
- * @param *desc The pointer to the WindowDesc to be created
- * @param window_number the window number of the new window
- * @param data arbitrary data that is send with the WE_CREATE message
- * @return see Window pointer of the newly created window
- */
-Window *AllocateWindowDescFront(const WindowDesc *desc, int window_number, void *data)
-{
-	if (BringWindowToFrontById(desc->cls, window_number)) return NULL;
-	return LocalAllocateWindowDesc(desc, window_number, data);
+	if (desc->proc != NULL) this->FindWindowPlacementAndResize(desc->default_width, desc->default_height);
 }
 
 /** Do a search for a window at specific coordinates. For this we start
@@ -1108,14 +1289,14 @@
 			w->flags4 &= ~(WF_SCROLL_DOWN | WF_SCROLL_UP);
 			w->SetDirty();
 		}
-		CallWindowEventNP(w, WE_MOUSELOOP);
+		w->OnMouseLoop();
 	}
 
 	for (wz = _last_z_window; wz != _z_windows;) {
 		Window *w = *--wz;
 
 		if (w->flags4 & WF_TIMEOUT_MASK && !(--w->flags4 & WF_TIMEOUT_MASK)) {
-			CallWindowEventNP(w, WE_TIMEOUT);
+			w->OnTimeout();
 			if (w->desc_flags & WDF_UNCLICK_BUTTONS) w->RaiseButtons();
 		}
 	}
@@ -1133,15 +1314,13 @@
 	Window *w = GetCallbackWnd();
 	if (w == NULL) return;
 
-	WindowEvent e;
-	e.we.place.pt = GetTileBelowCursor();
-	if (e.we.place.pt.x == -1) {
+	Point pt = GetTileBelowCursor();
+	if (pt.x == -1) {
 		_thd.selend.x = -1;
 		return;
 	}
-	e.we.place.tile = TileVirtXY(e.we.place.pt.x, e.we.place.pt.y);
-	e.event = WE_PLACE_PRESIZE;
-	w->HandleWindowEvent(&e);
+
+	w->OnPlacePresize(pt, TileVirtXY(pt.x, pt.y));
 }
 
 static bool HandleDragDrop()
@@ -1153,12 +1332,10 @@
 
 	if (w != NULL) {
 		/* send an event in client coordinates. */
-		WindowEvent e;
-		e.event = WE_DRAGDROP;
-		e.we.dragdrop.pt.x = _cursor.pos.x - w->left;
-		e.we.dragdrop.pt.y = _cursor.pos.y - w->top;
-		e.we.dragdrop.widget = GetWidgetFromPos(w, e.we.dragdrop.pt.x, e.we.dragdrop.pt.y);
-		w->HandleWindowEvent(&e);
+		Point pt;
+		pt.x = _cursor.pos.x - w->left;
+		pt.y = _cursor.pos.y - w->top;
+		w->OnDragDrop(pt, GetWidgetFromPos(w, pt.x, pt.y));
 	}
 
 	ResetObjectToPlace();
@@ -1166,31 +1343,6 @@
 	return false;
 }
 
-static bool HandlePopupMenu()
-{
-	if (!_popup_menu_active) return true;
-
-	Window *w = FindWindowById(WC_TOOLBAR_MENU, 0);
-	if (w == NULL) {
-		_popup_menu_active = false;
-		return false;
-	}
-
-	WindowEvent e;
-	if (_left_button_down) {
-		e.event = WE_POPUPMENU_OVER;
-		e.we.popupmenu.pt = _cursor.pos;
-	} else {
-		_popup_menu_active = false;
-		e.event = WE_POPUPMENU_SELECT;
-		e.we.popupmenu.pt = _cursor.pos;
-	}
-
-	w->HandleWindowEvent(&e);
-
-	return false;
-}
-
 static bool HandleMouseOver()
 {
 	Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
@@ -1198,11 +1350,8 @@
 	/* We changed window, put a MOUSEOVER event to the last window */
 	if (_mouseover_last_w != NULL && _mouseover_last_w != w) {
 		/* Reset mouse-over coordinates of previous window */
-		WindowEvent e;
-		e.event = WE_MOUSEOVER;
-		e.we.mouseover.pt.x = -1;
-		e.we.mouseover.pt.y = -1;
-		_mouseover_last_w->HandleWindowEvent(&e);
+		Point pt = { -1, -1 };
+		_mouseover_last_w->OnMouseOver(pt, 0);
 	}
 
 	/* _mouseover_last_w will get reset when the window is deleted, see DeleteWindow() */
@@ -1210,14 +1359,12 @@
 
 	if (w != NULL) {
 		/* send an event in client coordinates. */
-		WindowEvent e;
-		e.event = WE_MOUSEOVER;
-		e.we.mouseover.pt.x = _cursor.pos.x - w->left;
-		e.we.mouseover.pt.y = _cursor.pos.y - w->top;
+		Point pt = { _cursor.pos.x - w->left, _cursor.pos.y - w->top };
+		int widget = 0;
 		if (w->widget != NULL) {
-			e.we.mouseover.widget = GetWidgetFromPos(w, e.we.mouseover.pt.x, e.we.mouseover.pt.y);
+			widget = GetWidgetFromPos(w, pt.x, pt.y);
 		}
-		w->HandleWindowEvent(&e);
+		w->OnMouseOver(pt, widget);
 	}
 
 	/* Mouseover never stops execution */
@@ -1421,7 +1568,6 @@
 			w->SetDirty();
 			return false;
 		} else if (w->flags4 & WF_SIZING) {
-			WindowEvent e;
 			int x, y;
 
 			/* Stop the sizing if the left mouse button was released */
@@ -1458,12 +1604,13 @@
 			/* ResizeWindow sets both pre- and after-size to dirty for redrawal */
 			ResizeWindow(w, x, y);
 
-			e.event = WE_RESIZE;
-			e.we.sizing.size.x = x + w->width;
-			e.we.sizing.size.y = y + w->height;
-			e.we.sizing.diff.x = x;
-			e.we.sizing.diff.y = y;
-			w->HandleWindowEvent(&e);
+			Point size;
+			Point diff;
+			size.x = x + w->width;
+			size.y = y + w->height;
+			diff.x = x;
+			diff.y = y;
+			w->OnResize(size, diff);
 			return false;
 		}
 	}
@@ -1554,8 +1701,6 @@
 
 static bool HandleViewportScroll()
 {
-	WindowEvent e;
-
 	bool scrollwheel_scrolling = _patches.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0);
 
 	if (!_scrolling_viewport) return true;
@@ -1568,32 +1713,32 @@
 		return true;
 	}
 
-	if (WP(w, vp_d).follow_vehicle != INVALID_VEHICLE && w == FindWindowById(WC_MAIN_WINDOW, 0)) {
+	if (w == FindWindowById(WC_MAIN_WINDOW, 0) && w->viewport->follow_vehicle != INVALID_VEHICLE) {
 		/* If the main window is following a vehicle, then first let go of it! */
-		const Vehicle *veh = GetVehicle(WP(w, vp_d).follow_vehicle);
+		const Vehicle *veh = GetVehicle(w->viewport->follow_vehicle);
 		ScrollMainWindowTo(veh->x_pos, veh->y_pos, true); /* This also resets follow_vehicle */
 		return true;
 	}
 
+	Point delta;
 	if (_patches.reverse_scroll) {
-		e.we.scroll.delta.x = -_cursor.delta.x;
-		e.we.scroll.delta.y = -_cursor.delta.y;
+		delta.x = -_cursor.delta.x;
+		delta.y = -_cursor.delta.y;
 	} else {
-		e.we.scroll.delta.x = _cursor.delta.x;
-		e.we.scroll.delta.y = _cursor.delta.y;
+		delta.x = _cursor.delta.x;
+		delta.y = _cursor.delta.y;
 	}
 
 	if (scrollwheel_scrolling) {
 		/* We are using scrollwheels for scrolling */
-		e.we.scroll.delta.x = _cursor.h_wheel;
-		e.we.scroll.delta.y = _cursor.v_wheel;
+		delta.x = _cursor.h_wheel;
+		delta.y = _cursor.v_wheel;
 		_cursor.v_wheel = 0;
 		_cursor.h_wheel = 0;
 	}
 
 	/* Create a scroll-event and send it to the window */
-	e.event = WE_SCROLL;
-	w->HandleWindowEvent(&e);
+	w->OnScroll(delta);
 
 	_cursor.delta.x = 0;
 	_cursor.delta.y = 0;
@@ -1652,59 +1797,11 @@
 	return true;
 }
 
-/** Send a message from one window to another. The receiving window is found by
- * @param w Window pointer pointing to the other window
- * @param msg Specifies the message to be sent
- * @param wparam Specifies additional message-specific information
- * @param lparam Specifies additional message-specific information
- */
-static void SendWindowMessageW(Window *w, uint msg, uint wparam, uint lparam)
-{
-	WindowEvent e;
-
-	e.event             = WE_MESSAGE;
-	e.we.message.msg    = msg;
-	e.we.message.wparam = wparam;
-	e.we.message.lparam = lparam;
-
-	w->HandleWindowEvent(&e);
-}
-
-/** Send a message from one window to another. The receiving window is found by
- * @param wnd_class see WindowClass class AND
- * @param wnd_num see WindowNumber number, mostly 0
- * @param msg Specifies the message to be sent
- * @param wparam Specifies additional message-specific information
- * @param lparam Specifies additional message-specific information
+/** Handle keyboard input.
+ * @param raw_key Lower 8 bits contain the ASCII character, the higher 16 bits the keycode
  */
-void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, int msg, int wparam, int lparam)
+void HandleKeypress(uint32 raw_key)
 {
-	Window *w = FindWindowById(wnd_class, wnd_num);
-	if (w != NULL) SendWindowMessageW(w, msg, wparam, lparam);
-}
-
-/** Send a message from one window to another. The message will be sent
- * to ALL windows of the windowclass specified in the first parameter
- * @param wnd_class see WindowClass class
- * @param msg Specifies the message to be sent
- * @param wparam Specifies additional message-specific information
- * @param lparam Specifies additional message-specific information
- */
-void SendWindowMessageClass(WindowClass wnd_class, int msg, int wparam, int lparam)
-{
-	Window* const *wz;
-
-	FOR_ALL_WINDOWS(wz) {
-		if ((*wz)->window_class == wnd_class) SendWindowMessageW(*wz, msg, wparam, lparam);
-	}
-}
-
-/** Handle keyboard input.
- * @param key Lower 8 bits contain the ASCII character, the higher 16 bits the keycode
- */
-void HandleKeypress(uint32 key)
-{
-	WindowEvent e;
 	/* Stores if a window with a textfield for typing is open
 	 * If this is the case, keypress events are only passed to windows with text fields and
 	 * to thein this main toolbar. */
@@ -1722,10 +1819,8 @@
 	if (!IsGeneratingWorld()) _current_player = _local_player;
 
 	/* Setup event */
-	e.event = WE_KEYPRESS;
-	e.we.keypress.key     = GB(key,  0, 16);
-	e.we.keypress.keycode = GB(key, 16, 16);
-	e.we.keypress.cont = true;
+	uint16 key     = GB(raw_key,  0, 16);
+	uint16 keycode = GB(raw_key, 16, 16);
 
 	/*
 	 * The Unicode standard defines an area called the private use area. Code points in this
@@ -1734,12 +1829,12 @@
 	 * on a system running OS X. We don't want these keys to show up in text fields and such,
 	 * and thus we have to clear the unicode character when we encounter such a key.
 	 */
-	if (e.we.keypress.key >= 0xE000 && e.we.keypress.key <= 0xF8FF) e.we.keypress.key = 0;
+	if (key >= 0xE000 && key <= 0xF8FF) key = 0;
 
 	/*
 	 * If both key and keycode is zero, we don't bother to process the event.
 	 */
-	if (e.we.keypress.key == 0 && e.we.keypress.keycode == 0) return;
+	if (key == 0 && keycode == 0) return;
 
 	/* check if we have a query string window open before allowing hotkeys */
 	if (FindWindowById(WC_QUERY_STRING,            0) != NULL ||
@@ -1765,15 +1860,13 @@
 				w->window_class != WC_COMPANY_PASSWORD_WINDOW) {
 			continue;
 		}
-		w->HandleWindowEvent(&e);
-		if (!e.we.keypress.cont) break;
+		;
+		if (!w->OnKeyPress(key, keycode)) return;
 	}
 
-	if (e.we.keypress.cont) {
-		Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
-		/* When there is no toolbar w is null, check for that */
-		if (w != NULL) w->HandleWindowEvent(&e);
-	}
+	Window *w = FindWindowById(WC_MAIN_TOOLBAR, 0);
+	/* When there is no toolbar w is null, check for that */
+	if (w != NULL) w->OnKeyPress(key, keycode);
 }
 
 /**
@@ -1781,16 +1874,10 @@
  */
 void HandleCtrlChanged()
 {
-	WindowEvent e;
-
-	e.event = WE_CTRL_CHANGED;
-	e.we.ctrl.cont = true;
-
 	/* Call the event, start with the uppermost window. */
 	for (Window* const *wz = _last_z_window; wz != _z_windows;) {
 		Window *w = *--wz;
-		w->HandleWindowEvent(&e);
-		if (!e.we.ctrl.cont) break;
+		if (!w->OnCTRLStateChange()) break;
 	}
 }
 
@@ -1828,14 +1915,14 @@
 			/* here allows scrolling in both x and y axis */
 #define scrollspeed 3
 			if (x - 15 < 0) {
-				WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
+				w->viewport->dest_scrollpos_x += ScaleByZoom((x - 15) * scrollspeed, vp->zoom);
 			} else if (15 - (vp->width - x) > 0) {
-				WP(w, vp_d).dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
+				w->viewport->dest_scrollpos_x += ScaleByZoom((15 - (vp->width - x)) * scrollspeed, vp->zoom);
 			}
 			if (y - 15 < 0) {
-				WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
+				w->viewport->dest_scrollpos_y += ScaleByZoom((y - 15) * scrollspeed, vp->zoom);
 			} else if (15 - (vp->height - y) > 0) {
-				WP(w, vp_d).dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
+				w->viewport->dest_scrollpos_y += ScaleByZoom((15 - (vp->height - y)) * scrollspeed, vp->zoom);
 			}
 #undef scrollspeed
 		}
@@ -1862,7 +1949,6 @@
 	UpdateTileSelection();
 	if (!VpHandlePlaceSizingDrag())  return;
 	if (!HandleDragDrop())           return;
-	if (!HandlePopupMenu())          return;
 	if (!HandleWindowDragging())     return;
 	if (!HandleScrollbarScrolling()) return;
 	if (!HandleViewportScroll())     return;
@@ -1884,13 +1970,8 @@
 
 	if (mousewheel != 0) {
 		if (_patches.scrollwheel_scrolling == 0) {
-			/* Scrollwheel is in zoom mode. Make the zoom event. */
-			WindowEvent e;
-
 			/* Send WE_MOUSEWHEEL event to window */
-			e.event = WE_MOUSEWHEEL;
-			e.we.wheel.wheel = mousewheel;
-			w->HandleWindowEvent(&e);
+			w->OnMouseWheel(mousewheel);
 		}
 
 		/* Dispatch a MouseWheelEvent for widgets if it is not a viewport */
@@ -1931,9 +2012,14 @@
 		}
 	} else {
 		switch (click) {
-			case MC_DOUBLE_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, true);
-				/* fallthough, and also give a single-click for backwards compatible */
-			case MC_LEFT: DispatchLeftClickEvent(w, x - w->left, y - w->top, false); break;
+			case MC_DOUBLE_LEFT:
+				DispatchLeftClickEvent(w, x - w->left, y - w->top, true);
+				if (_mouseover_last_w == NULL) break; // The window got removed.
+				/* fallthough, and also give a single-click for backwards compatibility */
+			case MC_LEFT:
+				DispatchLeftClickEvent(w, x - w->left, y - w->top, false);
+				break;
+
 			default:
 				if (!scrollwheel_scrolling || w == NULL || w->window_class != WC_SMALLMAP) break;
 				/* We try to use the scrollwheel to scroll since we didn't touch any of the buttons.
@@ -2014,7 +2100,7 @@
 
 	if (t >= 100) {
 		for (wz = _last_z_window; wz != _z_windows;) {
-			CallWindowEventNP(*--wz, WE_4);
+			(*--wz)->OnHundredthTick();
 		}
 		t = 0;
 	}
@@ -2039,27 +2125,6 @@
 	DrawMouseCursor();
 }
 
-
-/**
- * In a window with menu_d custom extension, retrieve the menu item number from a position
- * @param w Window holding the menu items
- * @param x X coordinate of the position
- * @param y Y coordinate of the position
- * @return Index number of the menu item, or \c -1 if no valid selection under position
- */
-int GetMenuItemIndex(const Window *w, int x, int y)
-{
-	if ((x -= w->left) >= 0 && x < w->width && (y -= w->top + 1) >= 0) {
-		y /= 10;
-
-		if (y < WP(w, const menu_d).item_count &&
-				!HasBit(WP(w, const menu_d).disabled_items, y)) {
-			return y;
-		}
-	}
-	return -1;
-}
-
 /**
  * Mark window as dirty (in need of repainting)
  * @param cls Window class
@@ -2110,9 +2175,9 @@
  * Mark window data as invalid (in need of re-computing)
  * @param w Window with invalid data
  */
-void InvalidateThisWindowData(Window *w)
+void InvalidateThisWindowData(Window *w, int data)
 {
-	CallWindowEventNP(w, WE_INVALIDATE_DATA);
+	w->OnInvalidateData(data);
 	w->SetDirty();
 }
 
@@ -2121,13 +2186,13 @@
  * @param cls Window class
  * @param number Window number within the class
  */
-void InvalidateWindowData(WindowClass cls, WindowNumber number)
+void InvalidateWindowData(WindowClass cls, WindowNumber number, int data)
 {
 	Window* const *wz;
 
 	FOR_ALL_WINDOWS(wz) {
 		Window *w = *wz;
-		if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w);
+		if (w->window_class == cls && w->window_number == number) InvalidateThisWindowData(w, data);
 	}
 }
 
@@ -2135,12 +2200,12 @@
  * Mark window data of all windows of a given class as invalid (in need of re-computing)
  * @param cls Window class
  */
-void InvalidateWindowClassesData(WindowClass cls)
+void InvalidateWindowClassesData(WindowClass cls, int data)
 {
 	Window* const *wz;
 
 	FOR_ALL_WINDOWS(wz) {
-		if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz);
+		if ((*wz)->window_class == cls) InvalidateThisWindowData(*wz, data);
 	}
 }
 
@@ -2150,7 +2215,7 @@
 void CallWindowTickEvent()
 {
 	for (Window * const *wz = _last_z_window; wz != _z_windows;) {
-		CallWindowEventNP(*--wz, WE_TICK);
+		(*--wz)->OnTick();
 	}
 }
 
@@ -2267,13 +2332,13 @@
 				if (neww - w->width != 0) {
 					ResizeWindow(w, min(neww, 640) - w->width, 0);
 
-					WindowEvent e;
-					e.event = WE_RESIZE;
-					e.we.sizing.size.x = w->width;
-					e.we.sizing.size.y = w->height;
-					e.we.sizing.diff.x = neww - w->width;
-					e.we.sizing.diff.y = 0;
-					w->HandleWindowEvent(&e);
+					Point size;
+					Point delta;
+					size.x = w->width;
+					size.y = w->height;
+					delta.x = neww - w->width;
+					delta.y = 0;
+					w->OnResize(size, delta);
 				}
 
 				top = w->top;
--- a/src/window_func.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/window_func.h	Sun May 11 20:09:34 2008 +0000
@@ -9,8 +9,6 @@
 #include "player_type.h"
 
 void SetWindowDirty(const Window *w);
-void SendWindowMessage(WindowClass wnd_class, WindowNumber wnd_num, int msg, int wparam, int lparam);
-void SendWindowMessageClass(WindowClass wnd_class, int msg, int wparam, int lparam);
 
 Window *FindWindowById(WindowClass cls, WindowNumber number);
 void ChangeWindowOwner(PlayerID old_player, PlayerID new_player);
@@ -23,18 +21,20 @@
 void ResetWindowSystem();
 void SetupColorsAndInitialWindow();
 void InputLoop();
-void InvalidateThisWindowData(Window *w);
-void InvalidateWindowData(WindowClass cls, WindowNumber number);
+
+void InvalidateThisWindowData(Window *w, int data = 0);
+void InvalidateWindowData(WindowClass cls, WindowNumber number, int data = 0);
+void InvalidateWindowClassesData(WindowClass cls, int data = 0);
 
 void DeleteNonVitalWindows();
 void DeleteAllNonVitalWindows();
 void HideVitalWindows();
 void ShowVitalWindows();
 
-void InvalidateWindow(WindowClass cls, WindowNumber number);
 void InvalidateWindowWidget(WindowClass cls, WindowNumber number, byte widget_index);
+void InvalidateWindow(WindowClass cls, WindowNumber number);
 void InvalidateWindowClasses(WindowClass cls);
-void InvalidateWindowClassesData(WindowClass cls);
+
 void DeleteWindowById(WindowClass cls, WindowNumber number);
 void DeleteWindowByClass(WindowClass cls);
 
--- a/src/window_gui.h	Wed May 07 21:09:51 2008 +0000
+++ b/src/window_gui.h	Sun May 11 20:09:34 2008 +0000
@@ -117,24 +117,20 @@
 	WE_DOUBLE_CLICK, ///< Left mouse button double click
 	WE_RCLICK,       ///< Right mouse button click
 	WE_MOUSEOVER,
-	WE_MOUSELOOP,
 	WE_MOUSEWHEEL,
-	WE_TICK,         ///< Regularly occurring event (about once every 20 seconds orso, 10 days) for slowly changing content (typically list sorting)
-	WE_4,            ///< Regularly occurring event for updating continuously changing window content (other than view ports), or timer expiring
+	WE_MOUSELOOP,    ///< Event for each mouse event in the game (at least once every game tick)
+	WE_TICK,         ///< Regularly occurring event (every game tick)
+	WE_100_TICKS,    ///< Regularly occurring event (every 100 game ticks, approximatelly 3 seconds)
 	WE_TIMEOUT,
 	WE_PLACE_OBJ,
 	WE_ABORT_PLACE_OBJ,
 	WE_ON_EDIT_TEXT,
-	WE_ON_EDIT_TEXT_CANCEL,
-	WE_POPUPMENU_SELECT,
-	WE_POPUPMENU_OVER,
 	WE_DRAGDROP,
 	WE_PLACE_DRAG,
 	WE_PLACE_MOUSEUP,
 	WE_PLACE_PRESIZE,
 	WE_DROPDOWN_SELECT,
 	WE_RESIZE,          ///< Request to resize the window, @see WindowEvent.we.resize
-	WE_MESSAGE,         ///< Receipt of a message from another window. @see WindowEvent.we.message, SendWindowMessage(), SendWindowMessageClass()
 	WE_SCROLL,
 	WE_INVALIDATE_DATA, ///< Notification that data displayed by the window is obsolete
 	WE_CTRL_CHANGED,    ///< CTRL key has changed state
@@ -179,10 +175,6 @@
 		} edittext;
 
 		struct {
-			Point pt;
-		} popupmenu;
-
-		struct {
 			int button;
 			int index;
 		} dropdown;
@@ -199,10 +191,8 @@
 		} keypress;
 
 		struct {
-			int msg;      ///< message to be sent
-			int wparam;   ///< additional message-specific information
-			int lparam;   ///< additional message-specific information
-		} message;
+			int data;
+		} invalidate;
 
 		struct {
 			Point delta;   ///< delta position against position of last call
@@ -281,24 +271,34 @@
 };
 
 /**
- * Message data structure for messages sent between winodows
- * @see SendWindowMessageW()
+ * Data structure for a window viewport
  */
-struct WindowMessage {
-	int msg;
-	int wparam;
-	int lparam;
+struct ViewportData : ViewPort {
+	VehicleID follow_vehicle;
+	int32 scrollpos_x;
+	int32 scrollpos_y;
+	int32 dest_scrollpos_x;
+	int32 dest_scrollpos_y;
 };
 
-/**
+	/**
  * Data structure for an opened window
  */
 struct Window : ZeroedMemoryAllocator {
 private:
 	WindowProc *wndproc;   ///< Event handler function for the window. Do not use directly, call HandleWindowEvent() instead.
+	void HandleWindowEvent(WindowEvent *e);
+
+protected:
+	void Initialize(int x, int y, int min_width, int min_height,
+			WindowProc *proc, WindowClass cls, const Widget *widget, int window_number, void *data);
+	void FindWindowPlacementAndResize(int def_width, int def_height);
+	void FindWindowPlacementAndResize(const WindowDesc *desc);
 
 public:
-	Window(WindowProc *proc) : wndproc(proc) {}
+	Window(int x, int y, int width, int height, WindowProc *proc, WindowClass cls, const Widget *widget, void *data = NULL);
+	Window(const WindowDesc *desc, void *data = NULL, WindowNumber number = 0);
+
 	virtual ~Window();
 
 	uint16 flags4;              ///< Window flags, @see WindowFlags
@@ -317,13 +317,12 @@
 
 	byte caption_color; ///< Background color of the window caption, contains PlayerID
 
-	ViewPort *viewport;    ///< Pointer to viewport, if present
+	ViewportData *viewport;      ///< Pointer to viewport data, if present
 	const Widget *original_widget; ///< Original widget layout, copied from WindowDesc
 	Widget *widget;        ///< Widgets of the window
 	uint widget_count;     ///< Number of widgets of the window
 	uint32 desc_flags;     ///< Window/widgets default flags setting, @see WindowDefaultFlag
 
-	WindowMessage message; ///< Buffer for storing received messages (for communication between different window events)
 	Window *parent;        ///< Parent window
 	byte custom[WINDOW_CUSTOM_SIZE]; ///< Additional data depending on window type
 
@@ -351,7 +350,173 @@
 
 	void SetDirty() const;
 
-	virtual void HandleWindowEvent(WindowEvent *e);
+	/*** Event handling ***/
+
+	/**
+	 * This window is currently being repainted.
+	 */
+	virtual void OnPaint();
+
+
+	/**
+	 * A key has been pressed.
+	 * @param key     the Unicode value of the key.
+	 * @param keycode the untranslated key code including shift state.
+	 * @return true if the key press has been handled and no other
+	 *         window should receive the event.
+	 */
+	virtual bool OnKeyPress(uint16 key, uint16 keycode);
+
+	/**
+	 * The state of the control key has changed
+	 * @return true if the change has been handled and no other
+	 *         window should receive the event.
+	 */
+	virtual bool OnCTRLStateChange();
+
+
+	/**
+	 * A click with the left mouse button has been made on the window.
+	 * @param pt     the point inside the window that has been clicked.
+	 * @param widget the clicked widget.
+	 */
+	virtual void OnClick(Point pt, int widget);
+
+	/**
+	 * A double click with the left mouse button has been made on the window.
+	 * @param pt     the point inside the window that has been clicked.
+	 * @param widget the clicked widget.
+	 */
+	virtual void OnDoubleClick(Point pt, int widget);
+
+	/**
+	 * A click with the right mouse button has been made on the window.
+	 * @param pt     the point inside the window that has been clicked.
+	 * @param widget the clicked widget.
+	 */
+	virtual void OnRightClick(Point pt, int widget);
+
+	/**
+	 * A dragged 'object' has been released.
+	 * @param pt     the point inside the window where the release took place.
+	 * @param widget the widget where the release took place.
+	 */
+	virtual void OnDragDrop(Point pt, int widget);
+
+	/**
+	 * Handle the request for (viewport) scrolling.
+	 * @param delta the amount the viewport must be scrolled.
+	 */
+	virtual void OnScroll(Point delta);
+
+	/**
+	 * The mouse is currently moving over the window or has just moved outside
+	 * of the window. In the latter case pt is (-1, -1).
+	 * @param pt     the point inside the window that the mouse hovers over.
+	 * @param widget the widget the mouse hovers over.
+	 */
+	virtual void OnMouseOver(Point pt, int widget);
+
+	/**
+	 * The mouse wheel has been turned.
+	 * @param wheel the amount of movement of the mouse wheel.
+	 */
+	virtual void OnMouseWheel(int wheel);
+
+
+	/**
+	 * Called for every mouse loop run, which is at least once per (game) tick.
+	 */
+	virtual void OnMouseLoop();
+
+	/**
+	 * Called once per (game) tick.
+	 */
+	virtual void OnTick();
+
+	/**
+	 * Called once every 100 (game) ticks.
+	 */
+	virtual void OnHundredthTick();
+
+	/**
+	 * Called when this window's timeout has been reached.
+	 */
+	virtual void OnTimeout();
+
+
+	/**
+	 * Called when the window got resized.
+	 * @param new_size the new size of the window.
+	 * @param delta    the amount of which the window size changed.
+	 */
+	virtual void OnResize(Point new_size, Point delta);
+
+	/**
+	 * A dropdown option associated to this window has been selected.
+	 * @param widget the widget (button) that the dropdown is associated with.
+	 * @param index  the element in the dropdown that is selected.
+	 */
+	virtual void OnDropdownSelect(int widget, int index);
+
+	/**
+	 * The query window opened from this window has closed.
+	 * @param str the new value of the string or NULL if the window
+	 *            was cancelled.
+	 */
+	virtual void OnQueryTextFinished(char *str);
+
+	/**
+	 * Some data on this window has become invalid.
+	 * @param data information about the changed data.
+	 */
+	virtual void OnInvalidateData(int data = 0);
+
+
+	/**
+	 * The user clicked some place on the map when a tile highlight mode
+	 * has been set.
+	 * @param pt   the exact point on the map that has been clicked.
+	 * @param tile the tile on the map that has been clicked.
+	 */
+	virtual void OnPlaceObject(Point pt, TileIndex tile);
+
+	/**
+	 * The user cancelled a tile highlight mode that has been set.
+	 */
+	virtual void OnPlaceObjectAbort();
+
+
+	/**
+	 * The user is dragging over the map when the tile highlight mode
+	 * has been set.
+	 * @param select_method the method of selection (allowed directions)
+	 * @param select_proc   what will be created when the drag is over.
+	 * @param pt            the exact point on the map where the mouse is.
+	 */
+	virtual void OnPlaceDrag(ViewportPlaceMethod select_method, byte select_proc, Point pt);
+
+	/**
+	 * The user has dragged over the map when the tile highlight mode
+	 * has been set.
+	 * @param select_method the method of selection (allowed directions)
+	 * @param select_proc   what should be created.
+	 * @param pt            the exact point on the map where the mouse was released.
+	 * @param start_tile    the begin tile of the drag.
+	 * @param end_tile      the end tile of the drag.
+	 */
+	virtual void OnPlaceMouseUp(ViewportPlaceMethod select_method, byte select_proc, Point pt, TileIndex start_tile, TileIndex end_tile);
+
+	/**
+	 * The user moves over the map when a tile highlight mode has been set
+	 * when the special mouse mode has been set to 'PRESIZE' mode. An
+	 * example of this is the tile highlight for dock building.
+	 * @param pt   the exact point on the map where the mouse is.
+	 * @param tile the tile on the map where the mouse is.
+	 */
+	virtual void OnPlacePresize(Point pt, TileIndex tile);
+
+	/*** End of the event handling ***/
 };
 
 struct menu_d {
@@ -373,71 +538,6 @@
 };
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(def_d));
 
-struct void_d {
-	void *data;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(void_d));
-
-struct tree_d {
-	uint16 base;
-	uint16 count;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(tree_d));
-
-struct tooltips_d {
-	StringID string_id;
-	byte paramcount;
-	uint64 params[5];
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(tooltips_d));
-
-struct depot_d {
-	VehicleID sel;
-	VehicleType type;
-	bool generate_list;
-	uint16 engine_list_length;
-	uint16 wagon_list_length;
-	uint16 engine_count;
-	uint16 wagon_count;
-	Vehicle **vehicle_list;
-	Vehicle **wagon_list;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(depot_d));
-
-struct vehicledetails_d {
-	byte tab;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehicledetails_d));
-
-struct smallmap_d {
-	int32 scroll_x;
-	int32 scroll_y;
-	int32 subscroll;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(smallmap_d));
-
-struct vp_d {
-	VehicleID follow_vehicle;
-	int32 scrollpos_x;
-	int32 scrollpos_y;
-	int32 dest_scrollpos_x;
-	int32 dest_scrollpos_y;
-	ViewPort vp_data;          ///< Screen position and zoom of the viewport
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vp_d));
-
-struct highscore_d {
-	uint32 background_img;
-	int8 rank;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(highscore_d));
-
-struct scroller_d {
-	int height;
-	uint16 counter;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(scroller_d));
-
 enum SortListFlags {
 	VL_NONE    = 0,      ///< no sort
 	VL_DESC    = 1 << 0, ///< sort descending or ascending
@@ -445,7 +545,6 @@
 	VL_REBUILD = 1 << 2, ///< create sort-listing to use for qsort and friends
 	VL_END     = 1 << 3,
 };
-
 DECLARE_ENUM_AS_BIT_SET(SortListFlags);
 
 struct Listing {
@@ -453,29 +552,14 @@
 	byte criteria; ///< Sorting criteria
 };
 
-struct list_d {
+template <typename T>
+struct GUIList {
+	T* sort_list;        ///< The items to sort.
+	SortListFlags flags; ///< used to control sorting/resorting/etc.
 	uint16 list_length;  ///< length of the list being sorted
+	uint16 resort_timer; ///< resort list after a given amount of ticks if set
 	byte sort_type;      ///< what criteria to sort on
-	SortListFlags flags; ///< used to control sorting/resorting/etc.
-	uint16 resort_timer; ///< resort list after a given amount of ticks if set
 };
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(list_d));
-
-struct message_d {
-	int msg;
-	int wparam;
-	int lparam;
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(message_d));
-
-struct vehiclelist_d {
-	const Vehicle** sort_list;  // List of vehicles (sorted)
-	Listing *_sorting;          // pointer to the appropiate subcategory of _sorting
-	uint16 length_of_sort_list; // Keeps track of how many vehicle pointers sort list got space for
-	VehicleType vehicle_type;   // The vehicle type that is sorted
-	list_d l;                   // General list struct
-};
-assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(vehiclelist_d));
 
 /****************** THESE ARE NOT WIDGET TYPES!!!!! *******************/
 enum WindowWidgetBehaviours {
@@ -545,25 +629,27 @@
 	WF_SCROLL2           = 1 << 13,
 };
 
-/* window.cpp */
-void CallWindowEventNP(Window *w, int event);
-void CallWindowTickEvent();
-
 Window *BringWindowToFrontById(WindowClass cls, WindowNumber number);
 Window *FindWindowFromPt(int x, int y);
 
 bool IsWindowOfPrototype(const Window *w, const Widget *widget);
-void AssignWidgetToWindow(Window *w, const Widget *widget);
-Window *AllocateWindow(int x, int y, int width, int height,
-			WindowProc *proc, WindowClass cls, const Widget *widget,
-			void *data = NULL);
 
-Window *AllocateWindowDesc(const WindowDesc *desc, void *data = NULL);
-Window *AllocateWindowDescFront(const WindowDesc *desc, int window_number, void *data = NULL);
+/**
+ * Open a new window.
+ * @param *desc The pointer to the WindowDesc to be created
+ * @param window_number the window number of the new window
+ * @param data arbitrary data that is send with the WE_CREATE message
+ * @return see Window pointer of the newly created window
+ */
+template <typename Wcls>
+Wcls *AllocateWindowDescFront(const WindowDesc *desc, int window_number, void *data = NULL)
+{
+	if (BringWindowToFrontById(desc->cls, window_number)) return NULL;
+	return new Wcls(desc, data, window_number);
+}
 
 void DrawWindowViewport(const Window *w);
 
-int GetMenuItemIndex(const Window *w, int x, int y);
 void RelocateAllWindows(int neww, int newh);
 
 /* misc_gui.cpp */
@@ -600,7 +686,6 @@
 
 extern bool _scrolling_scrollbar;
 extern bool _scrolling_viewport;
-extern bool _popup_menu_active;
 
 extern byte _special_mouse_mode;
 enum SpecialMouseMode {