(svn r9874) -Feature: advanced vehicle lists a.k.a. group interface. Now you can make groups of vehicles and perform all kinds of tasks on that given group. Original code by nycom and graphics by skidd13.
authorrubidium
Sat, 19 May 2007 09:40:18 +0000
changeset 7139 4ae3ab180d05
parent 7138 feca3eff4054
child 7140 422457e07b54
(svn r9874) -Feature: advanced vehicle lists a.k.a. group interface. Now you can make groups of vehicles and perform all kinds of tasks on that given group. Original code by nycom and graphics by skidd13.
bin/data/group.grf
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/aircraft_cmd.cpp
src/autoreplace_cmd.cpp
src/autoreplace_gui.cpp
src/build_vehicle_gui.cpp
src/command.cpp
src/command.h
src/economy.cpp
src/engine.cpp
src/engine.h
src/gfxinit.cpp
src/group.h
src/group_cmd.cpp
src/group_gui.cpp
src/gui.h
src/lang/english.txt
src/misc.cpp
src/openttd.cpp
src/openttd.h
src/player.h
src/players.cpp
src/saveload.cpp
src/settings.cpp
src/settings_gui.cpp
src/strgen/strgen.cpp
src/strings.cpp
src/table/control_codes.h
src/table/files.h
src/table/sprites.h
src/train_cmd.cpp
src/variables.h
src/vehicle.cpp
src/vehicle.h
src/vehicle_gui.cpp
src/vehicle_gui.h
src/window.h
Binary file bin/data/group.grf has changed
--- a/projects/openttd.vcproj	Sat May 19 09:13:08 2007 +0000
+++ b/projects/openttd.vcproj	Sat May 19 09:40:18 2007 +0000
@@ -453,6 +453,9 @@
 				RelativePath=".\..\src\gfxinit.h">
 			</File>
 			<File
+				RelativePath=".\..\src\group.h">
+			</File>
+			<File
 				RelativePath=".\..\src\gui.h">
 			</File>
 			<File
@@ -703,6 +706,9 @@
 				RelativePath=".\..\src\graph_gui.cpp">
 			</File>
 			<File
+				RelativePath=".\..\src\group_gui.cpp">
+			</File>
+			<File
 				RelativePath=".\..\src\industry_gui.cpp">
 			</File>
 			<File
@@ -791,6 +797,9 @@
 				RelativePath=".\..\src\dummy_land.cpp">
 			</File>
 			<File
+				RelativePath=".\..\src\group_cmd.cpp">
+			</File>
+			<File
 				RelativePath=".\..\src\industry_cmd.cpp">
 			</File>
 			<File
--- a/projects/openttd_vs80.vcproj	Sat May 19 09:13:08 2007 +0000
+++ b/projects/openttd_vs80.vcproj	Sat May 19 09:40:18 2007 +0000
@@ -832,6 +832,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\group.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\gui.h"
 				>
 			</File>
@@ -1164,6 +1168,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\group_gui.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\industry_gui.cpp"
 				>
 			</File>
@@ -1280,6 +1288,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\group_cmd.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\industry_cmd.cpp"
 				>
 			</File>
--- a/source.list	Sat May 19 09:13:08 2007 +0000
+++ b/source.list	Sat May 19 09:40:18 2007 +0000
@@ -118,6 +118,7 @@
 genworld.h
 gfx.h
 gfxinit.h
+group.h
 gui.h
 hal.h
 heightmap.h
@@ -202,6 +203,7 @@
 engine_gui.cpp
 genworld_gui.cpp
 graph_gui.cpp
+group_gui.cpp
 industry_gui.cpp
 intro_gui.cpp
 main_gui.cpp
@@ -232,6 +234,7 @@
 clear_cmd.cpp
 disaster_cmd.cpp
 dummy_land.cpp
+group_cmd.cpp
 industry_cmd.cpp
 misc_cmd.cpp
 order_cmd.cpp
