00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "gui.h"
00009 #include "window_gui.h"
00010 #include "textbuf_gui.h"
00011 #include "station.h"
00012 #include "player_func.h"
00013 #include "economy_func.h"
00014 #include "town.h"
00015 #include "command_func.h"
00016 #include "variables.h"
00017 #include "vehicle_gui.h"
00018 #include "cargotype.h"
00019 #include "station_gui.h"
00020 #include "station.h"
00021 #include "strings_func.h"
00022 #include "core/alloc_func.hpp"
00023 #include "window_func.h"
00024 #include "viewport_func.h"
00025 #include "gfx_func.h"
00026 #include "widgets/dropdown_func.h"
00027
00028 #include "table/strings.h"
00029 #include "table/sprites.h"
00030
00031 typedef int CDECL StationSortListingTypeFunction(const void*, const void*);
00032
00033 static StationSortListingTypeFunction StationNameSorter;
00034 static StationSortListingTypeFunction StationTypeSorter;
00035 static StationSortListingTypeFunction StationWaitingSorter;
00036 static StationSortListingTypeFunction StationRatingMaxSorter;
00037
00038 bool _station_show_coverage;
00039
00054 static void StationsWndShowStationRating(int x, int y, CargoID type, uint amount, byte rating)
00055 {
00056 static const uint units_full = 576;
00057 static const uint rating_full = 224;
00058
00059 const CargoSpec *cs = GetCargo(type);
00060 if (!cs->IsValid()) return;
00061
00062 int colour = cs->rating_colour;
00063 uint w = (minu(amount, units_full) + 5) / 36;
00064
00065
00066 if (w != 0) GfxFillRect(x, y, x + w - 1, y + 6, colour);
00067
00068
00069
00070 if (w == 0) {
00071 uint rest = amount / 5;
00072 if (rest != 0) {
00073 w += x;
00074 GfxFillRect(w, y + 6 - rest, w, y + 6, colour);
00075 }
00076 }
00077
00078 DrawString(x + 1, y, cs->abbrev, TC_BLACK);
00079
00080
00081 y += 8;
00082 GfxFillRect(x + 1, y, x + 14, y, 0xB8);
00083 rating = minu(rating, rating_full) / 16;
00084 if (rating != 0) GfxFillRect(x + 1, y, x + rating, y, 0xD0);
00085 }
00086
00087 const StringID _station_sort_listing[] = {
00088 STR_SORT_BY_DROPDOWN_NAME,
00089 STR_SORT_BY_FACILITY,
00090 STR_SORT_BY_WAITING,
00091 STR_SORT_BY_RATING_MAX,
00092 INVALID_STRING_ID
00093 };
00094
00095 static char _bufcache[64];
00096 static const Station* _last_station;
00097 static int _internal_sort_order;
00098
00099 static int CDECL StationNameSorter(const void *a, const void *b)
00100 {
00101 const Station* st1 = *(const Station**)a;
00102 const Station* st2 = *(const Station**)b;
00103 char buf1[64];
00104 int r;
00105
00106 SetDParam(0, st1->index);
00107 GetString(buf1, STR_STATION, lastof(buf1));
00108
00109 if (st2 != _last_station) {
00110 _last_station = st2;
00111 SetDParam(0, st2->index);
00112 GetString(_bufcache, STR_STATION, lastof(_bufcache));
00113 }
00114
00115 r = strcmp(buf1, _bufcache);
00116 return (_internal_sort_order & 1) ? -r : r;
00117 }
00118
00119 static int CDECL StationTypeSorter(const void *a, const void *b)
00120 {
00121 const Station* st1 = *(const Station**)a;
00122 const Station* st2 = *(const Station**)b;
00123 return (_internal_sort_order & 1) ? st2->facilities - st1->facilities : st1->facilities - st2->facilities;
00124 }
00125
00126 static const uint32 _cargo_filter_max = ~0;
00127 static uint32 _cargo_filter = _cargo_filter_max;
00128
00129 static int CDECL StationWaitingSorter(const void *a, const void *b)
00130 {
00131 const Station* st1 = *(const Station**)a;
00132 const Station* st2 = *(const Station**)b;
00133 Money sum1 = 0, sum2 = 0;
00134
00135 for (CargoID j = 0; j < NUM_CARGO; j++) {
00136 if (!HasBit(_cargo_filter, j)) continue;
00137 if (!st1->goods[j].cargo.Empty()) sum1 += GetTransportedGoodsIncome(st1->goods[j].cargo.Count(), 20, 50, j);
00138 if (!st2->goods[j].cargo.Empty()) sum2 += GetTransportedGoodsIncome(st2->goods[j].cargo.Count(), 20, 50, j);
00139 }
00140
00141 return (_internal_sort_order & 1) ? ClampToI32(sum2 - sum1) : ClampToI32(sum1 - sum2);
00142 }
00143
00152 static int CDECL StationRatingMaxSorter(const void *a, const void *b)
00153 {
00154 const Station* st1 = *(const Station**)a;
00155 const Station* st2 = *(const Station**)b;
00156 byte maxr1 = 0;
00157 byte maxr2 = 0;
00158
00159 for (CargoID j = 0; j < NUM_CARGO; j++) {
00160 if (HasBit(st1->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr1 = max(maxr1, st1->goods[j].rating);
00161 if (HasBit(st2->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr2 = max(maxr2, st2->goods[j].rating);
00162 }
00163
00164 return (_internal_sort_order & 1) ? maxr2 - maxr1 : maxr1 - maxr2;
00165 }
00166
00168 enum StationListFlags {
00169 SL_ORDER = 1 << 0,
00170 SL_RESORT = 1 << 1,
00171 SL_REBUILD = 1 << 2,
00172 };
00173
00174 DECLARE_ENUM_AS_BIT_SET(StationListFlags);
00175
00177 struct plstations_d {
00178 const Station** sort_list;
00179 uint16 list_length;
00180 uint16 resort_timer;
00181 byte sort_type;
00182 byte flags;
00183 };
00184 assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(plstations_d));
00185
00189 void RebuildStationLists()
00190 {
00191 Window* const *wz;
00192
00193 FOR_ALL_WINDOWS(wz) {
00194 Window *w = *wz;
00195 if (w->window_class == WC_STATION_LIST) {
00196 WP(w, plstations_d).flags |= SL_REBUILD;
00197 SetWindowDirty(w);
00198 }
00199 }
00200 }
00201
00205 void ResortStationLists()
00206 {
00207 Window* const *wz;
00208
00209 FOR_ALL_WINDOWS(wz) {
00210 Window *w = *wz;
00211 if (w->window_class == WC_STATION_LIST) {
00212 WP(w, plstations_d).flags |= SL_RESORT;
00213 SetWindowDirty(w);
00214 }
00215 }
00216 }
00217
00227 static void BuildStationsList(plstations_d* sl, PlayerID owner, byte facilities, uint32 cargo_filter, bool include_empty)
00228 {
00229 uint n = 0;
00230 const Station *st;
00231
00232 if (!(sl->flags & SL_REBUILD)) return;
00233
00234
00235 const Station** station_sort = MallocT<const Station*>(GetMaxStationIndex() + 1);
00236
00237 DEBUG(misc, 3, "Building station list for player %d", owner);
00238
00239 FOR_ALL_STATIONS(st) {
00240 if (st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy() && HasStationInUse(st->index, owner))) {
00241 if (facilities & st->facilities) {
00242 int num_waiting_cargo = 0;
00243 for (CargoID j = 0; j < NUM_CARGO; j++) {
00244 if (!st->goods[j].cargo.Empty()) {
00245 num_waiting_cargo++;
00246 if (HasBit(cargo_filter, j)) {
00247 station_sort[n++] = st;
00248 break;
00249 }
00250 }
00251 }
00252
00253 if (num_waiting_cargo == 0 && include_empty) {
00254 station_sort[n++] = st;
00255 }
00256 }
00257 }
00258 }
00259
00260 free((void*)sl->sort_list);
00261 sl->sort_list = MallocT<const Station*>(n);
00262 sl->list_length = n;
00263
00264 for (uint i = 0; i < n; ++i) sl->sort_list[i] = station_sort[i];
00265
00266 sl->flags &= ~SL_REBUILD;
00267 sl->flags |= SL_RESORT;
00268 free((void*)station_sort);
00269 }
00270
00271
00277 static void SortStationsList(plstations_d *sl)
00278 {
00279 static StationSortListingTypeFunction* const _station_sorter[] = {
00280 &StationNameSorter,
00281 &StationTypeSorter,
00282 &StationWaitingSorter,
00283 &StationRatingMaxSorter
00284 };
00285
00286 if (!(sl->flags & SL_RESORT)) return;
00287
00288 _internal_sort_order = sl->flags & SL_ORDER;
00289 _last_station = NULL;
00290 qsort((void*)sl->sort_list, sl->list_length, sizeof(sl->sort_list[0]), _station_sorter[sl->sort_type]);
00291
00292 sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
00293 sl->flags &= ~SL_RESORT;
00294 }
00295
00302 static void PlayerStationsWndProc(Window *w, WindowEvent *e)
00303 {
00304 const PlayerID owner = (PlayerID)w->window_number;
00305 static byte facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00306 static Listing station_sort = {0, 0};
00307 static bool include_empty = true;
00308
00309 plstations_d *sl = &WP(w, plstations_d);
00310
00311 switch (e->event) {
00312 case WE_CREATE:
00313 if (_cargo_filter == _cargo_filter_max) _cargo_filter = _cargo_mask;
00314
00315 for (uint i = 0; i < 5; i++) {
00316 if (HasBit(facilities, i)) w->LowerWidget(i + SLW_TRAIN);
00317 }
00318 w->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00319 w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
00320 w->SetWidgetLoweredState(SLW_NOCARGOWAITING, include_empty);
00321
00322 sl->sort_list = NULL;
00323 sl->flags = SL_REBUILD;
00324 sl->sort_type = station_sort.criteria;
00325 if (station_sort.order) sl->flags |= SL_ORDER;
00326
00327
00328 sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
00329 break;
00330
00331 case WE_PAINT: {
00332 BuildStationsList(sl, owner, facilities, _cargo_filter, include_empty);
00333 SortStationsList(sl);
00334
00335 SetVScrollCount(w, sl->list_length);
00336
00337
00338 SetDParam(0, owner);
00339 SetDParam(1, w->vscroll.count);
00340
00341
00342 w->widget[SLW_SORTDROPBTN].data = _station_sort_listing[sl->sort_type];
00343
00344 DrawWindowWidgets(w);
00345
00346
00347 DrawSortButtonState(w, SLW_SORTBY, sl->flags & SL_ORDER ? SBS_DOWN : SBS_UP);
00348
00349 int cg_ofst;
00350 int x = 89;
00351 int y = 14;
00352 int xb = 2;
00353
00354 uint i = 0;
00355 for (CargoID c = 0; c < NUM_CARGO; c++) {
00356 const CargoSpec *cs = GetCargo(c);
00357 if (!cs->IsValid()) continue;
00358
00359 cg_ofst = HasBit(_cargo_filter, c) ? 2 : 1;
00360 GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, cs->rating_colour);
00361 DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, cs->abbrev, TC_BLACK);
00362 x += 14;
00363 i++;
00364 }
00365
00366 x += 6;
00367 cg_ofst = w->IsWidgetLowered(SLW_NOCARGOWAITING) ? 2 : 1;
00368 DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, TC_BLACK);
00369 x += 14;
00370 cg_ofst = w->IsWidgetLowered(SLW_CARGOALL) ? 2 : 1;
00371 DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00372
00373 cg_ofst = w->IsWidgetLowered(SLW_FACILALL) ? 2 : 1;
00374 DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00375
00376 if (w->vscroll.count == 0) {
00377 DrawString(xb, 40, STR_304A_NONE, TC_FROMSTRING);
00378 return;
00379 }
00380
00381 int max = min(w->vscroll.pos + w->vscroll.cap, sl->list_length);
00382 y = 40;
00383
00384 for (int i = w->vscroll.pos; i < max; ++i) {
00385 const Station *st = sl->sort_list[i];
00386 int x;
00387
00388 assert(st->xy != 0);
00389
00390
00391
00392 assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
00393
00394 SetDParam(0, st->index);
00395 SetDParam(1, st->facilities);
00396 x = DrawString(xb, y, STR_3049_0, TC_FROMSTRING) + 5;
00397
00398
00399 for (CargoID j = 0; j < NUM_CARGO; j++) {
00400 if (!st->goods[j].cargo.Empty()) {
00401 StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
00402 x += 20;
00403 }
00404 }
00405 y += 10;
00406 }
00407 break;
00408 }
00409
00410 case WE_CLICK:
00411 switch (e->we.click.widget) {
00412 case SLW_LIST: {
00413 uint32 id_v = (e->we.click.pt.y - 41) / 10;
00414
00415 if (id_v >= w->vscroll.cap) return;
00416
00417 id_v += w->vscroll.pos;
00418
00419 if (id_v >= sl->list_length) return;
00420
00421 const Station *st = sl->sort_list[id_v];
00422
00423 assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
00424 ScrollMainWindowToTile(st->xy);
00425 break;
00426 }
00427
00428 case SLW_TRAIN:
00429 case SLW_TRUCK:
00430 case SLW_BUS:
00431 case SLW_AIRPLANE:
00432 case SLW_SHIP:
00433 if (_ctrl_pressed) {
00434 ToggleBit(facilities, e->we.click.widget - SLW_TRAIN);
00435 w->ToggleWidgetLoweredState(e->we.click.widget);
00436 } else {
00437 uint i;
00438 FOR_EACH_SET_BIT(i, facilities) {
00439 w->RaiseWidget(i + SLW_TRAIN);
00440 }
00441 SetBit(facilities, e->we.click.widget - SLW_TRAIN);
00442 w->LowerWidget(e->we.click.widget);
00443 }
00444 w->SetWidgetLoweredState(SLW_FACILALL, facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00445 sl->flags |= SL_REBUILD;
00446 SetWindowDirty(w);
00447 break;
00448
00449 case SLW_FACILALL:
00450 for (uint i = 0; i < 5; i++) {
00451 w->LowerWidget(i + SLW_TRAIN);
00452 }
00453 w->LowerWidget(SLW_FACILALL);
00454
00455 facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00456 sl->flags |= SL_REBUILD;
00457 SetWindowDirty(w);
00458 break;
00459
00460 case SLW_CARGOALL: {
00461 uint i = 0;
00462 for (CargoID c = 0; c < NUM_CARGO; c++) {
00463 if (!GetCargo(c)->IsValid()) continue;
00464 w->LowerWidget(i + SLW_CARGOSTART);
00465 i++;
00466 }
00467 w->LowerWidget(SLW_NOCARGOWAITING);
00468 w->LowerWidget(SLW_CARGOALL);
00469
00470 _cargo_filter = _cargo_mask;
00471 include_empty = true;
00472 sl->flags |= SL_REBUILD;
00473 SetWindowDirty(w);
00474 break;
00475 }
00476
00477 case SLW_SORTBY:
00478 sl->flags ^= SL_ORDER;
00479 station_sort.order = HasBit(sl->flags, 0);
00480 sl->flags |= SL_RESORT;
00481 w->flags4 |= 5 << WF_TIMEOUT_SHL;
00482 w->LowerWidget(SLW_SORTBY);
00483 SetWindowDirty(w);
00484 break;
00485
00486 case SLW_SORTDROPBTN:
00487 ShowDropDownMenu(w, _station_sort_listing, sl->sort_type, SLW_SORTDROPBTN, 0, 0);
00488 break;
00489
00490 case SLW_NOCARGOWAITING:
00491 if (_ctrl_pressed) {
00492 include_empty = !include_empty;
00493 w->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
00494 } else {
00495 for (uint i = SLW_CARGOSTART; i < w->widget_count; i++) {
00496 w->RaiseWidget(i);
00497 }
00498
00499 _cargo_filter = 0;
00500 include_empty = true;
00501
00502 w->LowerWidget(SLW_NOCARGOWAITING);
00503 }
00504 sl->flags |= SL_REBUILD;
00505 w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
00506 SetWindowDirty(w);
00507 break;
00508
00509 default:
00510 if (e->we.click.widget >= SLW_CARGOSTART) {
00511
00512 CargoID c;
00513 int i = 0;
00514 for (c = 0; c < NUM_CARGO; c++) {
00515 if (!GetCargo(c)->IsValid()) continue;
00516 if (e->we.click.widget - SLW_CARGOSTART == i) break;
00517 i++;
00518 }
00519
00520 if (_ctrl_pressed) {
00521 ToggleBit(_cargo_filter, c);
00522 w->ToggleWidgetLoweredState(e->we.click.widget);
00523 } else {
00524 for (uint i = SLW_CARGOSTART; i < w->widget_count; i++) {
00525 w->RaiseWidget(i);
00526 }
00527 w->RaiseWidget(SLW_NOCARGOWAITING);
00528
00529 _cargo_filter = 0;
00530 include_empty = false;
00531
00532 SetBit(_cargo_filter, c);
00533 w->LowerWidget(e->we.click.widget);
00534 }
00535 sl->flags |= SL_REBUILD;
00536 w->SetWidgetLoweredState(SLW_CARGOALL, _cargo_filter == _cargo_mask && include_empty);
00537 SetWindowDirty(w);
00538 }
00539 break;
00540 }
00541 break;
00542
00543 case WE_DROPDOWN_SELECT:
00544 if (sl->sort_type != e->we.dropdown.index) {
00545
00546 sl->sort_type = e->we.dropdown.index;
00547 station_sort.criteria = sl->sort_type;
00548 sl->flags |= SL_RESORT;
00549 }
00550 SetWindowDirty(w);
00551 break;
00552
00553 case WE_TICK:
00554 if (_pause_game != 0) break;
00555 if (--sl->resort_timer == 0) {
00556 DEBUG(misc, 3, "Periodic rebuild station list player %d", owner);
00557 sl->resort_timer = DAY_TICKS * PERIODIC_RESORT_DAYS;
00558 sl->flags |= SL_REBUILD;
00559 SetWindowDirty(w);
00560 }
00561 break;
00562
00563 case WE_TIMEOUT:
00564 w->RaiseWidget(SLW_SORTBY);
00565 SetWindowDirty(w);
00566 break;
00567
00568 case WE_RESIZE:
00569 w->vscroll.cap += e->we.sizing.diff.y / 10;
00570 break;
00571 }
00572 }
00573
00574 static const Widget _player_stations_widgets[] = {
00575 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00576 { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 345, 0, 13, STR_3048_STATIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
00577 { WWT_STICKYBOX, RESIZE_LR, 14, 346, 357, 0, 13, 0x0, STR_STICKY_BUTTON},
00578 { WWT_PANEL, RESIZE_RB, 14, 0, 345, 37, 161, 0x0, STR_3057_STATION_NAMES_CLICK_ON},
00579 { WWT_SCROLLBAR, RESIZE_LRB, 14, 346, 357, 37, 149, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00580 { WWT_RESIZEBOX, RESIZE_LRTB, 14, 346, 357, 150, 161, 0x0, STR_RESIZE_BUTTON},
00581
00582 { WWT_TEXTBTN, RESIZE_NONE, 14, 0, 13, 14, 24, STR_TRAIN, STR_USE_CTRL_TO_SELECT_MORE},
00583 { WWT_TEXTBTN, RESIZE_NONE, 14, 14, 27, 14, 24, STR_LORRY, STR_USE_CTRL_TO_SELECT_MORE},
00584 { WWT_TEXTBTN, RESIZE_NONE, 14, 28, 41, 14, 24, STR_BUS, STR_USE_CTRL_TO_SELECT_MORE},
00585 { WWT_TEXTBTN, RESIZE_NONE, 14, 42, 55, 14, 24, STR_PLANE, STR_USE_CTRL_TO_SELECT_MORE},
00586 { WWT_TEXTBTN, RESIZE_NONE, 14, 56, 69, 14, 24, STR_SHIP, STR_USE_CTRL_TO_SELECT_MORE},
00587 { WWT_PANEL, RESIZE_NONE, 14, 70, 83, 14, 24, 0x0, STR_SELECT_ALL_FACILITIES},
00588
00589 { WWT_PANEL, RESIZE_NONE, 14, 83, 88, 14, 24, 0x0, STR_NULL},
00590 { WWT_PANEL, RESIZE_NONE, 14, 89, 102, 14, 24, 0x0, STR_NO_WAITING_CARGO},
00591 { WWT_PANEL, RESIZE_NONE, 14, 103, 116, 14, 24, 0x0, STR_SELECT_ALL_TYPES},
00592 { WWT_PANEL, RESIZE_RIGHT, 14, 117, 357, 14, 24, 0x0, STR_NULL},
00593
00594 { WWT_TEXTBTN, RESIZE_NONE, 14, 0, 80, 25, 36, STR_SORT_BY, STR_SORT_ORDER_TIP},
00595 { WWT_DROPDOWN, RESIZE_NONE, 14, 81, 243, 25, 36, 0x0, STR_SORT_CRITERIA_TIP},
00596 { WWT_PANEL, RESIZE_RIGHT, 14, 244, 357, 25, 36, 0x0, STR_NULL},
00597 { WIDGETS_END},
00598 };
00599
00600 static const WindowDesc _player_stations_desc = {
00601 WDP_AUTO, WDP_AUTO, 358, 162, 358, 162,
00602 WC_STATION_LIST, WC_NONE,
00603 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00604 _player_stations_widgets,
00605 PlayerStationsWndProc
00606 };
00607
00613 void ShowPlayerStations(PlayerID player)
00614 {
00615 if (!IsValidPlayer(player)) return;
00616
00617 Window *w = AllocateWindowDescFront(&_player_stations_desc, player);
00618 if (w == NULL) return;
00619
00620 w->caption_color = (byte)w->window_number;
00621 w->vscroll.cap = 12;
00622 w->resize.step_height = 10;
00623 w->resize.height = w->height - 10 * 7;
00624
00625
00626 uint num_active = 0;
00627 for (CargoID c = 0; c < NUM_CARGO; c++) {
00628 if (GetCargo(c)->IsValid()) num_active++;
00629 }
00630
00631 w->widget_count += num_active;
00632 w->widget = ReallocT(w->widget, w->widget_count + 1);
00633 w->widget[w->widget_count].type = WWT_LAST;
00634
00635 uint i = 0;
00636 for (CargoID c = 0; c < NUM_CARGO; c++) {
00637 if (!GetCargo(c)->IsValid()) continue;
00638
00639 Widget *wi = &w->widget[SLW_CARGOSTART + i];
00640 wi->type = WWT_PANEL;
00641 wi->display_flags = RESIZE_NONE;
00642 wi->color = 14;
00643 wi->left = 89 + i * 14;
00644 wi->right = wi->left + 13;
00645 wi->top = 14;
00646 wi->bottom = 24;
00647 wi->data = 0;
00648 wi->tooltips = STR_USE_CTRL_TO_SELECT_MORE;
00649
00650 if (HasBit(_cargo_filter, c)) w->LowerWidget(SLW_CARGOSTART + i);
00651 i++;
00652 }
00653
00654 w->widget[SLW_NOCARGOWAITING].left += num_active * 14;
00655 w->widget[SLW_NOCARGOWAITING].right += num_active * 14;
00656 w->widget[SLW_CARGOALL].left += num_active * 14;
00657 w->widget[SLW_CARGOALL].right += num_active * 14;
00658 w->widget[SLW_PAN_RIGHT].left += num_active * 14;
00659
00660 if (num_active > 15) {
00661
00662 ResizeWindow(w, (num_active - 15) * 14, 0);
00663 w->resize.width = w->width;
00664 }
00665 }
00666
00667 static const Widget _station_view_widgets[] = {
00668 { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW},
00669 { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 236, 0, 13, STR_300A_0, STR_018C_WINDOW_TITLE_DRAG_THIS},
00670 { WWT_STICKYBOX, RESIZE_LR, 14, 237, 248, 0, 13, 0x0, STR_STICKY_BUTTON},
00671 { WWT_PANEL, RESIZE_RB, 14, 0, 236, 14, 65, 0x0, STR_NULL},
00672 { WWT_SCROLLBAR, RESIZE_LRB, 14, 237, 248, 14, 65, 0x0, STR_0190_SCROLL_BAR_SCROLLS_LIST},
00673 { WWT_PANEL, RESIZE_RTB, 14, 0, 248, 66, 97, 0x0, STR_NULL},
00674 { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 59, 98, 109, STR_00E4_LOCATION, STR_3053_CENTER_MAIN_VIEW_ON_STATION},
00675 { WWT_PUSHTXTBTN, RESIZE_TB, 14, 60, 120, 98, 109, STR_3032_RATINGS, STR_3054_SHOW_STATION_RATINGS},
00676 { WWT_PUSHTXTBTN, RESIZE_RTB, 14, 121, 180, 98, 109, STR_0130_RENAME, STR_3055_CHANGE_NAME_OF_STATION},
00677 { WWT_PUSHTXTBTN, RESIZE_LRTB, 14, 181, 194, 98, 109, STR_TRAIN, STR_SCHEDULED_TRAINS_TIP },
00678 { WWT_PUSHTXTBTN, RESIZE_LRTB, 14, 195, 208, 98, 109, STR_LORRY, STR_SCHEDULED_ROAD_VEHICLES_TIP },
00679 { WWT_PUSHTXTBTN, RESIZE_LRTB, 14, 209, 222, 98, 109, STR_PLANE, STR_SCHEDULED_AIRCRAFT_TIP },
00680 { WWT_PUSHTXTBTN, RESIZE_LRTB, 14, 223, 236, 98, 109, STR_SHIP, STR_SCHEDULED_SHIPS_TIP },
00681 { WWT_RESIZEBOX, RESIZE_LRTB, 14, 237, 248, 98, 109, 0x0, STR_RESIZE_BUTTON},
00682 { WIDGETS_END},
00683 };
00684
00693 static void DrawCargoIcons(CargoID i, uint waiting, int x, int y, uint width)
00694 {
00695 uint num = min((waiting + 5) / 10, width / 10);
00696 if (num == 0) return;
00697
00698 const CargoSpec *cs = GetCargo(i);
00699 SpriteID sprite;
00700
00701 if (cs->sprite == 0xFFFF) {
00702
00703 sprite = GetCustomCargoSprite(cs);
00704 } else {
00705 sprite = cs->sprite;
00706 }
00707
00708 if (sprite == 0) sprite = SPR_CARGO_GOODS;
00709
00710 do {
00711 DrawSprite(sprite, PAL_NONE, x, y);
00712 x += 10;
00713 } while (--num);
00714 }
00715
00716 struct CargoData {
00717 CargoID cargo;
00718 StationID source;
00719 uint count;
00720
00721 CargoData(CargoID cargo, StationID source, uint count) :
00722 cargo(cargo),
00723 source(source),
00724 count(count)
00725 { }
00726 };
00727
00728 typedef std::list<CargoData> CargoDataList;
00729
00735 static void DrawStationViewWindow(Window *w)
00736 {
00737 StationID station_id = w->window_number;
00738 const Station* st = GetStation(station_id);
00739 int x, y;
00740 int pos;
00741 StringID str;
00742 CargoDataList cargolist;
00743
00744
00745 for (CargoID i = 0; i < NUM_CARGO; i++) {
00746 if (!st->goods[i].cargo.Empty()) {
00747
00748 cargolist.push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
00749
00750
00751 const CargoList::List *packets = st->goods[i].cargo.Packets();
00752 for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
00753 const CargoPacket *cp = *it;
00754 if (cp->source != station_id) {
00755 bool added = false;
00756
00757
00758 for (CargoDataList::iterator jt = cargolist.begin(); jt != cargolist.end(); jt++) {
00759 CargoData *cd = &(*jt);
00760 if (cd->cargo == i && cd->source == cp->source) {
00761 cd->count += cp->count;
00762 added = true;
00763 break;
00764 }
00765 }
00766
00767 if (!added) cargolist.push_back(CargoData(i, cp->source, cp->count));
00768 }
00769 }
00770 }
00771 }
00772 SetVScrollCount(w, cargolist.size() + 1);
00773
00774
00775 w->SetWidgetDisabledState(SVW_RENAME, st->owner != _local_player);
00776 w->SetWidgetDisabledState(SVW_TRAINS, !(st->facilities & FACIL_TRAIN));
00777 w->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
00778 w->SetWidgetDisabledState(SVW_PLANES, !(st->facilities & FACIL_AIRPORT));
00779 w->SetWidgetDisabledState(SVW_SHIPS, !(st->facilities & FACIL_DOCK));
00780
00781 SetDParam(0, st->index);
00782 SetDParam(1, st->facilities);
00783 DrawWindowWidgets(w);
00784
00785 x = 2;
00786 y = 15;
00787 pos = w->vscroll.pos;
00788
00789 uint width = w->widget[SVW_WAITING].right - w->widget[SVW_WAITING].left - 4;
00790 int maxrows = w->vscroll.cap;
00791
00792 if (--pos < 0) {
00793 str = STR_00D0_NOTHING;
00794 for (CargoID i = 0; i < NUM_CARGO; i++) {
00795 if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
00796 }
00797 SetDParam(0, str);
00798 DrawString(x, y, STR_0008_WAITING, TC_FROMSTRING);
00799 y += 10;
00800 }
00801
00802 for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
00803 if (--pos < 0) {
00804 const CargoData *cd = &(*it);
00805 if (cd->source == INVALID_STATION) {
00806
00807 DrawCargoIcons(cd->cargo, cd->count, x, y, width);
00808 SetDParam(0, cd->cargo);
00809 SetDParam(1, cd->count);
00810 DrawStringRightAligned(x + width, y, STR_0009, TC_FROMSTRING);
00811 } else {
00812 SetDParam(0, cd->cargo);
00813 SetDParam(1, cd->count);
00814 SetDParam(2, cd->source);
00815 DrawStringRightAlignedTruncated(x + width, y, STR_EN_ROUTE_FROM, TC_FROMSTRING, width);
00816 }
00817
00818 y += 10;
00819 }
00820 }
00821
00822 if (w->widget[SVW_ACCEPTS].data == STR_3032_RATINGS) {
00823 char *b = _userstring;
00824 bool first = true;
00825
00826 b = InlineString(b, STR_000C_ACCEPTS);
00827
00828 for (CargoID i = 0; i < NUM_CARGO; i++) {
00829 if (b >= lastof(_userstring) - (1 + 2 * 4)) break;
00830 if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
00831 if (first) {
00832 first = false;
00833 } else {
00834
00835 *b++ = ',';
00836 *b++ = ' ';
00837 }
00838 b = InlineString(b, GetCargo(i)->name);
00839 }
00840 }
00841
00842
00843 if (first) b = InlineString(b, STR_00D0_NOTHING);
00844
00845 *b = '\0';
00846
00847
00848 assert(b < endof(_userstring));
00849
00850 DrawStringMultiLine(2, w->widget[SVW_ACCEPTLIST].top + 1, STR_SPEC_USERSTRING, w->widget[SVW_ACCEPTLIST].right - w->widget[SVW_ACCEPTLIST].left);
00851 } else {
00852 y = w->widget[SVW_RATINGLIST].top + 1;
00853
00854 DrawString(2, y, STR_3034_LOCAL_RATING_OF_TRANSPORT, TC_FROMSTRING);
00855 y += 10;
00856
00857 for (CargoID i = 0; i < NUM_CARGO; i++) {
00858 const CargoSpec *cs = GetCargo(i);
00859 if (!cs->IsValid()) continue;
00860
00861 const GoodsEntry *ge = &st->goods[i];
00862 if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
00863
00864 SetDParam(0, cs->name);
00865 SetDParam(2, ge->rating * 101 >> 8);
00866 SetDParam(1, STR_3035_APPALLING + (ge->rating >> 5));
00867 DrawString(8, y, STR_303D, TC_FROMSTRING);
00868 y += 10;
00869 }
00870 }
00871 }
00872
00873
00880 static void StationViewWndProc(Window *w, WindowEvent *e)
00881 {
00882 switch (e->event) {
00883 case WE_PAINT:
00884 DrawStationViewWindow(w);
00885 break;
00886
00887 case WE_CLICK:
00888 switch (e->we.click.widget) {
00889 case SVW_LOCATION:
00890 ScrollMainWindowToTile(GetStation(w->window_number)->xy);
00891 break;
00892
00893 case SVW_RATINGS:
00894 SetWindowDirty(w);
00895
00896 if (w->widget[SVW_RATINGS].data == STR_3032_RATINGS) {
00897
00898 w->widget[SVW_RATINGS].data = STR_3033_ACCEPTS;
00899 w->widget[SVW_RATINGS].tooltips = STR_3056_SHOW_LIST_OF_ACCEPTED_CARGO;
00900 ResizeWindowForWidget(w, SVW_ACCEPTLIST, 0, 100);
00901 } else {
00902
00903 w->widget[SVW_RATINGS].data = STR_3032_RATINGS;
00904 w->widget[SVW_RATINGS].tooltips = STR_3054_SHOW_STATION_RATINGS;
00905 ResizeWindowForWidget(w, SVW_ACCEPTLIST, 0, -100);
00906 }
00907
00908 SetWindowDirty(w);
00909 break;
00910
00911 case SVW_RENAME:
00912 SetDParam(0, w->window_number);
00913 ShowQueryString(STR_STATION, STR_3030_RENAME_STATION_LOADING, 31, 180, w, CS_ALPHANUMERAL);
00914 break;
00915
00916 case SVW_TRAINS: {
00917 const Station *st = GetStation(w->window_number);
00918 ShowVehicleListWindow(st->owner, VEH_TRAIN, (StationID)w->window_number);
00919 break;
00920 }
00921
00922 case SVW_ROADVEHS: {
00923 const Station *st = GetStation(w->window_number);
00924 ShowVehicleListWindow(st->owner, VEH_ROAD, (StationID)w->window_number);
00925 break;
00926 }
00927
00928 case SVW_PLANES: {
00929 const Station *st = GetStation(w->window_number);
00930
00931 PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
00932 ShowVehicleListWindow(owner, VEH_AIRCRAFT, (StationID)w->window_number);
00933 break;
00934 }
00935
00936 case SVW_SHIPS: {
00937 const Station *st = GetStation(w->window_number);
00938
00939 PlayerID owner = (st->owner == OWNER_NONE) ? _current_player : st->owner;
00940 ShowVehicleListWindow(owner, VEH_SHIP, (StationID)w->window_number);
00941 break;
00942 }
00943 }
00944 break;
00945
00946 case WE_ON_EDIT_TEXT:
00947 if (e->we.edittext.str[0] != '\0') {
00948 _cmd_text = e->we.edittext.str;
00949 DoCommandP(0, w->window_number, 0, NULL,
00950 CMD_RENAME_STATION | CMD_MSG(STR_3031_CAN_T_RENAME_STATION));
00951 }
00952 break;
00953
00954 case WE_DESTROY: {
00955 WindowNumber wno =
00956 (w->window_number << 16) | VLW_STATION_LIST | GetStation(w->window_number)->owner;
00957
00958 DeleteWindowById(WC_TRAINS_LIST, wno | (VEH_TRAIN << 11));
00959 DeleteWindowById(WC_ROADVEH_LIST, wno | (VEH_ROAD << 11));
00960 DeleteWindowById(WC_SHIPS_LIST, wno | (VEH_SHIP << 11));
00961 DeleteWindowById(WC_AIRCRAFT_LIST, wno | (VEH_AIRCRAFT << 11));
00962 break;
00963 }
00964
00965 case WE_RESIZE:
00966 if (e->we.sizing.diff.x != 0) ResizeButtons(w, SVW_LOCATION, SVW_RENAME);
00967 w->vscroll.cap += e->we.sizing.diff.y / (int)w->resize.step_height;
00968 break;
00969 }
00970 }
00971
00972
00973 static const WindowDesc _station_view_desc = {
00974 WDP_AUTO, WDP_AUTO, 249, 110, 249, 110,
00975 WC_STATION_VIEW, WC_NONE,
00976 WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00977 _station_view_widgets,
00978 StationViewWndProc
00979 };
00980
00986 void ShowStationViewWindow(StationID station)
00987 {
00988 Window *w = AllocateWindowDescFront(&_station_view_desc, station);
00989 if (w == NULL) return;
00990
00991 PlayerID owner = GetStation(w->window_number)->owner;
00992 if (owner != OWNER_NONE) w->caption_color = owner;
00993 w->vscroll.cap = 5;
00994 w->resize.step_height = 10;
00995 }