src/station_gui.cpp
changeset 9191 471b623a46b8
parent 9190 31a03ef45575
child 9203 082fbf0500c4
equal deleted inserted replaced
9190:31a03ef45575 9191:471b623a46b8
   163 	}
   163 	}
   164 
   164 
   165 	return (_internal_sort_order & 1) ? maxr2 - maxr1 : maxr1 - maxr2;
   165 	return (_internal_sort_order & 1) ? maxr2 - maxr1 : maxr1 - maxr2;
   166 }
   166 }
   167 
   167 
   168 typedef GUIList<const Station*> plstations_d;
   168 typedef GUIList<const Station*> GUIStationList;
   169 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(plstations_d));
       
   170 
   169 
   171 /**
   170 /**
   172  * Set the station sort flag for all station-list windows.
   171  * Set the station sort flag for all station-list windows.
   173  * @param sl_flag Sort list flag to set for all station-list windows
   172  * @param sl_flag Sort list flag to set for all station-list windows
   174  */
   173  */
   177 	Window *const *wz;
   176 	Window *const *wz;
   178 
   177 
   179 	FOR_ALL_WINDOWS(wz) {
   178 	FOR_ALL_WINDOWS(wz) {
   180 		Window *w = *wz;
   179 		Window *w = *wz;
   181 		if (w->window_class == WC_STATION_LIST) {
   180 		if (w->window_class == WC_STATION_LIST) {
   182 			WP(w, plstations_d).flags |= sl_flag;
   181 			dynamic_cast<GUIStationList*>(w)->flags |= sl_flag;
   183 			w->SetDirty();
   182 			w->SetDirty();
   184 		}
   183 		}
   185 	}
   184 	}
   186 }
   185 }
   187 
   186 
   208  * @param owner player whose stations are to be in list
   207  * @param owner player whose stations are to be in list
   209  * @param facilities types of stations of interest
   208  * @param facilities types of stations of interest
   210  * @param cargo_filter bitmap of cargo types to include
   209  * @param cargo_filter bitmap of cargo types to include
   211  * @param include_empty whether we should include stations without waiting cargo
   210  * @param include_empty whether we should include stations without waiting cargo
   212  */
   211  */
   213 static void BuildStationsList(plstations_d *sl, PlayerID owner, byte facilities, uint32 cargo_filter, bool include_empty)
   212 static void BuildStationsList(GUIStationList *sl, PlayerID owner, byte facilities, uint32 cargo_filter, bool include_empty)
   214 {
   213 {
   215 	uint n = 0;
   214 	uint n = 0;
   216 	const Station *st;
   215 	const Station *st;
   217 
   216 
   218 	if (!(sl->flags & VL_REBUILD)) return;
   217 	if (!(sl->flags & VL_REBUILD)) return;
   258 /**
   257 /**
   259  * Sort station list if the VL_RESORT flag is set
   258  * Sort station list if the VL_RESORT flag is set
   260  *
   259  *
   261  * @param sl pointer to plstations_d (station list and flags)
   260  * @param sl pointer to plstations_d (station list and flags)
   262  */
   261  */
   263 static void SortStationsList(plstations_d *sl)
   262 static void SortStationsList(GUIStationList *sl)
   264 {
   263 {
   265 	static StationSortListingTypeFunction *const _station_sorter[] = {
   264 	static StationSortListingTypeFunction *const _station_sorter[] = {
   266 		&StationNameSorter,
   265 		&StationNameSorter,
   267 		&StationTypeSorter,
   266 		&StationTypeSorter,
   268 		&StationWaitingSorter,
   267 		&StationWaitingSorter,
   278 	sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
   277 	sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
   279 	sl->flags &= ~VL_RESORT;
   278 	sl->flags &= ~VL_RESORT;
   280 }
   279 }
   281 
   280 
   282 /**
   281 /**
   283  * Fuction called when any WindowEvent occurs for PlayerStations window
   282  * The list of stations per player.
   284  *
   283  */
   285  * @param w pointer to the PlayerStations window
   284 struct PlayerStationsWindow : public Window, public GUIStationList
   286  * @param e pointer to window event
   285 {
   287  */
   286 	static Listing station_sort;
   288 static void PlayerStationsWndProc(Window *w, WindowEvent *e)
   287 	static byte facilities;
   289 {
   288 	static bool include_empty;
   290 	const PlayerID owner = (PlayerID)w->window_number;
   289 
   291 	static byte facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
   290 	PlayerStationsWindow(const WindowDesc *desc, void *data, WindowNumber window_number) : Window(desc, data, window_number)
   292 	static Listing station_sort = {0, 0};
   291 	{
   293 	static bool include_empty = true;
   292 		this->caption_color = (byte)this->window_number;
   294 
   293 		this->vscroll.cap = 12;
   295 	plstations_d *sl = &WP(w, plstations_d);
   294 		this->resize.step_height = 10;
   296 
   295 		this->resize.height = this->height - 10 * 7; // minimum if 5 in the list
   297 	switch (e->event) {
   296 
   298 		case WE_CREATE:
   297 		/* Add cargo filter buttons */
   299 			if (_cargo_filter == _cargo_filter_max) _cargo_filter = _cargo_mask;
   298 		uint num_active = 0;
   300 
   299 		for (CargoID c = 0; c < NUM_CARGO; c++) {
   301 			for (uint i = 0; i < 5; i++) {
   300 			if (GetCargo(c)->IsValid()) num_active++;
   302 				if (HasBit(facilities, i)) w->LowerWidget(i + SLW_TRAIN);
   301 		}
   303 			}
   302 
   304 			w->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
   303 		this->widget_count += num_active;
   305 			w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
   304 		this->widget = ReallocT(this->widget, this->widget_count + 1);
   306 			w->SetWidgetLoweredState(SLW_NOCARGOWAITING, include_empty);
   305 		this->widget[this->widget_count].type = WWT_LAST;
   307 
   306 
   308 			sl->sort_list = NULL;
   307 		uint i = 0;
   309 			sl->flags = VL_REBUILD;
   308 		for (CargoID c = 0; c < NUM_CARGO; c++) {
   310 			sl->sort_type = station_sort.criteria;
   309 			if (!GetCargo(c)->IsValid()) continue;
   311 			if (station_sort.order) sl->flags |= VL_DESC;
   310 
   312 
   311 			Widget *wi = &this->widget[SLW_CARGOSTART + i];
   313 			/* set up resort timer */
   312 			wi->type     = WWT_PANEL;
   314 			sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
   313 			wi->display_flags = RESIZE_NONE;
   315 			break;
   314 			wi->color    = 14;
   316 
   315 			wi->left     = 89 + i * 14;
   317 		case WE_PAINT: {
   316 			wi->right    = wi->left + 13;
   318 			BuildStationsList(sl, owner, facilities, _cargo_filter, include_empty);
   317 			wi->top      = 14;
   319 			SortStationsList(sl);
   318 			wi->bottom   = 24;
   320 
   319 			wi->data     = 0;
   321 			SetVScrollCount(w, sl->list_length);
   320 			wi->tooltips = STR_USE_CTRL_TO_SELECT_MORE;
   322 
   321 
   323 			/* draw widgets, with player's name in the caption */
   322 			if (HasBit(_cargo_filter, c)) this->LowerWidget(SLW_CARGOSTART + i);
   324 			SetDParam(0, owner);
   323 			i++;
   325 			SetDParam(1, w->vscroll.count);
   324 		}
   326 
   325 
   327 			/* Set text of sort by dropdown */
   326 		this->widget[SLW_NOCARGOWAITING].left += num_active * 14;
   328 			w->widget[SLW_SORTDROPBTN].data = _station_sort_listing[sl->sort_type];
   327 		this->widget[SLW_NOCARGOWAITING].right += num_active * 14;
   329 
   328 		this->widget[SLW_CARGOALL].left += num_active * 14;
   330 			DrawWindowWidgets(w);
   329 		this->widget[SLW_CARGOALL].right += num_active * 14;
   331 
   330 		this->widget[SLW_PAN_RIGHT].left += num_active * 14;
   332 			/* draw arrow pointing up/down for ascending/descending sorting */
   331 
   333 			DrawSortButtonState(w, SLW_SORTBY, sl->flags & VL_DESC ? SBS_DOWN : SBS_UP);
   332 		if (num_active > 15) {
   334 
   333 			/* Resize and fix the minimum width, if necessary */
   335 			int cg_ofst;
   334 			ResizeWindow(this, (num_active - 15) * 14, 0);
   336 			int x = 89;
   335 			this->resize.width = this->width;
   337 			int y = 14;
   336 		}
   338 			int xb = 2; ///< offset from left of widget
   337 
   339 
   338 		if (_cargo_filter == _cargo_filter_max) _cargo_filter = _cargo_mask;
   340 			uint i = 0;
   339 
   341 			for (CargoID c = 0; c < NUM_CARGO; c++) {
   340 		for (uint i = 0; i < 5; i++) {
   342 				const CargoSpec *cs = GetCargo(c);
   341 			if (HasBit(facilities, i)) this->LowerWidget(i + SLW_TRAIN);
   343 				if (!cs->IsValid()) continue;
   342 		}
   344 
   343 		this->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
   345 				cg_ofst = HasBit(_cargo_filter, c) ? 2 : 1;
   344 		this->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
   346 				GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, cs->rating_colour);
   345 		this->SetWidgetLoweredState(SLW_NOCARGOWAITING, include_empty);
   347 				DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, cs->abbrev, TC_BLACK);
   346 
   348 				x += 14;
   347 		this->sort_list = NULL;
   349 				i++;
   348 		this->flags = VL_REBUILD;
   350 			}
   349 		this->sort_type = station_sort.criteria;
   351 
   350 		if (station_sort.order) this->flags |= VL_DESC;
   352 			x += 6;
   351 
   353 			cg_ofst = w->IsWidgetLowered(SLW_NOCARGOWAITING) ? 2 : 1;
   352 		/* set up resort timer */
   354 			DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, TC_BLACK);
   353 		this->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
       
   354 	}
       
   355 
       
   356 	virtual void OnPaint()
       
   357 	{
       
   358 		PlayerID owner = (PlayerID)this->window_number;
       
   359 
       
   360 		BuildStationsList(this, owner, facilities, _cargo_filter, include_empty);
       
   361 		SortStationsList(this);
       
   362 
       
   363 		SetVScrollCount(this, this->list_length);
       
   364 
       
   365 		/* draw widgets, with player's name in the caption */
       
   366 		SetDParam(0, owner);
       
   367 		SetDParam(1, this->vscroll.count);
       
   368 
       
   369 		/* Set text of sort by dropdown */
       
   370 		this->widget[SLW_SORTDROPBTN].data = _station_sort_listing[this->sort_type];
       
   371 
       
   372 		DrawWindowWidgets(this);
       
   373 
       
   374 		/* draw arrow pointing up/down for ascending/descending sorting */
       
   375 		DrawSortButtonState(this, SLW_SORTBY, this->flags & VL_DESC ? SBS_DOWN : SBS_UP);
       
   376 
       
   377 		int cg_ofst;
       
   378 		int x = 89;
       
   379 		int y = 14;
       
   380 		int xb = 2; ///< offset from left of widget
       
   381 
       
   382 		uint i = 0;
       
   383 		for (CargoID c = 0; c < NUM_CARGO; c++) {
       
   384 			const CargoSpec *cs = GetCargo(c);
       
   385 			if (!cs->IsValid()) continue;
       
   386 
       
   387 			cg_ofst = HasBit(_cargo_filter, c) ? 2 : 1;
       
   388 			GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, cs->rating_colour);
       
   389 			DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, cs->abbrev, TC_BLACK);
   355 			x += 14;
   390 			x += 14;
   356 			cg_ofst = w->IsWidgetLowered(SLW_CARGOALL) ? 2 : 1;
   391 			i++;
   357 			DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
   392 		}
   358 
   393 
   359 			cg_ofst = w->IsWidgetLowered(SLW_FACILALL) ? 2 : 1;
   394 		x += 6;
   360 			DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
   395 		cg_ofst = this->IsWidgetLowered(SLW_NOCARGOWAITING) ? 2 : 1;
   361 
   396 		DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, TC_BLACK);
   362 			if (w->vscroll.count == 0) { // player has no stations
   397 		x += 14;
   363 				DrawString(xb, 40, STR_304A_NONE, TC_FROMSTRING);
   398 		cg_ofst = this->IsWidgetLowered(SLW_CARGOALL) ? 2 : 1;
   364 				return;
   399 		DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
   365 			}
   400 
   366 
   401 		cg_ofst = this->IsWidgetLowered(SLW_FACILALL) ? 2 : 1;
   367 			int max = min(w->vscroll.pos + w->vscroll.cap, sl->list_length);
   402 		DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
   368 			y = 40; // start of the list-widget
   403 
   369 
   404 		if (this->vscroll.count == 0) { // player has no stations
   370 			for (int i = w->vscroll.pos; i < max; ++i) { // do until max number of stations of owner
   405 			DrawString(xb, 40, STR_304A_NONE, TC_FROMSTRING);
   371 				const Station *st = sl->sort_list[i];
   406 			return;
   372 				int x;
   407 		}
   373 
   408 
   374 				assert(st->xy != 0);
   409 		int max = min(this->vscroll.pos + this->vscroll.cap, this->list_length);
   375 
   410 		y = 40; // start of the list-widget
   376 				/* Do not do the complex check HasStationInUse here, it may be even false
   411 
   377 				 * when the order had been removed and the station list hasn't been removed yet */
   412 		for (int i = this->vscroll.pos; i < max; ++i) { // do until max number of stations of owner
       
   413 			const Station *st = this->sort_list[i];
       
   414 			int x;
       
   415 
       
   416 			assert(st->xy != 0);
       
   417 
       
   418 			/* Do not do the complex check HasStationInUse here, it may be even false
       
   419 				* when the order had been removed and the station list hasn't been removed yet */
       
   420 			assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
       
   421 
       
   422 			SetDParam(0, st->index);
       
   423 			SetDParam(1, st->facilities);
       
   424 			x = DrawString(xb, y, STR_3049_0, TC_FROMSTRING) + 5;
       
   425 
       
   426 			/* show cargo waiting and station ratings */
       
   427 			for (CargoID j = 0; j < NUM_CARGO; j++) {
       
   428 				if (!st->goods[j].cargo.Empty()) {
       
   429 					StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
       
   430 					x += 20;
       
   431 				}
       
   432 			}
       
   433 			y += 10;
       
   434 		}
       
   435 	}
       
   436 
       
   437 	virtual void OnClick(Point pt, int widget)
       
   438 	{
       
   439 		PlayerID owner = (PlayerID)this->window_number;
       
   440 
       
   441 		switch (widget) {
       
   442 			case SLW_LIST: {
       
   443 				uint32 id_v = (pt.y - 41) / 10;
       
   444 
       
   445 				if (id_v >= this->vscroll.cap) return; // click out of bounds
       
   446 
       
   447 				id_v += this->vscroll.pos;
       
   448 
       
   449 				if (id_v >= this->list_length) return; // click out of list bound
       
   450 
       
   451 				const Station *st = this->sort_list[id_v];
       
   452 				/* do not check HasStationInUse - it is slow and may be invalid */
   378 				assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
   453 				assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
   379 
   454 
   380 				SetDParam(0, st->index);
   455 				if (_ctrl_pressed) {
   381 				SetDParam(1, st->facilities);
   456 					ShowExtraViewPortWindow(st->xy);
   382 				x = DrawString(xb, y, STR_3049_0, TC_FROMSTRING) + 5;
   457 				} else {
   383 
   458 					ScrollMainWindowToTile(st->xy);
   384 				/* show cargo waiting and station ratings */
   459 				}
   385 				for (CargoID j = 0; j < NUM_CARGO; j++) {
   460 				break;
   386 					if (!st->goods[j].cargo.Empty()) {
   461 			}
   387 						StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
   462 
   388 						x += 20;
   463 			case SLW_TRAIN:
       
   464 			case SLW_TRUCK:
       
   465 			case SLW_BUS:
       
   466 			case SLW_AIRPLANE:
       
   467 			case SLW_SHIP:
       
   468 				if (_ctrl_pressed) {
       
   469 					ToggleBit(facilities, widget - SLW_TRAIN);
       
   470 					this->ToggleWidgetLoweredState(widget);
       
   471 				} else {
       
   472 					uint i;
       
   473 					FOR_EACH_SET_BIT(i, facilities) {
       
   474 						this->RaiseWidget(i + SLW_TRAIN);
   389 					}
   475 					}
   390 				}
   476 					SetBit(facilities, widget - SLW_TRAIN);
   391 				y += 10;
   477 					this->LowerWidget(widget);
   392 			}
   478 				}
   393 			break;
   479 				this->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
   394 		}
   480 				this->flags |= VL_REBUILD;
   395 
   481 				this->SetDirty();
   396 		case WE_CLICK:
   482 				break;
   397 			switch (e->we.click.widget) {
   483 
   398 				case SLW_LIST: {
   484 			case SLW_FACILALL:
   399 					uint32 id_v = (e->we.click.pt.y - 41) / 10;
   485 				for (uint i = 0; i < 5; i++) {
   400 
   486 					this->LowerWidget(i + SLW_TRAIN);
   401 					if (id_v >= w->vscroll.cap) return; // click out of bounds
   487 				}
   402 
   488 				this->LowerWidget(SLW_FACILALL);
   403 					id_v += w->vscroll.pos;
   489 
   404 
   490 				facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
   405 					if (id_v >= sl->list_length) return; // click out of list bound
   491 				this->flags |= VL_REBUILD;
   406 
   492 				this->SetDirty();
   407 					const Station *st = sl->sort_list[id_v];
   493 				break;
   408 					/* do not check HasStationInUse - it is slow and may be invalid */
   494 
   409 					assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
   495 			case SLW_CARGOALL: {
   410 
   496 				uint i = 0;
   411 					if (_ctrl_pressed) {
   497 				for (CargoID c = 0; c < NUM_CARGO; c++) {
   412 						ShowExtraViewPortWindow(st->xy);
   498 					if (!GetCargo(c)->IsValid()) continue;
   413 					} else {
   499 					this->LowerWidget(i + SLW_CARGOSTART);
   414 						ScrollMainWindowToTile(st->xy);
   500 					i++;
       
   501 				}
       
   502 				this->LowerWidget(SLW_NOCARGOWAITING);
       
   503 				this->LowerWidget(SLW_CARGOALL);
       
   504 
       
   505 				_cargo_filter = _cargo_mask;
       
   506 				include_empty = true;
       
   507 				this->flags |= VL_REBUILD;
       
   508 				this->SetDirty();
       
   509 				break;
       
   510 			}
       
   511 
       
   512 			case SLW_SORTBY: // flip sorting method asc/desc
       
   513 				this->flags ^= VL_DESC; //DESC-flag
       
   514 				station_sort.order = HasBit(this->flags, 0);
       
   515 				this->flags |= VL_RESORT;
       
   516 				this->flags4 |= 5 << WF_TIMEOUT_SHL;
       
   517 				this->LowerWidget(SLW_SORTBY);
       
   518 				this->SetDirty();
       
   519 				break;
       
   520 
       
   521 			case SLW_SORTDROPBTN: // select sorting criteria dropdown menu
       
   522 				ShowDropDownMenu(this, _station_sort_listing, this->sort_type, SLW_SORTDROPBTN, 0, 0);
       
   523 				break;
       
   524 
       
   525 			case SLW_NOCARGOWAITING:
       
   526 				if (_ctrl_pressed) {
       
   527 					include_empty = !include_empty;
       
   528 					this->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
       
   529 				} else {
       
   530 					for (uint i = SLW_CARGOSTART; i < this->widget_count; i++) {
       
   531 						this->RaiseWidget(i);
   415 					}
   532 					}
   416 					break;
   533 
   417 				}
   534 					_cargo_filter = 0;
   418 
   535 					include_empty = true;
   419 				case SLW_TRAIN:
   536 
   420 				case SLW_TRUCK:
   537 					this->LowerWidget(SLW_NOCARGOWAITING);
   421 				case SLW_BUS:
   538 				}
   422 				case SLW_AIRPLANE:
   539 				this->flags |= VL_REBUILD;
   423 				case SLW_SHIP:
   540 				this->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
   424 					if (_ctrl_pressed) {
   541 				this->SetDirty();
   425 						ToggleBit(facilities, e->we.click.widget - SLW_TRAIN);
   542 				break;
   426 						w->ToggleWidgetLoweredState(e->we.click.widget);
   543 
   427 					} else {
   544 			default:
   428 						uint i;
   545 				if (widget >= SLW_CARGOSTART) { // change cargo_filter
   429 						FOR_EACH_SET_BIT(i, facilities) {
   546 					/* Determine the selected cargo type */
   430 							w->RaiseWidget(i + SLW_TRAIN);
   547 					CargoID c;
   431 						}
   548 					int i = 0;
   432 						SetBit(facilities, e->we.click.widget - SLW_TRAIN);
   549 					for (c = 0; c < NUM_CARGO; c++) {
   433 						w->LowerWidget(e->we.click.widget);
       
   434 					}
       
   435 					w->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
       
   436 					sl->flags |= VL_REBUILD;
       
   437 					w->SetDirty();
       
   438 					break;
       
   439 
       
   440 				case SLW_FACILALL:
       
   441 					for (uint i = 0; i < 5; i++) {
       
   442 						w->LowerWidget(i + SLW_TRAIN);
       
   443 					}
       
   444 					w->LowerWidget(SLW_FACILALL);
       
   445 
       
   446 					facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
       
   447 					sl->flags |= VL_REBUILD;
       
   448 					w->SetDirty();
       
   449 					break;
       
   450 
       
   451 				case SLW_CARGOALL: {
       
   452 					uint i = 0;
       
   453 					for (CargoID c = 0; c < NUM_CARGO; c++) {
       
   454 						if (!GetCargo(c)->IsValid()) continue;
   550 						if (!GetCargo(c)->IsValid()) continue;
   455 						w->LowerWidget(i + SLW_CARGOSTART);
   551 						if (widget - SLW_CARGOSTART == i) break;
   456 						i++;
   552 						i++;
   457 					}
   553 					}
   458 					w->LowerWidget(SLW_NOCARGOWAITING);
   554 
   459 					w->LowerWidget(SLW_CARGOALL);
       
   460 
       
   461 					_cargo_filter = _cargo_mask;
       
   462 					include_empty = true;
       
   463 					sl->flags |= VL_REBUILD;
       
   464 					w->SetDirty();
       
   465 					break;
       
   466 				}
       
   467 
       
   468 				case SLW_SORTBY: // flip sorting method asc/desc
       
   469 					sl->flags ^= VL_DESC; //DESC-flag
       
   470 					station_sort.order = HasBit(sl->flags, 0);
       
   471 					sl->flags |= VL_RESORT;
       
   472 					w->flags4 |= 5 << WF_TIMEOUT_SHL;
       
   473 					w->LowerWidget(SLW_SORTBY);
       
   474 					w->SetDirty();
       
   475 					break;
       
   476 
       
   477 				case SLW_SORTDROPBTN: // select sorting criteria dropdown menu
       
   478 					ShowDropDownMenu(w, _station_sort_listing, sl->sort_type, SLW_SORTDROPBTN, 0, 0);
       
   479 					break;
       
   480 
       
   481 				case SLW_NOCARGOWAITING:
       
   482 					if (_ctrl_pressed) {
   555 					if (_ctrl_pressed) {
   483 						include_empty = !include_empty;
   556 						ToggleBit(_cargo_filter, c);
   484 						w->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
   557 						this->ToggleWidgetLoweredState(widget);
   485 					} else {
   558 					} else {
   486 						for (uint i = SLW_CARGOSTART; i < w->widget_count; i++) {
   559 						for (uint i = SLW_CARGOSTART; i < this->widget_count; i++) {
   487 							w->RaiseWidget(i);
   560 							this->RaiseWidget(i);
   488 						}
   561 						}
       
   562 						this->RaiseWidget(SLW_NOCARGOWAITING);
   489 
   563 
   490 						_cargo_filter = 0;
   564 						_cargo_filter = 0;
   491 						include_empty = true;
   565 						include_empty = false;
   492 
   566 
   493 						w->LowerWidget(SLW_NOCARGOWAITING);
   567 						SetBit(_cargo_filter, c);
       
   568 						this->LowerWidget(widget);
   494 					}
   569 					}
   495 					sl->flags |= VL_REBUILD;
   570 					this->flags |= VL_REBUILD;
   496 					w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
   571 					this->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
   497 					w->SetDirty();
   572 					this->SetDirty();
   498 					break;
   573 				}
   499 
   574 				break;
   500 				default:
   575 		}
   501 					if (e->we.click.widget >= SLW_CARGOSTART) { // change cargo_filter
   576 	}
   502 						/* Determine the selected cargo type */
   577 
   503 						CargoID c;
   578 	virtual void OnDropdownSelect(int widget, int index)
   504 						int i = 0;
   579 	{
   505 						for (c = 0; c < NUM_CARGO; c++) {
   580 		if (this->sort_type != index) {
   506 							if (!GetCargo(c)->IsValid()) continue;
   581 			/* value has changed -> resort */
   507 							if (e->we.click.widget - SLW_CARGOSTART == i) break;
   582 			this->sort_type = index;
   508 							i++;
   583 			station_sort.criteria = this->sort_type;
   509 						}
   584 			this->flags |= VL_RESORT;
   510 
   585 		}
   511 						if (_ctrl_pressed) {
   586 		this->SetDirty();
   512 							ToggleBit(_cargo_filter, c);
   587 	}
   513 							w->ToggleWidgetLoweredState(e->we.click.widget);
   588 
   514 						} else {
   589 	virtual void OnTick()
   515 							for (uint i = SLW_CARGOSTART; i < w->widget_count; i++) {
   590 	{
   516 								w->RaiseWidget(i);
   591 		if (_pause_game != 0) return;
   517 							}
   592 		if (--this->resort_timer == 0) {
   518 							w->RaiseWidget(SLW_NOCARGOWAITING);
   593 			DEBUG(misc, 3, "Periodic rebuild station list player %d", this->window_number);
   519 
   594 			this->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
   520 							_cargo_filter = 0;
   595 			this->flags |= VL_REBUILD;
   521 							include_empty = false;
   596 			this->SetDirty();
   522 
   597 		}
   523 							SetBit(_cargo_filter, c);
   598 	}
   524 							w->LowerWidget(e->we.click.widget);
   599 
   525 						}
   600 	virtual void OnTimeout()
   526 						sl->flags |= VL_REBUILD;
   601 	{
   527 						w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
   602 		this->RaiseWidget(SLW_SORTBY);
   528 						w->SetDirty();
   603 		this->SetDirty();
   529 					}
   604 	}
   530 					break;
   605 
   531 			}
   606 	virtual void OnResize(Point new_size, Point delta)
   532 			break;
   607 	{
   533 
   608 		this->vscroll.cap += delta.y / 10;
   534 		case WE_DROPDOWN_SELECT: // we have selected a dropdown item in the list
   609 	}
   535 			if (sl->sort_type != e->we.dropdown.index) {
   610 };
   536 				/* value has changed -> resort */
   611 
   537 				sl->sort_type = e->we.dropdown.index;
   612 Listing PlayerStationsWindow::station_sort = {0, 0};
   538 				station_sort.criteria = sl->sort_type;
   613 byte PlayerStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
   539 				sl->flags |= VL_RESORT;
   614 bool PlayerStationsWindow::include_empty = true;
   540 			}
   615 
   541 			w->SetDirty();
       
   542 			break;
       
   543 
       
   544 		case WE_TICK:
       
   545 			if (_pause_game != 0) break;
       
   546 			if (--sl->resort_timer == 0) {
       
   547 				DEBUG(misc, 3, "Periodic rebuild station list player %d", owner);
       
   548 				sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
       
   549 				sl->flags |= VL_REBUILD;
       
   550 				w->SetDirty();
       
   551 			}
       
   552 			break;
       
   553 
       
   554 		case WE_TIMEOUT:
       
   555 			w->RaiseWidget(SLW_SORTBY);
       
   556 			w->SetDirty();
       
   557 			break;
       
   558 
       
   559 		case WE_RESIZE:
       
   560 			w->vscroll.cap += e->we.sizing.diff.y / 10;
       
   561 			break;
       
   562 	}
       
   563 }
       
   564 
   616 
   565 static const Widget _player_stations_widgets[] = {
   617 static const Widget _player_stations_widgets[] = {
   566 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},            // SLW_CLOSEBOX
   618 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},            // SLW_CLOSEBOX
   567 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   345,     0,    13, STR_3048_STATIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
   619 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   345,     0,    13, STR_3048_STATIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
   568 {  WWT_STICKYBOX,     RESIZE_LR,    14,   346,   357,     0,    13, 0x0,               STR_STICKY_BUTTON},
   620 {  WWT_STICKYBOX,     RESIZE_LR,    14,   346,   357,     0,    13, 0x0,               STR_STICKY_BUTTON},
   591 static const WindowDesc _player_stations_desc = {
   643 static const WindowDesc _player_stations_desc = {
   592 	WDP_AUTO, WDP_AUTO, 358, 162, 358, 162,
   644 	WDP_AUTO, WDP_AUTO, 358, 162, 358, 162,
   593 	WC_STATION_LIST, WC_NONE,
   645 	WC_STATION_LIST, WC_NONE,
   594 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
   646 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
   595 	_player_stations_widgets,
   647 	_player_stations_widgets,
   596 	PlayerStationsWndProc
   648 	NULL
   597 };
   649 };
   598 
   650 
   599 /**
   651 /**
   600  * Opens window with list of player's stations
   652  * Opens window with list of player's stations
   601  *
   653  *
   603  */
   655  */
   604 void ShowPlayerStations(PlayerID player)
   656 void ShowPlayerStations(PlayerID player)
   605 {
   657 {
   606 	if (!IsValidPlayer(player)) return;
   658 	if (!IsValidPlayer(player)) return;
   607 
   659 
   608 	Window *w = AllocateWindowDescFront<Window>(&_player_stations_desc, player);
   660 	AllocateWindowDescFront<PlayerStationsWindow>(&_player_stations_desc, player);
   609 	if (w == NULL) return;
       
   610 
       
   611 	w->caption_color = (byte)w->window_number;
       
   612 	w->vscroll.cap = 12;
       
   613 	w->resize.step_height = 10;
       
   614 	w->resize.height = w->height - 10 * 7; // minimum if 5 in the list
       
   615 
       
   616 	/* Add cargo filter buttons */
       
   617 	uint num_active = 0;
       
   618 	for (CargoID c = 0; c < NUM_CARGO; c++) {
       
   619 		if (GetCargo(c)->IsValid()) num_active++;
       
   620 	}
       
   621 
       
   622 	w->widget_count += num_active;
       
   623 	w->widget = ReallocT(w->widget, w->widget_count + 1);
       
   624 	w->widget[w->widget_count].type = WWT_LAST;
       
   625 
       
   626 	uint i = 0;
       
   627 	for (CargoID c = 0; c < NUM_CARGO; c++) {
       
   628 		if (!GetCargo(c)->IsValid()) continue;
       
   629 
       
   630 		Widget *wi = &w->widget[SLW_CARGOSTART + i];
       
   631 		wi->type     = WWT_PANEL;
       
   632 		wi->display_flags = RESIZE_NONE;
       
   633 		wi->color    = 14;
       
   634 		wi->left     = 89 + i * 14;
       
   635 		wi->right    = wi->left + 13;
       
   636 		wi->top      = 14;
       
   637 		wi->bottom   = 24;
       
   638 		wi->data     = 0;
       
   639 		wi->tooltips = STR_USE_CTRL_TO_SELECT_MORE;
       
   640 
       
   641 		if (HasBit(_cargo_filter, c)) w->LowerWidget(SLW_CARGOSTART + i);
       
   642 		i++;
       
   643 	}
       
   644 
       
   645 	w->widget[SLW_NOCARGOWAITING].left += num_active * 14;
       
   646 	w->widget[SLW_NOCARGOWAITING].right += num_active * 14;
       
   647 	w->widget[SLW_CARGOALL].left += num_active * 14;
       
   648 	w->widget[SLW_CARGOALL].right += num_active * 14;
       
   649 	w->widget[SLW_PAN_RIGHT].left += num_active * 14;
       
   650 
       
   651 	if (num_active > 15) {
       
   652 		/* Resize and fix the minimum width, if necessary */
       
   653 		ResizeWindow(w, (num_active - 15) * 14, 0);
       
   654 		w->resize.width = w->width;
       
   655 	}
       
   656 }
   661 }
   657 
   662 
   658 static const Widget _station_view_widgets[] = {
   663 static const Widget _station_view_widgets[] = {
   659 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},                // SVW_CLOSEBOX
   664 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},                // SVW_CLOSEBOX
   660 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   236,     0,    13, STR_300A_0,        STR_018C_WINDOW_TITLE_DRAG_THIS},
   665 {    WWT_CAPTION,  RESIZE_RIGHT,    14,    11,   236,     0,    13, STR_300A_0,        STR_018C_WINDOW_TITLE_DRAG_THIS},