--- a/src/aircraft_cmd.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/aircraft_cmd.cpp	Sat May 19 09:40:18 2007 +0000
@@ -1684,7 +1684,7 @@
 	/* check if the aircraft needs to be replaced or renewed and send it to a hangar if needed
 	 * unless it is due for renewal but the engine is no longer available */
 	if (v->owner == _local_player && (
-				EngineHasReplacementForPlayer(p, v->engine_type) ||
+				EngineHasReplacementForPlayer(p, v->engine_type, v->group_id) ||
 				((p->engine_renew && v->age - v->max_age > p->engine_renew_months * 30) &&
 				HASBIT(GetEngine(v->engine_type)->player_avail, _local_player))
 			)) {
@@ -1742,7 +1742,7 @@
 	if (v->current_order.type != OT_GOTO_DEPOT && v->owner == _local_player) {
 		/* only the vehicle owner needs to calculate the rest (locally) */
 		const Player* p = GetPlayer(v->owner);
-		if (EngineHasReplacementForPlayer(p, v->engine_type) ||
+		if (EngineHasReplacementForPlayer(p, v->engine_type, v->group_id) ||
 			(p->engine_renew && v->age - v->max_age > (p->engine_renew_months * 30))) {
 			/* send the aircraft to the hangar at next airport */
 			_current_player = _local_player;
--- a/src/autoreplace_cmd.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/autoreplace_cmd.cpp	Sat May 19 09:40:18 2007 +0000
@@ -16,6 +16,7 @@
 #include "train.h"
 #include "aircraft.h"
 #include "cargotype.h"
+#include "group.h"
 
 
 /*
@@ -136,8 +137,17 @@
 	char vehicle_name[32];
 	CargoID replacement_cargo_type;
 
-	new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type);
-	if (new_engine_type == INVALID_ENGINE) new_engine_type = old_v->engine_type;
+	/* If the vehicle belongs to a group, check if the group is protected from the global autoreplace.
+	 *  If not, chek if an global auto replacement is defined */
+	new_engine_type = (IsValidGroupID(old_v->group_id) && GetGroup(old_v->group_id)->replace_protection) ?
+			INVALID_ENGINE :
+			EngineReplacementForPlayer(p, old_v->engine_type, DEFAULT_GROUP);
+
+	/* If we don't set new_egnine_type previously, we try to check if an autoreplacement was defined
+	 *  for the group and the engine_type of the vehicle */
+	if (new_engine_type == INVALID_ENGINE && !IsDefaultGroupID(old_v->group_id)) {
+		new_engine_type = EngineReplacementForPlayer(p, old_v->engine_type, old_v->group_id);
+	}
 
 	replacement_cargo_type = GetNewCargoTypeForReplace(old_v, new_engine_type);
 
@@ -165,6 +175,7 @@
 		new_v = GetVehicle(_new_vehicle_id);
 		*w = new_v; //we changed the vehicle, so MaybeReplaceVehicle needs to work on the new one. Now we tell it what the new one is
 
+		new_v->group_id = old_v->group_id;
 		/* refit if needed */
 		if (replacement_cargo_type != CT_NO_REFIT) {
 			if (CmdFailed(DoCommand(0, new_v->index, replacement_cargo_type, DC_EXEC, GetCmdRefitVeh(new_v)))) {
@@ -194,6 +205,7 @@
 			new_v->profit_this_year = old_v->profit_this_year;
 			new_v->profit_last_year = old_v->profit_last_year;
 			new_v->service_interval = old_v->service_interval;
+			new_v->group_id = old_v->group_id;
 			new_front = true;
 			new_v->unitnumber = old_v->unitnumber; // use the same unit number
 			new_v->dest_tile  = old_v->dest_tile;
@@ -211,6 +223,10 @@
 				if (temp_v != NULL) {
 					DoCommand(0, (new_v->index << 16) | temp_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 				}
+			} else if (!IsDefaultGroupID(old_v->group_id) && IsValidGroupID(old_v->group_id)) {
+				/* Increase the new num engines of the group for the ships, aircraft, and road vehicles
+					The old new num engine is decrease in the destroyvehicle function */
+				GetGroup(old_v->group_id)->num_engines[new_v->engine_type]++;
 			}
 		}
 		/* We are done setting up the new vehicle. Now we move the cargo from the old one to the new one */
@@ -305,8 +321,17 @@
 			if (!p->engine_renew ||
 					w->age - w->max_age < (p->engine_renew_months * 30) || // replace if engine is too old
 					w->max_age == 0) { // rail cars got a max age of 0
-				if (!EngineHasReplacementForPlayer(p, w->engine_type)) // updates to a new model
+				/* If the vehicle belongs to a group, check if the group is protected from the global autoreplace.
+				   If not, chek if an global auto remplacement is defined */
+				if (IsValidGroupID(w->group_id)) {
+					if (!EngineHasReplacementForPlayer(p, w->engine_type, w->group_id) && (
+							GetGroup(w->group_id)->replace_protection ||
+							!EngineHasReplacementForPlayer(p, w->engine_type, DEFAULT_GROUP))) {
+						continue;
+					}
+				} else if (!EngineHasReplacementForPlayer(p, w->engine_type, DEFAULT_GROUP)) {
 					continue;
+				}
 			}
 
 			/* Now replace the vehicle */
--- a/src/autoreplace_gui.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/autoreplace_gui.cpp	Sat May 19 09:40:18 2007 +0000
@@ -14,6 +14,7 @@
 #include "variables.h"
 #include "vehicle_gui.h"
 #include "newgrf_engine.h"
+#include "group.h"
 
 
 static RailType _railtype_selected_in_replace_gui;
@@ -150,8 +151,11 @@
 		if (type == VEH_TRAIN && !GenerateReplaceRailList(e, 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 = IsDefaultGroupID(selected_group) ? p->num_engines[e] : GetGroup(selected_group)->num_engines[e];
+
 			/* Skip drawing the engines we don't have any of and haven't set for replacement */
-			if (p->num_engines[e] == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), e) == INVALID_ENGINE) continue;
+			if (num_engines == 0 && EngineReplacementForPlayer(GetPlayer(_local_player), e, 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(e, type, _local_player)) continue; // we need to be able to build the engine
@@ -202,7 +206,7 @@
 }
 
 
-void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count);
+void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
 
 static void ReplaceVehicleWndProc(Window *w, WindowEvent *e)
 {
@@ -231,6 +235,7 @@
 
 			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];
@@ -242,15 +247,15 @@
 			SetWindowWidgetDisabledState(w, 4,
 										 selected_id[0] == INVALID_ENGINE ||
 										 selected_id[1] == INVALID_ENGINE ||
-										 EngineReplacementForPlayer(p, selected_id[1]) != INVALID_ENGINE ||
-										 EngineReplacementForPlayer(p, selected_id[0]) == selected_id[1]);
+										 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 */
 			SetWindowWidgetDisabledState(w, 6,
 										 selected_id[0] == INVALID_ENGINE ||
-										 !EngineHasReplacementForPlayer(p, selected_id[0]));
+										 !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]);
@@ -277,10 +282,10 @@
 
 			/* sets up the string for the vehicle that is being replaced to */
 			if (selected_id[0] != INVALID_ENGINE) {
-				if (!EngineHasReplacementForPlayer(p, selected_id[0])) {
+				if (!EngineHasReplacementForPlayer(p, selected_id[0], selected_group)) {
 					SetDParam(0, STR_NOT_REPLACING);
 				} else {
-					SetDParam(0, GetCustomEngineName(EngineReplacementForPlayer(p, selected_id[0])));
+					SetDParam(0, GetCustomEngineName(EngineReplacementForPlayer(p, selected_id[0], selected_group)));
 				}
 			} else {
 				SetDParam(0, STR_NOT_REPLACING_VEHICLE_SELECTED);
@@ -296,7 +301,7 @@
 				EngineID end    = min((i == 0 ? w->vscroll.cap : w->vscroll2.cap) + start, EngList_Count(&list));
 
 				/* Do the actual drawing */
-				DrawEngineList((VehicleType)w->window_number, x, 15, list, start, end, WP(w, replaceveh_d).sel_engine[i], i == 0);
+				DrawEngineList((VehicleType)w->window_number, x, 15, list, start, end, WP(w, replaceveh_d).sel_engine[i], i == 0, selected_group);
 
 				/* Also draw the details if an engine is selected */
 				if (WP(w, replaceveh_d).sel_engine[i] != INVALID_ENGINE) {
@@ -328,12 +333,12 @@
 				case 4: { /* 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, veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
+					DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16) , veh_from + (veh_to << 16), NULL, CMD_SET_AUTOREPLACE);
 				} break;
 
 				case 6: { /* Stop replacing */
 					EngineID veh_from = WP(w, replaceveh_d).sel_engine[0];
-					DoCommandP(0, 3, veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
+					DoCommandP(0, 3 + (WP(w, replaceveh_d).sel_group << 16), veh_from + (INVALID_ENGINE << 16), NULL, CMD_SET_AUTOREPLACE);
 				} break;
 
 				case 7:
@@ -509,4 +514,37 @@
 
 	w->caption_color = _local_player;
 	w->vscroll2.cap = w->vscroll.cap;   // these two are always the same
+	WP(w, replaceveh_d).sel_group = DEFAULT_GROUP;
+ }
+
+void ShowReplaceGroupVehicleWindow(GroupID id_g, VehicleType vehicletype)
+{
+	Window *w;
+
+	DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
+
+	switch (vehicletype) {
+		default: NOT_REACHED();
+		case VEH_TRAIN:
+			w = AllocateWindowDescFront(&_replace_rail_vehicle_desc, vehicletype);
+			w->vscroll.cap  = 8;
+			w->resize.step_height = 14;
+			WP(w, replaceveh_d).wagon_btnstate = true;
+			break;
+		case VEH_ROAD:
+			w = AllocateWindowDescFront(&_replace_road_vehicle_desc, vehicletype);
+			w->vscroll.cap  = 8;
+			w->resize.step_height = 14;
+			break;
+		case VEH_SHIP:
+		case VEH_AIRCRAFT:
+			w = AllocateWindowDescFront(&_replace_ship_aircraft_vehicle_desc, vehicletype);
+			w->vscroll.cap  = 4;
+			w->resize.step_height = 24;
+			break;
+	}
+
+	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
 }
--- a/src/build_vehicle_gui.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/build_vehicle_gui.cpp	Sat May 19 09:40:18 2007 +0000
@@ -27,6 +27,7 @@
 #include "date.h"
 #include "strings.h"
 #include "cargotype.h"
+#include "group.h"
 
 
 enum BuildVehicleWidgets {
@@ -759,7 +760,7 @@
  * @param selected_id what engine to highlight as selected, if any
  * @param show_count Display the number of vehicles (used by autoreplace)
  */
-void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count)
+void DrawEngineList(VehicleType type, int x, int y, const EngineList eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
 {
 	byte step_size = GetVehicleListHeight(type);
 	byte x_offset = 0;
@@ -795,11 +796,12 @@
 
 	for (; min < max; min++, y += step_size) {
 		const EngineID engine = eng_list[min];
+		const uint num_engines = IsDefaultGroupID(selected_group) ? p->num_engines[engine] : GetGroup(selected_group)->num_engines[engine];
 
 		DrawString(x + x_offset, y, GetCustomEngineName(engine), engine == selected_id ? 0xC : 0x10);
-		DrawVehicleEngine(type, x, y + y_offset, engine, (show_count && p->num_engines[engine] == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_player));
+		DrawVehicleEngine(type, x, y + y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_player));
 		if (show_count) {
-			SetDParam(0, p->num_engines[engine]);
+			SetDParam(0, num_engines);
 			DrawStringRightAligned(213, y + (GetVehicleListHeight(type) == 14 ? 3 : 8), STR_TINY_BLACK, 0);
 		}
 	}
@@ -833,7 +835,7 @@
 	SetDParam(0, bv->filter.railtype + STR_881C_NEW_RAIL_VEHICLES); // This should only affect rail vehicles
 	DrawWindowWidgets(w);
 
-	DrawEngineList(bv->vehicle_type, 2, 27, bv->eng_list, w->vscroll.pos, max, bv->sel_engine, false);
+	DrawEngineList(bv->vehicle_type, 2, 27, bv->eng_list, w->vscroll.pos, max, bv->sel_engine, false, DEFAULT_GROUP);
 
 	if (bv->sel_engine != INVALID_ENGINE) {
 		const Widget *wi = &w->widget[BUILD_VEHICLE_WIDGET_PANEL];
--- a/src/command.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/command.cpp	Sat May 19 09:40:18 2007 +0000
@@ -168,6 +168,12 @@
 DEF_COMMAND(CmdDepotSellAllVehicles);
 DEF_COMMAND(CmdDepotMassAutoReplace);
 
+DEF_COMMAND(CmdCreateGroup);
+DEF_COMMAND(CmdRenameGroup);
+DEF_COMMAND(CmdDeleteGroup);
+DEF_COMMAND(CmdAddVehicleGroup);
+DEF_COMMAND(CmdAddSharedVehicleGroup);
+DEF_COMMAND(CmdRemoveAllVehiclesGroup);
 /* The master command table */
 static const Command _command_proc_table[] = {
 	{CmdBuildRailroadTrack,                  0}, /*   0 */
@@ -313,6 +319,12 @@
 	{CmdMassStartStopVehicle,                0}, /* 117 */
 	{CmdDepotSellAllVehicles,                0}, /* 118 */
 	{CmdDepotMassAutoReplace,                0}, /* 119 */
+	{CmdCreateGroup,                         0}, /* 120 */
+	{CmdDeleteGroup,                         0}, /* 121 */
+	{CmdRenameGroup,                         0}, /* 122 */
+	{CmdAddVehicleGroup,                     0}, /* 123 */
+	{CmdAddSharedVehicleGroup,               0}, /* 124 */
+	{CmdRemoveAllVehiclesGroup,              0}, /* 125 */
 };
 
 /* This function range-checks a cmd, and checks if the cmd is not NULL */
--- a/src/command.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/command.h	Sat May 19 09:40:18 2007 +0000
@@ -143,6 +143,12 @@
 	CMD_MASS_START_STOP              = 117,
 	CMD_DEPOT_SELL_ALL_VEHICLES      = 118,
 	CMD_DEPOT_MASS_AUTOREPLACE       = 119,
+	CMD_CREATE_GROUP                 = 120,
+	CMD_DELETE_GROUP                 = 121,
+	CMD_RENAME_GROUP                 = 122,
+	CMD_ADD_VEHICLE_GROUP            = 123,
+	CMD_ADD_SHARED_VEHICLE_GROUP     = 124,
+	CMD_REMOVE_ALL_VEHICLES_GROUP    = 125,
 };
 
 enum {
--- a/src/economy.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/economy.cpp	Sat May 19 09:40:18 2007 +0000
@@ -38,6 +38,7 @@
 #include "date.h"
 #include "cargotype.h"
 #include "player_face.h"
+#include "group.h"
 
 /* Score info */
 const ScoreInfo _score_info[] = {
@@ -359,6 +360,7 @@
 					DeleteVehicle(v);
 				} else {
 					v->owner = new_player;
+					v->group_id = DEFAULT_GROUP;
 					if (IsEngineCountable(v)) GetPlayer(new_player)->num_engines[v->engine_type]++;
 					switch (v->type) {
 						case VEH_TRAIN:    if (IsFrontEngine(v)) v->unitnumber = ++num_train; break;
--- a/src/engine.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/engine.cpp	Sat May 19 09:40:18 2007 +0000
@@ -20,6 +20,7 @@
 #include "newgrf_cargo.h"
 #include "date.h"
 #include "table/engines.h"
+#include "group.h"
 
 EngineInfo _engine_info[TOTAL_NUM_ENGINES];
 RailVehicleInfo _rail_vehicle_info[NUM_TRAIN_ENGINES];
@@ -481,6 +482,7 @@
 
 		er->to = INVALID_ENGINE;
 		er->next = NULL;
+		er->group_id = DEFAULT_GROUP;
 		return er;
 	}
 
@@ -493,12 +495,12 @@
 /**
  * Retrieves the EngineRenew that specifies the replacement of the given
  * engine type from the given renewlist */
-static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine)
+static EngineRenew *GetEngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
 {
 	EngineRenew *er = (EngineRenew *)erl;
 
 	while (er) {
-		if (er->from == engine) return er;
+		if (er->from == engine && er->group_id == group) return er;
 		er = er->next;
 	}
 	return NULL;
@@ -517,18 +519,18 @@
 	*erl = NULL; // Empty list
 }
 
-EngineID EngineReplacement(EngineRenewList erl, EngineID engine)
+EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group)
 {
-	const EngineRenew *er = GetEngineReplacement(erl, engine);
+	const EngineRenew *er = GetEngineReplacement(erl, engine, group);
 	return er == NULL ? INVALID_ENGINE : er->to;
 }
 
-int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, uint32 flags)
+int32 AddEngineReplacement(EngineRenewList *erl, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags)
 {
 	EngineRenew *er;
 
 	/* Check if the old vehicle is already in the list */
-	er = GetEngineReplacement(*erl, old_engine);
+	er = GetEngineReplacement(*erl, old_engine, group);
 	if (er != NULL) {
 		if (flags & DC_EXEC) er->to = new_engine;
 		return 0;
@@ -540,6 +542,7 @@
 	if (flags & DC_EXEC) {
 		er->from = old_engine;
 		er->to = new_engine;
+		er->group_id = group;
 
 		/* Insert before the first element */
 		er->next = (EngineRenew *)(*erl);
@@ -549,14 +552,14 @@
 	return 0;
 }
 
-int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, uint32 flags)
+int32 RemoveEngineReplacement(EngineRenewList *erl, EngineID engine, GroupID group, uint32 flags)
 {
 	EngineRenew *er = (EngineRenew *)(*erl);
 	EngineRenew *prev = NULL;
 
 	while (er)
 	{
-		if (er->from == engine) {
+		if (er->from == engine && er->group_id == group) {
 			if (flags & DC_EXEC) {
 				if (prev == NULL) { // First element
 					/* The second becomes the new first element */
@@ -577,11 +580,11 @@
 }
 
 static const SaveLoad _engine_renew_desc[] = {
-	SLE_VAR(EngineRenew, from, SLE_UINT16),
-	SLE_VAR(EngineRenew, to,   SLE_UINT16),
+	    SLE_VAR(EngineRenew, from,     SLE_UINT16),
+	    SLE_VAR(EngineRenew, to,       SLE_UINT16),
 
-	SLE_REF(EngineRenew, next, REF_ENGINE_RENEWS),
-
+	    SLE_REF(EngineRenew, next,     REF_ENGINE_RENEWS),
+	SLE_CONDVAR(EngineRenew, group_id, SLE_UINT16, 60, SL_MAX_VERSION),
 	SLE_END()
 };
 
@@ -607,6 +610,9 @@
 
 		er = GetEngineRenew(index);
 		SlObject(er, _engine_renew_desc);
+
+		/* Advanced vehicle lists got added */
+		if (CheckSavegameVersion(60)) er->group_id = DEFAULT_GROUP;
 	}
 }
 
--- a/src/engine.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/engine.h	Sat May 19 09:40:18 2007 +0000
@@ -272,6 +272,7 @@
 	EngineID from;
 	EngineID to;
 	EngineRenew *next;
+	GroupID group_id;
 };
 
 /**
@@ -317,7 +318,7 @@
  * @return The engine type to replace with, or INVALID_ENGINE if no
  * replacement is in the list.
  */
-EngineID EngineReplacement(EngineRenewList erl, EngineID engine);
+EngineID EngineReplacement(EngineRenewList erl, EngineID engine, GroupID group);
 
 /**
  * Add an engine replacement to the given renewlist.
@@ -327,7 +328,7 @@
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID new_engine, uint32 flags);
+int32 AddEngineReplacement(EngineRenewList* erl, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags);
 
 /**
  * Remove an engine replacement from a given renewlist.
@@ -336,7 +337,7 @@
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-int32 RemoveEngineReplacement(EngineRenewList* erl, EngineID engine, uint32 flags);
+int32 RemoveEngineReplacement(EngineRenewList* erl, EngineID engine, GroupID group, uint32 flags);
 
 /** When an engine is made buildable or is removed from being buildable, add/remove it from the build/autoreplace lists
  * @param type The type of engine
--- a/src/gfxinit.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/gfxinit.cpp	Sat May 19 09:40:18 2007 +0000
@@ -393,6 +393,9 @@
 	assert(load_index == SPR_ROADSTOP_BASE);
 	load_index += LoadGrfFile("roadstops.grf", load_index, i++);
 
+	assert(load_index == SPR_GROUP_BASE);
+	load_index += LoadGrfFile("group.grf", load_index, i++);
+
 	/* Initialize the unicode to sprite mapping table */
 	InitializeUnicodeGlyphMap();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/group.h	Sat May 19 09:40:18 2007 +0000
@@ -0,0 +1,97 @@
+/* $Id$ */
+
+/** @file group.h */
+
+#ifndef GROUP_H
+#define GROUP_H
+
+#include "oldpool.h"
+
+enum {
+	DEFAULT_GROUP = 0xFFFE,
+	INVALID_GROUP = 0xFFFF,
+};
+
+struct Group {
+	StringID string_id;                     ///< Group Name
+
+	uint16 num_vehicle;                     ///< Number of vehicles wich belong to the group
+	PlayerID owner;                         ///< Group Owner
+	GroupID index;                          ///< Array index
+	VehicleTypeByte vehicle_type;           ///< Vehicle type of the group
+
+	bool replace_protection;                ///< If set to true, the global autoreplace have no effect on the group
+	uint16 num_engines[TOTAL_NUM_ENGINES];  ///< Caches the number of engines of each type the player owns (no need to save this)
+};
+
+DECLARE_OLD_POOL(Group, Group, 5, 2047)
+
+
+static inline bool IsValidGroup(const Group *g)
+{
+	return g->string_id != STR_NULL;
+}
+
+static inline void DestroyGroup(Group *g)
+{
+	DeleteName(g->string_id);
+}
+
+static inline void DeleteGroup(Group *g)
+{
+	DestroyGroup(g);
+	g->string_id = STR_NULL;
+}
+
+static inline bool IsValidGroupID(GroupID index)
+{
+	return index < GetGroupPoolSize() && IsValidGroup(GetGroup(index));
+}
+
+static inline bool IsDefaultGroupID(GroupID index)
+{
+	return (index == DEFAULT_GROUP);
+}
+
+static inline StringID GetGroupName(GroupID index)
+{
+	if (!IsValidGroupID(index)) return STR_NULL;
+
+	return GetGroup(index)->string_id;
+}
+
+
+#define FOR_ALL_GROUPS_FROM(g, start) for (g = GetGroup(start); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) if (IsValidGroup(g))
+#define FOR_ALL_GROUPS(g) FOR_ALL_GROUPS_FROM(g, 0)
+
+/**
+ * Get the current size of the GroupPool
+ */
+static inline uint GetGroupArraySize(void)
+{
+	const Group *g;
+	uint num = 0;
+
+	FOR_ALL_GROUPS(g) num++;
+
+	return num;
+}
+
+static inline void IncreaseGroupNumVehicle(GroupID id_g)
+{
+	if (IsValidGroupID(id_g)) GetGroup(id_g)->num_vehicle++;
+}
+
+static inline void DecreaseGroupNumVehicle(GroupID id_g)
+{
+	if (IsValidGroupID(id_g)) GetGroup(id_g)->num_vehicle--;
+}
+
+
+void InitializeGroup();
+void SetTrainGroupID(Vehicle *v, GroupID grp);
+void UpdateTrainGroupID(Vehicle *v);
+void RemoveVehicleFromGroup(const Vehicle *v);
+void RemoveAllGroupsForPlayer(const Player *p);
+
+#endif /* GROUP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/group_cmd.cpp	Sat May 19 09:40:18 2007 +0000
@@ -0,0 +1,390 @@
+/* $Id$ */
+
+/** @file group_cmd.cpp Handling of the engine groups */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "functions.h"
+#include "player.h"
+#include "table/strings.h"
+#include "command.h"
+#include "vehicle.h"
+#include "saveload.h"
+#include "debug.h"
+#include "group.h"
+#include "train.h"
+#include "aircraft.h"
+#include "string.h"
+
+/**
+ * Update the num engines of a groupID. Decrease the old one and increase the new one
+ * @note called in SetTrainGroupID and UpdateTrainGroupID
+ * @param i     EngineID we have to update
+ * @param old_g index of the old group
+ * @param new_g index of the new group
+ */
+static inline void UpdateNumEngineGroup(EngineID i, GroupID old_g, GroupID new_g)
+{
+	if (old_g != new_g) {
+		/* Decrease the num engines of EngineID i of the old group if it's not the default one */
+		if (!IsDefaultGroupID(old_g) && IsValidGroupID(old_g)) GetGroup(old_g)->num_engines[i]--;
+
+		/* Increase the num engines of EngineID i of the new group if it's not the new one */
+		if (!IsDefaultGroupID(new_g) && IsValidGroupID(new_g)) GetGroup(new_g)->num_engines[i]++;
+	}
+}
+
+
+/**
+ * Called if a new block is added to the group-pool
+ */
+static void GroupPoolNewBlock(uint start_item)
+{
+	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
+	 * TODO - This is just a temporary stage, this will be removed. */
+	for (Group *g = GetGroup(start_item); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) g->index = start_item++;
+}
+
+DEFINE_OLD_POOL(Group, Group, GroupPoolNewBlock, NULL)
+
+static Group *AllocateGroup(void)
+{
+	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
+	 * TODO - This is just a temporary stage, this will be removed. */
+	for (Group *g = GetGroup(0); g != NULL; g = (g->index + 1U < GetGroupPoolSize()) ? GetGroup(g->index + 1) : NULL) {
+		if (!IsValidGroup(g)) {
+			const GroupID index = g->index;
+
+			memset(g, 0, sizeof(*g));
+			g->index = index;
+
+			return g;
+		}
+	}
+
+	/* Check if we can add a block to the pool */
+	return (AddBlockToPool(&_Group_pool)) ? AllocateGroup() : NULL;
+}
+
+void InitializeGroup(void)
+{
+	CleanPool(&_Group_pool);
+	AddBlockToPool(&_Group_pool);
+}
+
+
+/**
+ * Add a vehicle to a group
+ * @param tile unused
+ * @param p1   vehicle type
+ * @param p2   unused
+ */
+int32 CmdCreateGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	VehicleType vt = (VehicleType)p1;
+	if (!IsPlayerBuildableVehicleType(vt)) return CMD_ERROR;
+
+	Group *g = AllocateGroup();
+	if (g == NULL) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		g->owner = _current_player;
+		g->string_id = STR_SV_GROUP_NAME;
+		g->replace_protection = false;
+		g->vehicle_type = vt;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Add a vehicle to a group
+ * @param tile unused
+ * @param p1   index of array group
+ *      - p1 bit 0-15 : GroupID
+ * @param p2   unused
+ */
+int32 CmdDeleteGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	if (!IsValidGroupID(p1)) return CMD_ERROR;
+
+	Group *g = GetGroup(p1);
+	if (g->owner != _current_player) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		Vehicle *v;
+
+		/* Add all vehicles belong to the group to the default group */
+		FOR_ALL_VEHICLES(v) {
+			if (v->group_id == g->index && v->type == g->vehicle_type) v->group_id = DEFAULT_GROUP;
+		}
+
+		/* If we set an autoreplace for the group we delete, remove it. */
+		if (_current_player < MAX_PLAYERS) {
+			Player *p;
+			EngineRenew *er;
+
+			p = GetPlayer(_current_player);
+			FOR_ALL_ENGINE_RENEWS(er) {
+				if (er->group_id == g->index) RemoveEngineReplacementForPlayer(p, er->from, g->index, flags);
+			}
+		}
+
+		/* Delete the Replace Vehicle Windows */
+		DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type);
+		DeleteGroup(g);
+	}
+
+	return 0;
+}
+
+
+/**
+ * Rename a group
+ * @param tile unused
+ * @param p1   index of array group
+ *   - p1 bit 0-15 : GroupID
+ * @param p2   unused
+ */
+int32 CmdRenameGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	if (!IsValidGroupID(p1) || StrEmpty(_cmd_text)) return CMD_ERROR;
+
+	/* Create the name */
+	StringID str = AllocateName(_cmd_text, 0);
+	if (str == STR_NULL) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		Group *g = GetGroup(p1);
+
+		/* Delete the old name */
+		DeleteName(g->string_id);
+		/* Assign the new one */
+		g->string_id = str;
+		g->owner = _current_player;
+	}
+
+	return 0;
+}
+
+
+/**
+ * Add a vehicle to a group
+ * @param tile unused
+ * @param p1   index of array group
+ *   - p1 bit 0-15 : GroupID
+ * @param p2   vehicle to add to a group
+ *   - p2 bit 0-15 : VehicleID
+ */
+int32 CmdAddVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	GroupID new_g = p1;
+
+	if (!IsValidVehicleID(p2) || !IsValidGroupID(new_g)) return CMD_ERROR;
+
+	Vehicle *v = GetVehicle(p2);
+	if (v->owner != _current_player || (v->type == VEH_TRAIN && !IsFrontEngine(v))) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		DecreaseGroupNumVehicle(v->group_id);
+		IncreaseGroupNumVehicle(new_g);
+
+		switch (v->type) {
+			default: NOT_REACHED();
+			case VEH_TRAIN:
+				SetTrainGroupID(v, new_g);
+				break;
+			case VEH_ROAD:
+			case VEH_SHIP:
+			case VEH_AIRCRAFT:
+				if (IsEngineCountable(v)) UpdateNumEngineGroup(v->engine_type, v->group_id, new_g);
+				v->group_id = new_g;
+				break;
+		}
+
+		/* Update the Replace Vehicle Windows */
+		InvalidateWindow(WC_REPLACE_VEHICLE, v->type);
+	}
+
+	return 0;
+}
+
+/**
+ * Add all shared vehicles of all vehicles from a group
+ * @param tile unused
+ * @param p1   index of group array
+ *  - p1 bit 0-15 : GroupID
+ * @param p2   type of vehicles
+ */
+int32 CmdAddSharedVehicleGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	VehicleType type = (VehicleType)p2;
+	if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		Vehicle *v;
+		VehicleType type = (VehicleType)p2;
+		GroupID id_g = p1;
+		uint subtype = (type == VEH_AIRCRAFT) ? AIR_AIRCRAFT : 0;
+
+		/* Find the first front engine which belong to the group id_g
+		 * then add all shared vehicles of this front engine to the group id_g */
+		FOR_ALL_VEHICLES(v) {
+			if ((v->type == type) && (
+					(type == VEH_TRAIN && IsFrontEngine(v)) ||
+					(type != VEH_TRAIN && v->subtype <= subtype))) {
+				if (v->group_id != id_g) continue;
+
+				/* For each shared vehicles add it to the group */
+				for (Vehicle *v2 = GetFirstVehicleFromSharedList(v); v2 != NULL; v2 = v2->next_shared) {
+					if (v2->group_id != id_g) CmdAddVehicleGroup(tile, flags, id_g, v2->index);
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * Remove all vehicles from a group
+ * @param tile unused
+ * @param p1   index of group array
+ * - p1 bit 0-15 : GroupID
+ * @param p2   type of vehicles
+ */
+int32 CmdRemoveAllVehiclesGroup(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
+{
+	VehicleType type = (VehicleType)p2;
+	if (!IsValidGroupID(p1) || !IsPlayerBuildableVehicleType(type)) return CMD_ERROR;
+
+	if (flags & DC_EXEC) {
+		GroupID old_g = p1;
+		uint subtype = (type == VEH_AIRCRAFT) ? AIR_AIRCRAFT : 0;
+		Vehicle *v;
+
+		/* Find each Vehicle that belongs to the group old_g and add it to the default group */
+		FOR_ALL_VEHICLES(v) {
+			if ((v->type == type) && (
+					(type == VEH_TRAIN && IsFrontEngine(v)) ||
+					(type != VEH_TRAIN && v->subtype <= subtype))) {
+				if (v->group_id != old_g) continue;
+
+				/* Add The Vehicle to the default group */
+				CmdAddVehicleGroup(tile, flags, DEFAULT_GROUP, v->index);
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Decrease the num_vehicle variable before delete an front engine from a group
+ * @note Called in CmdSellRailWagon and DeleteLasWagon,
+ * @param v     FrontEngine of the train we want to remove.
+ */
+void RemoveVehicleFromGroup(const Vehicle *v)
+{
+	if (!IsValidVehicle(v) || v->type != VEH_TRAIN || !IsFrontEngine(v)) return;
+
+	if (!IsDefaultGroupID(v->group_id)) DecreaseGroupNumVehicle(v->group_id);
+}
+
+
+/**
+ * Affect the groupID of a train to new_g.
+ * @note called in CmdAddVehicleGroup and CmdMoveRailVehicle
+ * @param v     First vehicle of the chain.
+ * @param new_g index of array group
+ */
+void SetTrainGroupID(Vehicle *v, GroupID new_g)
+{
+	if (!IsValidGroupID(new_g) && !IsDefaultGroupID(new_g)) return;
+
+	assert(IsValidVehicle(v) && v->type == VEH_TRAIN && IsFrontEngine(v));
+
+	for (Vehicle *u = v; u != NULL; u = u->next) {
+		if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
+
+		u->group_id = new_g;
+	}
+
+	/* Update the Replace Vehicle Windows */
+	InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
+}
+
+
+/**
+ * Recalculates the groupID of a train. Should be called each time a vehicle is added
+ * to/removed from the chain,.
+ * @note this needs to be called too for 'wagon chains' (in the depot, without an engine)
+ * @note Called in CmdBuildRailVehicle, CmdBuildRailWagon, CmdMoveRailVehicle, CmdSellRailWagon
+ * @param v First vehicle of the chain.
+ */
+void UpdateTrainGroupID(Vehicle *v)
+{
+	assert(IsValidVehicle(v) && v->type == VEH_TRAIN && (IsFrontEngine(v) || IsFreeWagon(v)));
+
+	GroupID new_g = IsFrontEngine(v) ? v->group_id : (GroupID)DEFAULT_GROUP;
+	for (Vehicle *u = v; u != NULL; u = u->next) {
+		if (IsEngineCountable(u)) UpdateNumEngineGroup(u->engine_type, u->group_id, new_g);
+
+		u->group_id = new_g;
+	}
+
+	/* Update the Replace Vehicle Windows */
+	InvalidateWindow(WC_REPLACE_VEHICLE, VEH_TRAIN);
+}
+
+
+void RemoveAllGroupsForPlayer(const Player *p)
+{
+	Group *g;
+
+	FOR_ALL_GROUPS(g) {
+		if (p->index == g->owner) DeleteGroup(g);
+	}
+}
+
+
+static const SaveLoad _group_desc[] = {
+  SLE_VAR(Group, string_id,          SLE_UINT16),
+  SLE_VAR(Group, num_vehicle,        SLE_UINT16),
+  SLE_VAR(Group, owner,              SLE_UINT8),
+  SLE_VAR(Group, vehicle_type,       SLE_UINT8),
+  SLE_VAR(Group, replace_protection, SLE_BOOL),
+  SLE_END()
+};
+
+
+static void Save_GROUP(void)
+{
+	Group *g;
+
+	FOR_ALL_GROUPS(g) {
+		SlSetArrayIndex(g->index);
+		SlObject(g, _group_desc);
+	}
+}
+
+
+static void Load_GROUP(void)
+{
+	int index;
+
+	while ((index = SlIterateArray()) != -1) {
+		if (!AddBlockIfNeeded(&_Group_pool, index)) {
+			error("Groups: failed loading savegame: too many groups");
+		}
+
+		Group *g = GetGroup(index);
+		SlObject(g, _group_desc);
+	}
+}
+
+extern const ChunkHandler _group_chunk_handlers[] = {
+	{ 'GRPS', Save_GROUP, Load_GROUP, CH_ARRAY | CH_LAST},
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/group_gui.cpp	Sat May 19 09:40:18 2007 +0000
@@ -0,0 +1,795 @@
+/* $Id$ */
+
+/** @file group_gui.cpp */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "functions.h"
+#include "table/strings.h"
+#include "table/sprites.h"
+#include "window.h"
+#include "gui.h"
+#include "gfx.h"
+#include "vehicle.h"
+#include "command.h"
+#include "engine.h"
+#include "vehicle_gui.h"
+#include "depot.h"
+#include "train.h"
+#include "date.h"
+#include "group.h"
+#include "helpers.hpp"
+#include "viewport.h"
+#include "strings.h"
+#include "debug.h"
+
+
+struct Sorting {
+	Listing aircraft;
+	Listing roadveh;
+	Listing ship;
+	Listing train;
+};
+
+static Sorting _sorting;
+
+
+static void BuildGroupList(grouplist_d* gl, PlayerID owner, VehicleType vehicle_type)
+{
+	const Group** list;
+	const Group *g;
+	uint n = 0;
+
+	if (!(gl->l.flags & VL_REBUILD)) return;
+
+	list = MallocT<const Group*>(GetGroupArraySize());
+	if (list == NULL) {
+		error("Could not allocate memory for the group-sorting-list");
+	}
+
+	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);
+	if (n != 0 && gl->sort_list == NULL) {
+		error("Could not allocate memory for the group-sorting-list");
+	}
+	gl->l.list_length = n;
+
+	for (uint i = 0; i < n; ++i) gl->sort_list[i] = list[i];
+	free((void*)list);
+
+	gl->l.flags &= ~VL_REBUILD;
+	gl->l.flags |= VL_RESORT;
+}
+
+
+static int CDECL GroupNameSorter(const void *a, const void *b)
+{
+	static const Group *last_group[2] = { NULL, NULL };
+	static char         last_name[2][64] = { "", "" };
+
+	const Group *ga = *(const Group**)a;
+	const Group *gb = *(const Group**)b;
+	int r;
+
+	if (ga != last_group[0]) {
+		last_group[0] = ga;
+		SetDParam(0, ga->index);
+		GetString(last_name[0], ga->string_id, lastof(last_name[0]));
+	}
+
+	if (gb != last_group[1]) {
+		last_group[1] = gb;
+		SetDParam(0, gb->index);
+		GetString(last_name[1], gb->string_id, lastof(last_name[1]));
+	}
+
+	r = strcmp(last_name[0], last_name[1]); // sort by name
+
+	if (r == 0) return ga->index - gb->index;
+
+	return r;
+}
+
+
+static void SortGroupList(grouplist_d *gl)
+{
+	if (!(gl->l.flags & VL_RESORT)) return;
+
+	qsort((void*)gl->sort_list, gl->l.list_length, sizeof(gl->sort_list[0]), GroupNameSorter);
+
+	gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+	gl->l.flags &= ~VL_RESORT;
+}
+
+
+enum GroupListWidgets {
+	GRP_WIDGET_CLOSEBOX = 0,
+	GRP_WIDGET_CAPTION,
+	GRP_WIDGET_STICKY,
+	GRP_WIDGET_EMPTY_TOP_LEFT,
+	GRP_WIDGET_ALL_VEHICLES,
+	GRP_WIDGET_LIST_GROUP,
+	GRP_WIDGET_LIST_GROUP_SCROLLBAR,
+	GRP_WIDGET_SORT_BY_ORDER,
+	GRP_WIDGET_SORT_BY_TEXT,
+	GRP_WIDGET_SORT_BY_DROPDOWN,
+	GRP_WIDGET_EMPTY_TOP_RIGHT,
+	GRP_WIDGET_LIST_VEHICLE,
+	GRP_WIDGET_LIST_VEHICLE_SCROLLBAR,
+	GRP_WIDGET_CREATE_GROUP,
+	GRP_WIDGET_DELETE_GROUP,
+	GRP_WIDGET_RENAME_GROUP,
+	GRP_WIDGET_EMPTY1,
+	GRP_WIDGET_REPLACE_PROTECTION,
+	GRP_WIDGET_EMPTY2,
+	GRP_WIDGET_AVAILABLE_VEHICLES,
+	GRP_WIDGET_MANAGE_VEHICLES,
+	GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
+	GRP_WIDGET_STOP_ALL,
+	GRP_WIDGET_START_ALL,
+	GRP_WIDGET_EMPTY_BOTTOM_RIGHT,
+	GRP_WIDGET_RESIZE,
+};
+
+
+static const Widget _group_widgets[] = {
+{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,             STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   513,     0,    13, 0x0,                  STR_018C_WINDOW_TITLE_DRAG_THIS},
+{  WWT_STICKYBOX,     RESIZE_LR,    14,   514,   525,     0,    13, 0x0,                  STR_STICKY_BUTTON},
+{      WWT_PANEL,   RESIZE_NONE,    14,     0,   200,    14,    25, 0x0,                  STR_NULL},
+{      WWT_PANEL,   RESIZE_NONE,    14,     0,   200,    26,    39, 0x0,                  STR_NULL},
+{     WWT_MATRIX, RESIZE_BOTTOM,    14,     0,   188,    39,   220, 0x701,                STR_GROUPS_CLICK_ON_GROUP_FOR_TIP},
+{  WWT_SCROLLBAR, RESIZE_BOTTOM,    14,   189,   200,    26,   220, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{ WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   201,   281,    14,    25, STR_SORT_BY,          STR_SORT_ORDER_TIP},
+{      WWT_PANEL,   RESIZE_NONE,    14,   282,   435,    14,    25, 0x0,                  STR_SORT_CRITERIA_TIP},
+{    WWT_TEXTBTN,   RESIZE_NONE,    14,   436,   447,    14,    25, STR_0225,             STR_SORT_CRITERIA_TIP},
+{      WWT_PANEL,  RESIZE_RIGHT,    14,   448,   525,    14,    25, 0x0,                  STR_NULL},
+{     WWT_MATRIX,     RESIZE_RB,    14,   201,   513,    26,   233, 0x701,                STR_NULL},
+{ WWT_SCROLL2BAR,    RESIZE_LRB,    14,   514,   525,    26,   233, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,     0,    23,   221,   245, 0x0,                  STR_GROUP_CREATE_TIP},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,    24,    47,   221,   245, 0x0,                  STR_GROUP_DELETE_TIP},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,    48,    71,   221,   245, 0x0,                  STR_GROUP_RENAME_TIP},
+{      WWT_PANEL,     RESIZE_TB,    14,    72,   164,   221,   245, 0x0,                  STR_NULL},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,   165,   188,   221,   245, 0x0,                  STR_GROUP_REPLACE_PROTECTION_TIP},
+{      WWT_PANEL,     RESIZE_TB,    14,   189,   200,   221,   245, 0x0,                  STR_NULL},
+{ WWT_PUSHTXTBTN,     RESIZE_TB,    14,   201,   306,   234,   245, 0x0,                  STR_AVAILABLE_ENGINES_TIP},
+{    WWT_TEXTBTN,     RESIZE_TB,    14,   307,   411,   234,   245, STR_MANAGE_LIST,      STR_MANAGE_LIST_TIP},
+{    WWT_TEXTBTN,     RESIZE_TB,    14,   412,   423,   234,   245, STR_0225,             STR_MANAGE_LIST_TIP},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,   424,   435,   234,   245, SPR_FLAG_VEH_STOPPED, STR_MASS_STOP_LIST_TIP},
+{ WWT_PUSHIMGBTN,     RESIZE_TB,    14,   436,   447,   234,   245, SPR_FLAG_VEH_RUNNING, STR_MASS_START_LIST_TIP},
+{      WWT_PANEL,    RESIZE_RTB,    14,   448,   513,   234,   245, 0x0,                  STR_NULL},
+{  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   514,   525,   234,   245, 0x0,                  STR_RESIZE_BUTTON},
+{   WIDGETS_END},
+};
+
+
+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 = 10 * 29;
+	w->resize.step_width = 1;
+
+	switch (gv->vehicle_type) {
+		default: NOT_REACHED();
+		case VEH_TRAIN:
+		case VEH_ROAD:
+			w->vscroll.cap = 14;
+			w->vscroll2.cap = 8;
+			w->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
+			break;
+		case VEH_SHIP:
+		case VEH_AIRCRAFT:
+			w->vscroll.cap = 10;
+			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(); break;
+		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 = DEFAULT_GROUP;
+
+	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;
+
+			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;
+
+			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();
+	}
+}
+
+/**
+ * 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);
+	const Player *p = GetPlayer(owner);
+	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);
+
+	switch(e->event) {
+		case WE_CREATE:
+			CreateVehicleGroupWindow(w);
+			break;
+
+		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 default group, 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, IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST);
+			SortVehicleList(gv);
+
+
+			BuildGroupList(gl, owner, gv->vehicle_type);
+			SortGroupList(gl);
+
+			SetVScrollCount(w, gl->l.list_length);
+			SetVScroll2Count(w, gv->l.list_length);
+
+			/* Disable all lists management button when the list is empty */
+			SetWindowWidgetsDisabledState(w, gv->l.list_length == 0,
+					GRP_WIDGET_STOP_ALL,
+					GRP_WIDGET_START_ALL,
+					GRP_WIDGET_MANAGE_VEHICLES,
+					GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN,
+					WIDGET_LIST_END);
+
+			/* Disable the group specific function when we select the default group */
+			SetWindowWidgetsDisabledState(w, IsDefaultGroupID(gv->group_sel),
+					GRP_WIDGET_DELETE_GROUP,
+					GRP_WIDGET_RENAME_GROUP,
+					GRP_WIDGET_REPLACE_PROTECTION,
+					WIDGET_LIST_END);
+
+			/* If selected_group == DEFAULT_GROUP, draw the standard caption
+			   We list all vehicles */
+			if (IsDefaultGroupID(gv->group_sel)) {
+				SetDParam(0, p->name_1);
+				SetDParam(1, p->name_2);
+				SetDParam(2, 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(); break;
+				}
+			} else {
+				const Group *g = GetGroup(gv->group_sel);
+
+				SetDParam(0, g->index);
+				SetDParam(1, g->num_vehicle);
+
+				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(); break;
+				}
+			}
+
+
+			DrawWindowWidgets(w);
+
+			/* Draw Matrix Group
+			 * The selected group is drawn in white */
+			StringID str;
+
+			switch (gv->vehicle_type) {
+				case VEH_TRAIN:    str = STR_GROUP_ALL_TRAINS;    break;
+				case VEH_ROAD:     str = STR_GROUP_ALL_ROADS;     break;
+				case VEH_SHIP:     str = STR_GROUP_ALL_SHIPS;     break;
+				case VEH_AIRCRAFT: str = STR_GROUP_ALL_AIRCRAFTS; break;
+				default: NOT_REACHED(); break;
+			}
+			DrawString(10, y1, str, IsDefaultGroupID(gv->group_sel) ? 12 : 16);
+
+			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_SV_GROUP_NAME, (gv->group_sel == g->index) ? 12 : 16);
+
+				/* 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) ? 12 : 16);
+			}
+
+			/* Draw Matrix Vehicle according to the vehicle list built before */
+			DrawString(285, 15, _vehicle_sort_listing[gv->l.sort_type], 0x10);
+			DoDrawString(gv->l.flags & VL_DESC ? DOWNARROW : UPARROW, 269, 15, 0x10);
+
+			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];
+				StringID str;
+
+				assert(v->type == gv->vehicle_type && v->owner == owner);
+
+				DrawVehicleImage(v, x + 19, y2 + 6, w->hscroll.cap, 0, gv->vehicle_sel);
+				DrawVehicleProfitButton(v, x, y2 + 13);
+
+				if (IsVehicleInDepot(v)) {
+					str = STR_021F;
+				} else {
+					str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
+				}
+				SetDParam(0, v->unitnumber);
+				DrawString(x, y2 + 2, str, 0);
+
+				if (w->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG2) DrawSmallOrderList(v, x + 138, y2);
+
+				if (v->profit_this_year < 0) {
+					str = v->profit_last_year < 0 ?
+							STR_PROFIT_BAD_THIS_YEAR_BAD_LAST_YEAR :
+							STR_PROFIT_BAD_THIS_YEAR_GOOD_LAST_YEAR;
+				} else {
+					str = v->profit_last_year < 0 ?
+							STR_PROFIT_GOOD_THIS_YEAR_BAD_LAST_YEAR :
+							STR_PROFIT_GOOD_THIS_YEAR_GOOD_LAST_YEAR;
+				}
+
+				SetDParam(0, v->profit_this_year);
+				SetDParam(1, v->profit_last_year);
+				DrawString(x + 19, y2 + w->resize.step_height - 8, str, 0);
+
+				if (IsValidGroupID(v->group_id)) {
+					SetDParam(0, v->group_id);
+					DrawString(x + 19, y2, STR_GROUP_TINY_NAME, 16);
+				}
+
+				y2 += w->resize.step_height;
+			}
+
+			break;
+		}
+
+		case WE_CLICK:
+			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);
+					SetWindowDirty(w);
+					break;
+
+				case GRP_WIDGET_SORT_BY_TEXT:
+				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, 0);
+					return;
+
+				case GRP_WIDGET_ALL_VEHICLES: // All vehicles button
+					if (!IsDefaultGroupID(gv->group_sel)) {
+						gv->group_sel = DEFAULT_GROUP;
+						gv->l.flags |= VL_REBUILD;
+						SetWindowDirty(w);
+					}
+					break;
+
+				case GRP_WIDGET_LIST_GROUP: { // Matrix Group
+					uint16 id_g = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 13) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
+
+					if (id_g >= w->vscroll.cap) return;
+
+					id_g += w->vscroll.pos;
+
+					if (id_g >= gl->l.list_length) return;
+
+					gv->group_sel = gl->sort_list[id_g]->index;;
+
+					gv->l.flags |= VL_REBUILD;
+					SetWindowDirty(w);
+					break;
+				}
+
+				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 (IsValidVehicle(v)) {
+						CursorID image;
+
+						switch (gv->vehicle_type) {
+							case VEH_TRAIN:    image = GetTrainImage(v, DIR_W);    break;
+							case VEH_ROAD:     image = GetRoadVehImage(v, DIR_W);  break;
+							case VEH_SHIP:     image = GetShipImage(v, DIR_W);     break;
+							case VEH_AIRCRAFT: image = GetAircraftImage(v, DIR_W); break;
+							default: NOT_REACHED(); break;
+						}
+
+						SetObjectToPlaceWnd(image, GetVehiclePalette(v), 4, w);
+					}
+
+					SetWindowDirty(w);
+					break;
+				}
+
+				case GRP_WIDGET_CREATE_GROUP: // Create a new group
+					if (!CmdFailed(DoCommandP(0, gv->vehicle_type, 0, NULL, CMD_CREATE_GROUP | CMD_MSG(STR_GROUP_CAN_T_CREATE)))) {
+						SetWindowDirty(w);
+						gl->l.flags |= VL_REBUILD;
+					}
+					break;
+
+				case GRP_WIDGET_DELETE_GROUP: // Delete the selected group
+					if (!CmdFailed(DoCommandP(0, gv->group_sel, 0, NULL, CMD_DELETE_GROUP | CMD_MSG(STR_GROUP_CAN_T_DELETE)))) {
+						gv->group_sel = DEFAULT_GROUP;
+						gv->l.flags |= VL_REBUILD;
+						gl->l.flags |= VL_REBUILD;
+						SetWindowDirty(w);
+					}
+					break;
+
+				case GRP_WIDGET_RENAME_GROUP: { // Rename the selected roup
+					assert(!IsDefaultGroupID(gv->group_sel));
+
+					const Group *g = GetGroup(gv->group_sel);
+
+					SetDParam(0, g->index);
+					ShowQueryString(g->string_id, STR_GROUP_RENAME_CAPTION, 31, 150, w, CS_ALPHANUMERAL);
+				}	break;
+
+
+				case GRP_WIDGET_AVAILABLE_VEHICLES:
+					ShowBuildVehicleWindow(0, gv->vehicle_type);
+					break;
+
+				case GRP_WIDGET_MANAGE_VEHICLES:
+				case GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN:  {
+					static StringID action_str[] = {
+						STR_REPLACE_VEHICLES,
+						STR_SEND_FOR_SERVICING,
+						STR_SEND_TRAIN_TO_DEPOT,
+						STR_NULL,
+						STR_NULL,
+						INVALID_STRING_ID
+					};
+
+					action_str[3] = IsDefaultGroupID(gv->group_sel) ? INVALID_STRING_ID : STR_GROUP_ADD_SHARED_VEHICLE;
+					action_str[4] = IsDefaultGroupID(gv->group_sel) ? INVALID_STRING_ID : STR_GROUP_REMOVE_ALL_VEHICLES;
+
+					ShowDropDownMenu(w, action_str, 0, GRP_WIDGET_MANAGE_VEHICLES_DROPDOWN, 0, 0);
+					break;
+				}
+
+
+				case GRP_WIDGET_START_ALL:
+				case GRP_WIDGET_STOP_ALL: { // Start/stop all vehicles of the list
+					DoCommandP(0, gv->group_sel, ((IsDefaultGroupID(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);
+
+					break;
+				}
+
+				case GRP_WIDGET_REPLACE_PROTECTION:
+					if (!IsDefaultGroupID(gv->group_sel)) {
+						Group *g = GetGroup(gv->group_sel);
+
+						g->replace_protection = !g->replace_protection;
+					}
+					break;
+			}
+
+			break;
+
+		case WE_DRAGDROP: {
+			switch (e->we.click.widget) {
+				case GRP_WIDGET_ALL_VEHICLES: // All trains
+					if (!CmdFailed(DoCommandP(0, DEFAULT_GROUP , gv->vehicle_sel, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE)))) {
+						gv->l.flags |= VL_REBUILD;
+					}
+
+					gv->vehicle_sel = INVALID_VEHICLE;
+
+					SetWindowDirty(w);
+
+					break;
+
+				case GRP_WIDGET_LIST_GROUP: { // Maxtrix group
+					uint16 id_g = (e->we.click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET - 13) / PLY_WND_PRC__SIZE_OF_ROW_TINY;
+					const VehicleID vindex = gv->vehicle_sel;
+
+					gv->vehicle_sel = INVALID_VEHICLE;
+
+					SetWindowDirty(w);
+
+					if (id_g >= w->vscroll.cap) return;
+
+					id_g += w->vscroll.pos;
+
+					if (id_g >= gl->l.list_length) return;
+
+					if (!CmdFailed(DoCommandP(0,  gl->sort_list[id_g]->index , vindex, NULL, CMD_ADD_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_VEHICLE)))) {
+						gv->l.flags |= VL_REBUILD;
+					}
+
+					break;
+				}
+
+				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;
+
+					SetWindowDirty(w);
+
+					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];
+
+					if (vindex == v->index) {
+						switch (gv->vehicle_type) {
+							default: NOT_REACHED(); break;
+							case VEH_TRAIN:    ShowTrainViewWindow(v);    break;
+							case VEH_ROAD:     ShowRoadVehViewWindow(v);  break;
+							case VEH_SHIP:     ShowShipViewWindow(v);     break;
+							case VEH_AIRCRAFT: ShowAircraftViewWindow(v); break;
+						}
+					}
+
+					break;
+				}
+			}
+			break;
+		}
+
+		case WE_ON_EDIT_TEXT:
+			if (!StrEmpty(e->we.edittext.str)) {
+				_cmd_text = e->we.edittext.str;
+
+				if (!CmdFailed(DoCommandP(0, gv->group_sel, 0, NULL, CMD_RENAME_GROUP | CMD_MSG(STR_GROUP_CAN_T_RENAME)))) {
+					SetWindowDirty(w);
+					gl->l.flags |= VL_REBUILD;
+				}
+			}
+			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);
+
+					switch (e->we.dropdown.index) {
+						case 0: // Replace window
+							ShowReplaceGroupVehicleWindow(gv->group_sel, gv->vehicle_type);
+							break;
+						case 1: // Send for servicing
+							DoCommandP(0, gv->group_sel, ((IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
+										| DEPOT_MASS_SEND
+										| DEPOT_SERVICE, NULL, GetCmdSendToDepot(gv->vehicle_type));
+							break;
+						case 2: // Send to Depots
+							DoCommandP(0, gv->group_sel, ((IsDefaultGroupID(gv->group_sel) ? VLW_STANDARD : VLW_GROUP_LIST) & VLW_MASK)
+										| DEPOT_MASS_SEND, NULL, GetCmdSendToDepot(gv->vehicle_type));
+							break;
+						case 3: // Add shared Vehicles
+							assert(!IsDefaultGroupID(gv->group_sel));
+
+							if (!CmdFailed(DoCommandP(0, gv->group_sel, gv->vehicle_type, NULL, CMD_ADD_SHARED_VEHICLE_GROUP | CMD_MSG(STR_GROUP_CAN_T_ADD_SHARED_VEHICLE)))) {
+								gv->l.flags |= VL_REBUILD;
+							}
+							break;
+						case 4: // Remove all Vehicles from the selected group
+							assert(!IsDefaultGroupID(gv->group_sel));
+
+							if (!CmdFailed(DoCommandP(0, gv->group_sel, gv->vehicle_type, NULL, CMD_REMOVE_ALL_VEHICLES_GROUP | CMD_MSG(STR_GROUP_CAN_T_REMOVE_ALL_VEHICLES)))) {
+								gv->l.flags |= VL_REBUILD;
+							}
+							break;
+						default: NOT_REACHED();
+					}
+					break;
+
+				default: NOT_REACHED();
+			}
+
+			SetWindowDirty(w);
+			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 (--gv->l.resort_timer == 0) {
+				gv->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+				gv->l.flags |= VL_RESORT;
+				SetWindowDirty(w);
+			}
+			if (--gl->l.resort_timer == 0) {
+				gl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
+				gl->l.flags |= VL_RESORT;
+				SetWindowDirty(w);
+			}
+			break;
+		}
+}
+
+
+static const WindowDesc _group_desc = {
+	WDP_AUTO, WDP_AUTO, 526, 246,
+	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
+};
+
+void ShowPlayerGroup(PlayerID player, VehicleType vehicle_type)
+{
+	WindowClass wc;
+
+	switch (vehicle_type) {
+		default: NOT_REACHED();
+		case VEH_TRAIN:    wc = WC_TRAINS_LIST;   break;
+		case VEH_ROAD:     wc = WC_ROADVEH_LIST;  break;
+		case VEH_SHIP:     wc = WC_SHIPS_LIST;    break;
+		case VEH_AIRCRAFT: wc = WC_AIRCRAFT_LIST; break;
+	}
+
+	WindowNumber num = (vehicle_type << 11) | VLW_GROUP_LIST | player;
+	DeleteWindowById(wc, num);
+	Window *w = AllocateWindowDescFront(&_group_desc, num);
+	if (w == NULL) return;
+
+	w->window_class = wc;
+
+	switch (vehicle_type) {
+		default: NOT_REACHED();
+		case VEH_ROAD:
+			ResizeWindow(w, -66,   0);
+			/* FALL THROUGH */
+		case VEH_TRAIN:
+			w->resize.height = w->height - (PLY_WND_PRC__SIZE_OF_ROW_SMALL * 4); // Minimum of 4 vehicles
+			break;
+
+		case VEH_SHIP:
+		case VEH_AIRCRAFT:
+			ResizeWindow(w, -66, -52);
+			w->resize.height = w->height;  // Minimum of 4 vehicles
+			break;
+	}
+
+	/* Set the minimum window size to the current window size */
+	w->resize.width = w->width;
+}
--- a/src/gui.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/gui.h	Sat May 19 09:40:18 2007 +0000
@@ -139,4 +139,6 @@
 /* vehicle_gui.cpp */
 void InitializeGUI();
 
