summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--aircraft_cmd.c2
-rw-r--r--aircraft_gui.c273
-rw-r--r--gui.h2
-rw-r--r--industry_gui.c43
-rw-r--r--lang/english.txt38
-rw-r--r--misc.c1
-rw-r--r--roadveh_cmd.c3
-rw-r--r--roadveh_gui.c269
-rw-r--r--settings_gui.c184
-rw-r--r--ship_cmd.c2
-rw-r--r--ship_gui.c278
-rw-r--r--station_gui.c58
-rw-r--r--town_gui.c8
-rw-r--r--train_cmd.c7
-rw-r--r--train_gui.c285
-rw-r--r--ttd.dsp8
-rw-r--r--ttd.h10
-rw-r--r--ttd.vcproj6
-rw-r--r--unix.c2
-rw-r--r--vehicle.c4
-rw-r--r--vehicle.h3
-rw-r--r--vehicle_gui.c147
-rw-r--r--vehicle_gui.h79
-rw-r--r--water_cmd.c2
-rw-r--r--widget.c183
-rw-r--r--win32.c2
-rw-r--r--window.h1
28 files changed, 1286 insertions, 616 deletions
diff --git a/Makefile b/Makefile
index 084ff4ee2..6f9aa191f 100644
--- a/Makefile
+++ b/Makefile
@@ -446,7 +446,7 @@ ttd_SOURCES = \
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
diff --git a/aircraft_cmd.c b/aircraft_cmd.c
index c842f0bee..a9530636b 100644
--- a/aircraft_cmd.c
+++ b/aircraft_cmd.c
@@ -336,6 +336,7 @@ int32 CmdBuildAircraft(int x, int y, uint32 flags, uint32 p1, uint32 p2)
}
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 @@ static bool CheckStoppedInHangar(Vehicle *v)
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);
diff --git a/aircraft_gui.c b/aircraft_gui.c
index 450c612cd..0a723f895 100644
--- a/aircraft_gui.c
+++ b/aircraft_gui.c
@@ -864,102 +864,175 @@ static void DrawSmallSchedule(Vehicle *v, int x, int y) {
}
}
-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;
+// used to get a sorted list of the vehicles
+static SortStruct _aircraft_sort[NUM_NORMAL_VEHICLES];
+static uint16 _num_aircraft_sort[MAX_PLAYERS];
- FOR_ALL_VEHICLES(v) {
- if (v->type == VEH_Aircraft && v->subtype <= 2 && v->owner == owner)
- num++;
+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);
- SetVScrollCount(w, num);
+ DEBUG(misc, 1) ("Resorting Aircraft list player %d...", owner+1);
+}
+
+static void PlayerAircraftWndProc(Window *w, WindowEvent *e)
+{
+ switch(e->event) {
+ case WE_PAINT: {
+ uint32 i;
+ const byte window_number = (byte)w->window_number;
+
+ if (_aircraft_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria
+ w->disabled_state |= (1 << 3);
+
+ 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);
-
- 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);
+ 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
+
+ while (i < _num_aircraft_sort[window_number]) {
+ StringID str;
+ v = DEREF_VEHICLE(_aircraft_sort[i].index);
+
+ DrawAircraftImage(v, x + 19, y + 6, INVALID_VEHICLE);
+ DrawVehicleProfitButton(v, x, y+13);
+
+ 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);
- 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);
- }
+ 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);
+ DrawSmallSchedule(v, x+136, y);
- y += 36;
- }
+ 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;
+ } break;
- case WE_CLICK:
+ case WE_CLICK: {
switch(e->click.widget) {
- case 2: { /* click on aircraft */
- int sel;
- Vehicle *v;
- byte owner;
+ 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
- sel = (e->click.pt.y - 14) / 36;
+ id_v += w->vscroll.pos;
- 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;
- }
+ {
+ 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 @@ static void PlayerAircraftWndProc(Window *w, WindowEvent *e)
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 @@ void ShowPlayerAircraft(int player)
{
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 @@ void ShowPlayerAircraft(int player)
if (w) {
w->caption_color = w->window_number;
- w->vscroll.cap = 4;
+ w->vscroll.cap = 4; // maximum number of vehicles shown
}
}
diff --git a/gui.h b/gui.h
index 394d20c4b..b8f0e8fc5 100644
--- a/gui.h
+++ b/gui.h
@@ -108,8 +108,6 @@ void ShowBuildIndustryWindow();
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;
diff --git a/industry_gui.c b/industry_gui.c
index de697f918..b67f2360f 100644
--- a/industry_gui.c
+++ b/industry_gui.c
@@ -388,45 +388,44 @@ static byte _last_industry_idx;
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 @@ static int CDECL IndustrySorter(const void *a, const void *b)
r = strcmp(buf1, _bufcache);
}
- if (_industry_sort_order & 1)
- r = -r;
-
+ if (_industry_sort_order & 1) r = -r;
return r;
}
@@ -464,7 +461,7 @@ static void MakeSortedIndustryList()
_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...");
}
diff --git a/lang/english.txt b/lang/english.txt
index 85369fc7a..9df37dee2 100644
--- a/lang/english.txt
+++ b/lang/english.txt
@@ -352,9 +352,24 @@ STR_015E_QUIT_GAME :Abandon game
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_HUNGARIAN :Hungarian
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_NO_JETCRASH :{LTBLUE}Jetplanes will not crash (frequently) on smal
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_BUBBLE_GENERATOR_TIP :{BLACK}Construct Bubble Generator
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_8817_COST_WEIGHT_T_SPEED_POWER :{BLACK}Cost: {GOLD}{CURRENCY}{BLACK} Weight
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 @@ STR_TRAIN_STOPPING_VEL :{RED}Stopping, {VELOCITY}
##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_9801_DOCK_CONSTRUCTION :{WHITE}Dock construction
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_A005_NEW_AIRCRAFT :{WHITE}New Aircraft
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)
diff --git a/misc.c b/misc.c
index 5f86d8445..12cb98dc7 100644
--- a/misc.c
+++ b/misc.c
@@ -121,6 +121,7 @@ void InitializeGame()
InitializeIndustries();
InitializeNameMgr();
+ InitializeVehiclesGuiList();
InitializeTrains();
InitializePlayers();
diff --git a/roadveh_cmd.c b/roadveh_cmd.c
index cc8250ffc..9267b462b 100644
--- a/roadveh_cmd.c
+++ b/roadveh_cmd.c
@@ -271,6 +271,7 @@ int32 CmdBuildRoadVeh(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdSellRoadVeh(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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 @@ static void RoadVehDelete(Vehicle *v)
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);
diff --git a/roadveh_gui.c b/roadveh_gui.c
index 8c77b081c..a48b727ed 100644
--- a/roadveh_gui.c
+++ b/roadveh_gui.c
@@ -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 @@ void ShowRoadDepotWindow(uint tile)
}
}
+// 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;
-
- FOR_ALL_VEHICLES(v) {
- if (v->type == VEH_Road && v->owner == owner)
- num++;
- }
- SetVScrollCount(w, num);
+ case WE_PAINT: {
+ uint32 i;
+ const byte window_number = (byte)w->window_number;
+
+ 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);
-
- 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;
- }
+ 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
+
+ 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;
+ } break;
- case WE_CLICK:
+ case WE_CLICK: {
switch(e->click.widget) {
- case 2: {
- int idx = (e->click.pt.y - 14) / 26;
- Vehicle *v;
- byte owner;
+ 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
- if ((uint)idx >= 7)
- break;
+ id_v += w->vscroll.pos;
+
+ {
+ byte owner = (byte)w->window_number;
+ uint16 adder = (owner == 0) ? 0 : _num_road_sort[owner - 1]; // first element in list
+ Vehicle *v;
- idx += w->vscroll.pos;
+ if (id_v + adder >= _num_road_sort[owner]) { return;} // click out of vehicle bound
- owner = (byte)w->window_number;
+ v = DEREF_VEHICLE(_road_sort[adder+id_v].index); // add the offset id_x to that
- FOR_ALL_VEHICLES(v) {
- if (v->type == VEH_Road && v->owner == owner &&
- --idx < 0) {
- ShowRoadVehViewWindow(v);
- break;
- }
+ 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 @@ static void PlayerRoadVehWndProc(Window *w, WindowEvent *e)
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
};
diff --git a/settings_gui.c b/settings_gui.c
index 58dea0b8e..6a6ddcd5a 100644
--- a/settings_gui.c
+++ b/settings_gui.c
@@ -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();
diff --git a/ship_cmd.c b/ship_cmd.c
index de853968e..96ae9e25f 100644
--- a/ship_cmd.c
+++ b/ship_cmd.c
@@ -852,6 +852,7 @@ int32 CmdBuildShip(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdSellShip(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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);
diff --git a/ship_gui.c b/ship_gui.c
index 293132275..12f927b2b 100644
--- a/ship_gui.c
+++ b/ship_gui.c
@@ -9,7 +9,6 @@
#include "station.h"
#include "command.h"
#include "player.h"
-//#include "town.h"
#include "engine.h"
@@ -868,100 +867,175 @@ static void DrawSmallShipSchedule(Vehicle *v, int x, int y) {
}
}
+// 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;
-
- FOR_ALL_VEHICLES(v) {
- if (v->type == VEH_Ship && v->owner == owner)
- num++;
- }
- SetVScrollCount(w, num);
+ case WE_PAINT: {
+ uint32 i;
+ const byte window_number = (byte)w->window_number;
+
+ 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
+
+ while (i < _num_ship_sort[window_number]) {
+ StringID str;
+ v = DEREF_VEHICLE(_ship_sort[i].index);
+
+ 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);
- 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);
- }
+ 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);
- DrawSmallShipSchedule(v, x+138, y);
- y += 36;
- }
+ 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:
+ } 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;
+ 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
- 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;
- }
+ 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 @@ static void PlayerShipsWndProc(Window *w, WindowEvent *e)
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
};
diff --git a/station_gui.c b/station_gui.c
index 17cb74240..be2cb28ca 100644
--- a/station_gui.c
+++ b/station_gui.c
@@ -44,47 +44,37 @@ static void StationsWndShowStationRating(int x, int y, int type, uint acceptance
}
}
-// 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;
-
- // 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);
+ SortStruct *cmp1, *cmp2;
+ cmp1 = (SortStruct*)a;
+ cmp2 = (SortStruct*)b;
+
+ 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);
-
- 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
+ GetString(_bufcache, st->string_id);
}
- 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 @@ static void MakeSortedStationList(Window *w)
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 @@ static void PlayerStationsWndProc(Window *w, WindowEvent *e)
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 @@ static void PlayerStationsWndProc(Window *w, WindowEvent *e)
}
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;
diff --git a/town_gui.c b/town_gui.c
index 3a8bf8726..8d6cfbd5d 100644
--- a/town_gui.c
+++ b/town_gui.c
@@ -351,7 +351,7 @@ static uint _num_town_sort;
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 @@ static int CDECL TownSorterByName(const void *a, const void *b)
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 @@ static void MakeSortedTownList()
_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...");
}
diff --git a/train_cmd.c b/train_cmd.c
index cb6306d91..342883e1f 100644
--- a/train_cmd.c
+++ b/train_cmd.c
@@ -496,6 +496,7 @@ int32 CmdBuildRailVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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 @@ static void DeleteLastWagon(Vehicle *v)
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);
diff --git a/train_gui.c b/train_gui.c
index d11832ff8..551b0e8e7 100644
--- a/train_gui.c
+++ b/train_gui.c
@@ -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 @@ static void CcBuildWagon(bool success, uint tile, uint32 p1, uint32 p2)
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 @@ static const Widget _train_depot_widgets[] = {
{ 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 @@ static void ShowRailVehicleRefitWindow(Vehicle *v)
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 @@ void ShowTrainDetailsWindow(Vehicle *v)
WP(w,traindetails_d).tab = 0;
}
-// draw the vehicle profit button in the vehicle list window.
-void DrawVehicleProfitButton(Vehicle *v, int x, int y)
+// used to get a sorted list of the vehicles
+static SortStruct _train_sort[NUM_NORMAL_VEHICLES];
+static uint16 _num_train_sort[MAX_PLAYERS];
+
+static void MakeSortedTrainList(byte owner)
{
- 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);
-}
+ 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);
-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,
-};
+ _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 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;
-
- FOR_ALL_VEHICLES(v) {
- if (v->type == VEH_Train && v->subtype == 0 && v->owner == owner)
- num++;
- }
-
- SetVScrollCount(w, num);
+ case WE_PAINT: {
+ uint32 i;
+ const byte window_number = (byte)w->window_number;
+
+ if (_train_sort_type[window_number] == SORT_BY_UNSORTED) // disable 'Sort By' tooltip on Unsorted sorting criteria
+ w->disabled_state |= (1 << 3);
+
+ 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);
+
+ DrawTrainImage(v, x + 21, y + 6, 10, 0, INVALID_VEHICLE);
+ DrawVehicleProfitButton(v, x, y+13);
- y += 26;
- }
+ 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;
+ 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
- if ((uint)idx >= 7)
- break;
+ id_v += w->vscroll.pos;
- idx += w->vscroll.pos;
+ {
+ byte owner = (byte)w->window_number;
+ uint16 adder = (owner == 0) ? 0 : _num_train_sort[owner - 1]; // first element in list
+ Vehicle *v;
- owner = (byte)w->window_number;
+ if (id_v + adder >= _num_train_sort[owner]) { return;} // click out of vehicle bound
- FOR_ALL_VEHICLES(v) {
- if (v->type == VEH_Train && v->subtype == 0 && v->owner == owner &&
- --idx < 0) {
- ShowTrainViewWindow(v);
- break;
- }
+ 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 @@ static void PlayerTrainsWndProc(Window *w, WindowEvent *e)
}
} 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 @@ void ShowPlayerTrains(int player)
{
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
}
}
diff --git a/ttd.dsp b/ttd.dsp
index 7fc94ede5..7a5f08f86 100644
--- a/ttd.dsp
+++ b/ttd.dsp
@@ -358,6 +358,10 @@ SOURCE=.\vehicle.c
# 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 @@ SOURCE=.\vehicle.h
# 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
diff --git a/ttd.h b/ttd.h
index 1f0dd3752..ab4ffa391 100644
--- a/ttd.h
+++ b/ttd.h
@@ -32,6 +32,16 @@ typedef struct YearMonthDay {
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.
diff --git a/ttd.vcproj b/ttd.vcproj
index 59f19d41c..b3b0a0a09 100644
--- a/ttd.vcproj
+++ b/ttd.vcproj
@@ -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"
diff --git a/unix.c b/unix.c
index 081c2d82a..90663f72d 100644
--- a/unix.c
+++ b/unix.c
@@ -43,7 +43,7 @@ int compare_FiosItems (const void *a, const void *b) {
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);
diff --git a/vehicle.c b/vehicle.c
index 7e37989c4..5d9c55b3c 100644
--- a/vehicle.c
+++ b/vehicle.c
@@ -1399,6 +1399,10 @@ int32 CmdNameVehicle(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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);
diff --git a/vehicle.h b/vehicle.h
index f45e1e035..8061992ce 100644
--- a/vehicle.h
+++ b/vehicle.h
@@ -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 @@ byte GetDirectionTowards(Vehicle *v, int x, int y);
#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 */
diff --git a/vehicle_gui.c b/vehicle_gui.c
new file mode 100644
index 000000000..751e3d230
--- /dev/null
+++ b/vehicle_gui.c
@@ -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;
+}
diff --git a/vehicle_gui.h b/vehicle_gui.h
new file mode 100644
index 000000000..b6da5085f
--- /dev/null
+++ b/vehicle_gui.h
@@ -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 */
diff --git a/water_cmd.c b/water_cmd.c
index fa22d5a1e..789ceb446 100644
--- a/water_cmd.c
+++ b/water_cmd.c
@@ -531,6 +531,7 @@ static void FloodVehicle(Vehicle *v)
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 @@ static void FloodVehicle(Vehicle *v)
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);
}
diff --git a/widget.c b/widget.c
index 5d4ff9e4d..d56304287 100644
--- a/widget.c
+++ b/widget.c
@@ -373,3 +373,186 @@ draw_default:;
}
}
+
+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;
+}
diff --git a/win32.c b/win32.c
index b79c297fa..e16a84676 100644
--- a/win32.c
+++ b/win32.c
@@ -1470,7 +1470,7 @@ int CDECL compare_FiosItems (const void *a, const void *b) {
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);
diff --git a/window.h b/window.h
index abe414715..c4110f92f 100644
--- a/window.h
+++ b/window.h
@@ -422,6 +422,7 @@ int32 PositionMainToolbar(Window *w);
/* 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);