(svn r165) -Feature: Option to sort vehicles in vehicle-list window by different criteria. Total independent sort for all types and players. Periodic resort of list every 10 TTD days. Thank you for your graphical inspiration follow and buxo (since none of you provided any code).
authordarkvater
Mon, 06 Sep 2004 18:15:13 +0000
changeset 164 0cbdf3c9bde1
parent 163 deb9b58db3f4
child 165 f81fa8c27236
(svn r165) -Feature: Option to sort vehicles in vehicle-list window by different criteria. Total independent sort for all types and players. Periodic resort of list every 10 TTD days. Thank you for your graphical inspiration follow and buxo (since none of you provided any code).
-Fix: Sorter icon pointing down 'v' sorts in every window lowest value first, '^' highest value first
-CodeChange: move Dropdownlist from settings_gui.c to widget.c. More in place there.
Makefile
aircraft_cmd.c
aircraft_gui.c
gui.h
industry_gui.c
lang/english.txt
misc.c
roadveh_cmd.c
roadveh_gui.c
settings_gui.c
ship_cmd.c
ship_gui.c
station_gui.c
town_gui.c
train_cmd.c
train_gui.c
ttd.dsp
ttd.h
ttd.vcproj
unix.c
vehicle.c
vehicle.h
vehicle_gui.c
vehicle_gui.h
water_cmd.c
widget.c
win32.c
window.h
--- a/Makefile	Mon Sep 06 16:57:40 2004 +0000
+++ b/Makefile	Mon Sep 06 18:15:13 2004 +0000
@@ -446,7 +446,7 @@
 	smallmap_gui.c sound.c spritecache.c station_cmd.c station_gui.c \
 	strings.c subsidy_gui.c terraform_gui.c texteff.c town_cmd.c \
 	town_gui.c train_cmd.c train_gui.c tree_cmd.c ttd.c tunnelbridge_cmd.c \
-	unmovable_cmd.c vehicle.c viewport.c water_cmd.c widget.c window.c \
+	unmovable_cmd.c vehicle.c vehicle_gui.c viewport.c water_cmd.c widget.c window.c \
 
 ifdef WITH_SDL
 ttd_SOURCES += sdl.c
--- a/aircraft_cmd.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/aircraft_cmd.c	Mon Sep 06 18:15:13 2004 +0000
@@ -336,6 +336,7 @@
 		}
 
 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
+		_vehicle_sort_dirty[VEHAIRCRAFT] = true; // build aircraft
 		InvalidateWindow(WC_AIRCRAFT_LIST, v->owner);
 		InvalidateWindow(WC_COMPANY, v->owner);
 	}
@@ -367,6 +368,7 @@
 void DoDeleteAircraft(Vehicle *v)
 {
 	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
+	_vehicle_sort_dirty[VEHAIRCRAFT] = true; // delete aircraft
 	InvalidateWindow(WC_AIRCRAFT_LIST, v->owner);
 	InvalidateWindow(WC_COMPANY, v->owner);
 	DeleteVehicleChain(v);
--- a/aircraft_gui.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/aircraft_gui.c	Mon Sep 06 18:15:13 2004 +0000
@@ -864,102 +864,175 @@
 	}
 }
 