+void ShowPlayerGroup(PlayerID player, VehicleType veh);
+
 #endif /* GUI_H */
--- a/src/lang/english.txt	Sat May 19 09:13:08 2007 +0000
+++ b/src/lang/english.txt	Sat May 19 09:40:18 2007 +0000
@@ -1098,6 +1098,7 @@
 STR_CONFIG_PATCHES_SCROLLWHEEL_OFF                              :Off
 STR_CONFIG_PATCHES_SCROLLWHEEL_MULTIPLIER                       :{LTBLUE}Map scrollwheel speed: {ORANGE}{STRING1}
 STR_CONFIG_PATCHES_PAUSE_ON_NEW_GAME                            :{LTBLUE}Automatically pause when starting a new game: {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_ADVANCED_VEHICLE_LISTS                       :{LTBLUE}Use the advanced vehicle list: {ORANGE}{STRING1}
 
 STR_CONFIG_PATCHES_MAX_TRAINS                                   :{LTBLUE}Max trains per player: {ORANGE}{STRING1}
 STR_CONFIG_PATCHES_MAX_ROADVEH                                  :{LTBLUE}Max road vehicles per player: {ORANGE}{STRING1}
@@ -2012,6 +2013,8 @@
 STR_SV_STNAME_HELIPORT                                          :{STRING1} Heliport
 STR_SV_STNAME_FOREST                                            :{STRING1} Forest
 
+STR_SV_GROUP_NAME                                               :{GROUP}
+
 ############ end of savegame specific region!
 
 ##id 0x6800
@@ -3187,3 +3190,41 @@
 STR_TRANSPARENT_BUILDINGS_DESC                                  :{BLACK}Toggle transparency for buildables like stations, depots, waypoints and catenary
 STR_TRANSPARENT_BRIDGES_DESC                                    :{BLACK}Toggle transparency for bridges
 STR_TRANSPARENT_STRUCTURES_DESC                                 :{BLACK}Toggle transparency for structures like lighthouses and antennas, maybe in future for eyecandy
+
+##### Mass Order
+STR_GROUP_NAME_FORMAT                                           :Group {COMMA}
+STR_GROUP_TINY_NAME                                             :{TINYFONT}{GROUP}
+STR_GROUP_ALL_TRAINS                                            :All trains
+STR_GROUP_ALL_ROADS                                             :All road vehicles
+STR_GROUP_ALL_SHIPS                                             :All ships
+STR_GROUP_ALL_AIRCRAFTS                                         :All aircraft
+STR_GROUP_TINY_NUM                                              :{TINYFONT}{COMMA}
+STR_GROUP_ADD_SHARED_VEHICLE                                    :Add shared vehicles
+STR_GROUP_REMOVE_ALL_VEHICLES                                   :Remove all vehicles
+
+STR_GROUP_TRAINS_CAPTION                                        :{WHITE}{GROUP} - {COMMA} Train{P "" s}
+STR_GROUP_ROADVEH_CAPTION                                       :{WHITE}{GROUP} - {COMMA} Road Vehicle{P "" s}
+STR_GROUP_SHIPS_CAPTION                                         :{WHITE}{GROUP} - {COMMA} Ship{P "" s}
+STR_GROUP_AIRCRAFTS_CAPTION                                     :{WHITE}{GROUP} - {COMMA} Aircraft
+STR_GROUP_RENAME_CAPTION                                        :{BLACK}Rename a group
+STR_GROUP_REPLACE_CAPTION                                       :{WHITE}Replace Vehicles of "{GROUP}"
+
+STR_GROUP_CAN_T_CREATE                                          :{WHITE}Can't create group...
+STR_GROUP_CAN_T_DELETE                                          :{WHITE}Can't delete this group...
+STR_GROUP_CAN_T_RENAME                                          :{WHITE}Can't rename group...
+STR_GROUP_CAN_T_REMOVE_ALL_VEHICLES                             :{WHITE}Can't remove all vehicles from this group...
+STR_GROUP_CAN_T_ADD_VEHICLE                                     :{WHITE}Can't add the vehicle to this group...
+STR_GROUP_CAN_T_ADD_SHARED_VEHICLE                              :{WHITE}Can't add shared vehicles to group...
+
+STR_GROUPS_CLICK_ON_GROUP_FOR_TIP                               :{BLACK}Groups - Click on a group to list all vehicles of this group
+STR_GROUP_CREATE_TIP                                            :{BLACK}Click to create a group
+STR_GROUP_DELETE_TIP                                            :{BLACK}Delete the selected group
+STR_GROUP_RENAME_TIP                                            :{BLACK}Rename the selected group
+STR_GROUP_REPLACE_PROTECTION_TIP                                :{BLACK}Click to protect this group from global autoreplace
+
+STR_PROFIT_GOOD_THIS_YEAR_GOOD_LAST_YEAR                        :{TINYFONT}{BLACK}Profit this year: {GREEN}{CURRENCY} {BLACK}(last year: {GREEN}{CURRENCY}{BLACK})
+STR_PROFIT_BAD_THIS_YEAR_GOOD_LAST_YEAR                         :{TINYFONT}{BLACK}Profit this year: {RED}{CURRENCY} {BLACK}(last year: {GREEN}{CURRENCY}{BLACK})
+STR_PROFIT_GOOD_THIS_YEAR_BAD_LAST_YEAR                         :{TINYFONT}{BLACK}Profit this year: {GREEN}{CURRENCY} {BLACK}(last year: {RED}{CURRENCY}{BLACK})
+STR_PROFIT_BAD_THIS_YEAR_BAD_LAST_YEAR                          :{TINYFONT}{BLACK}Profit this year: {RED}{CURRENCY} {BLACK}(last year: {RED}{CURRENCY}{BLACK})
+
+########
--- a/src/misc.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/misc.cpp	Sat May 19 09:40:18 2007 +0000
@@ -22,6 +22,7 @@
 #include "newgrf_house.h"
 #include "date.h"
 #include "cargotype.h"
+#include "group.h"
 
 char _name_array[512][32];
 
@@ -120,6 +121,7 @@
 	InitializeWaypoints();
 	InitializeDepots();
 	InitializeOrders();
+	InitializeGroup();
 
 	InitNewsItemStructs();
 	InitializeLandscape();
--- a/src/openttd.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/openttd.cpp	Sat May 19 09:40:18 2007 +0000
@@ -63,6 +63,7 @@
 #include "newgrf_house.h"
 #include "newgrf_commons.h"
 #include "player_face.h"
+#include "group.h"
 
 #include "bridge_map.h"
 #include "clear_map.h"
@@ -294,6 +295,7 @@
 	CleanPool(&_Vehicle_pool);
 	CleanPool(&_Sign_pool);
 	CleanPool(&_Order_pool);
+	CleanPool(&_Group_pool);
 
 	free((void*)_town_sort);
 	free((void*)_industry_sort);
@@ -1954,6 +1956,20 @@
 		_opt.diff.number_towns++;
 	}
 
