src/station_gui.cpp
changeset 5584 1111b4d36e35
parent 5475 2e6990a8c7c4
child 5587 167d9a91ef02
equal deleted inserted replaced
5583:136d8764c7e6 5584:1111b4d36e35
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "debug.h"
       
     6 #include "functions.h"
       
     7 #include "strings.h"
       
     8 #include "table/strings.h"
       
     9 #include "window.h"
       
    10 #include "gui.h"
       
    11 #include "station.h"
       
    12 #include "gfx.h"
       
    13 #include "player.h"
       
    14 #include "economy.h"
       
    15 #include "town.h"
       
    16 #include "command.h"
       
    17 #include "variables.h"
       
    18 #include "vehicle_gui.h"
       
    19 #include "date.h"
       
    20 #include "vehicle.h"
       
    21 
       
    22 enum StationListWidgets {
       
    23 	STATIONLIST_WIDGET_CLOSEBOX = 0,
       
    24 	STATIONLIST_WIDGET_LIST = 3,
       
    25 	STATIONLIST_WIDGET_TRAIN =6,
       
    26 	STATIONLIST_WIDGET_TRUCK,
       
    27 	STATIONLIST_WIDGET_BUS,
       
    28 	STATIONLIST_WIDGET_AIRPLANE,
       
    29 	STATIONLIST_WIDGET_SHIP,
       
    30 	STATIONLIST_WIDGET_CARGOSTART = 12,
       
    31 	STATIONLIST_WIDGET_NOCARGOWAITING = 24,
       
    32 	STATIONLIST_WIDGET_FACILALL = 26,
       
    33 	STATIONLIST_WIDGET_CARGOALL,
       
    34 	STATIONLIST_WIDGET_SORTBY,
       
    35 	STATIONLIST_WIDGET_SORTCRITERIA,
       
    36 	STATIONLIST_WIDGET_SORTDROPBTN,
       
    37 	CARGO_ALL_SELECTED = 0x1FFF,
       
    38 };
       
    39 
       
    40 typedef int CDECL StationSortListingTypeFunction(const void*, const void*);
       
    41 
       
    42 static StationSortListingTypeFunction StationNameSorter;
       
    43 static StationSortListingTypeFunction StationTypeSorter;
       
    44 static StationSortListingTypeFunction StationWaitingSorter;
       
    45 static StationSortListingTypeFunction StationRatingMaxSorter;
       
    46 
       
    47 /** Draw small boxes of cargo amount and ratings data at the given
       
    48  * coordinates. If amount exceeds 576 units, it is shown 'full', same
       
    49  * goes for the rating: at above 90% orso (224) it is also 'full'
       
    50  * Each cargo-bar is 16 pixels wide and 6 pixels high
       
    51  * Each rating 14 pixels wide and 1 pixel high and is 1 pixel below the cargo-bar
       
    52  * @param x,y X/Y coordinate to draw the box at
       
    53  * @param type Cargo type
       
    54  * @param amount Cargo amount
       
    55  * @param rating ratings data for that particular cargo */
       
    56 static void StationsWndShowStationRating(int x, int y, CargoID type, uint amount, byte rating)
       
    57 {
       
    58 	int colour = _cargo_colours[type];
       
    59 	uint w = (minu(amount, 576) + 5) / 36;
       
    60 
       
    61 	/* Draw total cargo (limited) on station (fits into 16 pixels) */
       
    62 	if (w != 0) GfxFillRect(x, y, x + w - 1, y + 6, colour);
       
    63 
       
    64 	/* Draw a one pixel-wide bar of additional cargo meter, useful
       
    65 	 * for stations with only a small amount (<=30) */
       
    66 	if (w == 0) {
       
    67 		uint rest = amount / 5;
       
    68 		if (rest != 0) {
       
    69 			w += x;
       
    70 			GfxFillRect(w, y + 6 - rest, w, y + 6, colour);
       
    71 		}
       
    72 	}
       
    73 
       
    74 	DrawString(x + 1, y, _cargoc.names_short[type], 0x10);
       
    75 
       
    76 	/* Draw green/red ratings bar (fits into 14 pixels) */
       
    77 	y += 8;
       
    78 	GfxFillRect(x + 1, y, x + 14, y, 0xB8);
       
    79 	rating = minu(rating,  224) / 16;
       
    80 	if (rating != 0) GfxFillRect(x + 1, y, x + rating, y, 0xD0);
       
    81 }
       
    82 
       
    83 const StringID _station_sort_listing[] = {
       
    84 	STR_SORT_BY_DROPDOWN_NAME,
       
    85 	STR_SORT_BY_FACILITY,
       
    86 	STR_SORT_BY_WAITING,
       
    87 	STR_SORT_BY_RATING_MAX,
       
    88 	INVALID_STRING_ID
       
    89 };
       
    90 
       
    91 static char _bufcache[64];
       
    92 static const Station* _last_station;
       
    93 static int _internal_sort_order;
       
    94 
       
    95 static int CDECL StationNameSorter(const void *a, const void *b)
       
    96 {
       
    97 	const Station* st1 = *(const Station**)a;
       
    98 	const Station* st2 = *(const Station**)b;
       
    99 	char buf1[64];
       
   100 	int r;
       
   101 
       
   102 	SetDParam(0, st1->index);
       
   103 	GetString(buf1, STR_STATION, lastof(buf1));
       
   104 
       
   105 	if (st2 != _last_station) {
       
   106 		_last_station = st2;
       
   107 		SetDParam(0, st2->index);
       
   108 		GetString(_bufcache, STR_STATION, lastof(_bufcache));
       
   109 	}
       
   110 
       
   111 	r =  strcmp(buf1, _bufcache); // sort by name
       
   112 	return (_internal_sort_order & 1) ? -r : r;
       
   113 }
       
   114 
       
   115 static int CDECL StationTypeSorter(const void *a, const void *b)
       
   116 {
       
   117 	const Station* st1 = *(const Station**)a;
       
   118 	const Station* st2 = *(const Station**)b;
       
   119 	return (_internal_sort_order & 1) ? st2->facilities - st1->facilities : st1->facilities - st2->facilities;
       
   120 }
       
   121 
       
   122 static int CDECL StationWaitingSorter(const void *a, const void *b)
       
   123 {
       
   124 	const Station* st1 = *(const Station**)a;
       
   125 	const Station* st2 = *(const Station**)b;
       
   126 	int sum1 = 0, sum2 = 0;
       
   127 	int j;
       
   128 
       
   129 	for (j = 0; j < NUM_CARGO; j++) {
       
   130 		if (st1->goods[j].waiting_acceptance & 0xfff) sum1 += GetTransportedGoodsIncome(st1->goods[j].waiting_acceptance & 0xfff, 20, 50, j);
       
   131 		if (st2->goods[j].waiting_acceptance & 0xfff) sum2 += GetTransportedGoodsIncome(st2->goods[j].waiting_acceptance & 0xfff, 20, 50, j);
       
   132 	}
       
   133 
       
   134 	return (_internal_sort_order & 1) ? sum2 - sum1 : sum1 - sum2;
       
   135 }
       
   136 
       
   137 static int CDECL StationRatingMaxSorter(const void *a, const void *b)
       
   138 {
       
   139 	const Station* st1 = *(const Station**)a;
       
   140 	const Station* st2 = *(const Station**)b;
       
   141 	byte maxr1 = 0;
       
   142 	byte maxr2 = 0;
       
   143 	int j;
       
   144 
       
   145 	for (j = 0; j < NUM_CARGO; j++) {
       
   146 		if (st1->goods[j].waiting_acceptance & 0xfff) maxr1 = max(maxr1, st1->goods[j].rating);
       
   147 		if (st2->goods[j].waiting_acceptance & 0xfff) maxr2 = max(maxr2, st2->goods[j].rating);
       
   148 	}
       
   149 
       
   150 	return (_internal_sort_order & 1) ? maxr2 - maxr1 : maxr1 - maxr2;
       
   151 }
       
   152 
       
   153 typedef enum StationListFlags {
       
   154 	SL_ORDER   = 0x01,
       
   155 	SL_RESORT  = 0x02,
       
   156 	SL_REBUILD = 0x04,
       
   157 } StationListFlags;
       
   158 
       
   159 typedef struct plstations_d {
       
   160 	const Station** sort_list;
       
   161 	uint16 list_length;
       
   162 	byte sort_type;
       
   163 	StationListFlags flags;
       
   164 	uint16 resort_timer;  //was byte refresh_counter;
       
   165 } plstations_d;
       
   166 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(plstations_d));
       
   167 
       
   168 void RebuildStationLists(void)
       
   169 {
       
   170 	Window* const *wz;
       
   171 
       
   172 	FOR_ALL_WINDOWS(wz) {
       
   173 		Window *w = *wz;
       
   174 		if (w->window_class == WC_STATION_LIST) {
       
   175 			WP(w, plstations_d).flags |= SL_REBUILD;
       
   176 			SetWindowDirty(w);
       
   177 		}
       
   178 	}
       
   179 }
       
   180 
       
   181 void ResortStationLists(void)
       
   182 {
       
   183 	Window* const *wz;
       
   184 
       
   185 	FOR_ALL_WINDOWS(wz) {
       
   186 		Window *w = *wz;
       
   187 		if (w->window_class == WC_STATION_LIST) {
       
   188 			WP(w, plstations_d).flags |= SL_RESORT;
       
   189 			SetWindowDirty(w);
       
   190 		}
       
   191 	}
       
   192 }
       
   193 
       
   194 static void BuildStationsList(plstations_d* sl, PlayerID owner, byte facilities, uint16 cargo_filter)
       
   195 {
       
   196 	uint n = 0;
       
   197 	uint i, j;
       
   198 	const Station** station_sort;
       
   199 	const Station *st;
       
   200 
       
   201 	if (!(sl->flags & SL_REBUILD)) return;
       
   202 
       
   203 	/* Create array for sorting */
       
   204 	station_sort = malloc((GetMaxStationIndex() + 1) * sizeof(station_sort[0]));
       
   205 	if (station_sort == NULL) error("Could not allocate memory for the station-sorting-list");
       
   206 
       
   207 	DEBUG(misc, 3, "Building station list for player %d", owner);
       
   208 
       
   209 	FOR_ALL_STATIONS(st) {
       
   210 		if (st->owner == owner) {
       
   211 			if (facilities & st->facilities) { //only stations with selected facilities
       
   212 				int num_waiting_cargo = 0;
       
   213 				for (j = 0; j < NUM_CARGO; j++) {
       
   214 					if (st->goods[j].waiting_acceptance & 0xFFF) {
       
   215 						num_waiting_cargo++; //count number of waiting cargo
       
   216 						if (HASBIT(cargo_filter, j)) {
       
   217 							station_sort[n++] = st;
       
   218 							break;
       
   219 						}
       
   220 					}
       
   221 				}
       
   222 				//stations without waiting cargo
       
   223 				if (num_waiting_cargo == 0 && HASBIT(cargo_filter, NUM_CARGO)) {
       
   224 					station_sort[n++] = st;
       
   225 				}
       
   226 			}
       
   227 		}
       
   228 	}
       
   229 
       
   230 	free((void*)sl->sort_list);
       
   231 	sl->sort_list = malloc(n * sizeof(sl->sort_list[0]));
       
   232 	if (n != 0 && sl->sort_list == NULL) error("Could not allocate memory for the station-sorting-list");
       
   233 	sl->list_length = n;
       
   234 
       
   235 	for (i = 0; i < n; ++i) sl->sort_list[i] = station_sort[i];
       
   236 
       
   237 	sl->flags &= ~SL_REBUILD;
       
   238 	sl->flags |= SL_RESORT;
       
   239 	free((void*)station_sort);
       
   240 }
       
   241 
       
   242 static void SortStationsList(plstations_d *sl)
       
   243 {
       
   244 	static StationSortListingTypeFunction* const _station_sorter[] = {
       
   245 		&StationNameSorter,
       
   246 		&StationTypeSorter,
       
   247 		&StationWaitingSorter,
       
   248 		&StationRatingMaxSorter
       
   249 	};
       
   250 
       
   251 	if (!(sl->flags & SL_RESORT)) return;
       
   252 
       
   253 	_internal_sort_order = sl->flags & SL_ORDER;
       
   254 	_last_station = NULL; // used for "cache" in namesorting
       
   255 	qsort((void*)sl->sort_list, sl->list_length, sizeof(sl->sort_list[0]), _station_sorter[sl->sort_type]);
       
   256 
       
   257 	sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
       
   258 	sl->flags &= ~SL_RESORT;
       
   259 }
       
   260 
       
   261 static void PlayerStationsWndProc(Window *w, WindowEvent *e)
       
   262 {
       
   263 	const PlayerID owner = w->window_number;
       
   264 	static byte facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
       
   265 	static uint16 cargo_filter = CARGO_ALL_SELECTED;
       
   266 	plstations_d *sl = &WP(w, plstations_d);
       
   267 
       
   268 	switch (e->event) {
       
   269 	case WE_CREATE: { /* set up resort timer */
       
   270 		int i;
       
   271 		for (i = 0; i < 5; i++) {
       
   272 			if (HASBIT(facilities, i)) LowerWindowWidget(w, i + STATIONLIST_WIDGET_TRAIN);
       
   273 		}
       
   274 		for (i = 0; i < NUM_CARGO; i++) {
       
   275 			if (HASBIT(cargo_filter, i)) LowerWindowWidget(w, i + STATIONLIST_WIDGET_CARGOSTART);
       
   276 		}
       
   277 		SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
       
   278 		SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_CARGOALL, cargo_filter == CARGO_ALL_SELECTED);
       
   279 		SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_NOCARGOWAITING, HASBIT(cargo_filter, STATIONLIST_WIDGET_NOCARGOWAITING - NUM_CARGO));
       
   280 
       
   281 		sl->sort_list = NULL;
       
   282 		sl->flags = SL_REBUILD;
       
   283 		sl->sort_type = 0;
       
   284 		sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
       
   285 		break;
       
   286 	}
       
   287 
       
   288 	case WE_PAINT: {
       
   289 		BuildStationsList(sl, owner, facilities, cargo_filter);
       
   290 		SortStationsList(sl);
       
   291 
       
   292 		SetVScrollCount(w, sl->list_length);
       
   293 
       
   294 		/* draw widgets, with player's name in the caption */
       
   295 		{
       
   296 			const Player* p = GetPlayer(owner);
       
   297 			SetDParam(0, p->name_1);
       
   298 			SetDParam(1, p->name_2);
       
   299 			SetDParam(2, w->vscroll.count);
       
   300 			DrawWindowWidgets(w);
       
   301 		}
       
   302 
       
   303 		{
       
   304 			int max;
       
   305 			int i, cg_ofst;
       
   306 			int x = 0, y = 0, xb = 2; // offset from top of widget
       
   307 
       
   308 			/* draw sorting criteria string */
       
   309 			DrawString(85, 26, _station_sort_listing[sl->sort_type], 0x10);
       
   310 			/* draw arrow pointing up/down for ascending/descending sorting */
       
   311 			DoDrawString(sl->flags & SL_ORDER ? DOWNARROW : UPARROW, 69, 26, 0x10);
       
   312 
       
   313 
       
   314 			x = 89;
       
   315 			y = 14;
       
   316 
       
   317 			for (i = 0; i < NUM_CARGO; i++) {
       
   318 				cg_ofst = IsWindowWidgetLowered(w, i + STATIONLIST_WIDGET_CARGOSTART) ? 2 : 1;
       
   319 
       
   320 				GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, _cargo_colours[i]);
       
   321 				DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, _cargoc.names_short[i], 0x10);
       
   322 				x += 14;
       
   323 			}
       
   324 
       
   325 			x += 6;
       
   326 			cg_ofst = IsWindowWidgetLowered(w, STATIONLIST_WIDGET_NOCARGOWAITING) ? 2 : 1;
       
   327 			DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, 16);
       
   328 			x += 14;
       
   329 			cg_ofst = IsWindowWidgetLowered(w, STATIONLIST_WIDGET_CARGOALL) ? 2 : 1;
       
   330 			DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, 16);
       
   331 
       
   332 			cg_ofst = IsWindowWidgetLowered(w, STATIONLIST_WIDGET_FACILALL) ? 2 : 1;
       
   333 			DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, 16);
       
   334 
       
   335 			if (w->vscroll.count == 0) { // player has no stations
       
   336 				DrawString(xb, 40, STR_304A_NONE, 0);
       
   337 				return;
       
   338 			}
       
   339 
       
   340 			max = min(w->vscroll.pos + w->vscroll.cap, sl->list_length);
       
   341 			y = 40; // start of the list-widget
       
   342 
       
   343 			for (i = w->vscroll.pos; i < max; ++i) { // do until max number of stations of owner
       
   344 				const Station *st = sl->sort_list[i];
       
   345 				CargoID j;
       
   346 				int x;
       
   347 
       
   348 				assert(st->xy != 0);
       
   349 				assert(st->owner == owner);
       
   350 
       
   351 				SetDParam(0, st->index);
       
   352 				SetDParam(1, st->facilities);
       
   353 				x = DrawString(xb, y, STR_3049_0, 0) + 5;
       
   354 
       
   355 				// show cargo waiting and station ratings
       
   356 				for (j = 0; j != NUM_CARGO; j++) {
       
   357 					uint amount = GB(st->goods[j].waiting_acceptance, 0, 12);
       
   358 
       
   359 					if (amount != 0) {
       
   360 						StationsWndShowStationRating(x, y, j, amount, st->goods[j].rating);
       
   361 						x += 20;
       
   362 					}
       
   363 				}
       
   364 				y += 10;
       
   365 			}
       
   366 		}
       
   367 	} break;
       
   368 
       
   369 	case WE_CLICK: {
       
   370 		switch (e->we.click.widget) {
       
   371 		case STATIONLIST_WIDGET_LIST: {
       
   372 			const Station* st;
       
   373 
       
   374 			uint32 id_v = (e->we.click.pt.y - 41) / 10;
       
   375 
       
   376 			if (id_v >= w->vscroll.cap) return; // click out of bounds
       
   377 
       
   378 			id_v += w->vscroll.pos;
       
   379 
       
   380 			if (id_v >= sl->list_length) return; // click out of list bound
       
   381 
       
   382 			st = sl->sort_list[id_v];
       
   383 			assert(st->owner == owner);
       
   384 			ScrollMainWindowToTile(st->xy);
       
   385 			break;
       
   386 		}
       
   387 
       
   388 		case STATIONLIST_WIDGET_TRAIN:
       
   389 		case STATIONLIST_WIDGET_TRUCK:
       
   390 		case STATIONLIST_WIDGET_BUS:
       
   391 		case STATIONLIST_WIDGET_AIRPLANE:
       
   392 		case STATIONLIST_WIDGET_SHIP:
       
   393 			if (_ctrl_pressed) {
       
   394 				TOGGLEBIT(facilities, e->we.click.widget - STATIONLIST_WIDGET_TRAIN);
       
   395 				ToggleWidgetLoweredState(w, e->we.click.widget);
       
   396 			} else {
       
   397 				int i;
       
   398 				for (i = 0; facilities != 0; i++, facilities >>= 1) {
       
   399 					if (HASBIT(facilities, 0)) RaiseWindowWidget(w, i + STATIONLIST_WIDGET_TRAIN);
       
   400 				}
       
   401 				SETBIT(facilities, e->we.click.widget - STATIONLIST_WIDGET_TRAIN);
       
   402 				LowerWindowWidget(w, e->we.click.widget);
       
   403 			}
       
   404 			SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
       
   405 			sl->flags |= SL_REBUILD;
       
   406 			SetWindowDirty(w);
       
   407 		break;
       
   408 
       
   409 		case STATIONLIST_WIDGET_FACILALL: {
       
   410 			int i;
       
   411 			for (i = 0; i < 5; i++) {
       
   412 				LowerWindowWidget(w, i + STATIONLIST_WIDGET_TRAIN);
       
   413 			}
       
   414 			LowerWindowWidget(w, STATIONLIST_WIDGET_FACILALL);
       
   415 
       
   416 			facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
       
   417 			sl->flags |= SL_REBUILD;
       
   418 			SetWindowDirty(w);
       
   419 			break;
       
   420 		}
       
   421 		case STATIONLIST_WIDGET_CARGOALL: {
       
   422 			int i;
       
   423 			for (i = 0; i < NUM_CARGO; i++) {
       
   424 				LowerWindowWidget(w, i + STATIONLIST_WIDGET_CARGOSTART);
       
   425 			}
       
   426 			LowerWindowWidget(w, STATIONLIST_WIDGET_NOCARGOWAITING);
       
   427 			LowerWindowWidget(w, STATIONLIST_WIDGET_CARGOALL);
       
   428 
       
   429 			cargo_filter = CARGO_ALL_SELECTED;
       
   430 			sl->flags |= SL_REBUILD;
       
   431 			SetWindowDirty(w);
       
   432 			break;
       
   433 		}
       
   434 		case STATIONLIST_WIDGET_SORTBY: /*flip sorting method asc/desc*/
       
   435 			TOGGLEBIT(sl->flags, 0); //DESC-flag
       
   436 			sl->flags |= SL_RESORT;
       
   437 			w->flags4 |= 5 << WF_TIMEOUT_SHL;
       
   438 			LowerWindowWidget(w, STATIONLIST_WIDGET_SORTBY);
       
   439 			SetWindowDirty(w);
       
   440 		break;
       
   441 
       
   442 		case STATIONLIST_WIDGET_SORTCRITERIA:
       
   443 		case STATIONLIST_WIDGET_SORTDROPBTN: /* select sorting criteria dropdown menu */
       
   444 			ShowDropDownMenu(w, _station_sort_listing, sl->sort_type, STATIONLIST_WIDGET_SORTDROPBTN, 0, 0);
       
   445 		break;
       
   446 
       
   447 		default:
       
   448 			if (e->we.click.widget >= STATIONLIST_WIDGET_CARGOSTART && e->we.click.widget <= STATIONLIST_WIDGET_NOCARGOWAITING) { //change cargo_filter
       
   449 				if (_ctrl_pressed) {
       
   450 					TOGGLEBIT(cargo_filter, e->we.click.widget - STATIONLIST_WIDGET_CARGOSTART);
       
   451 					ToggleWidgetLoweredState(w, e->we.click.widget);
       
   452 				} else {
       
   453 					int i;
       
   454 					for (i = 0; cargo_filter != 0; i++, cargo_filter >>= 1) {
       
   455 						if (HASBIT(cargo_filter, 0)) RaiseWindowWidget(w, i + STATIONLIST_WIDGET_CARGOSTART);
       
   456 					}
       
   457 					SETBIT(cargo_filter, e->we.click.widget - STATIONLIST_WIDGET_CARGOSTART);
       
   458 					LowerWindowWidget(w, e->we.click.widget);
       
   459 				}
       
   460 				sl->flags |= SL_REBUILD;
       
   461 				SetWindowWidgetLoweredState(w, STATIONLIST_WIDGET_CARGOALL, cargo_filter == CARGO_ALL_SELECTED);
       
   462 				SetWindowDirty(w);
       
   463 			}
       
   464 		}
       
   465 	} break;
       
   466 
       
   467 	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
       
   468 		if (sl->sort_type != e->we.dropdown.index) {
       
   469 			// value has changed -> resort
       
   470 			sl->sort_type = e->we.dropdown.index;
       
   471 			sl->flags |= SL_RESORT;
       
   472 		}
       
   473 		SetWindowDirty(w);
       
   474 		break;
       
   475 
       
   476 	case WE_TICK:
       
   477 		if (--sl->resort_timer == 0) {
       
   478 			DEBUG(misc, 3, "Periodic rebuild station list player %d", owner);
       
   479 			sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
       
   480 			sl->flags |= VL_REBUILD;
       
   481 			SetWindowDirty(w);
       
   482 		}
       
   483 		break;
       
   484 
       
   485 	case WE_TIMEOUT:
       
   486 		RaiseWindowWidget(w, STATIONLIST_WIDGET_SORTBY);
       
   487 		SetWindowDirty(w);
       
   488 		break;
       
   489 
       
   490 	case WE_RESIZE:
       
   491 		w->vscroll.cap += e->we.sizing.diff.y / 10;
       
   492 		break;
       
   493 	}
       
   494 }
       
   495 
       
   496 static const Widget _player_stations_widgets[] = {
       
   497 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},
       
   498 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   345,     0,    13, STR_3048_STATIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
       
   499 {  WWT_STICKYBOX,     RESIZE_LR,    14,   346,   357,     0,    13, 0x0,               STR_STICKY_BUTTON},
       
   500 {      WWT_PANEL,     RESIZE_RB,    14,     0,   345,    37,   161, 0x0,               STR_3057_STATION_NAMES_CLICK_ON},
       
   501 {  WWT_SCROLLBAR,    RESIZE_LRB,    14,   346,   357,    37,   149, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
   502 {  WWT_RESIZEBOX,   RESIZE_LRTB,    14,   346,   357,   150,   161, 0x0,               STR_RESIZE_BUTTON},
       
   503 //Index 6
       
   504 {    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,    13,    14,    24, STR_TRAIN,         STR_USE_CTRL_TO_SELECT_MORE},
       
   505 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    14,    27,    14,    24, STR_LORRY,         STR_USE_CTRL_TO_SELECT_MORE},
       
   506 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    28,    41,    14,    24, STR_BUS,           STR_USE_CTRL_TO_SELECT_MORE},
       
   507 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    42,    55,    14,    24, STR_PLANE,         STR_USE_CTRL_TO_SELECT_MORE},
       
   508 {    WWT_TEXTBTN,   RESIZE_NONE,    14,    56,    69,    14,    24, STR_SHIP,          STR_USE_CTRL_TO_SELECT_MORE},
       
   509 //Index 11
       
   510 {      WWT_PANEL,   RESIZE_NONE,    14,    83,    88,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   511 {      WWT_PANEL,   RESIZE_NONE,    14,    89,   102,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   512 {      WWT_PANEL,   RESIZE_NONE,    14,   103,   116,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   513 {      WWT_PANEL,   RESIZE_NONE,    14,   117,   130,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   514 {      WWT_PANEL,   RESIZE_NONE,    14,   131,   144,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   515 {      WWT_PANEL,   RESIZE_NONE,    14,   145,   158,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   516 {      WWT_PANEL,   RESIZE_NONE,    14,   159,   172,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   517 {      WWT_PANEL,   RESIZE_NONE,    14,   173,   186,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   518 {      WWT_PANEL,   RESIZE_NONE,    14,   187,   200,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   519 {      WWT_PANEL,   RESIZE_NONE,    14,   201,   214,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   520 {      WWT_PANEL,   RESIZE_NONE,    14,   215,   228,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   521 {      WWT_PANEL,   RESIZE_NONE,    14,   229,   242,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   522 {      WWT_PANEL,   RESIZE_NONE,    14,   243,   256,    14,    24, 0x0,               STR_USE_CTRL_TO_SELECT_MORE},
       
   523 {      WWT_PANEL,   RESIZE_NONE,    14,   257,   270,    14,    24, 0x0,               STR_NO_WAITING_CARGO},
       
   524 {      WWT_PANEL,  RESIZE_RIGHT,    14,   285,   357,    14,    24, 0x0,               STR_NULL},
       
   525 
       
   526 //26
       
   527 {      WWT_PANEL,   RESIZE_NONE,    14,    70,    83,    14,    24, 0x0,               STR_SELECT_ALL_FACILITIES},
       
   528 {      WWT_PANEL,   RESIZE_NONE,    14,   271,   284,    14,    24, 0x0,               STR_SELECT_ALL_TYPES},
       
   529 
       
   530 //28
       
   531 {    WWT_TEXTBTN,   RESIZE_NONE,    14,     0,    80,    25,    36, STR_SORT_BY,       STR_SORT_ORDER_TIP},
       
   532 {      WWT_PANEL,   RESIZE_NONE,    14,    81,   232,    25,    36, 0x0,               STR_SORT_CRITERIA_TIP},
       
   533 {    WWT_TEXTBTN,   RESIZE_NONE,    14,   233,   243,    25,    36, STR_0225,          STR_SORT_CRITERIA_TIP},
       
   534 {      WWT_PANEL,  RESIZE_RIGHT,    14,   244,   357,    25,    36, 0x0,               STR_NULL},
       
   535 {   WIDGETS_END},
       
   536 };
       
   537 
       
   538 static const WindowDesc _player_stations_desc = {
       
   539 	WDP_AUTO, WDP_AUTO, 358, 162,
       
   540 	WC_STATION_LIST,0,
       
   541 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
       
   542 	_player_stations_widgets,
       
   543 	PlayerStationsWndProc
       
   544 };
       
   545 
       
   546 
       
   547 void ShowPlayerStations(PlayerID player)
       
   548 {
       
   549 	Window *w;
       
   550 
       
   551 	if (!IsValidPlayer(player)) return;
       
   552 
       
   553 	w = AllocateWindowDescFront(&_player_stations_desc, player);
       
   554 	if (w != NULL) {
       
   555 		w->caption_color = (byte)w->window_number;
       
   556 		w->vscroll.cap = 12;
       
   557 		w->resize.step_height = 10;
       
   558 		w->resize.height = w->height - 10 * 7; // minimum if 5 in the list
       
   559 	}
       
   560 }
       
   561 
       
   562 static const Widget _station_view_expanded_widgets[] = {
       
   563 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},
       
   564 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   236,     0,    13, STR_300A_0,        STR_018C_WINDOW_TITLE_DRAG_THIS},
       
   565 {  WWT_STICKYBOX,   RESIZE_NONE,    14,   237,   248,     0,    13, 0x0,               STR_STICKY_BUTTON},
       
   566 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   236,    14,    65, 0x0,               STR_NULL},
       
   567 {  WWT_SCROLLBAR,   RESIZE_NONE,    14,   237,   248,    14,    65, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
   568 {      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,               STR_NULL},
       
   569 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   248,    66,   197, 0x0,               STR_NULL},
       
   570 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    63,   198,   209, STR_00E4_LOCATION, STR_3053_CENTER_MAIN_VIEW_ON_STATION},
       
   571 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,    64,   128,   198,   209, STR_3033_ACCEPTS,  STR_3056_SHOW_LIST_OF_ACCEPTED_CARGO},
       
   572 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   129,   192,   198,   209, STR_0130_RENAME,   STR_3055_CHANGE_NAME_OF_STATION},
       
   573 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   193,   206,   198,   209, STR_TRAIN,         STR_SCHEDULED_TRAINS_TIP },
       
   574 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   207,   220,   198,   209, STR_LORRY,         STR_SCHEDULED_ROAD_VEHICLES_TIP },
       
   575 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   221,   234,   198,   209, STR_PLANE,         STR_SCHEDULED_AIRCRAFT_TIP },
       
   576 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   235,   248,   198,   209, STR_SHIP,          STR_SCHEDULED_SHIPS_TIP },
       
   577 {   WIDGETS_END},
       
   578 };
       
   579 
       
   580 static const Widget _station_view_widgets[] = {
       
   581 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},
       
   582 {    WWT_CAPTION,   RESIZE_NONE,    14,    11,   236,     0,    13, STR_300A_0,        STR_018C_WINDOW_TITLE_DRAG_THIS},
       
   583 {  WWT_STICKYBOX,   RESIZE_NONE,    14,   237,   248,     0,    13, 0x0,               STR_STICKY_BUTTON},
       
   584 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   236,    14,    65, 0x0,               STR_NULL},
       
   585 {  WWT_SCROLLBAR,   RESIZE_NONE,    14,   237,   248,    14,    65, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
       
   586 {      WWT_PANEL,   RESIZE_NONE,    14,     0,   248,    66,    97, 0x0,               STR_NULL},
       
   587 {      WWT_EMPTY,   RESIZE_NONE,     0,     0,     0,     0,     0, 0x0,               STR_NULL},
       
   588 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,     0,    63,    98,   109, STR_00E4_LOCATION, STR_3053_CENTER_MAIN_VIEW_ON_STATION},
       
   589 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,    64,   128,    98,   109, STR_3032_RATINGS,  STR_3054_SHOW_STATION_RATINGS},
       
   590 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   129,   192,    98,   109, STR_0130_RENAME,   STR_3055_CHANGE_NAME_OF_STATION},
       
   591 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   193,   206,    98,   109, STR_TRAIN,         STR_SCHEDULED_TRAINS_TIP },
       
   592 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   207,   220,    98,   109, STR_LORRY,         STR_SCHEDULED_ROAD_VEHICLES_TIP },
       
   593 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   221,   234,    98,   109, STR_PLANE,         STR_SCHEDULED_AIRCRAFT_TIP },
       
   594 { WWT_PUSHTXTBTN,   RESIZE_NONE,    14,   235,   248,    98,   109, STR_SHIP,          STR_SCHEDULED_SHIPS_TIP },
       
   595 {   WIDGETS_END},
       
   596 };
       
   597 
       
   598 static void DrawStationViewWindow(Window *w)
       
   599 {
       
   600 	StationID station_id = w->window_number;
       
   601 	const Station* st = GetStation(station_id);
       
   602 	uint i;
       
   603 	uint num;
       
   604 	int x,y;
       
   605 	int pos;
       
   606 	StringID str;
       
   607 
       
   608 	num = 1;
       
   609 	for (i = 0; i != NUM_CARGO; i++) {
       
   610 		if (GB(st->goods[i].waiting_acceptance, 0, 12) != 0) {
       
   611 			num++;
       
   612 			if (st->goods[i].enroute_from != station_id) num++;
       
   613 		}
       
   614 	}
       
   615 	SetVScrollCount(w, num);
       
   616 
       
   617 	SetWindowWidgetDisabledState(w,  9, st->owner != _local_player);
       
   618 	SetWindowWidgetDisabledState(w, 10, !(st->facilities & FACIL_TRAIN));
       
   619 	SetWindowWidgetDisabledState(w, 11, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
       
   620 	SetWindowWidgetDisabledState(w, 12, !(st->facilities & FACIL_AIRPORT));
       
   621 	SetWindowWidgetDisabledState(w, 13, !(st->facilities & FACIL_DOCK));
       
   622 
       
   623 	SetDParam(0, st->index);
       
   624 	SetDParam(1, st->facilities);
       
   625 	DrawWindowWidgets(w);
       
   626 
       
   627 	x = 2;
       
   628 	y = 15;
       
   629 	pos = w->vscroll.pos;
       
   630 
       
   631 	if (--pos < 0) {
       
   632 		str = STR_00D0_NOTHING;
       
   633 		for (i = 0; i != NUM_CARGO; i++) {
       
   634 			if (GB(st->goods[i].waiting_acceptance, 0, 12) != 0) str = STR_EMPTY;
       
   635 		}
       
   636 		SetDParam(0, str);
       
   637 		DrawString(x, y, STR_0008_WAITING, 0);
       
   638 		y += 10;
       
   639 	}
       
   640 
       
   641 	i = 0;
       
   642 	do {
       
   643 		uint waiting = GB(st->goods[i].waiting_acceptance, 0, 12);
       
   644 		if (waiting == 0) continue;
       
   645 
       
   646 		num = (waiting + 5) / 10;
       
   647 		if (num != 0) {
       
   648 			int cur_x = x;
       
   649 			num = min(num, 23);
       
   650 			do {
       
   651 				DrawSprite(_cargoc.sprites[i], cur_x, y);
       
   652 				cur_x += 10;
       
   653 			} while (--num);
       
   654 		}
       
   655 
       
   656 		if ( st->goods[i].enroute_from == station_id) {
       
   657 			if (--pos < 0) {
       
   658 				SetDParam(1, waiting);
       
   659 				SetDParam(0, i);
       
   660 				DrawStringRightAligned(x + 234, y, STR_0009, 0);
       
   661 				y += 10;
       
   662 			}
       
   663 		} else {
       
   664 			/* enroute */
       
   665 			if (--pos < 0) {
       
   666 				SetDParam(1, waiting);
       
   667 				SetDParam(0, i);
       
   668 				DrawStringRightAligned(x + 234, y, STR_000A_EN_ROUTE_FROM, 0);
       
   669 				y += 10;
       
   670 			}
       
   671 
       
   672 			if (pos > -5 && --pos < 0) {
       
   673 				SetDParam(0, st->goods[i].enroute_from);
       
   674 				DrawStringRightAligned(x + 234, y, STR_000B, 0);
       
   675 				y += 10;
       
   676 			}
       
   677 		}
       
   678 	} while (pos > -5 && ++i != NUM_CARGO);
       
   679 
       
   680 	if (IsWindowOfPrototype(w, _station_view_widgets)) {
       
   681 		char *b = _userstring;
       
   682 		bool first = true;
       
   683 
       
   684 		b = InlineString(b, STR_000C_ACCEPTS);
       
   685 
       
   686 		for (i = 0; i != NUM_CARGO; i++) {
       
   687 			if (b >= endof(_userstring) - 5 - 1) break;
       
   688 			if (st->goods[i].waiting_acceptance & 0x8000) {
       
   689 				if (first) {
       
   690 					first = false;
       
   691 				} else {
       
   692 					/* Add a comma if this is not the first item */
       
   693 					*b++ = ',';
       
   694 					*b++ = ' ';
       
   695 				}
       
   696 				b = InlineString(b, _cargoc.names_s[i]);
       
   697 			}
       
   698 		}
       
   699 
       
   700 		/* If first is still true then no cargo is accepted */
       
   701 		if (first) b = InlineString(b, STR_00D0_NOTHING);
       
   702 
       
   703 		*b = '\0';
       
   704 		DrawStringMultiLine(2, 67, STR_SPEC_USERSTRING, 245);
       
   705 	} else {
       
   706 		DrawString(2, 67, STR_3034_LOCAL_RATING_OF_TRANSPORT, 0);
       
   707 
       
   708 		y = 77;
       
   709 		for (i = 0; i != NUM_CARGO; i++) {
       
   710 			if (st->goods[i].enroute_from != INVALID_STATION) {
       
   711 				SetDParam(0, _cargoc.names_s[i]);
       
   712 				SetDParam(2, st->goods[i].rating * 101 >> 8);
       
   713 				SetDParam(1, STR_3035_APPALLING + (st->goods[i].rating >> 5));
       
   714 				DrawString(8, y, STR_303D, 0);
       
   715 				y += 10;
       
   716 			}
       
   717 		}
       
   718 	}
       
   719 }
       
   720 
       
   721 
       
   722 static void StationViewWndProc(Window *w, WindowEvent *e)
       
   723 {
       
   724 	switch (e->event) {
       
   725 	case WE_PAINT:
       
   726 		DrawStationViewWindow(w);
       
   727 		break;
       
   728 
       
   729 	case WE_CLICK:
       
   730 		switch (e->we.click.widget) {
       
   731 		case 7:
       
   732 			ScrollMainWindowToTile(GetStation(w->window_number)->xy);
       
   733 			break;
       
   734 
       
   735 		case 8:
       
   736 			SetWindowDirty(w);
       
   737 
       
   738 			/* toggle height/widget set */
       
   739 			if (IsWindowOfPrototype(w, _station_view_expanded_widgets)) {
       
   740 				AssignWidgetToWindow(w, _station_view_widgets);
       
   741 				w->height = 110;
       
   742 			} else {
       
   743 				AssignWidgetToWindow(w, _station_view_expanded_widgets);
       
   744 				w->height = 210;
       
   745 			}
       
   746 
       
   747 			SetWindowDirty(w);
       
   748 			break;
       
   749 
       
   750 		case 9: {
       
   751 			SetDParam(0, w->window_number);
       
   752 			ShowQueryString(STR_STATION, STR_3030_RENAME_STATION_LOADING, 31, 180, w, CS_ALPHANUMERAL);
       
   753 		} break;
       
   754 
       
   755 		case 10: { /* Show a list of scheduled trains to this station */
       
   756 			const Station *st = GetStation(w->window_number);
       
   757 			ShowVehicleListWindow(st->owner, w->window_number, VEH_Train);
       
   758 			break;
       
   759 		}
       
   760 
       
   761 		case 11: { /* Show a list of scheduled road-vehicles to this station */
       
   762 			const Station *st = GetStation(w->window_number);
       
   763 			ShowVehicleListWindow(st->owner, w->window_number, VEH_Road);
       
   764 			break;
       
   765 		}
       
   766 
       
   767 		case 12: { /* Show a list of scheduled aircraft to this station */
       
   768 			const Station *st = GetStation(w->window_number);
       
   769 			/* Since oilrigs have no owners, show the scheduled aircraft of current player */
       
   770 			PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
       
   771 			ShowVehicleListWindow(owner, w->window_number, VEH_Aircraft);
       
   772 			break;
       
   773 		}
       
   774 
       
   775 		case 13: { /* Show a list of scheduled ships to this station */
       
   776 			const Station *st = GetStation(w->window_number);
       
   777 			/* Since oilrigs/bouys have no owners, show the scheduled ships of current player */
       
   778 			PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
       
   779 			ShowVehicleListWindow(owner, w->window_number, VEH_Ship);
       
   780 			break;
       
   781 		}
       
   782 		}
       
   783 		break;
       
   784 
       
   785 	case WE_ON_EDIT_TEXT:
       
   786 		if (e->we.edittext.str[0] != '\0') {
       
   787 			_cmd_text = e->we.edittext.str;
       
   788 			DoCommandP(0, w->window_number, 0, NULL,
       
   789 				CMD_RENAME_STATION | CMD_MSG(STR_3031_CAN_T_RENAME_STATION));
       
   790 		}
       
   791 		break;
       
   792 
       
   793 	case WE_DESTROY: {
       
   794 		WindowNumber wno =
       
   795 			(w->window_number << 16) | GetStation(w->window_number)->owner;
       
   796 
       
   797 		DeleteWindowById(WC_TRAINS_LIST, wno);
       
   798 		DeleteWindowById(WC_ROADVEH_LIST, wno);
       
   799 		DeleteWindowById(WC_SHIPS_LIST, wno);
       
   800 		DeleteWindowById(WC_AIRCRAFT_LIST, wno);
       
   801 		break;
       
   802 	}
       
   803 	}
       
   804 }
       
   805 
       
   806 
       
   807 static const WindowDesc _station_view_desc = {
       
   808 	WDP_AUTO, WDP_AUTO, 249, 110,
       
   809 	WC_STATION_VIEW,0,
       
   810 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON,
       
   811 	_station_view_widgets,
       
   812 	StationViewWndProc
       
   813 };
       
   814 
       
   815 void ShowStationViewWindow(StationID station)
       
   816 {
       
   817 	Window *w;
       
   818 
       
   819 	w = AllocateWindowDescFront(&_station_view_desc, station);
       
   820 	if (w != NULL) {
       
   821 		PlayerID owner = GetStation(w->window_number)->owner;
       
   822 		if (owner != OWNER_NONE) w->caption_color = owner;
       
   823 		w->vscroll.cap = 5;
       
   824 	}
       
   825 }