+// used to get a sorted list of the vehicles
+static SortStruct _aircraft_sort[NUM_NORMAL_VEHICLES];
+static uint16 _num_aircraft_sort[MAX_PLAYERS];
+
+static void MakeSortedAircraftList(byte owner)
+{
+	SortStruct *firstelement;
+	Vehicle *v;
+	uint32 n = 0;
+	uint16 *i;
+
+	if (_vehicle_sort_dirty[VEHAIRCRAFT]) { // only resort the whole array if vehicles have been added/removed
+		// reset to 0 just to be sure
+		for (i = _num_aircraft_sort; i != endof(_num_aircraft_sort); i++) {*i = 0;}
+
+		FOR_ALL_VEHICLES(v) {
+			if(v->type == VEH_Aircraft && v->subtype <= 2) {
+				_aircraft_sort[n].index = v->index;
+				_aircraft_sort[n++].owner = v->owner;
+				_num_aircraft_sort[v->owner]++; // add number of aircraft of player
+			}
+		}
+
+		// create cumulative aircraft-ownage
+		// aircraft are stored as a cummulative index, eg 25, 41, 43. This means
+		// Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2
+		for (i = &_num_aircraft_sort[1]; i != endof(_num_aircraft_sort); i++) {*i += *(i-1);}
+	
+
+		// sort by owner, then only subsort the requested owner-vehicles
+		qsort(_aircraft_sort, n, sizeof(_aircraft_sort[0]), GeneralOwnerSorter);
+
+		_last_vehicle_idx = 0; // used for "cache" in namesorting
+		_vehicle_sort_dirty[VEHAIRCRAFT] = false;
+	}
+
+	if (owner == 0) { // first element starts at 0th element and has n elements as described above
+		firstelement =	&_aircraft_sort[0];
+		n =							_num_aircraft_sort[0];
+	}	else { // nth element starts at the end of the previous one, and has n elements as described above
+		firstelement =	&_aircraft_sort[_num_aircraft_sort[owner-1]];
+		n =							_num_aircraft_sort[owner] - _num_aircraft_sort[owner-1];
+	}
+
+	_internal_sort_type				= _aircraft_sort_type[owner];
+	_internal_sort_order			= _aircraft_sort_order[owner];
+	_internal_name_sorter_id	= STR_SV_AIRCRAFT_NAME;
+	// only name sorting needs a different procedure, all others are handled by the general sorter
+	qsort(firstelement, n, sizeof(_aircraft_sort[0]), (_internal_sort_type == SORT_BY_NAME) ? VehicleNameSorter : GeneralVehicleSorter);
+
+	DEBUG(misc, 1) ("Resorting Aircraft list player %d...", owner+1);
+}
+
 static void PlayerAircraftWndProc(Window *w, WindowEvent *e)
 {
 	switch(e->event) {
-	case WE_PAINT:
-		/* determine amount of items for scroller */
-		{
-			Vehicle *v;
-			int num = 0;
-			byte owner = (byte)w->window_number;
+	case WE_PAINT: {
+		uint32 i;
+		const byte window_number = (byte)w->window_number;
 
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner)
-					num++;
-			}
+		if (_aircraft_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria
+			w->disabled_state |= (1 << 3);
 
-			SetVScrollCount(w, num);
+		if (_aircraft_sort_dirty[window_number] || _vehicle_sort_dirty[VEHAIRCRAFT]) {
+			_aircraft_sort_dirty[window_number] = false;
+			MakeSortedAircraftList(window_number);
+			/* reset sorting timeout */
+			w->custom[0] = DAY_TICKS;
+			w->custom[1] = PERIODIC_RESORT_DAYS;
 		}
 
+		// aircraft are stored as a cummulative index, eg 25, 41, 43. This means
+		// Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 aircraft
+		i = (window_number == 0) ? 0 : _num_aircraft_sort[window_number-1];
+		SetVScrollCount(w, _num_aircraft_sort[window_number] - i);
+		
 		/* draw the widgets */
 		{
-			Player *p = DEREF_PLAYER(w->window_number);
+			Player *p = DEREF_PLAYER(window_number);
+			/* Company Name -- (###) Aircraft */
 			SET_DPARAM16(0, p->name_1);
 			SET_DPARAM32(1, p->name_2);
+			SET_DPARAM16(2, w->vscroll.count);
+			SET_DPARAM16(3, _vehicle_sort_listing[_aircraft_sort_type[window_number]]);
 			DrawWindowWidgets(w);
 		}
+		/* draw arrow pointing up/down for ascending/descending soring */
+		DoDrawString(_aircraft_sort_order[window_number] & 1 ? "\xAA" : "\xA0", 85, 15, 0x10);
 
 		/* draw the aircraft */
 		{
 			Vehicle *v;
-			int pos = w->vscroll.pos;
-			byte owner = (byte)w->window_number;
-			int x = 2;
-			int y = 15;
-
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner &&
-						--pos < 0 && pos >= -4) {
-					StringID str;
-
-					DrawAircraftImage(v, x + 19, y + 6, INVALID_VEHICLE);
-					DrawVehicleProfitButton(v, x, y+13);
+			int n = 0;
+			const int x = 2;			// offset from left side of widget
+			int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;	// offset from top of widget
+			i += w->vscroll.pos;	// offset from sorted aircraft list of current player
 
-					SET_DPARAM16(0, v->unitnumber);
-					if (IsAircraftHangarTile(v->tile)) {
-						str = STR_021F;
-					} else {
-						str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
-					}
-					DrawString(x, y+2, str, 0);
+			while (i < _num_aircraft_sort[window_number]) {
+				StringID str;
+				v = DEREF_VEHICLE(_aircraft_sort[i].index);
 
-					SET_DPARAM32(0, v->profit_this_year);
-					SET_DPARAM32(1, v->profit_last_year);
-					DrawString(x+19, y + 28, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
+				DrawAircraftImage(v, x + 19, y + 6, INVALID_VEHICLE);
+				DrawVehicleProfitButton(v, x, y+13);
 
-					if (v->string_id != STR_SV_AIRCRAFT_NAME) {
-						SET_DPARAM16(0, v->string_id);
-						DrawString(x+19, y, STR_01AB, 0);
-					}
+				SET_DPARAM16(0, v->unitnumber);
+				if (IsAircraftHangarTile(v->tile)) {
+					str = STR_021F;
+				} else {
+					str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
+				}
+				DrawString(x, y+2, str, 0);
 
-					DrawSmallSchedule(v, x+136, y);
 
-					y += 36;
+				SET_DPARAM32(0, v->profit_this_year);
+				SET_DPARAM32(1, v->profit_last_year);
+				DrawString(x+19, y + 28, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
+				
+				if (v->string_id != STR_SV_AIRCRAFT_NAME) {
+					SET_DPARAM16(0, v->string_id);
+					DrawString(x+19, y, STR_01AB, 0);
 				}
+
+				DrawSmallSchedule(v, x+136, y);
+
+				y += PLY_WND_PRC__SIZE_OF_ROW_BIG;
+				i++; // next aircraft
+				if (++n == w->vscroll.cap) { break;} // max number of aircraft in the window
 			}
 		}
-		break;
-
-	case WE_CLICK:
-		switch(e->click.widget) {
-		case 2: { /* click on aircraft */
-			int sel;
-			Vehicle *v;
-			byte owner;
-
-			sel = (e->click.pt.y - 14) / 36;
+		}	break;
 
-			if ((uint)sel >= 4)
-				break;
-			sel += w->vscroll.pos;
-			owner = (byte)w->window_number;
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner &&
-						--sel < 0) {
-					ShowAircraftViewWindow(v);
-					break;
-				}
+	case WE_CLICK: {
+		switch(e->click.widget) {
+		case 3: /* Flip sorting method ascending/descending */
+			_aircraft_sort_order[(byte)w->window_number] ^= 1;
+			_aircraft_sort_dirty[(byte)w->window_number] = true;
+			SetWindowDirty(w);
+			break;
+		case 4: case 5:/* Select sorting criteria dropdown menu */
+			ShowDropDownMenu(w, _vehicle_sort_listing, _aircraft_sort_type[(byte)w->window_number], 5, 0); // do it for widget 5
+			return;
+		case 6: { /* Matrix to show vehicles */
+			int id_v = (e->click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / PLY_WND_PRC__SIZE_OF_ROW_BIG;
+			
+			if ((uint)id_v >= w->vscroll.cap) { return;} // click out of bounds
+
+			id_v += w->vscroll.pos;
+
+			{
+				byte owner		= (byte)w->window_number;
+				uint16 adder	= (owner == 0) ? 0 : _num_aircraft_sort[owner - 1]; // first element in list
+				Vehicle *v;
+
+				if (id_v + adder >= _num_aircraft_sort[owner]) { return;} // click out of vehicle bound
+
+				v	= DEREF_VEHICLE(_aircraft_sort[adder+id_v].index); // add the offset id_x to that
+
+				assert(v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner && v->owner == _aircraft_sort[adder+id_v].owner);
+
+				ShowAircraftViewWindow(v);
 			}
-			break;
-		}
-		case 4: { /* build new */
+		} break;
+
+		case 8: { /* Build new Vehicle */
 			uint tile;
 
 			tile = _last_built_aircraft_depot_tile;
 			do {
-				if (_map_owner[tile] == _local_player &&
-						IsAircraftHangarTile(tile)) {
+				if (_map_owner[tile] == _local_player && IsAircraftHangarTile(tile)) {
 					ShowAircraftDepotWindow(tile);
 					ShowBuildAircraftWindow(tile);
 					return;
@@ -967,43 +1040,79 @@
 
 				tile = TILE_MASK(tile + 1);
 			} while(tile != _last_built_aircraft_depot_tile);
+
 			ShowBuildAircraftWindow(0);
 		} break;
 		}
+	}	break;
+
+	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
+		if (_aircraft_sort_type[(byte)w->window_number] != e->dropdown.index) // if value hasn't changed, dont resort list
+			_aircraft_sort_dirty[(byte)w->window_number] = true;
+
+		_aircraft_sort_type[(byte)w->window_number] = e->dropdown.index;
+
+		if (_aircraft_sort_type[(byte)w->window_number] != SORT_BY_UNSORTED) // enable 'Sort By' if a sorter criteria is chosen
+			w->disabled_state &= ~(1 << 3);
+
+		SetWindowDirty(w);
+		break;
+	case WE_CREATE: /* set up resort timer */
+		w->custom[0] = DAY_TICKS;
+		w->custom[1] = PERIODIC_RESORT_DAYS;
+		break;
+	case WE_TICK: /* resort the list every 20 seconds orso (10 days) */
+		if (--w->custom[0] == 0) {
+			w->custom[0] = DAY_TICKS;
+			if (--w->custom[1] == 0) {
+				w->custom[1] = PERIODIC_RESORT_DAYS;
+				_aircraft_sort_dirty[(byte)w->window_number] = true;
+				DEBUG(misc, 1) ("Periodic resort Aircraft list player %d...", w->window_number+1);
+				SetWindowDirty(w);
+			}
+		}
 		break;
 	}
 }
 
 static const Widget _player_aircraft_widgets[] = {
-{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_A009_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS},
-{     WWT_MATRIX,    14,     0,   248,    14,   157, 0x401, STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT},
-{  WWT_SCROLLBAR,    14,   249,   259,    14,   157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,    14,     0,   129,   158,   169, STR_A003_NEW_AIRCRAFT, STR_A020_BUILD_NEW_AIRCRAFT_REQUIRES},
-{     WWT_IMGBTN,    14,   130,   259,   158,   169, 0x0, 0},
+{   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,							STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_A009_AIRCRAFT,			STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,    14,     0,    15,    14,    25, 0x0,										0},
+{ WWT_PUSHTXTBTN,    14,    16,    96,    14,    25, SRT_SORT_BY,						STR_SORT_TIP},
+{    WWT_TEXTBTN,    14,    97,   248,    14,    25, STR_02E7,							0},
+{   WWT_CLOSEBOX,    14,   249,   259,    14,    25, STR_0225,							STR_SORT_TIP},
+{     WWT_MATRIX,    14,     0,   248,    26,   169, 0x401,									STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT},
+{  WWT_SCROLLBAR,    14,   249,   259,    26,   169, 0x0,										STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{ WWT_PUSHTXTBTN,    14,     0,   129,   170,   181, STR_A003_NEW_AIRCRAFT,	STR_A020_BUILD_NEW_AIRCRAFT_REQUIRES},
+{      WWT_PANEL,    14,   130,   259,   170,   181, 0x0,										0},
 {      WWT_LAST},
 };
 
 static const WindowDesc _player_aircraft_desc = {
-	-1, -1, 260, 170,
+	-1, -1, 260, 182,
 	WC_AIRCRAFT_LIST,0,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 	_player_aircraft_widgets,
 	PlayerAircraftWndProc
 };
 
 static const Widget _other_player_aircraft_widgets[] = {
-{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_A009_AIRCRAFT, STR_018C_WINDOW_TITLE_DRAG_THIS},
-{     WWT_MATRIX,    14,     0,   248,    14,   157, 0x401, STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT},
-{  WWT_SCROLLBAR,    14,   249,   259,    14,   157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,							STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_A009_AIRCRAFT,			STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,    14,     0,    15,    14,    25, 0x0,										0},
+{ WWT_PUSHTXTBTN,    14,    16,    96,    14,    25, SRT_SORT_BY,						STR_SORT_TIP},
+{    WWT_TEXTBTN,    14,    97,   248,    14,    25, STR_02E7,							0},
+{   WWT_CLOSEBOX,    14,   249,   259,    14,    25, STR_0225,							STR_SORT_TIP},
+{     WWT_MATRIX,    14,     0,   248,    26,   169, 0x401,									STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT},
+{  WWT_SCROLLBAR,    14,   249,   259,    26,   169, 0x0,										STR_0190_SCROLL_BAR_SCROLLS_LIST},
 {      WWT_LAST},
 };
 
 static const WindowDesc _other_player_aircraft_desc = {
-	-1, -1, 260, 158,
+	-1, -1, 260, 170,
 	WC_AIRCRAFT_LIST,0,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 	_other_player_aircraft_widgets,
 	PlayerAircraftWndProc
 };
@@ -1012,7 +1121,7 @@
 {
 	Window *w;
 
-	if ( player == _local_player) {
+	if (player == _local_player) {
 		w = AllocateWindowDescFront(&_player_aircraft_desc, player);
 	} else  {
 		w = AllocateWindowDescFront(&_other_player_aircraft_desc, player);
@@ -1020,6 +1129,6 @@
 
 	if (w) {
 		w->caption_color = w->window_number;
-		w->vscroll.cap = 4;
+		w->vscroll.cap = 4; // maximum number of vehicles shown
 	}
 }
--- a/gui.h	Mon Sep 06 16:57:40 2004 +0000
+++ b/gui.h	Mon Sep 06 18:15:13 2004 +0000
@@ -108,8 +108,6 @@
 void ShowQueryString(StringID str, StringID caption, int maxlen, int maxwidth, byte window_class, uint16 window_number);
 void ShowMusicWindow();
 
-void DrawVehicleProfitButton(Vehicle *v, int x, int y);
-
 /* main_gui.c */
 VARDEF byte _newspaper_flag;
 VARDEF byte _construct_mode;
--- a/industry_gui.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/industry_gui.c	Mon Sep 06 18:15:13 2004 +0000
@@ -388,45 +388,44 @@
 
 static byte _industry_sort_order;
 
-static int CDECL IndustrySorter(const void *a, const void *b)
+static int CDECL GeneralIndustrySorter(const void *a, const void *b)
 {
 	char buf1[96];
-	Industry *i, *j;
 	byte val;
+	Industry *i = DEREF_INDUSTRY(*(byte*)a);
+	Industry *j = DEREF_INDUSTRY(*(byte*)b);
 	int r = 0;
 
-	i = DEREF_INDUSTRY(*(byte*)a);
-	j = DEREF_INDUSTRY(*(byte*)b);
-
 	switch (_industry_sort_order >> 1) {
-	case 0: 
-		r = 0; 
-		break;
-	case 1: /* Case 1, sort by type */
+	/* case 0: Sort by Name (handled later) */
+	case 1: /* Sort by Type */
 		r = i->type - j->type; 
 		break;
-	case 2: /* Case 2, sort by production */
-		if (i->produced_cargo[0] != 0xFF && j->produced_cargo[0] != 0xFF) { //producing any cargo?
-				if (i->produced_cargo[1] == 0xFF) //producing one or two things?
+	// FIXME - Production & Transported sort need to be inversed...but, WTF it does not wanna!
+	// FIXME - And no simple --> "if (!(_industry_sort_order & 1)) r = -r;" hack at the bottom!!
+	case 2: { /* Sort by Production */
+		if (i->produced_cargo[0] != 0xFF && j->produced_cargo[0] != 0xFF) { // both industries produce cargo?
+				if (i->produced_cargo[1] == 0xFF) // producing one or two things?
 					r = j->total_production[0] - i->total_production[0];
 				else	
 					r = (j->total_production[0] + j->total_production[1]) / 2 - (i->total_production[0] + i->total_production[1]) / 2;
-		} else if (i->produced_cargo[0] == 0xFF && j->produced_cargo[0] == 0xFF) //None of them producing anything, let them go to the name-sorting
+		} else if (i->produced_cargo[0] == 0xFF && j->produced_cargo[0] == 0xFF) // none of them producing anything, let them go to the name-sorting
 			r = 0;
-		else if (i->produced_cargo[0] == 0xFF) //Non-producers, end up last/first in list
+		else if (i->produced_cargo[0] == 0xFF) // end up the non-producer industry first/last in list
 			r = 1;
 		else
 			r = -1;
 		break;
-	case 3: /* Case 3, sort by transportation */
-		if (i->produced_cargo[0] != 0xFF && j->produced_cargo[0] != 0xFF) { //producing any cargo?
-				if (i->produced_cargo[1] == 0xFF) //producing one or two things?
+	}
+	case 3: /* Sort by Transported amount */
+		if (i->produced_cargo[0] != 0xFF && j->produced_cargo[0] != 0xFF) { // both industries produce cargo?
+				if (i->produced_cargo[1] == 0xFF) // producing one or two things?
 					r = (j->pct_transported[0] * 100 >> 8) - (i->pct_transported[0] * 100 >> 8);
 				else 
 					r = ((j->pct_transported[0] * 100 >> 8) + (j->pct_transported[1] * 100 >> 8)) / 2 - ((i->pct_transported[0] * 100 >> 8) + (i->pct_transported[1] * 100 >> 8)) / 2;
-		} else if (i->produced_cargo[0] == 0xFF && j->produced_cargo[0] == 0xFF) //None of them producing anything, let them go to the name-sorting
+		} else if (i->produced_cargo[0] == 0xFF && j->produced_cargo[0] == 0xFF) // none of them producing anything, let them go to the name-sorting
 			r = 0;
-		else if (i->produced_cargo[0] == 0xFF) //Non-producers, end up last/first in list
+		else if (i->produced_cargo[0] == 0xFF) // end up the non-producer industry first/last in list
 			r = 1;
 		else
 			r = -1;
@@ -446,9 +445,7 @@
 		r = strcmp(buf1, _bufcache);
 	}
 	
-	if (_industry_sort_order & 1)
-		r = -r;
-	
+	if (_industry_sort_order & 1) r = -r;	
 	return r;
 }
 
@@ -464,7 +461,7 @@
 	_num_industry_sort = n;
 	_last_industry_idx = 255; // used for "cache"
 
-	qsort(_industry_sort, n, 1, IndustrySorter);
+	qsort(_industry_sort, n, 1, GeneralIndustrySorter);
 
 	DEBUG(misc, 1) ("Resorting Industries list...");
 }
--- a/lang/english.txt	Mon Sep 06 16:57:40 2004 +0000
+++ b/lang/english.txt	Mon Sep 06 18:15:13 2004 +0000
@@ -352,9 +352,24 @@
 STR_015F_QUIT											:Exit
 STR_0160_ARE_YOU_SURE_YOU_WANT_TO	:{YELLOW}Are you sure you want to abandon this game?
 STR_0161_QUIT_GAME								:{WHITE}Abandon Game
-STR_SORT_TIP											:{BLACK}Select sorting order
-STR_SORT_BY_NAME									:{BLACK}Name
-STR_SORT_BY_DATE									:{BLACK}Date
+STR_SORT_TIP											:{BLACK}Select sorting order descending/ascending
+SRT_SORT_BY												:{BLACK}Sort by
+
+STR_SORT_BY_POPULATION										:{BLACK}Population
+STR_SORT_BY_PRODUCTION										:{BLACK}Production
+STR_SORT_BY_TYPE													:{BLACK}Type
+STR_SORT_BY_TRANSPORTED										:{BLACK}Transported
+STR_SORT_BY_NAME													:{BLACK}Name
+STR_SORT_BY_DROPDOWN_NAME									:Name
+STR_SORT_BY_DATE													:{BLACK}Date
+STR_SORT_BY_UNSORTED											:Unsorted
+STR_SORT_BY_NUMBER												:Number
+STR_SORT_BY_PROFIT_LAST_YEAR							:Profit last year
+STR_SORT_BY_PROFIT_THIS_YEAR							:Profit this year
+STR_SORT_BY_AGE														:Age
+STR_SORT_BY_RELIABILITY										:Reliability
+STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE	:Total capacity per cargo type
+STR_SORT_BY_MAX_SPEED											:Maximum speed
 
 ############ range for months starts
 STR_0162_JAN							:Jan
@@ -843,8 +858,8 @@
 STR_TOWNNAME_AUSTRIAN					:Austrian
 ############ end of	townname region
 
-STR_CURR_POUNDS							:Pounds ({POUNDSIGN})
-STR_CURR_DOLLARS						:Dollars ($)
+STR_CURR_POUNDS						:Pounds ({POUNDSIGN})
+STR_CURR_DOLLARS					:Dollars ($)
 STR_CURR_FF								:Franc (FF)
 STR_CURR_DM								:Deutschmark (DM)
 STR_CURR_YEN							:Yen ({YENSIGN})
@@ -1052,8 +1067,6 @@
 STR_CHEAT_SWITCH_CLIMATE			:{LTBLUE}Switch climate: {ORANGE} {STRING}
 STR_CHEAT_CHANGE_DATE					:{LTBLUE}Change date: {ORANGE} {DATE_SHORT}
 
-STR_SORT_BY_POPULATION					:{BLACK}Population
-
 STR_HEADING_FOR_CHECKPOINT				:{LTBLUE}Heading for {CHECKPOINT}
 STR_HEADING_FOR_CHECKPOINT_VEL			:{LTBLUE}Heading for {CHECKPOINT}, {VELOCITY}
 
@@ -1136,9 +1149,6 @@
 STR_CONSTRUCT_TOFFEE_QUARRY_TIP			:{BLACK}Fund Toffee Quarry
 STR_CONSTRUCT_SUGAR_MINE_TIP			:{BLACK}Construct Sugar Mine
 
-STR_SORT_BY_PRODUCTION				:{BLACK}Production
-STR_SORT_BY_TYPE					:{BLACK}Type
-STR_SORT_BY_TRANSPORTED				:{BLACK}Transported
 STR_INDUSTRYDIR_CAPTION					:{WHITE}Industries
 STR_INDUSTRYDIR_ITEM					:{ORANGE}{TOWN} {STRING}{BLACK} ({STRING}){YELLOW} ({COMMA16}% transported)
 STR_INDUSTRYDIR_ITEM_TWO				:{ORANGE}{TOWN} {STRING}{BLACK} ({STRING}/{STRING}){YELLOW} ({COMMA16}%/{COMMA16}% transported)
@@ -2228,7 +2238,7 @@
 STR_8818_INFORMATION					:{BLACK}Information
 STR_8819_TRAIN_TOO_LONG					:{WHITE}Train too long
 STR_881A_TRAINS_CAN_ONLY_BE_ALTERED		:{WHITE}Trains can only be altered when stopped inside a depot
-STR_881B_TRAINS							:{WHITE}{STRING} - Trains
+STR_881B_TRAINS							:{WHITE}{STRING} - {COMMA16} Trains
 STR_881C_NEW_RAIL_VEHICLES				:{WHITE}New Rail Vehicles
 STR_881D_NEW_MONORAIL_VEHICLES			:{WHITE}New Monorail Vehicles
 STR_881E_NEW_MAGLEV_VEHICLES			:{WHITE}New Maglev Vehicles
@@ -2319,7 +2329,7 @@
 
 ##id 0x9000
 STR_9000_ROAD_VEHICLE_IN_THE_WAY		:{WHITE}Road vehicle in the way
-STR_9001_ROAD_VEHICLES					:{WHITE}{STRING} - Road Vehicles
+STR_9001_ROAD_VEHICLES					:{WHITE}{STRING} - {COMMA16} Road Vehicles
 STR_9002								:{WHITE}{STRING}
 STR_9003_ROAD_VEHICLE_DEPOT				:{WHITE}{TOWN} Road Vehicle Depot
 STR_9004_NEW_VEHICLES					:{BLACK}New Vehicles
@@ -2384,7 +2394,7 @@
 STR_9802_CAN_T_BUILD_DOCK_HERE			:{WHITE}Can't build dock here...
 STR_9803_SHIP_DEPOT						:{WHITE}{TOWN} Ship Depot
 STR_9804_NEW_SHIPS						:{BLACK}New Ships
-STR_9805_SHIPS							:{WHITE}{STRING} - Ships
+STR_9805_SHIPS							:{WHITE}{STRING} - {COMMA16} Ships
 STR_9806_CAN_T_BUILD_SHIPS				:{WHITE}Can't build ships...
 STR_9807_MUST_BUILD_SHIP_DEPOT_FIRST	:{WHITE}Must build ship depot first
 STR_9808_NEW_SHIPS						:{WHITE}New Ships
@@ -2460,7 +2470,7 @@
 STR_A006_BUILD_AIRCRAFT					:{BLACK}Build Aircraft
 STR_A007_COST_SPEED_CAPACITY_PASSENGERS	:{BLACK}Cost: {GOLD}{CURRENCY}{BLACK} Speed: {GOLD}{VELOCITY}{}{BLACK}Capacity: {GOLD}{COMMA16} passengers, {COMMA16} bags of mail{}{BLACK}Running Cost: {GOLD}{CURRENCY}/yr{}{BLACK}Designed: {GOLD}{NUMU16}{BLACK}  Life: {GOLD}{COMMA16} years{}{BLACK}Max. Reliability: {GOLD}{COMMA8}%
 STR_A008_CAN_T_BUILD_AIRCRAFT			:{WHITE}Can't build aircraft...
-STR_A009_AIRCRAFT						:{WHITE}{STRING} - Aircraft
+STR_A009_AIRCRAFT						:{WHITE}{STRING} - {COMMA16} Aircraft
 STR_A00A								:{WHITE}{STRING}
 STR_A00B_ORDERS							:{WHITE}{STRING} (Orders)
 STR_A00C_DETAILS						:{WHITE}{STRING} (Details)
--- a/misc.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/misc.c	Mon Sep 06 18:15:13 2004 +0000
@@ -121,6 +121,7 @@
 	InitializeIndustries();
 	
 	InitializeNameMgr();
+	InitializeVehiclesGuiList();
 	InitializeTrains();
 
 	InitializePlayers();
--- a/roadveh_cmd.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/roadveh_cmd.c	Mon Sep 06 18:15:13 2004 +0000
@@ -271,6 +271,7 @@
 		VehiclePositionChanged(v);
 
 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
+		_vehicle_sort_dirty[VEHROAD] = true; // build a new bus/truck
 		InvalidateWindow(WC_ROADVEH_LIST, v->owner);
 		InvalidateWindow(WC_COMPANY, v->owner);
 	}
@@ -314,6 +315,7 @@
 	if (flags & DC_EXEC) {
 		// Invalidate depot
 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
+		_vehicle_sort_dirty[VEHROAD] = true; // sell a bus/truck
 		InvalidateWindow(WC_ROADVEH_LIST, v->owner);
 		InvalidateWindow(WC_COMPANY, v->owner);
 		DeleteWindowById(WC_VEHICLE_VIEW, v->index);
@@ -505,6 +507,7 @@
 	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
 	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 		
+	_vehicle_sort_dirty[VEHROAD] = true; // delete bus/truck (eg. crash for example)
 	InvalidateWindow(WC_ROADVEH_LIST, v->owner);
 	InvalidateWindow(WC_COMPANY, v->owner);
 
--- a/roadveh_gui.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/roadveh_gui.c	Mon Sep 06 18:15:13 2004 +0000
@@ -9,7 +9,6 @@
 #include "station.h"
 #include "command.h"
 #include "player.h"
-//#include "town.h"
 #include "engine.h"
 
 extern const byte _roadveh_price[88];
@@ -704,100 +703,172 @@
 	}
 }
 
+// used to get a sorted list of the vehicles
+static SortStruct _road_sort[NUM_NORMAL_VEHICLES];
+static uint16 _num_road_sort[MAX_PLAYERS];
+
+static void MakeSortedRoadList(byte owner)
+{
+	SortStruct *firstelement;
+	Vehicle *v;
+	uint32 n = 0;
+	uint16 *i;
+
+	if (_vehicle_sort_dirty[VEHROAD]) { // only resort the whole array if vehicles have been added/removed
+		// reset to 0 just to be sure
+		for (i = _num_road_sort; i != endof(_num_road_sort); i++) {*i = 0;}
+
+		FOR_ALL_VEHICLES(v) {
+			if(v->type == VEH_Road) {
+				_road_sort[n].index = v->index;
+				_road_sort[n++].owner = v->owner;
+				_num_road_sort[v->owner]++; // add number of roads of player
+			}
+		}
+
+		// create cumulative road-ownage
+		// roads are stored as a cummulative index, eg 25, 41, 43. This means
+		// Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2
+		for (i = &_num_road_sort[1]; i != endof(_num_road_sort); i++) {*i += *(i-1);}
+	
+
+		// sort by owner, then only subsort the requested owner-vehicles
+		qsort(_road_sort, n, sizeof(_road_sort[0]), GeneralOwnerSorter);
+
+		_last_vehicle_idx = 0; // used for "cache" in namesorting
+		_vehicle_sort_dirty[VEHROAD] = false;
+	}
+
+	if (owner == 0) { // first element starts at 0th element and has n elements as described above
+		firstelement =	&_road_sort[0];
+		n =							_num_road_sort[0];
+	}	else { // nth element starts at the end of the previous one, and has n elements as described above
+		firstelement =	&_road_sort[_num_road_sort[owner-1]];
+		n =							_num_road_sort[owner] - _num_road_sort[owner-1];
+	}
+
+	_internal_sort_type				= _road_sort_type[owner];
+	_internal_sort_order			= _road_sort_order[owner];
+	_internal_name_sorter_id	= STR_SV_ROADVEH_NAME;
+	// only name sorting needs a different procedure, all others are handled by the general sorter
+	qsort(firstelement, n, sizeof(_road_sort[0]), (_internal_sort_type == SORT_BY_NAME) ? VehicleNameSorter : GeneralVehicleSorter);
+
+	DEBUG(misc, 1) ("Resorting Roadvehicles list player %d...", owner+1);
+}
+
 static void PlayerRoadVehWndProc(Window *w, WindowEvent *e)
 {
 	switch(e->event) {
-	case WE_PAINT:
-		/* determine amount of items for scroller */
-		{
-			Vehicle *v;
-			int num = 0;
-			byte owner = (byte)w->window_number;
+	case WE_PAINT: {
+		uint32 i;
+		const byte window_number = (byte)w->window_number;
 
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Road && v->owner == owner)
-					num++;
-			}
-			SetVScrollCount(w, num);
+		if (_road_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria
+			w->disabled_state |= (1 << 3);
+
+		if (_road_sort_dirty[window_number] || _vehicle_sort_dirty[VEHROAD]) {
+			_road_sort_dirty[window_number] = false;
+			MakeSortedRoadList(window_number);
+			/* reset sorting timeout */
+			w->custom[0] = DAY_TICKS;
+			w->custom[1] = PERIODIC_RESORT_DAYS;
 		}
 
+		// roads are stored as a cummulative index, eg 25, 41, 43. This means
+		// Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 roads
+		i = (window_number == 0) ? 0 : _num_road_sort[window_number-1];
+		SetVScrollCount(w, _num_road_sort[window_number] - i);
+		
 		/* draw the widgets */
 		{
-			Player *p = DEREF_PLAYER(w->window_number);
+			Player *p = DEREF_PLAYER(window_number);
+			/* Company Name -- (###) Roadvehicles */
 			SET_DPARAM16(0, p->name_1);
 			SET_DPARAM32(1, p->name_2);
+			SET_DPARAM16(2, w->vscroll.count);
+			SET_DPARAM16(3, _vehicle_sort_listing[_road_sort_type[window_number]]);
 			DrawWindowWidgets(w);
 		}
+		/* draw arrow pointing up/down for ascending/descending soring */
+		DoDrawString(_road_sort_order[window_number] & 1 ? "\xAA" : "\xA0", 85, 15, 0x10);
 
-		/* draw the road vehicles */
+		/* draw the roadvehicles */
 		{
 			Vehicle *v;
-			int pos = w->vscroll.pos;
-			byte owner = (byte)w->window_number;
-			int x = 2;
-			int y = 15;
-			
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Road && v->owner == owner &&
-						--pos < 0 && pos >= -7) {
-					StringID str;
-					
-					DrawRoadVehImage(v, x + 22, y + 6, INVALID_VEHICLE);
-					DrawVehicleProfitButton(v, x, y+13);
-					
-					SET_DPARAM16(0, v->unitnumber);
-					if (IsRoadDepotTile(v->tile)) {
-						str = STR_021F;
-					} else {
-						str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
-					}
-					DrawString(x, y+2, str, 0);
+			int n = 0;
+			const int x = 2;			// offset from left side of widget
+			int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;	// offset from top of widget
+			i += w->vscroll.pos;	// offset from sorted roads list of current player
 
-					SET_DPARAM32(0, v->profit_this_year);
-					SET_DPARAM32(1, v->profit_last_year);
-					DrawString(x + 24, y + 18, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
-					
-					if (v->string_id != STR_SV_ROADVEH_NAME) {
-						SET_DPARAM16(0, v->string_id);
-						DrawString(x+24, y, STR_01AB, 0);
-					}
-					y += 26;
-				}				
+			while (i < _num_road_sort[window_number]) {
+				StringID str;
+				v = DEREF_VEHICLE(_road_sort[i].index);
+
+				DrawRoadVehImage(v, x + 22, y + 6, INVALID_VEHICLE);
+				DrawVehicleProfitButton(v, x, y+13);
+
+				SET_DPARAM16(0, v->unitnumber);
+				if (IsRoadDepotTile(v->tile)) {
+					str = STR_021F;
+				} else {
+					str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
+				}
+				DrawString(x, y+2, str, 0);
+
+				SET_DPARAM32(0, v->profit_this_year);
+				SET_DPARAM32(1, v->profit_last_year);
+				DrawString(x + 24, y + 18, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
+				
+				if (v->string_id != STR_SV_ROADVEH_NAME) {
+					SET_DPARAM16(0, v->string_id);
+					DrawString(x+24, y, STR_01AB, 0);
+				}
+
+				y += PLY_WND_PRC__SIZE_OF_ROW_SMALL;
+				i++; // next road
+				if (++n == w->vscroll.cap) { break;} // max number of roads in the window
 			}
 		}
-		break;
-
-	case WE_CLICK:
-		switch(e->click.widget) {
-		case 2: {
-			int idx = (e->click.pt.y - 14) / 26;
-			Vehicle *v;
-			byte owner;
+		}	break;
 
-			if ((uint)idx >= 7)
-				break;
-
-			idx += w->vscroll.pos;
+	case WE_CLICK: {
+		switch(e->click.widget) {
+		case 3: /* Flip sorting method ascending/descending */
+			_road_sort_order[(byte)w->window_number] ^= 1;
+			_road_sort_dirty[(byte)w->window_number] = true;
+			SetWindowDirty(w);
+			break;
+		case 4: case 5:/* Select sorting criteria dropdown menu */
+			ShowDropDownMenu(w, _vehicle_sort_listing, _road_sort_type[(byte)w->window_number], 5, 0); // do it for widget 5
+			return;
+		case 6: { /* Matrix to show vehicles */
+			int id_v = (e->click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / PLY_WND_PRC__SIZE_OF_ROW_SMALL;
+			
+			if ((uint)id_v >= w->vscroll.cap) { return;} // click out of bounds
 
-			owner = (byte)w->window_number;
+			id_v += w->vscroll.pos;
 
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Road && v->owner == owner &&
-						--idx < 0) {	
-					ShowRoadVehViewWindow(v);
-					break;
-				}
+			{
+				byte owner		= (byte)w->window_number;
+				uint16 adder	= (owner == 0) ? 0 : _num_road_sort[owner - 1]; // first element in list
+				Vehicle *v;
+
+				if (id_v + adder >= _num_road_sort[owner]) { return;} // click out of vehicle bound
+
+				v	= DEREF_VEHICLE(_road_sort[adder+id_v].index); // add the offset id_x to that
+
+				assert(v->type == VEH_Road && v->owner == owner && v->owner == _road_sort[adder+id_v].owner);
+
+				ShowRoadVehViewWindow(v);
 			}
 		} break;
 
-		case 4: {
+		case 8: { /* Build new Vehicle */
 			uint tile;
 
 			tile = _last_built_road_depot_tile;
 			do {
-				if (_map_owner[tile] == _local_player &&
-						IsRoadDepotTile(tile)) {
-					
+				if (_map_owner[tile] == _local_player && IsRoadDepotTile(tile)) {					
 					ShowRoadDepotWindow(tile);
 					ShowBuildRoadVehWindow(tile);
 					return;
@@ -808,40 +879,76 @@
 			
 			ShowBuildRoadVehWindow(0);
 		} break;
-	} break;
+		}
+	}	break;
+
+	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
+		if (_road_sort_type[(byte)w->window_number] != e->dropdown.index) // if value hasn't changed, dont resort list
+			_road_sort_dirty[(byte)w->window_number] = true;
+
+		_road_sort_type[(byte)w->window_number] = e->dropdown.index;
+
+		if (_road_sort_type[(byte)w->window_number] != SORT_BY_UNSORTED) // enable 'Sort By' if a sorter criteria is chosen
+			w->disabled_state &= ~(1 << 3);
+
+		SetWindowDirty(w);
+		break;
+	case WE_CREATE: /* set up resort timer */
+		w->custom[0] = DAY_TICKS;
+		w->custom[1] = PERIODIC_RESORT_DAYS;
+		break;
+	case WE_TICK: /* resort the list every 20 seconds orso (10 days) */
+		if (--w->custom[0] == 0) {
+			w->custom[0] = DAY_TICKS;
+			if (--w->custom[1] == 0) {
+				w->custom[1] = PERIODIC_RESORT_DAYS;
+				_road_sort_dirty[(byte)w->window_number] = true;
+				DEBUG(misc, 1) ("Periodic resort Roadvehicles list player %d...", w->window_number+1);
+				SetWindowDirty(w);
+			}
+		}
+		break;
 	}
 }
 
 static const Widget _player_roadveh_widgets[] = {
-{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_9001_ROAD_VEHICLES, STR_018C_WINDOW_TITLE_DRAG_THIS},
-{     WWT_MATRIX,    14,     0,   248,    14,   195, 0x701, STR_901A_ROAD_VEHICLES_CLICK_ON},
-{  WWT_SCROLLBAR,    14,   249,   259,    14,   195, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,    14,     0,   129,   196,   207, STR_9004_NEW_VEHICLES, STR_901B_BUILD_NEW_ROAD_VEHICLES},
-{     WWT_IMGBTN,    14,   130,   259,   196,   207, 0x0, 0},
+{   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,								STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_9001_ROAD_VEHICLES,	STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,    14,     0,    15,    14,    25, 0x0,											0},
+{ WWT_PUSHTXTBTN,    14,    16,    96,    14,    25, SRT_SORT_BY,							STR_SORT_TIP},
+{    WWT_TEXTBTN,    14,    97,   248,    14,    25, STR_02E7,								0},
+{   WWT_CLOSEBOX,    14,   249,   259,    14,    25, STR_0225,								STR_SORT_TIP},
+{     WWT_MATRIX,    14,     0,   248,    26,   207, 0x701,										STR_901A_ROAD_VEHICLES_CLICK_ON},
+{  WWT_SCROLLBAR,    14,   249,   259,    26,   207, 0x0,											STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{ WWT_PUSHTXTBTN,    14,     0,   129,   208,   219, STR_9004_NEW_VEHICLES, STR_901B_BUILD_NEW_ROAD_VEHICLES},
+{     WWT_IMGBTN,    14,   130,   259,   208,   219, 0x0, 0},
 {      WWT_LAST},
 };
 
 static const WindowDesc _player_roadveh_desc = {
-	-1, -1, 260, 208,
+	-1, -1, 260, 220,
 	WC_ROADVEH_LIST,0,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 	_player_roadveh_widgets,
 	PlayerRoadVehWndProc
 };
 
 static const Widget _other_player_roadveh_widgets[] = {
-{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_9001_ROAD_VEHICLES, STR_018C_WINDOW_TITLE_DRAG_THIS},
-{     WWT_MATRIX,    14,     0,   248,    14,   195, 0x701, STR_901A_ROAD_VEHICLES_CLICK_ON},
-{  WWT_SCROLLBAR,    14,   249,   259,    14,   195, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,								STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_9001_ROAD_VEHICLES,	STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,    14,     0,    15,    14,    25, 0x0,											0},
+{ WWT_PUSHTXTBTN,    14,    16,    96,    14,    25, SRT_SORT_BY,							STR_SORT_TIP},
+{    WWT_TEXTBTN,    14,    97,   248,    14,    25, STR_02E7,								0},
+{   WWT_CLOSEBOX,    14,   249,   259,    14,    25, STR_0225,								STR_SORT_TIP},
+{     WWT_MATRIX,    14,     0,   248,    26,   207, 0x701,										STR_901A_ROAD_VEHICLES_CLICK_ON},
+{  WWT_SCROLLBAR,    14,   249,   259,    26,   207, 0x0,											STR_0190_SCROLL_BAR_SCROLLS_LIST},
 {      WWT_LAST},
 };
 
 static const WindowDesc _other_player_roadveh_desc = {
-	-1, -1, 260, 196,
+	-1, -1, 260, 208,
 	WC_ROADVEH_LIST,0,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 	_other_player_roadveh_widgets,
 	PlayerRoadVehWndProc
 };
--- a/settings_gui.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/settings_gui.c	Mon Sep 06 18:15:13 2004 +0000
@@ -3,198 +3,14 @@
 
 #include "window.h"
 #include "gui.h"
-#include "viewport.h"
 #include "gfx.h"
 #include "command.h"
 #include "engine.h"
 
-static uint32 _dropdown_disabled;
-static const StringID *_dropdown_items;
-static int _dropdown_selindex;
-static uint _dropdown_item_count;
-static byte _dropdown_button;
-static WindowClass _dropdown_windowclass;
-static WindowNumber _dropdown_windownum;
-static byte _dropdown_var1;
-static byte _dropdown_var2;
-
 static uint32 _difficulty_click_a;
 static uint32 _difficulty_click_b;
 static byte _difficulty_timeout;
 
-static Widget _dropdown_menu_widgets[] = {
-{     WWT_IMGBTN,     0,     0, 0,     0, 0, 0x0},
-{      WWT_LAST},
-};
-
-static int GetDropdownItem(Window *w)
-{
-	uint item;
-	int y;
-
-	if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0)
-		return -1;
-	
-	y = _cursor.pos.y - w->top - 2;
-
-	if (y < 0)
-		return - 1;
-
-	item = y / 10;
-	if (item >= _dropdown_item_count || HASBIT(_dropdown_disabled,item) || _dropdown_items[item] == 0)
-		return - 1;
-
-	return item;
-}
-
-void DropdownMenuWndProc(Window *w, WindowEvent *e)
-{
-	int item;
-
-	switch(e->event) {
-	case WE_PAINT: {
-		int x,y,i,sel;
-		uint32 dis;
-
-		DrawWindowWidgets(w);
-
-		x = 1;
-		y = 2;
-		sel = _dropdown_selindex;
-		dis = _dropdown_disabled;
-
-		for(i=0; _dropdown_items[i] != INVALID_STRING_ID; i++) {
-			if (_dropdown_items[i] != 0) {
-				if (sel == 0) {
-					GfxFillRect(x+1, y, x+w->width-4, y + 9, 0);
-				}
-				DrawString(x+2, y, _dropdown_items[i], sel==0 ? 12 : 16);
-
-				if (dis & 1) {
-					GfxFillRect(x, y, x+w->width-3, y + 9, 0x8000 + 
-						_color_list[_dropdown_menu_widgets[0].color].window_color_bga);
-				}
-			} else {
-				int color_1 = _color_list[_dropdown_menu_widgets[0].color].window_color_1a;
-				int color_2 = _color_list[_dropdown_menu_widgets[0].color].window_color_2;
-				GfxFillRect(x+1, y+3, x+w->width-5, y+3, color_1);
-				GfxFillRect(x+1, y+4, x+w->width-5, y+4, color_2);
-			}
-			y += 10;
-			sel--;
-			dis>>=1;
-		}
-	} break;
-
-	case WE_CLICK: {
-		item = GetDropdownItem(w);
-		if (item >= 0) {
-			_dropdown_var1 = 4;
-			_dropdown_selindex = item;
-			SetWindowDirty(w);
-		}
-	} break;
-
-	case WE_MOUSELOOP: {
-		Window *w2 = FindWindowById(_dropdown_windowclass, _dropdown_windownum);
-		if (w2 == NULL) {
-			DeleteWindow(w);
-			return;
-		}
-
-		if (_dropdown_var1 != 0 && --_dropdown_var1 == 0) {
-			WindowEvent e;
-			e.event = WE_DROPDOWN_SELECT;
-			e.dropdown.button = _dropdown_button;
-			e.dropdown.index = _dropdown_selindex;
-			w2->wndproc(w2, &e);
-			DeleteWindow(w);
-			return;
-		}
-
-		if (_dropdown_var2 != 0) {
-			item = GetDropdownItem(w);
-
-			if (!_left_button_clicked) {
-				_dropdown_var2 = 0;
-				if (item < 0)
-					return;
-				_dropdown_var1 = 2;
-			} else {
-				if (item < 0)
-					return;
-			}
-
-			_dropdown_selindex = item;
-			SetWindowDirty(w);
-		}
-	} break;
-		
-	case WE_DESTROY: {
-		Window *w2 = FindWindowById(_dropdown_windowclass, _dropdown_windownum);
-		if (w2 != NULL) {
-			CLRBIT(w2->click_state, _dropdown_button);
-			InvalidateWidget(w2, _dropdown_button);
-		}
-	} break;
-	}
-}
-
-void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask)
-{
-	WindowNumber num;
-	WindowClass cls;
-	int i,t1,t2;
-	const Widget *wi;
-	Window *w2;
-	uint32 old_click_state = w->click_state;
-	
-	_dropdown_disabled = disabled_mask;
-
-	cls = w->window_class;
-	num = w->window_number;
-	DeleteWindowById(WC_DROPDOWN_MENU, 0);
-	w = FindWindowById(cls, num);
-
-	if (HASBIT(old_click_state, button))
-		return;
-
-	SETBIT(w->click_state, button);
-
-	InvalidateWidget(w, button);
-	
-	for(i=0;strings[i] != INVALID_STRING_ID;i++);
-	if (i == 0)
-		return;
-
-	_dropdown_items = strings;
-	_dropdown_item_count = i;
-	_dropdown_selindex = selected;
-	
-	_dropdown_windowclass = w->window_class;
-	_dropdown_windownum = w->window_number;
-	_dropdown_button = button;
-	
-	_dropdown_var1 = 0;
-	_dropdown_var2 = 1;
-
-	wi = &w->widget[button];
-
-	_dropdown_menu_widgets[0].color = wi->color;
-
-	w2 = AllocateWindow(
-		w->left + wi[-1].left + 1,
-		w->top + wi->bottom + 2,
-		(_dropdown_menu_widgets[0].right=t1=wi->right - wi[-1].left, t1 + 1), 
-		(_dropdown_menu_widgets[0].bottom=t2=i*10+3, t2+1), 
-		DropdownMenuWndProc,
-		0x3F,
-		_dropdown_menu_widgets);
-
-
-	w2->flags4 &= ~WF_WHITE_BORDER_MASK;
-}
-
 extern const StringID _currency_string_list[];
 extern uint GetMaskOfAllowedCurrencies();
 
--- a/ship_cmd.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/ship_cmd.c	Mon Sep 06 18:15:13 2004 +0000
@@ -852,6 +852,7 @@
 		VehiclePositionChanged(v);
 
 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
+		_vehicle_sort_dirty[VEHSHIP] = true; // build a ship
 		InvalidateWindow(WC_SHIPS_LIST, v->owner);
 		InvalidateWindow(WC_COMPANY, v->owner);
 	}
@@ -875,6 +876,7 @@
 	
 	if (flags & DC_EXEC) {
 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
+		_vehicle_sort_dirty[VEHSHIP] = true; // sell a ship
 		InvalidateWindow(WC_SHIPS_LIST, v->owner);
 		InvalidateWindow(WC_COMPANY, v->owner);
 		DeleteWindowById(WC_VEHICLE_VIEW, v->index);
--- a/ship_gui.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/ship_gui.c	Mon Sep 06 18:15:13 2004 +0000
@@ -9,7 +9,6 @@
 #include "station.h"
 #include "command.h"
 #include "player.h"
-//#include "town.h"
 #include "engine.h"
 
 
@@ -868,100 +867,175 @@
 	}
 }
 
+// used to get a sorted list of the vehicles
+static SortStruct _ship_sort[NUM_NORMAL_VEHICLES];
+static uint16 _num_ship_sort[MAX_PLAYERS];
+
+static void MakeSortedShiptList(byte owner)
+{
+	SortStruct *firstelement;
+	Vehicle *v;
+	uint32 n = 0;
+	uint16 *i;
+
+	if (_vehicle_sort_dirty[VEHSHIP]) { // only resort the whole array if vehicles have been added/removed
+		// reset to 0 just to be sure
+		for (i = _num_ship_sort; i != endof(_num_ship_sort); i++) {*i = 0;}
+
+		FOR_ALL_VEHICLES(v) {
+			if(v->type == VEH_Ship) {
+				_ship_sort[n].index = v->index;
+				_ship_sort[n++].owner = v->owner;
+				_num_ship_sort[v->owner]++; // add number of trains of player
+			}
+		}
+
+		// create cumulative ship-ownage
+		// ships are stored as a cummulative index, eg 25, 41, 43. This means
+		// Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2
+		for (i = &_num_ship_sort[1]; i != endof(_num_ship_sort); i++) {*i += *(i-1);}
+	
+
+		// sort by owner, then only subsort the requested owner-vehicles
+		qsort(_ship_sort, n, sizeof(_ship_sort[0]), GeneralOwnerSorter);
+
+		_last_vehicle_idx = 0; // used for "cache" in namesorting
+		_vehicle_sort_dirty[VEHSHIP] = false;
+	}
+
+	if (owner == 0) { // first element starts at 0th element and has n elements as described above
+		firstelement =	&_ship_sort[0];
+		n =							_num_ship_sort[0];
+	}	else { // nth element starts at the end of the previous one, and has n elements as described above
+		firstelement =	&_ship_sort[_num_ship_sort[owner-1]];
+		n =							_num_ship_sort[owner] - _num_ship_sort[owner-1];
+	}
+
+	_internal_sort_type				= _ship_sort_type[owner];
+	_internal_sort_order			= _ship_sort_order[owner];
+	_internal_name_sorter_id	= STR_SV_SHIP_NAME;
+	// only name sorting needs a different procedure, all others are handled by the general sorter
+	qsort(firstelement, n, sizeof(_ship_sort[0]), (_internal_sort_type == SORT_BY_NAME) ? VehicleNameSorter : GeneralVehicleSorter);
+
+	DEBUG(misc, 1) ("Resorting Ships list player %d...", owner+1);
+}
+
 static void PlayerShipsWndProc(Window *w, WindowEvent *e)
 {
 	switch(e->event) {
-	case WE_PAINT:
-		/* determine amount of items for scroller */
-		{
-			Vehicle *v;
-			int num = 0;
-			byte owner = (byte)w->window_number;
+	case WE_PAINT: {
+		uint32 i;
+		const byte window_number = (byte)w->window_number;
 
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Ship && v->owner == owner)
-					num++;
-			}
-			SetVScrollCount(w, num);
+		if (_ship_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria
+			w->disabled_state |= (1 << 3);
+
+		if (_ship_sort_dirty[window_number] || _vehicle_sort_dirty[VEHSHIP]) {
+			_ship_sort_dirty[window_number] = false;
+			MakeSortedShiptList(window_number);
+			/* reset sorting timeout */
+			w->custom[0] = DAY_TICKS;
+			w->custom[1] = PERIODIC_RESORT_DAYS;
 		}
 
+		// ships are stored as a cummulative index, eg 25, 41, 43. This means
+		// Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 ships
+		i = (window_number == 0) ? 0 : _num_ship_sort[window_number-1];
+		SetVScrollCount(w, _num_ship_sort[window_number] - i);
+		
 		/* draw the widgets */
 		{
-			Player *p = DEREF_PLAYER(w->window_number);
+			Player *p = DEREF_PLAYER(window_number);
+			/* Company Name -- (###) Ships */
 			SET_DPARAM16(0, p->name_1);
 			SET_DPARAM32(1, p->name_2);
+			SET_DPARAM16(2, w->vscroll.count);
+			SET_DPARAM16(3, _vehicle_sort_listing[_ship_sort_type[window_number]]);
 			DrawWindowWidgets(w);
 		}
+		/* draw arrow pointing up/down for ascending/descending soring */
+		DoDrawString(_ship_sort_order[window_number] & 1 ? "\xAA" : "\xA0", 85, 15, 0x10);
 
-		/* draw the ships vehicles */
+		/* draw the ship vehicles */
 		{
 			Vehicle *v;
-			int pos = w->vscroll.pos;
-			byte owner = (byte)w->window_number;
-			int x = 2;
-			int y = 15;
-			
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Ship && v->owner == owner &&
-						--pos < 0 && pos >= -4) {
-					StringID str;
-					
-					DrawShipImage(v, x + 19, y + 6, INVALID_VEHICLE);
-					DrawVehicleProfitButton(v, x, y+13);
-					
-					SET_DPARAM16(0, v->unitnumber);
-					if (IsShipDepotTile(v->tile)) {
-						str = STR_021F;
-					} else {
-						str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
-					}
-					DrawString(x, y+2, str, 0);
+			int n = 0;
+			const int x = 2;			// offset from left side of widget
+			int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;	// offset from top of widget
+			i += w->vscroll.pos;	// offset from sorted ship list of current player
 
-					SET_DPARAM32(0, v->profit_this_year);
-					SET_DPARAM32(1, v->profit_last_year);
-					DrawString(x + 12, y + 28, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
-					
-					if (v->string_id != STR_SV_SHIP_NAME) {
-						SET_DPARAM16(0, v->string_id);
-						DrawString(x+12, y, STR_01AB, 0);
-					}
+			while (i < _num_ship_sort[window_number]) {
+				StringID str;
+				v = DEREF_VEHICLE(_ship_sort[i].index);
 
-					DrawSmallShipSchedule(v, x+138, y);
+				DrawShipImage(v, x + 19, y + 6, INVALID_VEHICLE);
+				DrawVehicleProfitButton(v, x, y+13);
 
-					y += 36;
-				}				
+				SET_DPARAM16(0, v->unitnumber);
+				if (IsShipDepotTile(v->tile)) {
+					str = STR_021F;
+				} else {
+					str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
+				}
+				DrawString(x, y+2, str, 0);
+
+				SET_DPARAM32(0, v->profit_this_year);
+				SET_DPARAM32(1, v->profit_last_year);
+				DrawString(x + 12, y + 28, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
+				
+				if (v->string_id != STR_SV_SHIP_NAME) {
+					SET_DPARAM16(0, v->string_id);
+					DrawString(x+12, y, STR_01AB, 0);
+				}
+
+				DrawSmallShipSchedule(v, x+138, y);
+
+
+				y += PLY_WND_PRC__SIZE_OF_ROW_BIG;
+				i++; // next ship
+				if (++n == w->vscroll.cap) { break;} // max number of ships in the window
 			}
 		}
-		break;
-	case WE_CLICK:
-		switch(e->click.widget) {
-		case 2: { /* click ship */
-			int sel;
-			Vehicle *v;
-			byte owner;
-	
-			sel = (e->click.pt.y - 14) / 36;
+		}	break;
 
-			if ((uint)sel >= 4)
-				break;
-			sel += w->vscroll.pos;
-			owner = (byte)w->window_number;
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Ship && v->owner == owner &&	--sel < 0) {	
-					ShowShipViewWindow(v);
-					break;
-				}
+	case WE_CLICK: {
+		switch(e->click.widget) {
+		case 3: /* Flip sorting method ascending/descending */
+			_ship_sort_order[(byte)w->window_number] ^= 1;
+			_ship_sort_dirty[(byte)w->window_number] = true;
+			SetWindowDirty(w);
+			break;
+		case 4: case 5:/* Select sorting criteria dropdown menu */
+			ShowDropDownMenu(w, _vehicle_sort_listing, _ship_sort_type[(byte)w->window_number], 5, 0); // do it for widget 5
+			return;
+		case 6: { /* Matrix to show vehicles */
+			int id_v = (e->click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / PLY_WND_PRC__SIZE_OF_ROW_BIG;
+			
+			if ((uint)id_v >= w->vscroll.cap) { return;} // click out of bounds
+
+			id_v += w->vscroll.pos;
+
+			{
+				byte owner		= (byte)w->window_number;
+				uint16 adder	= (owner == 0) ? 0 : _num_ship_sort[owner - 1]; // first element in list
+				Vehicle *v;
+
+				if (id_v + adder >= _num_ship_sort[owner]) { return;} // click out of vehicle bound
+
+				v	= DEREF_VEHICLE(_ship_sort[adder+id_v].index); // add the offset id_x to that
+
+				assert(v->type == VEH_Ship && v->owner == owner && v->owner == _ship_sort[adder+id_v].owner);
+
+				ShowShipViewWindow(v);
 			}
-			break;
-		}
-		case 4: {/* click buy */
+		} break;
+
+		case 8: { /* Build new Vehicle */
 			uint tile;
 
 			tile = _last_built_ship_depot_tile;
 			do {
-				if (_map_owner[tile] == _local_player &&
-						IsShipDepotTile(tile)) {
-					
+				if (_map_owner[tile] == _local_player && IsShipDepotTile(tile)) {					
 					ShowShipDepotWindow(tile);
 					ShowBuildShipWindow(tile);
 					return;
@@ -973,38 +1047,74 @@
 			ShowBuildShipWindow(0);
 		} break;
 		}
+	}	break;
+
+	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
+		if (_ship_sort_type[(byte)w->window_number] != e->dropdown.index) // if value hasn't changed, dont resort list
+			_ship_sort_dirty[(byte)w->window_number] = true;
+
+		_ship_sort_type[(byte)w->window_number] = e->dropdown.index;
+
+		if (_ship_sort_type[(byte)w->window_number] != SORT_BY_UNSORTED) // enable 'Sort By' if a sorter criteria is chosen
+			w->disabled_state &= ~(1 << 3);
+
+		SetWindowDirty(w);
+		break;
+	case WE_CREATE: /* set up resort timer */
+		w->custom[0] = DAY_TICKS;
+		w->custom[1] = PERIODIC_RESORT_DAYS;
+		break;
+	case WE_TICK: /* resort the list every 20 seconds orso (10 days) */
+		if (--w->custom[0] == 0) {
+			w->custom[0] = DAY_TICKS;
+			if (--w->custom[1] == 0) {
+				w->custom[1] = PERIODIC_RESORT_DAYS;
+				_ship_sort_dirty[(byte)w->window_number] = true;
+				DEBUG(misc, 1) ("Periodic resort Ships list player %d...", w->window_number+1);
+				SetWindowDirty(w);
+			}
+		}
 		break;
 	}
 }
-
 static const Widget _player_ships_widgets[] = {
-{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_9805_SHIPS, STR_018C_WINDOW_TITLE_DRAG_THIS},
-{     WWT_MATRIX,    14,     0,   248,    14,   157, 0x401, STR_9823_SHIPS_CLICK_ON_SHIP_FOR},
-{  WWT_SCROLLBAR,    14,   249,   259,    14,   157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,    14,     0,   129,   158,   169, STR_9804_NEW_SHIPS, STR_9824_BUILD_NEW_SHIPS_REQUIRES},
-{     WWT_IMGBTN,    14,   130,   259,   158,   169, 0x0},
+{   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,							STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_9805_SHIPS,				STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,    14,     0,    15,    14,    25, 0x0,										0},
+{ WWT_PUSHTXTBTN,    14,    16,    96,    14,    25, SRT_SORT_BY,						STR_SORT_TIP},
+{    WWT_TEXTBTN,    14,    97,   248,    14,    25, STR_02E7,							0},
+{   WWT_CLOSEBOX,    14,   249,   259,    14,    25, STR_0225,							STR_SORT_TIP},
+{     WWT_MATRIX,    14,     0,   248,    26,   169, 0x401,									STR_9823_SHIPS_CLICK_ON_SHIP_FOR},
+{  WWT_SCROLLBAR,    14,   249,   259,    26,   169, 0x0,										STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{ WWT_PUSHTXTBTN,    14,     0,   129,   170,   181, STR_9804_NEW_SHIPS,		STR_9824_BUILD_NEW_SHIPS_REQUIRES},
+{      WWT_PANEL,    14,   130,   259,   170,   181, 0x0,										0},
 {      WWT_LAST},
 };
+
 static const WindowDesc _player_ships_desc = {
-	-1, -1, 260, 170,
+	-1, -1, 260, 182,
 	WC_SHIPS_LIST,0,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 	_player_ships_widgets,
 	PlayerShipsWndProc
 };
 
 static const Widget _other_player_ships_widgets[] = {
-{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_9805_SHIPS, STR_018C_WINDOW_TITLE_DRAG_THIS},
-{     WWT_MATRIX,    14,     0,   248,    14,   157, 0x401, STR_9823_SHIPS_CLICK_ON_SHIP_FOR},
-{  WWT_SCROLLBAR,    14,   249,   259,    14,   157, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,							STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,    14,    11,   259,     0,    13, STR_9805_SHIPS,				STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,    14,     0,    15,    14,    25, 0x0,										0},
+{ WWT_PUSHTXTBTN,    14,    16,    96,    14,    25, SRT_SORT_BY,						STR_SORT_TIP},
+{    WWT_TEXTBTN,    14,    97,   248,    14,    25, STR_02E7,							0},
+{   WWT_CLOSEBOX,    14,   249,   259,    14,    25, STR_0225,							STR_SORT_TIP},
+{     WWT_MATRIX,    14,     0,   248,    26,   169, 0x401,									STR_9823_SHIPS_CLICK_ON_SHIP_FOR},
+{  WWT_SCROLLBAR,    14,   249,   259,    26,   169, 0x0,										STR_0190_SCROLL_BAR_SCROLLS_LIST},
 {      WWT_LAST},
 };
+
 static const WindowDesc _other_player_ships_desc = {
-	-1, -1, 260, 158,
+	-1, -1, 260, 170,
 	WC_SHIPS_LIST,0,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 	_other_player_ships_widgets,
 	PlayerShipsWndProc
 };
--- a/station_gui.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/station_gui.c	Mon Sep 06 18:15:13 2004 +0000
@@ -44,47 +44,37 @@
 	}
 }
 
-// used to get a sorted list of the stations
-typedef struct StationSort {
-	uint16	index;
-	byte		owner;
-} StationSort;
-
-static StationSort _station_sort[lengthof(_stations)];
+static SortStruct _station_sort[lengthof(_stations)];
 static uint16 _num_station_sort[MAX_PLAYERS];
 
 static char _bufcache[64];
 static uint16 _last_station_idx;
 
-static int CDECL StationSorterByName(const void *a, const void *b)
+static int CDECL StationNameSorter(const void *a, const void *b)
 {
 	char buf1[64];
 	Station *st;
-	StationSort *cmp1, *cmp2;
-	cmp1 = (StationSort*)a;
-	cmp2 = (StationSort*)b;
+	SortStruct *cmp1, *cmp2;
+	cmp1 = (SortStruct*)a;
+	cmp2 = (SortStruct*)b;
 
-	// sort stations by owner, and inside owner by name
-	if (cmp1->owner == cmp2->owner) {	// if same owner, sort by name
-		st = DEREF_STATION(cmp1->index);
+	st = DEREF_STATION(cmp1->index);
+	SET_DPARAM16(0, st->town->townnametype);
+	SET_DPARAM32(1, st->town->townnameparts);
+	GetString(buf1, st->string_id);
+
+	if ( cmp2->index != _last_station_idx) {
+		_last_station_idx = cmp2->index;
+		st = DEREF_STATION(cmp2->index);
 		SET_DPARAM16(0, st->town->townnametype);
 		SET_DPARAM32(1, st->town->townnameparts);
-		GetString(buf1, st->string_id);
+		GetString(_bufcache, st->string_id);
+	}
 
-		if ( cmp2->index != _last_station_idx) {
-			_last_station_idx = cmp2->index;
-			st = DEREF_STATION(cmp2->index);
-			SET_DPARAM16(0, st->town->townnametype);
-			SET_DPARAM32(1, st->town->townnameparts);
-			GetString(_bufcache, st->string_id);
-		}
-
-		return strcmp(buf1, _bufcache);	// sort by name
-	}
-	return cmp1->owner - cmp2->owner;	// sort by owner
+	return strcmp(buf1, _bufcache);	// sort by name
 }
 
-static void MakeSortedStationList(Window *w)
+static void MakeSortedStationList()
 {
 	Station *st;
 	uint16 n = 0;
@@ -106,7 +96,11 @@
 	for (i = &_num_station_sort[1]; i != endof(_num_station_sort); i++) {*i += *(i-1);}
 
 	_last_station_idx = 255; // used for "cache"
-	qsort(_station_sort, n, sizeof(_station_sort[0]), StationSorterByName);
+
+	// sort by owner, then only subsort the requested owner-vehicles
+	qsort(_station_sort, n, sizeof(_station_sort[0]), GeneralOwnerSorter);
+
+	qsort(_station_sort, n, sizeof(_station_sort[0]), StationNameSorter);
 	
 	DEBUG(misc, 1) ("Resorting Stations list...");
 }
@@ -118,12 +112,12 @@
 		byte i;
 		if (_station_sort_dirty) {
 			_station_sort_dirty = false;
-			MakeSortedStationList(w);
+			MakeSortedStationList();
 		}
 
 		// stations are stored as a cummulative index, eg 25, 41, 43. This means
 		// Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 stations
-		i = (byte)(w->window_number == 0) ? 0 : _num_station_sort[w->window_number-1];;
+		i = (byte)(w->window_number == 0) ? 0 : _num_station_sort[w->window_number-1];
 		SetVScrollCount(w, _num_station_sort[w->window_number] - i);
 
 		/* draw widgets, with player's name in the caption */
@@ -167,7 +161,7 @@
 				}
 				y += 10;
 				i++;	// next station
-				if (++p == 12) { break;} // max number of stations in 1 window
+				if (++p == w->vscroll.cap) { break;} // max number of stations in 1 window
 			}
 		}
 	} break;
--- a/town_gui.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/town_gui.c	Mon Sep 06 18:15:13 2004 +0000
@@ -351,7 +351,7 @@
 static char _bufcache[64];
 static byte _last_town_idx;
 
-static int CDECL TownSorterByName(const void *a, const void *b)
+static int CDECL TownNameSorter(const void *a, const void *b)
 {
 	char buf1[64];
 	Town *t;
@@ -374,11 +374,11 @@
 	return r;
 }
 
-static int CDECL TownSorterByPop(const void *a, const void *b)
+static int CDECL TownPopSorter(const void *a, const void *b)
 {
 	Town *ta = DEREF_TOWN(*(byte*)a);
 	Town *tb = DEREF_TOWN(*(byte*)b);
-	int r = tb->population - ta->population;
+	int r = ta->population - tb->population;
 	if (_town_sort_order & 1) r = -r;
 	return r;
 }
@@ -391,7 +391,7 @@
 	_num_town_sort = n;
 
 	_last_town_idx = 255; // used for "cache"
-	qsort(_town_sort, n, 1, _town_sort_order & 2 ? TownSorterByPop : TownSorterByName);
+	qsort(_town_sort, n, sizeof(_town_sort[0]), _town_sort_order & 2 ? TownPopSorter : TownNameSorter);
 
 	DEBUG(misc, 1) ("Resorting Towns list...");
 }
--- a/train_cmd.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/train_cmd.c	Mon Sep 06 18:15:13 2004 +0000
@@ -496,6 +496,7 @@
 			NormalizeTrainVehInDepot(v);
 
 			InvalidateWindow(WC_VEHICLE_DEPOT, tile);
+			_vehicle_sort_dirty[VEHTRAIN] = true; // build a trainengine
 			InvalidateWindow(WC_TRAINS_LIST, v->owner);
 			InvalidateWindow(WC_COMPANY, v->owner);
 		}
@@ -793,7 +794,10 @@
 	if (flags & DC_EXEC) {
 		// always redraw the depot. maybe redraw train list
 		InvalidateWindow(WC_VEHICLE_DEPOT, first->tile);
-		if (first->subtype == 0) InvalidateWindow(WC_TRAINS_LIST, first->owner);
+		if (first->subtype == 0) {
+			_vehicle_sort_dirty[VEHTRAIN] = true; // sell a wagon / locomotive
+			InvalidateWindow(WC_TRAINS_LIST, first->owner);
+		}
 		// when selling an attached locomotive. we need to delete its window.
 		if (v->subtype == 0) {
 			DeleteWindowById(WC_VEHICLE_VIEW, v->index);
@@ -2198,6 +2202,7 @@
 
 	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 	DeleteWindowById(WC_VEHICLE_VIEW, v->index);
+	_vehicle_sort_dirty[VEHTRAIN] = true; // remove crashed train
 	InvalidateWindow(WC_TRAINS_LIST, v->owner);
 	InvalidateWindow(WC_COMPANY, v->owner);
 
--- a/train_gui.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/train_gui.c	Mon Sep 06 18:15:13 2004 +0000
@@ -9,7 +9,6 @@
 #include "station.h"
 #include "command.h"
 #include "player.h"
-//#include "town.h"
 #include "engine.h"
 
 static Engine * const _rail_engines[3] = {
@@ -54,6 +53,7 @@
 		found = GetLastVehicleInChain(found);
 		// put the new wagon at the end of the loco.
 		DoCommandP(0, _new_wagon_id | (found->index<<16), 0, NULL, CMD_MOVE_RAIL_VEHICLE);
+		_vehicle_sort_dirty[VEHTRAIN] = true;
 	}
 }
 
@@ -562,7 +562,6 @@
 {   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,							STR_018B_CLOSE_WINDOW},
 {    WWT_CAPTION,    14,    11,   348,     0,    13, STR_8800_TRAIN_DEPOT,	STR_018C_WINDOW_TITLE_DRAG_THIS},
 {     WWT_MATRIX,    14,     0,   313,    14,    97, 0x601,									STR_883F_TRAINS_CLICK_ON_TRAIN_FOR},
-//{      WWT_PANEL,    14,   314,   337,    14,   108, 0x2A9,									STR_8841_DRAG_TRAIN_VEHICLE_TO_HERE},
 {      WWT_PANEL,    14,   314,   337,    14,    54, 0x2A9,									STR_8841_DRAG_TRAIN_VEHICLE_TO_HERE},
 {      WWT_PANEL,    14,   314,   337,    55,   108, 0x2BF,									STR_DRAG_WHOLE_TRAIN_TO_SELL_TIP},
 
@@ -700,9 +699,6 @@
 	WP(w,refit_d).sel = -1;
 }
 
-
-
-
 static Widget _train_view_widgets[] = {
 {   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
 {    WWT_CAPTION,    14,    11,   249,     0,    13, STR_882E, STR_018C_WINDOW_TITLE_DRAG_THIS},
@@ -1140,129 +1136,172 @@
 	WP(w,traindetails_d).tab = 0;
 }
 
-// draw the vehicle profit button in the vehicle list window.
-void DrawVehicleProfitButton(Vehicle *v, int x, int y)
-{
-	uint32 ormod;
+// used to get a sorted list of the vehicles
+static SortStruct _train_sort[NUM_NORMAL_VEHICLES];
+static uint16 _num_train_sort[MAX_PLAYERS];
 
-	// draw profit-based colored icons
-	if(v->age <= 365 * 2)
-		ormod = 0x3158000; // grey
-	else if(v->profit_last_year < 0)
-		ormod = 0x30b8000; //red
-	else if(v->profit_last_year < 10000)
-		ormod = 0x30a8000; // yellow
-	else
-		ormod = 0x30d8000; // green
-	DrawSprite((SPR_OPENTTD_BASE + 10) | ormod, x, y);
+static void MakeSortedTrainList(byte owner)
+{
+	SortStruct *firstelement;
+	Vehicle *v;
+	uint32 n = 0;
+	uint16 *i;
+
+	if (_vehicle_sort_dirty[VEHTRAIN]) { // only resort the whole array if vehicles have been added/removed
+		// reset to 0 just to be sure
+		for (i = _num_train_sort; i != endof(_num_train_sort); i++) {*i = 0;}
+
+		FOR_ALL_VEHICLES(v) {
+			if(v->type == VEH_Train && v->subtype == 0) {
+				_train_sort[n].index = v->index;
+				_train_sort[n++].owner = v->owner;
+				_num_train_sort[v->owner]++; // add number of trains of player
+			}
+		}
+
+		// create cumulative train-ownage
+		// trains are stored as a cummulative index, eg 25, 41, 43. This means
+		// Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2
+		for (i = &_num_train_sort[1]; i != endof(_num_train_sort); i++) {*i += *(i-1);}
+	
+
+		// sort by owner, then only subsort the requested owner-vehicles
+		qsort(_train_sort, n, sizeof(_train_sort[0]), GeneralOwnerSorter);
+
+		_last_vehicle_idx = 0; // used for "cache" in namesorting
+		_vehicle_sort_dirty[VEHTRAIN] = false;
+	}
+
+	if (owner == 0) { // first element starts at 0th element and has n elements as described above
+		firstelement =	&_train_sort[0];
+		n =							_num_train_sort[0];
+	}	else { // nth element starts at the end of the previous one, and has n elements as described above
+		firstelement =	&_train_sort[_num_train_sort[owner-1]];
+		n =							_num_train_sort[owner] - _num_train_sort[owner-1];
+	}
+
+	_internal_sort_type				= _train_sort_type[owner];
+	_internal_sort_order			= _train_sort_order[owner];
+	_internal_name_sorter_id	= STR_SV_TRAIN_NAME;
+	// only name sorting needs a different procedure, all others are handled by the general sorter
+	qsort(firstelement, n, sizeof(_train_sort[0]), (_internal_sort_type == SORT_BY_NAME) ? VehicleNameSorter : GeneralVehicleSorter);
+
+	DEBUG(misc, 1) ("Resorting Trains list player %d...", owner+1);
 }
 
-
-static const StringID _player_trains_tooltips[] = {
-	STR_018B_CLOSE_WINDOW,
-	STR_018C_WINDOW_TITLE_DRAG_THIS,
-	STR_883D_TRAINS_CLICK_ON_TRAIN_FOR,
-	STR_0190_SCROLL_BAR_SCROLLS_LIST,
-	STR_883E_BUILD_NEW_TRAINS_REQUIRES,
-	0,
-};
-
 static void PlayerTrainsWndProc(Window *w, WindowEvent *e)
 {
 	switch(e->event) {
-	case WE_PAINT:
-		/* determine amount of items for scroller */
-		{
-			Vehicle *v;
-			int num = 0;
-			byte owner = (byte)w->window_number;
+	case WE_PAINT: {
+		uint32 i;
+		const byte window_number = (byte)w->window_number;
 
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Train && v->subtype == 0 && v->owner == owner)
-					num++;
-			}
+		if (_train_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria
+			w->disabled_state |= (1 << 3);
 
-			SetVScrollCount(w, num);
+		if (_train_sort_dirty[window_number] || _vehicle_sort_dirty[VEHTRAIN]) {
+			_train_sort_dirty[window_number] = false;
+			MakeSortedTrainList(window_number);
+			/* reset sorting timeout */
+			w->custom[0] = DAY_TICKS;
+			w->custom[1] = PERIODIC_RESORT_DAYS;
 		}
+
+		// Trains are stored as a cummulative index, eg 25, 41, 43. This means
+		// Player0: 25; Player1: (41-25) 16; Player2: (43-41) 2 trains
+		i = (window_number == 0) ? 0 : _num_train_sort[window_number-1];
+		SetVScrollCount(w, _num_train_sort[window_number] - i);
 		
 		/* draw the widgets */
 		{
-			Player *p = DEREF_PLAYER(w->window_number);
+			Player *p = DEREF_PLAYER(window_number);
+			/* Company Name -- (###) Trains */
 			SET_DPARAM16(0, p->name_1);
 			SET_DPARAM32(1, p->name_2);
+			SET_DPARAM16(2, w->vscroll.count);
+			SET_DPARAM16(3, _vehicle_sort_listing[_train_sort_type[window_number]]);
 			DrawWindowWidgets(w);
 		}
+		/* draw arrow pointing up/down for ascending/descending soring */
+		DoDrawString(_train_sort_order[window_number] & 1 ? "\xAA" : "\xA0", 150, 15, 0x10);
 
 		/* draw the trains */
 		{
 			Vehicle *v;
-			int pos = w->vscroll.pos;
-			byte owner = (byte)w->window_number;
-			int x = 2;
-			int y = 15;
-			
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Train && v->subtype == 0 && v->owner == owner &&
-						--pos < 0 && pos >= -7) {
-					StringID str;
-					
-					DrawTrainImage(v, x + 21, y + 6, 10, 0, INVALID_VEHICLE);
-					DrawVehicleProfitButton(v, x, y+13);
-					
-					SET_DPARAM16(0, v->unitnumber);
-					if (IsTrainDepotTile(v->tile)) {
-						str = STR_021F;
-					} else {
-						str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
-					}
-					DrawString(x, y+2, str, 0);
+			int n = 0;
+			const int x = 2;			// offset from left side of widget
+			int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;	// offset from top of widget
+			i += w->vscroll.pos;	// offset from sorted trains list of current player
 
-					SET_DPARAM32(0, v->profit_this_year);
-					SET_DPARAM32(1, v->profit_last_year);
-					DrawString(x + 21, y + 18, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
-					
-					if (v->string_id != STR_SV_TRAIN_NAME) {
-						SET_DPARAM16(0, v->string_id);
-						DrawString(x+21, y, STR_01AB, 0);
-					}
+			while (i < _num_train_sort[window_number]) {
+				StringID str;
+				v = DEREF_VEHICLE(_train_sort[i].index);
 
-					y += 26;
-				}				
+				DrawTrainImage(v, x + 21, y + 6, 10, 0, INVALID_VEHICLE);
+				DrawVehicleProfitButton(v, x, y+13);
+
+				SET_DPARAM16(0, v->unitnumber);
+				if (IsTrainDepotTile(v->tile)) {
+					str = STR_021F;
+				} else {
+					str = v->age > v->max_age - 366 ? STR_00E3 : STR_00E2;
+				}
+				DrawString(x, y+2, str, 0);
+
+				SET_DPARAM32(0, v->profit_this_year);
+				SET_DPARAM32(1, v->profit_last_year);
+				DrawString(x + 21, y + 18, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, 0);
+				
+				if (v->string_id != STR_SV_TRAIN_NAME) {
+					SET_DPARAM16(0, v->string_id);
+					DrawString(x+21, y, STR_01AB, 0);
+				}
+
+				y += PLY_WND_PRC__SIZE_OF_ROW_SMALL;
+				i++; // next train
+				if (++n == w->vscroll.cap) { break;} // max number of trains in the window
 			}
 		}
-		break;
+		}	break;
 
 	case WE_CLICK: {
 		switch(e->click.widget) {
-		case 2: {
-			int idx = (e->click.pt.y - 0xE) / 26;
-			Vehicle *v;
-			byte owner;
-
-			if ((uint)idx >= 7)
-				break;
+		case 3: /* Flip sorting method ascending/descending */
+			_train_sort_order[(byte)w->window_number] ^= 1;
+			_train_sort_dirty[(byte)w->window_number] = true;
+			SetWindowDirty(w);
+			break;
+		case 4: case 5:/* Select sorting criteria dropdown menu */
+			ShowDropDownMenu(w, _vehicle_sort_listing, _train_sort_type[(byte)w->window_number], 5, 0); // do it for widget 5
+			return;
+		case 6: { /* Matrix to show vehicles */
+			int id_v = (e->click.pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / PLY_WND_PRC__SIZE_OF_ROW_SMALL;
+			
+			if ((uint)id_v >= w->vscroll.cap) { return;} // click out of bounds
 
-			idx += w->vscroll.pos;
-
-			owner = (byte)w->window_number;
+			id_v += w->vscroll.pos;
 
-			FOR_ALL_VEHICLES(v) {
-				if (v->type == VEH_Train && v->subtype == 0 && v->owner == owner &&
-						--idx < 0) {	
-					ShowTrainViewWindow(v);
-					break;
-				}
+			{
+				byte owner		= (byte)w->window_number;
+				uint16 adder	= (owner == 0) ? 0 : _num_train_sort[owner - 1]; // first element in list
+				Vehicle *v;
+
+				if (id_v + adder >= _num_train_sort[owner]) { return;} // click out of vehicle bound
+
+				v	= DEREF_VEHICLE(_train_sort[adder+id_v].index); // add the offset id_x to that
+
+				assert(v->type == VEH_Train && v->subtype == 0 && v->owner == owner && v->owner == _train_sort[adder+id_v].owner);
+
+				ShowTrainViewWindow(v);
 			}
 		} break;
 
-		case 4: {
+		case 8: { /* Build new Vehicle */
 			uint tile;
 
 			tile = _last_built_train_depot_tile;
 			do {
-				if (_map_owner[tile] == _local_player &&
-						IsTrainDepotTile(tile)) {
-					
+				if (_map_owner[tile] == _local_player && IsTrainDepotTile(tile)) {			
 					ShowTrainDepotWindow(tile);
 					ShowBuildTrainWindow(tile);
 					return;
@@ -1276,39 +1315,73 @@
 		}
 	}	break;
 
+	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
+		if (_train_sort_type[(byte)w->window_number] != e->dropdown.index) // if value hasn't changed, dont resort list
+			_train_sort_dirty[(byte)w->window_number] = true;
+
+		_train_sort_type[(byte)w->window_number] = e->dropdown.index;
+
+		if (_train_sort_type[(byte)w->window_number] != SORT_BY_UNSORTED) // enable 'Sort By' if a sorter criteria is chosen
+			w->disabled_state &= ~(1 << 3);
+
+		SetWindowDirty(w);
+		break;
+	case WE_CREATE: /* set up resort timer */
+		w->custom[0] = DAY_TICKS;
+		w->custom[1] = PERIODIC_RESORT_DAYS;
+		break;
+	case WE_TICK: /* resort the list every 20 seconds orso (10 days) */
+		if (--w->custom[0] == 0) {
+			w->custom[0] = DAY_TICKS;
+			if (--w->custom[1] == 0) {
+				w->custom[1] = PERIODIC_RESORT_DAYS;
+				_train_sort_dirty[(byte)w->window_number] = true;
+				DEBUG(misc, 1) ("Periodic resort Trains list player %d...", w->window_number+1);
+				SetWindowDirty(w);
+			}
+		}
+		break;
 	}
 }
 
 static const Widget _player_trains_widgets[] = {
 {   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,							STR_018B_CLOSE_WINDOW},
 {    WWT_CAPTION,    14,    11,   324,     0,    13, STR_881B_TRAINS,				STR_018C_WINDOW_TITLE_DRAG_THIS},
-{     WWT_MATRIX,    14,     0,   313,    14,   195, 0x701,									STR_883D_TRAINS_CLICK_ON_TRAIN_FOR},
-{  WWT_SCROLLBAR,    14,   314,   324,    14,   195, 0x0,										STR_0190_SCROLL_BAR_SCROLLS_LIST},
-{ WWT_PUSHTXTBTN,    14,     0,   161,   196,   207, STR_8815_NEW_VEHICLES,	STR_883E_BUILD_NEW_TRAINS_REQUIRES},
-{      WWT_PANEL,    14,   162,   324,   196,   207, 0x0,										0},
+{      WWT_PANEL,    14,     0,    80,    14,    25, 0x0,										0},
+{ WWT_PUSHTXTBTN,    14,    81,   161,    14,    25, SRT_SORT_BY,           STR_SORT_TIP},
+{    WWT_TEXTBTN,    14,   162,   313,    14,    25, STR_02E7,              0},
+{   WWT_CLOSEBOX,    14,   314,   324,    14,    25, STR_0225,              STR_SORT_TIP},
+{     WWT_MATRIX,    14,     0,   313,    26,   207, 0x701,									STR_883D_TRAINS_CLICK_ON_TRAIN_FOR},
+{  WWT_SCROLLBAR,    14,   314,   324,    26,   207, 0x0,										STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{ WWT_PUSHTXTBTN,    14,     0,   161,   208,   219, STR_8815_NEW_VEHICLES,	STR_883E_BUILD_NEW_TRAINS_REQUIRES},
+{      WWT_PANEL,    14,   162,   324,   208,   219, 0x0,										0},
 {      WWT_LAST},
 };
 
 static const WindowDesc _player_trains_desc = {
-	-1, -1, 325, 208,
+	-1, -1, 325, 220,
 	WC_TRAINS_LIST,0,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 	_player_trains_widgets,
 	PlayerTrainsWndProc
 };
 
 static const Widget _other_player_trains_widgets[] = {
-{   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,				STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,    14,    11,   324,     0,    13, STR_881B_TRAINS,	STR_018C_WINDOW_TITLE_DRAG_THIS},
-{     WWT_MATRIX,    14,     0,   313,    14,   195, 0x701,						STR_883D_TRAINS_CLICK_ON_TRAIN_FOR},
-{  WWT_SCROLLBAR,    14,   314,   324,    14,   195, 0x0,							STR_0190_SCROLL_BAR_SCROLLS_LIST},
+{   WWT_CLOSEBOX,    14,     0,    10,     0,    13, STR_00C5,							STR_018B_CLOSE_WINDOW},
+{    WWT_CAPTION,    14,    11,   324,     0,    13, STR_881B_TRAINS,				STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,    14,     0,    80,    14,    25, 0x0,										0},
+{ WWT_PUSHTXTBTN,    14,    81,   161,    14,    25, SRT_SORT_BY,           STR_SORT_TIP},
+{    WWT_TEXTBTN,    14,   162,   313,    14,    25, STR_02E7,              0},
+{   WWT_CLOSEBOX,    14,   314,   324,    14,    25, STR_0225,              STR_SORT_TIP},
+{     WWT_MATRIX,    14,     0,   313,    26,   207, 0x701,									STR_883D_TRAINS_CLICK_ON_TRAIN_FOR},
+{  WWT_SCROLLBAR,    14,   314,   324,    26,   207, 0x0,										STR_0190_SCROLL_BAR_SCROLLS_LIST},
 {      WWT_LAST},
 };
 
 static const WindowDesc _other_player_trains_desc = {
-	-1, -1, 325, 196,
+	-1, -1, 325, 208,
 	WC_TRAINS_LIST,0,
-	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
+	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 	_other_player_trains_widgets,
 	PlayerTrainsWndProc
 };
@@ -1317,13 +1390,13 @@
 {
 	Window *w;
 
-	if ( player == _local_player) {
+	if (player == _local_player) {
 		w = AllocateWindowDescFront(&_player_trains_desc, player);
-	} else  {
+	} else {
 		w = AllocateWindowDescFront(&_other_player_trains_desc, player);
 	}
 	if (w) {
 		w->caption_color = w->window_number;
-		w->vscroll.cap = 7;
+		w->vscroll.cap = 7; // maximum number of vehicles shown
 	}
 }
--- a/ttd.dsp	Mon Sep 06 16:57:40 2004 +0000
+++ b/ttd.dsp	Mon Sep 06 18:15:13 2004 +0000
@@ -358,6 +358,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=.\vehicle_gui.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\viewport.c
 # End Source File
 # Begin Source File
@@ -502,6 +506,10 @@
 # End Source File
 # Begin Source File
 
+SOURCE=.\vehicle_gui.h
+# End Source File
+# Begin Source File
+
 SOURCE=.\viewport.h
 # End Source File
 # Begin Source File
--- a/ttd.h	Mon Sep 06 16:57:40 2004 +0000
+++ b/ttd.h	Mon Sep 06 18:15:13 2004 +0000
@@ -32,6 +32,16 @@
 	int year, month, day;
 } YearMonthDay;
 
+/* --- 1 Day is 74 ticks ---
+* The game's internal structure is dictated by ticks. The date counter (date_fract) is an integer of
+* uint16 type, so it can have a max value of 65536. Every tick this variable (date_fract) is 
+* increased by 885. When it overflows, the new day loop is called. 
+* * this that means 1 day is : 65536 / 885 = 74 ticks
+* * 1 tick is approximately 27ms. 
+* * 1 day is thus about 2 seconds (74*27 = 1998) on a machine that can run OpenTTD normally
+*/
+#define DAY_TICKS 74
+
 #include "macros.h"
 
 // Forward declarations of structs.
--- a/ttd.vcproj	Mon Sep 06 16:57:40 2004 +0000
+++ b/ttd.vcproj	Mon Sep 06 18:15:13 2004 +0000
@@ -1168,6 +1168,9 @@
 				RelativePath="vehicle.h">
 			</File>
 			<File
+				RelativePath=".\vehicle_gui.h">
+			</File>
+			<File
 				RelativePath="viewport.h">
 			</File>
 			<File
@@ -2015,6 +2018,9 @@
 						BasicRuntimeChecks="3"/>
 				</FileConfiguration>
 			</File>
+			<File
+				RelativePath=".\vehicle_gui.c">
+			</File>
 		</Filter>
 		<Filter
 			Name="Landscape"
--- a/unix.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/unix.c	Mon Sep 06 18:15:13 2004 +0000
@@ -43,7 +43,7 @@
 	int r;
 
 	if (_savegame_sort_order < 2) // sort by date
-    r = da->mtime < db->mtime ? 1 : -1;
+    r = da->mtime < db->mtime ? -1 : 1;
 	else	
 		r = strcmp(da->title[0] ? da->title : da->name, db->title[0] ? db->title : db->name);
 
--- a/vehicle.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/vehicle.c	Mon Sep 06 18:15:13 2004 +0000
@@ -1399,6 +1399,10 @@
 		StringID old_str = v->string_id;
 		v->string_id = str;
 		DeleteName(old_str);
+		_train_sort_dirty[v->owner]			= true;
+		_aircraft_sort_dirty[v->owner]	= true;
+		_ship_sort_dirty[v->owner]			= true;
+		_road_sort_dirty[v->owner]			= true;
 		MarkWholeScreenDirty();
 	} else {
 		DeleteName(str);
--- a/vehicle.h	Mon Sep 06 16:57:40 2004 +0000
+++ b/vehicle.h	Mon Sep 06 18:15:13 2004 +0000
@@ -1,6 +1,8 @@
 #ifndef VEHICLE_H
 #define VEHICLE_H
 
+#include "vehicle_gui.h"
+
 typedef struct VehicleRail {
 	uint16 last_speed;		// NOSAVE: only used in UI
 	uint16 crash_anim_pos;
@@ -369,6 +371,7 @@
 #define BEGIN_ENUM_WAGONS(v) do {
 #define END_ENUM_WAGONS(v) } while ( (v=v->next) != NULL);
 
+#define DEREF_VEHICLE(i) (&_vehicles[i])
 #define FOR_ALL_VEHICLES(v) for(v=_vehicles; v != endof(_vehicles); v++)
 
 /* vehicle.c */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vehicle_gui.c	Mon Sep 06 18:15:13 2004 +0000
@@ -0,0 +1,147 @@
+#include "stdafx.h"
+#include "ttd.h"
+
+#include "vehicle.h"
+
+/* General Vehicle GUI based procedures that are independent of vehicle types */
+void InitializeVehiclesGuiList()
+{
+	bool *i;
+	for (i = _train_sort_dirty; i != endof(_train_sort_dirty); i++)
+		*i = true;
+
+	for (i = _aircraft_sort_dirty; i != endof(_aircraft_sort_dirty); i++)
+		*i = true;
+
+	for (i = _ship_sort_dirty; i != endof(_ship_sort_dirty); i++)
+		*i = true;
+
+	for (i = _road_sort_dirty; i != endof(_road_sort_dirty); i++)
+		*i = true;
+
+	for (i = _vehicle_sort_dirty; i != endof(_vehicle_sort_dirty); i++)
+		*i = true;
+
+	//memset(_train_sort_dirty, true, sizeof(_train_sort_dirty));
+}
+
+// draw the vehicle profit button in the vehicle list window.
+void DrawVehicleProfitButton(Vehicle *v, int x, int y)
+{
+	uint32 ormod;
+
+	// draw profit-based colored icons
+	if(v->age <= 365 * 2)
+		ormod = 0x3158000; // grey
+	else if(v->profit_last_year < 0)
+		ormod = 0x30b8000; //red
+	else if(v->profit_last_year < 10000)
+		ormod = 0x30a8000; // yellow
+	else
+		ormod = 0x30d8000; // green
+	DrawSprite((SPR_OPENTTD_BASE + 10) | ormod, x, y);
+}
+
+/************ Sorter functions *****************/
+int CDECL GeneralOwnerSorter(const void *a, const void *b)
+{
+	return (*(SortStruct*)a).owner - (*(SortStruct*)b).owner;
+}
+
+static char _bufcache[64];	// used together with _last_vehicle_idx to hopefully speed up stringsorting
+
+/* Variables you need to set before calling this function!
+* 1. (uint32)_internal_name_sorter_id:	default StringID of the vehicle when no name is set. eg
+*    STR_SV_TRAIN_NAME for trains or STR_SV_AIRCRAFT_NAME for aircraft
+* 2. (bool)_internal_sort_order:				sorting order, descending/ascending
+*/
+int CDECL VehicleNameSorter(const void *a, const void *b)
+{
+	SortStruct *cmp1 = (SortStruct*)a;
+	SortStruct *cmp2 = (SortStruct*)b;
+	Vehicle *v;
+	char buf1[64] = "\0";
+	int r;
+
+	v = DEREF_VEHICLE(cmp1->index);
+	if (v->string_id != _internal_name_sorter_id) {
+		SET_DPARAM16(0, v->string_id);
+		GetString(buf1, STR_0315);
+	}
+
+	if ( cmp2->index != _last_vehicle_idx) {
+		_last_vehicle_idx = cmp2->index;
+		v = DEREF_VEHICLE(cmp2->index);
+		_bufcache[0] = '\0';
+		if (v->string_id != _internal_name_sorter_id) {
+			SET_DPARAM16(0, v->string_id);
+			GetString(_bufcache, STR_0315);
+		}
+	}
+
+	r =  strcmp(buf1, _bufcache);	// sort by name
+	if (_internal_sort_order & 1) r = -r;
+	return r;
+}
+
+/* Variables you need to set before calling this function!
+* 1. (byte)_internal_sort_type:		the criteria by which to sort these vehicles (number, age, etc)
+* 2. (bool)_internal_sort_order:	sorting order, descending/ascending
+*/
+int CDECL GeneralVehicleSorter(const void *a, const void *b)
+{
+	SortStruct *cmp1 = (SortStruct*)a;
+	SortStruct *cmp2 = (SortStruct*)b;
+	Vehicle *va = DEREF_VEHICLE(cmp1->index);
+	Vehicle *vb = DEREF_VEHICLE(cmp2->index);
+	int r;
+
+	switch (_internal_sort_type) {
+		case SORT_BY_UNSORTED: /* Sort unsorted */
+			return va->index - vb->index;
+		case SORT_BY_NUMBER: /* Sort by Number */
+			r = va->unitnumber - vb->unitnumber;
+			break;
+		/* case SORT_BY_NAME: Sort by Name (VehicleNameSorter)*/
+		case SORT_BY_AGE: /* Sort by Age */
+			r = va->age - vb->age;
+			break;
+		case SORT_BY_PROFIT_THIS_YEAR: /* Sort by Profit this year */
+			r = va->profit_this_year - vb->profit_this_year;
+			break;
+		case SORT_BY_PROFIT_LAST_YEAR: /* Sort by Profit last year */
+			r = va->profit_last_year - vb->profit_last_year;
+			break;
+		case SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE: { /* Sort by Total capacity per cargotype */
+			// FIXME - someone write a normal cargo sorter that also works by cargo_cap,
+			// FIXME - since I seem to be unable to do so :S
+			Vehicle *ua = va;
+			Vehicle *ub = vb;
+			int i;
+			byte _cargo_counta[NUM_CARGO];
+			byte _cargo_countb[NUM_CARGO];
+			do {
+				_cargo_counta[ua->cargo_type]++;
+			} while ( (ua = ua->next) != NULL);
+			do {
+				_cargo_countb[ub->cargo_type]++;
+			} while ( (ub = ub->next) != NULL);
+
+			for (i = 0; i < NUM_CARGO; i++) {
+				r = _cargo_counta[i] - _cargo_countb[i];
+				if (r != 0)
+					break;
+			}
+		} break;
+		case SORT_BY_RELIABILITY: /* Sort by Reliability */
+			r = va->reliability - vb->reliability;
+			break;
+		case SORT_BY_MAX_SPEED: /* Sort by Max speed */
+			r = va->max_speed - vb->max_speed;
+			break;
+		default: NOT_REACHED();
+	}
+	
+	if (_internal_sort_order & 1) r = -r;
+	return r;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vehicle_gui.h	Mon Sep 06 18:15:13 2004 +0000
@@ -0,0 +1,79 @@
+#ifndef VEHICLE_GUI_H
+#define VEHICLE_GUI_H
+
+void DrawVehicleProfitButton(Vehicle *v, int x, int y);
+void InitializeVehiclesGuiList();
+
+/* sorter stuff */
+int CDECL GeneralOwnerSorter  (const void *a, const void *b);
+int CDECL VehicleNameSorter   (const void *a, const void *b);
+int CDECL GeneralVehicleSorter(const void *a, const void *b);
+VARDEF uint32	_internal_name_sorter_id;	// internal StringID for default vehicle-names
+VARDEF uint32	_last_vehicle_idx;				// cached index to hopefully speed up name-sorting
+VARDEF bool		_internal_sort_order;			// descending/ascending
+VARDEF byte		_internal_sort_type;			// Miscalleneous sorting criteria
+
+typedef struct SortStruct { // store owner through sorting process
+	uint32	index;
+	byte		owner;
+} SortStruct;
+
+#define PERIODIC_RESORT_DAYS 10
+
+enum VehicleSortListingTypes {
+ SORT_BY_UNSORTED											= 0,
+ SORT_BY_NUMBER												= 1,
+ SORT_BY_NAME													= 2,
+ SORT_BY_AGE													= 3,
+ SORT_BY_PROFIT_THIS_YEAR							= 4,
+ SORT_BY_PROFIT_LAST_YEAR							= 5,
+ SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE	= 6,
+ SORT_BY_RELIABILITY									= 7,
+ SORT_BY_MAX_SPEED										= 8
+};
+
+static const uint16 _vehicle_sort_listing[] = {
+	STR_SORT_BY_UNSORTED,
+	STR_SORT_BY_NUMBER,
+	STR_SORT_BY_DROPDOWN_NAME,
+	STR_SORT_BY_AGE,
+	STR_SORT_BY_PROFIT_THIS_YEAR,
+	STR_SORT_BY_PROFIT_LAST_YEAR,
+	STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
+	STR_SORT_BY_RELIABILITY,
+	STR_SORT_BY_MAX_SPEED,
+	INVALID_STRING_ID
+};
+
+enum VehicleSortTypes {
+	VEHTRAIN		= 0,
+	VEHROAD			= 1,
+	VEHSHIP			= 2,
+	VEHAIRCRAFT	= 3
+};
+
+VARDEF bool _vehicle_sort_dirty[4];	// global sort, vehicles added/removed (4 types of vehicles)
+
+VARDEF bool _train_sort_dirty[MAX_PLAYERS];			// vehicles for a given player needs to be resorted (new criteria)
+VARDEF byte _train_sort_type[MAX_PLAYERS];			// different criteria for sorting
+VARDEF bool _train_sort_order[MAX_PLAYERS];			// sort descending/ascending
+
+VARDEF bool _aircraft_sort_dirty[MAX_PLAYERS];	// vehicles for a given player needs to be resorted (new criteria)
+VARDEF byte _aircraft_sort_type[MAX_PLAYERS];		// different criteria for sorting
+VARDEF bool _aircraft_sort_order[MAX_PLAYERS];	// sort descending/ascending
+
+VARDEF bool _ship_sort_dirty[MAX_PLAYERS];			// vehicles for a given player needs to be resorted (new criteria)
+VARDEF byte _ship_sort_type[MAX_PLAYERS];				// different criteria for sorting
+VARDEF bool _ship_sort_order[MAX_PLAYERS];			// sort descending/ascending
+
+VARDEF bool _road_sort_dirty[MAX_PLAYERS];			// vehicles for a given player needs to be resorted (new criteria)
+VARDEF byte _road_sort_type[MAX_PLAYERS];				// different criteria for sorting
+VARDEF bool _road_sort_order[MAX_PLAYERS];			// sort descending/ascending
+
+enum {
+  PLY_WND_PRC__OFFSET_TOP_WIDGET	= 26,
+	PLY_WND_PRC__SIZE_OF_ROW_SMALL	= 26,
+  PLY_WND_PRC__SIZE_OF_ROW_BIG		= 36,
+};
+
+#endif /* VEHICLE_GUI_H */
--- a/water_cmd.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/water_cmd.c	Mon Sep 06 18:15:13 2004 +0000
@@ -531,6 +531,7 @@
 
 			v->vehstatus |= VS_CRASHED;
 			v->u.road.crashed_ctr = 2000;	// max 2220, disappear pretty fast
+			_vehicle_sort_dirty[VEHROAD] = true;
 			InvalidateWindow(WC_ROADVEH_LIST, v->owner);
 		}
 		
@@ -547,6 +548,7 @@
 
 			v = u;
 			v->u.rail.crash_anim_pos = 4000; // max 4440, disappear pretty fast
+			_vehicle_sort_dirty[VEHTRAIN] = true;
 			InvalidateWindow(WC_TRAINS_LIST, v->owner);						
 		}
 
--- a/widget.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/widget.c	Mon Sep 06 18:15:13 2004 +0000
@@ -373,3 +373,186 @@
 	}
 
 }
+
+static uint _dropdown_item_count;
+static uint32 _dropdown_disabled;
+static const StringID *_dropdown_items;
+static int _dropdown_selindex;
+static byte _dropdown_button;
+static WindowClass _dropdown_windowclass;
+static WindowNumber _dropdown_windownum;
+static byte _dropdown_var1;
+static byte _dropdown_var2;
+
+static Widget _dropdown_menu_widgets[] = {
+{     WWT_IMGBTN,     0,     0, 0,     0, 0, 0x0},
+{      WWT_LAST},
+};
+
+static int GetDropdownItem(Window *w)
+{
+	uint item;
+	int y;
+
+	if (GetWidgetFromPos(w, _cursor.pos.x - w->left, _cursor.pos.y - w->top) < 0)
+		return -1;
+	
+	y = _cursor.pos.y - w->top - 2;
+
+	if (y < 0)
+		return - 1;
+
+	item = y / 10;
+	if (item >= _dropdown_item_count || HASBIT(_dropdown_disabled,item) || _dropdown_items[item] == 0)
+		return - 1;
+
+	return item;
+}
+
+void DropdownMenuWndProc(Window *w, WindowEvent *e)
+{
+	int item;
+
+	switch(e->event) {
+	case WE_PAINT: {
+		int x,y,i,sel;
+		uint32 dis;
+
+		DrawWindowWidgets(w);
+
+		x = 1;
+		y = 2;
+		sel = _dropdown_selindex;
+		dis = _dropdown_disabled;
+
+		for(i=0; _dropdown_items[i] != INVALID_STRING_ID; i++) {
+			if (_dropdown_items[i] != 0) {
+				if (sel == 0) {
+					GfxFillRect(x+1, y, x+w->width-4, y + 9, 0);
+				}
+				DrawString(x+2, y, _dropdown_items[i], sel==0 ? 12 : 16);
+
+				if (dis & 1) {
+					GfxFillRect(x, y, x+w->width-3, y + 9, 0x8000 + 
+						_color_list[_dropdown_menu_widgets[0].color].window_color_bga);
+				}
+			} else {
+				int color_1 = _color_list[_dropdown_menu_widgets[0].color].window_color_1a;
+				int color_2 = _color_list[_dropdown_menu_widgets[0].color].window_color_2;
+				GfxFillRect(x+1, y+3, x+w->width-5, y+3, color_1);
+				GfxFillRect(x+1, y+4, x+w->width-5, y+4, color_2);
+			}
+			y += 10;
+			sel--;
+			dis>>=1;
+		}
+	} break;
+
+	case WE_CLICK: {
+		item = GetDropdownItem(w);
+		if (item >= 0) {
+			_dropdown_var1 = 4;
+			_dropdown_selindex = item;
+			SetWindowDirty(w);
+		}
+	} break;
+
+	case WE_MOUSELOOP: {
+		Window *w2 = FindWindowById(_dropdown_windowclass, _dropdown_windownum);
+		if (w2 == NULL) {
+			DeleteWindow(w);
+			return;
+		}
+
+		if (_dropdown_var1 != 0 && --_dropdown_var1 == 0) {
+			WindowEvent e;
+			e.event = WE_DROPDOWN_SELECT;
+			e.dropdown.button = _dropdown_button;
+			e.dropdown.index = _dropdown_selindex;
+			w2->wndproc(w2, &e);
+			DeleteWindow(w);
+			return;
+		}
+
+		if (_dropdown_var2 != 0) {
+			item = GetDropdownItem(w);
+
+			if (!_left_button_clicked) {
+				_dropdown_var2 = 0;
+				if (item < 0)
+					return;
+				_dropdown_var1 = 2;
+			} else {
+				if (item < 0)
+					return;
+			}
+
+			_dropdown_selindex = item;
+			SetWindowDirty(w);
+		}
+	} break;
+		
+	case WE_DESTROY: {
+		Window *w2 = FindWindowById(_dropdown_windowclass, _dropdown_windownum);
+		if (w2 != NULL) {
+			CLRBIT(w2->click_state, _dropdown_button);
+			InvalidateWidget(w2, _dropdown_button);
+		}
+	} break;
+	}
+}
+
+void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask)
+{
+	WindowNumber num;
+	WindowClass cls;
+	int i,t1,t2;
+	const Widget *wi;
+	Window *w2;
+	uint32 old_click_state = w->click_state;
+	
+	_dropdown_disabled = disabled_mask;
+
+	cls = w->window_class;
+	num = w->window_number;
+	DeleteWindowById(WC_DROPDOWN_MENU, 0);
+	w = FindWindowById(cls, num);
+
+	if (HASBIT(old_click_state, button))
+		return;
+
+	SETBIT(w->click_state, button);
+
+	InvalidateWidget(w, button);
+	
+	for(i=0;strings[i] != INVALID_STRING_ID;i++);
+	if (i == 0)
+		return;
+
+	_dropdown_items = strings;
+	_dropdown_item_count = i;
+	_dropdown_selindex = selected;
+	
+	_dropdown_windowclass = w->window_class;
+	_dropdown_windownum = w->window_number;
+	_dropdown_button = button;
+	
+	_dropdown_var1 = 0;
+	_dropdown_var2 = 1;
+
+	wi = &w->widget[button];
+
+	_dropdown_menu_widgets[0].color = wi->color;
+
+	w2 = AllocateWindow(
+		w->left + wi[-1].left + 1,
+		w->top + wi->bottom + 2,
+		(_dropdown_menu_widgets[0].right=t1=wi->right - wi[-1].left, t1 + 1), 
+		(_dropdown_menu_widgets[0].bottom=t2=i*10+3, t2+1), 
+		DropdownMenuWndProc,
+		0x3F,
+		_dropdown_menu_widgets);
+
+
+	w2->flags4 &= ~WF_WHITE_BORDER_MASK;
+}
--- a/win32.c	Mon Sep 06 16:57:40 2004 +0000
+++ b/win32.c	Mon Sep 06 18:15:13 2004 +0000
@@ -1470,7 +1470,7 @@
 	int r;
 
 	if (_savegame_sort_order < 2) // sort by date
-    r = da->mtime < db->mtime ? 1 : -1;
+    r = da->mtime < db->mtime ? -1 : 1;
 	else	
 		r = stricmp(da->title[0] ? da->title : da->name, db->title[0] ? db->title : db->name);
 
--- a/window.h	Mon Sep 06 16:57:40 2004 +0000
+++ b/window.h	Mon Sep 06 18:15:13 2004 +0000
@@ -422,6 +422,7 @@
 /* widget.c */
 int GetWidgetFromPos(Window *w, int x, int y);
 void DrawWindowWidgets(Window *w);
+void ShowDropDownMenu(Window *w, const StringID *strings, int selected, int button, uint32 disabled_mask);
 
 void HandleButtonClick(Window *w, byte widget);