+	/* Recalculate */
+	Group *g;
+	FOR_ALL_GROUPS(g) {
+		const Vehicle *v;
+		FOR_ALL_VEHICLES(v) {
+			if (!IsEngineCountable(v)) continue;
+
+			if (v->group_id != g->index || v->type != g->vehicle_type || v->owner != g->owner) continue;
+
+			g->num_engines[v->engine_type]++;
+		}
+	}
+
+
 	return true;
 }
 
--- a/src/openttd.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/openttd.h	Sat May 19 09:40:18 2007 +0000
@@ -40,6 +40,7 @@
 struct NewsItem;
 struct Industry;
 struct DrawPixelInfo;
+struct Group;
 typedef byte VehicleOrderID;  ///< The index of an order within its current vehicle (not pool related)
 typedef byte CargoID;
 typedef byte LandscapeID;
@@ -63,6 +64,7 @@
 typedef uint16 WaypointID;
 typedef uint16 OrderID;
 typedef uint16 SignID;
+typedef uint16 GroupID;
 typedef uint16 EngineRenewID;
 typedef uint16 DestinationID;
 
--- a/src/player.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/player.h	Sat May 19 09:40:18 2007 +0000
@@ -311,7 +311,7 @@
  * @return The engine type to replace with, or INVALID_ENGINE if no
  * replacement is in the list.
  */
-static inline EngineID EngineReplacementForPlayer(const Player *p, EngineID engine) { return EngineReplacement(p->engine_renew_list, engine); }
+static inline EngineID EngineReplacementForPlayer(const Player *p, EngineID engine, GroupID group) { return EngineReplacement(p->engine_renew_list, engine, group); }
 
 /**
  * Check if a player has a replacement set up for the given engine.
@@ -319,7 +319,7 @@
  * @param  engine Engine type to be replaced.
  * @return true if a replacement was set up, false otherwise.
  */
-static inline bool EngineHasReplacementForPlayer(const Player *p, EngineID engine) { return EngineReplacementForPlayer(p, engine) != INVALID_ENGINE; }
+static inline bool EngineHasReplacementForPlayer(const Player *p, EngineID engine, GroupID group) { return EngineReplacementForPlayer(p, engine, group) != INVALID_ENGINE; }
 
 /**
  * Add an engine replacement for the player.
@@ -329,7 +329,7 @@
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-static inline int32 AddEngineReplacementForPlayer(Player *p, EngineID old_engine, EngineID new_engine, uint32 flags) { return AddEngineReplacement(&p->engine_renew_list, old_engine, new_engine, flags); }
+static inline int32 AddEngineReplacementForPlayer(Player *p, EngineID old_engine, EngineID new_engine, GroupID group, uint32 flags) { return AddEngineReplacement(&p->engine_renew_list, old_engine, new_engine, group, flags); }
 
 /**
  * Remove an engine replacement for the player.
@@ -338,7 +338,7 @@
  * @param flags The calling command flags.
  * @return 0 on success, CMD_ERROR on failure.
  */
-static inline int32 RemoveEngineReplacementForPlayer(Player *p, EngineID engine, uint32 flags) {return RemoveEngineReplacement(&p->engine_renew_list, engine, flags); }
+static inline int32 RemoveEngineReplacementForPlayer(Player *p, EngineID engine, GroupID group, uint32 flags) {return RemoveEngineReplacement(&p->engine_renew_list, engine, group, flags); }
 
 /**
  * Reset the livery schemes to the player's primary colour.
--- a/src/players.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/players.cpp	Sat May 19 09:40:18 2007 +0000
@@ -27,6 +27,7 @@
 #include "date.h"
 #include "window.h"
 #include "player_face.h"
+#include "group.h"
 
 /**
  * Sets the local player and updates the patch settings that are set on a
@@ -638,6 +639,7 @@
  * if p1 = 2, then
  * - p2 = minimum amount of money available
  * if p1 = 3, then:
+ * - p1 bits  8-15 = engine group
  * - p2 bits  0-15 = old engine type
  * - p2 bits 16-31 = new engine type
  * if p1 = 4, then:
@@ -693,8 +695,11 @@
 		case 3: {
 			EngineID old_engine_type = GB(p2, 0, 16);
 			EngineID new_engine_type = GB(p2, 16, 16);
+			GroupID id_g = GB(p1, 16, 8);
 			int32 cost;
 
+			if (!IsValidGroupID(id_g)) return CMD_ERROR;
+
 			if (new_engine_type != INVALID_ENGINE) {
 				/* First we make sure that it's a valid type the user requested
 				 * check that it's an engine that is in the engine array */
@@ -714,9 +719,9 @@
 				if (!HASBIT(GetEngine(new_engine_type)->player_avail, _current_player))
 					return CMD_ERROR;
 
-				cost = AddEngineReplacementForPlayer(p, old_engine_type, new_engine_type, flags);
+				cost = AddEngineReplacementForPlayer(p, old_engine_type, new_engine_type, id_g, flags);
 			} else {
-				cost = RemoveEngineReplacementForPlayer(p, old_engine_type, flags);
+				cost = RemoveEngineReplacementForPlayer(p, old_engine_type,id_g, flags);
 			}
 
 			if (IsLocalPlayer()) InvalidateAutoreplaceWindow(old_engine_type);
@@ -901,6 +906,7 @@
 			p->is_active = false;
 		}
 		RemoveAllEngineReplacementForPlayer(p);
+		RemoveAllGroupsForPlayer(p);
 
 	} break;
 
--- a/src/saveload.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/saveload.cpp	Sat May 19 09:40:18 2007 +0000
@@ -29,7 +29,7 @@
 #include <setjmp.h>
 #include <list>
 
-extern const uint16 SAVEGAME_VERSION = 59;
+extern const uint16 SAVEGAME_VERSION = 60;
 uint16 _sl_version;       ///< the major savegame version identifier
 byte   _sl_minor_version; ///< the minor savegame version, DO NOT USE!
 
@@ -1257,6 +1257,7 @@
 extern const ChunkHandler _economy_chunk_handlers[];
 extern const ChunkHandler _animated_tile_chunk_handlers[];
 extern const ChunkHandler _newgrf_chunk_handlers[];
+extern const ChunkHandler _group_chunk_handlers[];
 
 static const ChunkHandler * const _chunk_handlers[] = {
 	_misc_chunk_handlers,
@@ -1274,6 +1275,7 @@
 	_player_chunk_handlers,
 	_animated_tile_chunk_handlers,
 	_newgrf_chunk_handlers,
+	_group_chunk_handlers,
 	NULL,
 };
 
--- a/src/settings.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/settings.cpp	Sat May 19 09:40:18 2007 +0000
@@ -1342,6 +1342,7 @@
 	SDT_VAR(Patches, scrollwheel_scrolling,SLE_UINT8,S,MS, 0,  0,  2, 0, STR_CONFIG_PATCHES_SCROLLWHEEL_SCROLLING, NULL),
 	SDT_VAR(Patches,scrollwheel_multiplier,SLE_UINT8,S, 0, 5,  1, 15, 1, STR_CONFIG_PATCHES_SCROLLWHEEL_MULTIPLIER,NULL),
 	SDT_BOOL(Patches, pause_on_newgame,              S, 0, false,        STR_CONFIG_PATCHES_PAUSE_ON_NEW_GAME,     NULL),
+	SDT_BOOL(Patches, advanced_vehicle_list,         S, 0, true,        STR_CONFIG_PATCHES_ADVANCED_VEHICLE_LISTS,     NULL),
 
 	/***************************************************************************/
 	/* Construction section of the GUI-configure patches window */
--- a/src/settings_gui.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/settings_gui.cpp	Sat May 19 09:40:18 2007 +0000
@@ -598,6 +598,7 @@
 	"scrollwheel_scrolling",
 	"scrollwheel_multiplier",
 	"pause_on_newgame",
+	"advanced_vehicle_list",
 };
 
 static const char *_patches_construction[] = {
--- a/src/strgen/strgen.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/strgen/strgen.cpp	Sat May 19 09:40:18 2007 +0000
@@ -506,6 +506,7 @@
 	{"WAYPOINT", EmitSingleChar, SCC_WAYPOINT_NAME, 1, 0}, // waypoint name
 	{"STATION",  EmitSingleChar, SCC_STATION_NAME,  1, 0},
 	{"TOWN",     EmitSingleChar, SCC_TOWN_NAME,     1, 0},
+	{"GROUP",    EmitSingleChar, SCC_GROUP_NAME,    1, 0},
 
 	// 0x9D is used for the pseudo command SETCASE
 	// 0x9E is used for case switching
--- a/src/strings.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/strings.cpp	Sat May 19 09:40:18 2007 +0000
@@ -25,6 +25,8 @@
 #include "industry.h"
 #include "helpers.hpp"
 #include "cargotype.h"
+#include "group.h"
+#include "debug.h"
 
 /* for opendir/readdir/closedir */
 # include "fios.h"
@@ -840,6 +842,18 @@
 				break;
 			}
 
+			case SCC_GROUP_NAME: { // {GROUP}
+				const Group *g = GetGroup(GetInt32(&argv));
+				int32 args[1];
+
+				assert(IsValidGroup(g));
+
+				args[0] = g->index;
+				buff = GetStringWithArgs(buff, (g->string_id == STR_SV_GROUP_NAME) ? (StringID)STR_GROUP_NAME_FORMAT : g->string_id, args, last);
+
+				break;
+			}
+
 			case SCC_CURRENCY_64: { // {CURRENCY64}
 				buff = FormatGenericCurrency(buff, _currency, GetInt64(&argv), false, last);
 				break;
--- a/src/table/control_codes.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/table/control_codes.h	Sat May 19 09:40:18 2007 +0000
@@ -26,6 +26,7 @@
 	SCC_WAYPOINT_NAME,
 	SCC_STATION_NAME,
 	SCC_TOWN_NAME,
+	SCC_GROUP_NAME,
 
 	SCC_CURRENCY_COMPACT,
 	SCC_CURRENCY_COMPACT_64,
--- a/src/table/files.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/table/files.h	Sat May 19 09:40:18 2007 +0000
@@ -62,4 +62,5 @@
 	{ "openttd.grf",   { 0x85, 0x4f, 0xf6, 0xb5, 0xd2, 0xf7, 0xbc, 0x1e, 0xb9, 0xdc, 0x44, 0xef, 0x35, 0x5f, 0x64, 0x9b } },
 	{ "trkfoundw.grf", { 0x12, 0x33, 0x3f, 0xa3, 0xd1, 0x86, 0x8b, 0x04, 0x53, 0x18, 0x9c, 0xee, 0xf9, 0x2d, 0xf5, 0x95 } },
 	{ "roadstops.grf", { 0x8c, 0xd9, 0x45, 0x21, 0x28, 0x82, 0x96, 0x45, 0x33, 0x22, 0x7a, 0xb9, 0x0d, 0xf3, 0x67, 0x4a } },
+	{ "group.grf",     { 0xe8, 0x52, 0x5f, 0x1c, 0x3e, 0xf9, 0x91, 0x9d, 0x0f, 0x70, 0x8c, 0x8a, 0x21, 0xa4, 0xc7, 0x02 } },
 };
--- a/src/table/sprites.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/table/sprites.h	Sat May 19 09:40:18 2007 +0000
@@ -128,6 +128,28 @@
 	SPR_TRUCK_STOP_DT_X_W = SPR_ROADSTOP_BASE + 6,
 	SPR_TRUCK_STOP_DT_X_E = SPR_ROADSTOP_BASE + 7,
 
+	SPR_GROUP_BASE                 = SPR_ROADSTOP_BASE + 8, // The sprites used for the group interface
+	SPR_GROUP_CREATE_TRAIN         = SPR_GROUP_BASE,
+	SPR_GROUP_CREATE_ROADVEH       = SPR_GROUP_BASE + 1,
+	SPR_GROUP_CREATE_SHIP          = SPR_GROUP_BASE + 2,
+	SPR_GROUP_CREATE_AIRCRAFT      = SPR_GROUP_BASE + 3,
+	SPR_GROUP_DELETE_TRAIN         = SPR_GROUP_BASE + 4,
+	SPR_GROUP_DELETE_ROADVEH       = SPR_GROUP_BASE + 5,
+	SPR_GROUP_DELETE_SHIP          = SPR_GROUP_BASE + 6,
+	SPR_GROUP_DELETE_AIRCRAFT      = SPR_GROUP_BASE + 7,
+	SPR_GROUP_RENAME_TRAIN         = SPR_GROUP_BASE + 8,
+	SPR_GROUP_RENAME_ROADVEH       = SPR_GROUP_BASE + 9,
+	SPR_GROUP_RENAME_SHIP          = SPR_GROUP_BASE + 10,
+	SPR_GROUP_RENAME_AIRCRAFT      = SPR_GROUP_BASE + 11,
+	SPR_GROUP_REPLACE_ON_TRAIN     = SPR_GROUP_BASE + 12,
+	SPR_GROUP_REPLACE_ON_ROADVEH   = SPR_GROUP_BASE + 13,
+	SPR_GROUP_REPLACE_ON_SHIP      = SPR_GROUP_BASE + 14,
+	SPR_GROUP_REPLACE_ON_AIRCRAFT  = SPR_GROUP_BASE + 15,
+	SPR_GROUP_REPLACE_OFF_TRAIN    = SPR_GROUP_BASE + 16,
+	SPR_GROUP_REPLACE_OFF_ROADVEH  = SPR_GROUP_BASE + 17,
+	SPR_GROUP_REPLACE_OFF_SHIP     = SPR_GROUP_BASE + 18,
+	SPR_GROUP_REPLACE_OFF_AIRCRAFT = SPR_GROUP_BASE + 19,
+
 	/* Manager face sprites */
 	SPR_GRADIENT = 874, // background gradient behind manager face
 
--- a/src/train_cmd.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/train_cmd.cpp	Sat May 19 09:40:18 2007 +0000
@@ -37,6 +37,7 @@
 #include "yapf/yapf.h"
 #include "date.h"
 #include "cargotype.h"
+#include "group.h"
 
 static bool TrainCheckIfLineEnds(Vehicle *v);
 static void TrainController(Vehicle *v, bool update_image);
@@ -637,12 +638,15 @@
 			v->cur_image = 0xAC2;
 			v->random_bits = VehicleRandomBits();
 
+			v->group_id = DEFAULT_GROUP;
+
 			AddArticulatedParts(vl);
 
 			_new_vehicle_id = v->index;
 
 			VehiclePositionChanged(v);
 			TrainConsistChanged(GetFirstVehicleInChain(v));
+			UpdateTrainGroupID(GetFirstVehicleInChain(v));
 
 			InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 			if (IsLocalPlayer()) {
@@ -797,6 +801,8 @@
 			v->vehicle_flags = 0;
 			if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SETBIT(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
 
+			v->group_id = DEFAULT_GROUP;
+
 			v->subtype = 0;
 			SetFrontEngine(v);
 			SetTrainEngine(v);
@@ -818,6 +824,7 @@
 
 			TrainConsistChanged(v);
 			UpdateTrainAcceleration(v);
+			UpdateTrainGroupID(v);
 
 			if (!HASBIT(p2, 1)) { // check if the cars should be added to the new vehicle
 				NormalizeTrainVehInDepot(v);
@@ -1113,6 +1120,16 @@
 		for (Vehicle *u = src_head; u != NULL; u = u->next) u->first = NULL;
 		for (Vehicle *u = dst_head; u != NULL; u = u->next) u->first = NULL;
 
+		/* If we move the front Engine and if the second vehicle is not an engine
+		   add the whole vehicle to the DEFAULT_GROUP */
+		if (IsFrontEngine(src) && !IsDefaultGroupID(src->group_id)) {
+			const Vehicle *v = GetNextVehicle(src);
+
+			if (v != NULL && !IsTrainEngine(v)) {
+				DoCommand(tile, DEFAULT_GROUP, v->index, flags, CMD_ADD_VEHICLE_GROUP);
+			}
+		}
+
 		if (HASBIT(p2, 0)) {
 			/* unlink ALL wagons */
 			if (src != src_head) {
@@ -1142,6 +1159,14 @@
 					SetFrontEngine(src);
 					assert(src->orders == NULL);
 					src->num_orders = 0;
+
+					// Decrease the engines number of the src engine_type
+					if (!IsDefaultGroupID(src->group_id) && IsValidGroupID(src->group_id)) {
+						GetGroup(src->group_id)->num_engines[src->engine_type]--;
+					}
+
+					// If we move an engine to a new line affect it to the DEFAULT_GROUP
+					src->group_id = DEFAULT_GROUP;
 				}
 			} else {
 				SetFreeWagon(src);
@@ -1203,13 +1228,18 @@
 		 * To do this, CmdMoveRailVehicle must be called once more
 		 * we can't loop forever here because next time we reach this line we will have a front engine */
 		if (src_head != NULL && !IsFrontEngine(src_head) && IsTrainEngine(src_head)) {
+			/* As in CmdMoveRailVehicle src_head->group_id will be equal to DEFAULT_GROUP
+			 * we need to save the group and reaffect it to src_head */
+			const GroupID tmp_g = src_head->group_id;
 			CmdMoveRailVehicle(0, flags, src_head->index | (INVALID_VEHICLE << 16), 1);
+			SetTrainGroupID(src_head, tmp_g);
 			src_head = NULL; // don't do anything more to this train since the new call will do it
 		}
 
 		if (src_head != NULL) {
 			NormaliseTrainConsist(src_head);
 			TrainConsistChanged(src_head);
+			UpdateTrainGroupID(src_head);
 			if (IsFrontEngine(src_head)) {
 				UpdateTrainAcceleration(src_head);
 				InvalidateWindow(WC_VEHICLE_DETAILS, src_head->index);
@@ -1224,6 +1254,7 @@
 		if (dst_head != NULL) {
 			NormaliseTrainConsist(dst_head);
 			TrainConsistChanged(dst_head);
+			UpdateTrainGroupID(dst_head);
 			if (IsFrontEngine(dst_head)) {
 				UpdateTrainAcceleration(dst_head);
 				InvalidateWindow(WC_VEHICLE_DETAILS, dst_head->index);
@@ -1364,6 +1395,8 @@
 					if (first->next_shared != NULL) {
 						first->next_shared->prev_shared = new_f;
 						new_f->next_shared = first->next_shared;
+					} else {
+						RemoveVehicleFromGroup(v);
 					}
 
 					/*
@@ -1394,6 +1427,7 @@
 				if (first != NULL) {
 					NormaliseTrainConsist(first);
 					TrainConsistChanged(first);
+					UpdateTrainGroupID(first);
 					if (IsFrontEngine(first)) {
 						InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
 						InvalidateWindow(WC_VEHICLE_REFIT, first->index);
@@ -1447,6 +1481,7 @@
 					first = UnlinkWagon(v, first);
 					DeleteDepotHighlightOfVehicle(v);
 					DeleteVehicle(v);
+					RemoveVehicleFromGroup(v);
 				}
 			}
 
@@ -1454,6 +1489,7 @@
 			if (flags & DC_EXEC && first != NULL) {
 				NormaliseTrainConsist(first);
 				TrainConsistChanged(first);
+				UpdateTrainGroupID(first);
 				if (IsFrontEngine(first)) UpdateTrainAcceleration(first);
 				InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
 				InvalidateWindow(WC_VEHICLE_REFIT, first->index);
@@ -3063,6 +3099,9 @@
 
 	BeginVehicleMove(v);
 	EndVehicleMove(v);
+
+	if (IsFrontEngine(v)) RemoveVehicleFromGroup(v);
+
 	DeleteVehicle(v);
 
 	if (v->u.rail.track != TRACK_BIT_DEPOT && v->u.rail.track != TRACK_BIT_WORMHOLE)
@@ -3148,6 +3187,7 @@
 
 	if (state >= 4440 && !(v->tick_counter&0x1F)) {
 		DeleteLastWagon(v);
+		InvalidateWindow(WC_REPLACE_VEHICLE, (v->group_id << 16) | VEH_TRAIN);
 	}
 }
 
--- a/src/variables.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/variables.h	Sat May 19 09:40:18 2007 +0000
@@ -130,6 +130,7 @@
 	bool measure_tooltip;               // Show a permanent tooltip when dragging tools
 	byte liveries;                      // Options for displaying company liveries, 0=none, 1=self, 2=all
 	bool prefer_teamchat;               // Choose the chat message target with <ENTER>, true=all players, false=your team
+	bool advanced_vehicle_list;         // Use the "advanced" vehicle list
 
 	uint8 toolbar_pos;                  // position of toolbars, 0=left, 1=center, 2=right
 	uint8 window_snap_radius;           // Windows snap at each other if closer than this
--- a/src/vehicle.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/vehicle.cpp	Sat May 19 09:40:18 2007 +0000
@@ -40,6 +40,7 @@
 #include "newgrf_engine.h"
 #include "newgrf_sound.h"
 #include "helpers.hpp"
+#include "group.h"
 #include "economy.h"
 
 #define INVALID_COORD (-0x8000)
@@ -111,7 +112,7 @@
 		return false; // Crashed vehicles don't need service anymore
 
 	if (_patches.no_servicing_if_no_breakdowns && _opt.diff.vehicle_breakdowns == 0) {
-		return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type);  /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */
+		return EngineHasReplacementForPlayer(GetPlayer(v->owner), v->engine_type, v->group_id);  /* Vehicles set for autoreplacing needs to go to a depot even if breakdowns are turned off */
 	}
 
 	return _patches.servint_ispercent ?
@@ -284,6 +285,8 @@
 	v->prev_shared = NULL;
 	v->depot_list  = NULL;
 	v->random_bits = 0;
+	v->group_id = DEFAULT_GROUP;
+
 	return v;
 }
 
@@ -580,6 +583,8 @@
 	if (IsEngineCountable(v)) {
 		GetPlayer(v->owner)->num_engines[v->engine_type]--;
 		if (v->owner == _local_player) InvalidateAutoreplaceWindow(v->engine_type);
+
+		if (!IsDefaultGroupID(v->group_id) && IsValidGroupID(v->group_id)) GetGroup(v->group_id)->num_engines[v->engine_type]--;
 	}
 
 	DeleteVehicleNews(v->index, INVALID_STRING_ID);
@@ -1821,6 +1826,12 @@
 		_new_vehicle_id = w_front->index;
 	}
 
+	if (flags & DC_EXEC) {
+		/* Cloned vehicles belong to the same group */
+		DoCommand(0, v_front->group_id, w_front->index, flags, CMD_ADD_VEHICLE_GROUP);
+	}
+
+
 	/* Take care of refitting. */
 	w = w_front;
 	v = v_front;
@@ -1973,6 +1984,7 @@
       <li>VLW_SHARED_ORDERS: index of order to generate a list for<li>
       <li>VLW_STANDARD: not used<li>
       <li>VLW_DEPOT_LIST: TileIndex of the depot/hangar to make the list for</li>
+      <li>VLW_GROUP_LIST: index of group to generate a list for</li>
     </ul>
 * @param window_type tells what kind of window the list is for. Use the VLW flags in vehicle_gui.h
 * @return the number of vehicles added to the list
@@ -2051,6 +2063,19 @@
 			break;
 		}
 
+ 		case VLW_GROUP_LIST:
+			FOR_ALL_VEHICLES(v) {
+				if (v->type == type && (
+							(type == VEH_TRAIN && IsFrontEngine(v)) ||
+							(type != VEH_TRAIN && v->subtype <= subtype)
+						) && v->owner == owner && v->group_id == index) {
+					if (n == *length_of_array) ExtendVehicleListSize(sort_list, length_of_array, GetNumVehicles() / 4);
+
+					(*sort_list)[n++] = v;
+				}
+			}
+			break;
+
 		default: NOT_REACHED(); break;
 	}
 
@@ -2667,6 +2692,8 @@
 	    SLE_REF(Vehicle, next_shared,          REF_VEHICLE),
 	    SLE_REF(Vehicle, prev_shared,          REF_VEHICLE),
 
+	SLE_CONDVAR(Vehicle, group_id,             SLE_UINT16,                60, SL_MAX_VERSION),
+
 	/* reserve extra space in savegame here. (currently 10 bytes) */
 	SLE_CONDNULL(10,                                                       2, SL_MAX_VERSION),
 
@@ -2865,6 +2892,9 @@
 			v->current_order.flags = (v->current_order.type & 0xF0) >> 4;
 			v->current_order.type.m_val &= 0x0F;
 		}
+
+		/* Advanced vehicle lists got added */
+		if (CheckSavegameVersion(60)) v->group_id = DEFAULT_GROUP;
 	}
 
 	/* Check for shared order-lists (we now use pointers for that) */
--- a/src/vehicle.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/vehicle.h	Sat May 19 09:40:18 2007 +0000
@@ -310,6 +310,8 @@
 	TileIndex cargo_loaded_at_xy;  ///< tile index where feeder cargo was loaded
 	uint32 value;
 
+	GroupID group_id;              ///< Index of group Pool array
+
 	union {
 		VehicleRail rail;
 		VehicleAir air;
--- a/src/vehicle_gui.cpp	Sat May 19 09:13:08 2007 +0000
+++ b/src/vehicle_gui.cpp	Sat May 19 09:40:18 2007 +0000
@@ -31,6 +31,7 @@
 #include "depot.h"
 #include "helpers.hpp"
 #include "cargotype.h"
+#include "group.h"
 
 struct Sorting {
 	Listing aircraft;
@@ -41,15 +42,6 @@
 
 static Sorting _sorting;
 
-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));
-
 static bool   _internal_sort_order;     // descending/ascending
 
 typedef int CDECL VehicleSortListingTypeFunction(const void*, const void*);
@@ -78,7 +70,7 @@
 	&VehicleValueSorter,
 };
 
-static const StringID _vehicle_sort_listing[] = {
+const StringID _vehicle_sort_listing[] = {
 	STR_SORT_BY_NUMBER,
 	STR_SORT_BY_DROPDOWN_NAME,
 	STR_SORT_BY_AGE,
@@ -134,7 +126,7 @@
 	}
 }
 
-static void BuildVehicleList(vehiclelist_d* vl, PlayerID owner, uint16 index, uint16 window_type)
+void BuildVehicleList(vehiclelist_d *vl, PlayerID owner, uint16 index, uint16 window_type)
 {
 	if (!(vl->l.flags & VL_REBUILD)) return;
 
@@ -146,7 +138,7 @@
 	vl->l.flags |= VL_RESORT;
 }
 
-static void SortVehicleList(vehiclelist_d *vl)
+void SortVehicleList(vehiclelist_d *vl)
 {
 	if (!(vl->l.flags & VL_RESORT)) return;
 
@@ -749,16 +741,6 @@
 	}
 }
 
-/*
- * Start of functions regarding vehicle list windows
- */
-
-enum {
-	PLY_WND_PRC__OFFSET_TOP_WIDGET = 26,
-	PLY_WND_PRC__SIZE_OF_ROW_SMALL = 26,
-	PLY_WND_PRC__SIZE_OF_ROW_BIG   = 36,
-};
-
 enum VehicleListWindowWidgets {
 	VLW_WIDGET_CLOSEBOX = 0,
 	VLW_WIDGET_CAPTION,
@@ -926,7 +908,7 @@
 	vl->l.resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS; // Set up resort timer
 }
 
-static void DrawSmallOrderList(const Vehicle *v, int x, int y)
+void DrawSmallOrderList(const Vehicle *v, int x, int y)
 {
 	const Order *order;
 	int sel, i = 0;
@@ -1275,7 +1257,11 @@
 
 void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type)
 {
-	ShowVehicleListWindowLocal(player, VLW_STANDARD, vehicle_type, 0);
+	if (player == _local_player && _patches.advanced_vehicle_list) {
+		ShowPlayerGroup(player, vehicle_type);
+	} else {
+		ShowVehicleListWindowLocal(player, VLW_STANDARD, vehicle_type, 0);
+	}
 }
 
 void ShowVehicleListWindow(const Vehicle *v)
--- a/src/vehicle_gui.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/vehicle_gui.h	Sat May 19 09:40:18 2007 +0000
@@ -15,21 +15,35 @@
 /* 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
 
+extern const StringID _vehicle_sort_listing[];
+
+/* Start of functions regarding vehicle list windows */
+enum {
+	PLY_WND_PRC__OFFSET_TOP_WIDGET = 26,
+	PLY_WND_PRC__SIZE_OF_ROW_TINY  = 13,
+	PLY_WND_PRC__SIZE_OF_ROW_SMALL = 26,
+	PLY_WND_PRC__SIZE_OF_ROW_BIG   = 36,
+	PLY_WND_PRC__SIZE_OF_ROW_BIG2  = 39,
+};
+
 /* Vehicle List Window type flags */
 enum {
 	VLW_STANDARD      = 0 << 8,
 	VLW_SHARED_ORDERS = 1 << 8,
 	VLW_STATION_LIST  = 2 << 8,
 	VLW_DEPOT_LIST    = 3 << 8,
+	VLW_GROUP_LIST    = 4 << 8,
 	VLW_MASK          = 0x700,
 };
 
 static inline bool ValidVLWFlags(uint16 flags)
 {
-	return (flags == VLW_STANDARD || flags == VLW_SHARED_ORDERS || flags == VLW_STATION_LIST || flags == VLW_DEPOT_LIST);
+	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);
@@ -54,6 +68,8 @@
 void ShowVehicleListWindow(PlayerID player, VehicleType vehicle_type, TileIndex depot_tile);
 
 void ShowReplaceVehicleWindow(VehicleType vehicletype);
+void DrawSmallOrderList(const Vehicle *v, int x, int y);
+void ShowReplaceGroupVehicleWindow(GroupID group, VehicleType veh);
 
 static inline void DrawVehicleImage(const Vehicle *v, int x, int y, int count, int skip, VehicleID selection)
 {
--- a/src/window.h	Sat May 19 09:13:08 2007 +0000
+++ b/src/window.h	Sat May 19 09:40:18 2007 +0000
@@ -348,6 +348,7 @@
 	bool update_left;
 	bool update_right;
 	bool init_lists;
+	GroupID sel_group;
 };
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(replaceveh_d));
 
@@ -478,6 +479,28 @@
 };
 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(dropdown_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));
+
+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));
 
 /****************** THESE ARE NOT WIDGET TYPES!!!!! *******************/
 enum WindowWidgetBehaviours {