00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "debug.h"
00014 #include "gui.h"
00015 #include "textbuf_gui.h"
00016 #include "company_func.h"
00017 #include "command_func.h"
00018 #include "vehicle_gui.h"
00019 #include "cargotype.h"
00020 #include "station_gui.h"
00021 #include "strings_func.h"
00022 #include "window_func.h"
00023 #include "viewport_func.h"
00024 #include "widgets/dropdown_func.h"
00025 #include "station_base.h"
00026 #include "waypoint_base.h"
00027 #include "tilehighlight_func.h"
00028 #include "company_base.h"
00029 #include "sortlist_type.h"
00030 #include "core/geometry_func.hpp"
00031 #include "vehiclelist.h"
00032 #include "town.h"
00033
00034 #include "widgets/station_widget.h"
00035
00036 #include "table/strings.h"
00037
00038 #include <set>
00039 #include <vector>
00040
00051 int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies)
00052 {
00053 TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
00054 uint32 cargo_mask = 0;
00055 if (_thd.drawstyle == HT_RECT && tile < MapSize()) {
00056 CargoArray cargoes;
00057 if (supplies) {
00058 cargoes = GetProductionAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00059 } else {
00060 cargoes = GetAcceptanceAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00061 }
00062
00063
00064 for (CargoID i = 0; i < NUM_CARGO; i++) {
00065 switch (sct) {
00066 case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00067 case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00068 case SCT_ALL: break;
00069 default: NOT_REACHED();
00070 }
00071 if (cargoes[i] >= (supplies ? 1U : 8U)) SetBit(cargo_mask, i);
00072 }
00073 }
00074 SetDParam(0, cargo_mask);
00075 return DrawStringMultiLine(left, right, top, INT32_MAX, supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO);
00076 }
00077
00083 void CheckRedrawStationCoverage(const Window *w)
00084 {
00085 if (_thd.dirty & 1) {
00086 _thd.dirty &= ~1;
00087 w->SetDirty();
00088 }
00089 }
00090
00106 static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating)
00107 {
00108 static const uint units_full = 576;
00109 static const uint rating_full = 224;
00110
00111 const CargoSpec *cs = CargoSpec::Get(type);
00112 if (!cs->IsValid()) return;
00113
00114 int colour = cs->rating_colour;
00115 TextColour tc = GetContrastColour(colour);
00116 uint w = (minu(amount, units_full) + 5) / 36;
00117
00118 int height = GetCharacterHeight(FS_SMALL);
00119
00120
00121 if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour);
00122
00123
00124
00125 if (w == 0) {
00126 uint rest = amount / 5;
00127 if (rest != 0) {
00128 w += left;
00129 GfxFillRect(w, y + height - rest, w, y + height, colour);
00130 }
00131 }
00132
00133 DrawString(left + 1, right, y, cs->abbrev, tc);
00134
00135
00136 y += height + 2;
00137 GfxFillRect(left + 1, y, left + 14, y, PC_RED);
00138 rating = minu(rating, rating_full) / 16;
00139 if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, PC_GREEN);
00140 }
00141
00142 typedef GUIList<const Station*> GUIStationList;
00143
00147 class CompanyStationsWindow : public Window
00148 {
00149 protected:
00150
00151 static Listing last_sorting;
00152 static byte facilities;
00153 static bool include_empty;
00154 static const uint32 cargo_filter_max;
00155 static uint32 cargo_filter;
00156 static const Station *last_station;
00157
00158
00159 static const StringID sorter_names[];
00160 static GUIStationList::SortFunction * const sorter_funcs[];
00161
00162 GUIStationList stations;
00163 Scrollbar *vscroll;
00164
00170 void BuildStationsList(const Owner owner)
00171 {
00172 if (!this->stations.NeedRebuild()) return;
00173
00174 DEBUG(misc, 3, "Building station list for company %d", owner);
00175
00176 this->stations.Clear();
00177
00178 const Station *st;
00179 FOR_ALL_STATIONS(st) {
00180 if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, true, owner))) {
00181 if (this->facilities & st->facilities) {
00182 int num_waiting_cargo = 0;
00183 for (CargoID j = 0; j < NUM_CARGO; j++) {
00184 if (HasBit(st->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) {
00185 num_waiting_cargo++;
00186 if (HasBit(this->cargo_filter, j)) {
00187 *this->stations.Append() = st;
00188 break;
00189 }
00190 }
00191 }
00192
00193 if (num_waiting_cargo == 0 && this->include_empty) {
00194 *this->stations.Append() = st;
00195 }
00196 }
00197 }
00198 }
00199
00200 this->stations.Compact();
00201 this->stations.RebuildDone();
00202
00203 this->vscroll->SetCount(this->stations.Length());
00204 }
00205
00207 static int CDECL StationNameSorter(const Station * const *a, const Station * const *b)
00208 {
00209 static char buf_cache[64];
00210 char buf[64];
00211
00212 SetDParam(0, (*a)->index);
00213 GetString(buf, STR_STATION_NAME, lastof(buf));
00214
00215 if (*b != last_station) {
00216 last_station = *b;
00217 SetDParam(0, (*b)->index);
00218 GetString(buf_cache, STR_STATION_NAME, lastof(buf_cache));
00219 }
00220
00221 return strcmp(buf, buf_cache);
00222 }
00223
00225 static int CDECL StationTypeSorter(const Station * const *a, const Station * const *b)
00226 {
00227 return (*a)->facilities - (*b)->facilities;
00228 }
00229
00231 static int CDECL StationWaitingSorter(const Station * const *a, const Station * const *b)
00232 {
00233 Money diff = 0;
00234
00235 CargoID j;
00236 FOR_EACH_SET_CARGO_ID(j, cargo_filter) {
00237 if (!(*a)->goods[j].cargo.Empty()) diff += GetTransportedGoodsIncome((*a)->goods[j].cargo.Count(), 20, 50, j);
00238 if (!(*b)->goods[j].cargo.Empty()) diff -= GetTransportedGoodsIncome((*b)->goods[j].cargo.Count(), 20, 50, j);
00239 }
00240
00241 return ClampToI32(diff);
00242 }
00243
00245 static int CDECL StationRatingMaxSorter(const Station * const *a, const Station * const *b)
00246 {
00247 byte maxr1 = 0;
00248 byte maxr2 = 0;
00249
00250 CargoID j;
00251 FOR_EACH_SET_CARGO_ID(j, cargo_filter) {
00252 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) maxr1 = max(maxr1, (*a)->goods[j].rating);
00253 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) maxr2 = max(maxr2, (*b)->goods[j].rating);
00254 }
00255
00256 return maxr1 - maxr2;
00257 }
00258
00260 static int CDECL StationRatingMinSorter(const Station * const *a, const Station * const *b)
00261 {
00262 byte minr1 = 255;
00263 byte minr2 = 255;
00264
00265 for (CargoID j = 0; j < NUM_CARGO; j++) {
00266 if (!HasBit(cargo_filter, j)) continue;
00267 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) minr1 = min(minr1, (*a)->goods[j].rating);
00268 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) minr2 = min(minr2, (*b)->goods[j].rating);
00269 }
00270
00271 return -(minr1 - minr2);
00272 }
00273
00275 void SortStationsList()
00276 {
00277 if (!this->stations.Sort()) return;
00278
00279
00280 this->last_station = NULL;
00281
00282
00283 this->SetWidgetDirty(WID_STL_LIST);
00284 }
00285
00286 public:
00287 CompanyStationsWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
00288 {
00289 this->stations.SetListing(this->last_sorting);
00290 this->stations.SetSortFuncs(this->sorter_funcs);
00291 this->stations.ForceRebuild();
00292 this->stations.NeedResort();
00293 this->SortStationsList();
00294
00295 this->CreateNestedTree(desc);
00296 this->vscroll = this->GetScrollbar(WID_STL_SCROLLBAR);
00297 this->FinishInitNested(desc, window_number);
00298 this->owner = (Owner)this->window_number;
00299
00300 const CargoSpec *cs;
00301 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
00302 if (!HasBit(this->cargo_filter, cs->Index())) continue;
00303 this->LowerWidget(WID_STL_CARGOSTART + index);
00304 }
00305
00306 if (this->cargo_filter == this->cargo_filter_max) this->cargo_filter = _cargo_mask;
00307
00308 for (uint i = 0; i < 5; i++) {
00309 if (HasBit(this->facilities, i)) this->LowerWidget(i + WID_STL_TRAIN);
00310 }
00311 this->SetWidgetLoweredState(WID_STL_NOCARGOWAITING, this->include_empty);
00312
00313 this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00314 }
00315
00316 ~CompanyStationsWindow()
00317 {
00318 this->last_sorting = this->stations.GetListing();
00319 }
00320
00321 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00322 {
00323 switch (widget) {
00324 case WID_STL_SORTBY: {
00325 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
00326 d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2;
00327 d.height += padding.height;
00328 *size = maxdim(*size, d);
00329 break;
00330 }
00331
00332 case WID_STL_SORTDROPBTN: {
00333 Dimension d = {0, 0};
00334 for (int i = 0; this->sorter_names[i] != INVALID_STRING_ID; i++) {
00335 d = maxdim(d, GetStringBoundingBox(this->sorter_names[i]));
00336 }
00337 d.width += padding.width;
00338 d.height += padding.height;
00339 *size = maxdim(*size, d);
00340 break;
00341 }
00342
00343 case WID_STL_LIST:
00344 resize->height = FONT_HEIGHT_NORMAL;
00345 size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
00346 break;
00347
00348 case WID_STL_TRAIN:
00349 case WID_STL_TRUCK:
00350 case WID_STL_BUS:
00351 case WID_STL_AIRPLANE:
00352 case WID_STL_SHIP:
00353 size->height = max<uint>(FONT_HEIGHT_SMALL, 10) + padding.height;
00354 break;
00355
00356 case WID_STL_CARGOALL:
00357 case WID_STL_FACILALL:
00358 case WID_STL_NOCARGOWAITING: {
00359 Dimension d = GetStringBoundingBox(widget == WID_STL_NOCARGOWAITING ? STR_ABBREV_NONE : STR_ABBREV_ALL);
00360 d.width += padding.width + 2;
00361 d.height += padding.height;
00362 *size = maxdim(*size, d);
00363 break;
00364 }
00365
00366 default:
00367 if (widget >= WID_STL_CARGOSTART) {
00368 Dimension d = GetStringBoundingBox(_sorted_cargo_specs[widget - WID_STL_CARGOSTART]->abbrev);
00369 d.width += padding.width + 2;
00370 d.height += padding.height;
00371 *size = maxdim(*size, d);
00372 }
00373 break;
00374 }
00375 }
00376
00377 virtual void OnPaint()
00378 {
00379 this->BuildStationsList((Owner)this->window_number);
00380 this->SortStationsList();
00381
00382 this->DrawWidgets();
00383 }
00384
00385 virtual void DrawWidget(const Rect &r, int widget) const
00386 {
00387 switch (widget) {
00388 case WID_STL_SORTBY:
00389
00390 this->DrawSortButtonState(WID_STL_SORTBY, this->stations.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
00391 break;
00392
00393 case WID_STL_LIST: {
00394 bool rtl = _current_text_dir == TD_RTL;
00395 int max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->stations.Length());
00396 int y = r.top + WD_FRAMERECT_TOP;
00397 for (int i = this->vscroll->GetPosition(); i < max; ++i) {
00398 const Station *st = this->stations[i];
00399 assert(st->xy != INVALID_TILE);
00400
00401
00402
00403 assert(st->owner == owner || st->owner == OWNER_NONE);
00404
00405 SetDParam(0, st->index);
00406 SetDParam(1, st->facilities);
00407 int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION);
00408 x += rtl ? -5 : 5;
00409
00410
00411 for (uint j = 0; j < _sorted_standard_cargo_specs_size; j++) {
00412 CargoID cid = _sorted_cargo_specs[j]->Index();
00413 if (!st->goods[cid].cargo.Empty()) {
00414
00415
00416
00417
00418 if (rtl) {
00419 x -= 20;
00420 if (x < r.left + WD_FRAMERECT_LEFT) break;
00421 }
00422 StationsWndShowStationRating(x, x + 16, y, cid, st->goods[cid].cargo.Count(), st->goods[cid].rating);
00423 if (!rtl) {
00424 x += 20;
00425 if (x > r.right - WD_FRAMERECT_RIGHT) break;
00426 }
00427 }
00428 }
00429 y += FONT_HEIGHT_NORMAL;
00430 }
00431
00432 if (this->vscroll->GetCount() == 0) {
00433 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_NONE);
00434 return;
00435 }
00436 break;
00437 }
00438
00439 case WID_STL_NOCARGOWAITING: {
00440 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00441 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER);
00442 break;
00443 }
00444
00445 case WID_STL_CARGOALL: {
00446 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00447 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER);
00448 break;
00449 }
00450
00451 case WID_STL_FACILALL: {
00452 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00453 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00454 break;
00455 }
00456
00457 default:
00458 if (widget >= WID_STL_CARGOSTART) {
00459 const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART];
00460 int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 2 : 1;
00461 GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour);
00462 TextColour tc = GetContrastColour(cs->rating_colour);
00463 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, cs->abbrev, tc, SA_HOR_CENTER);
00464 }
00465 break;
00466 }
00467 }
00468
00469 virtual void SetStringParameters(int widget) const
00470 {
00471 if (widget == WID_STL_CAPTION) {
00472 SetDParam(0, this->window_number);
00473 SetDParam(1, this->vscroll->GetCount());
00474 }
00475 }
00476
00477 virtual void OnClick(Point pt, int widget, int click_count)
00478 {
00479 switch (widget) {
00480 case WID_STL_LIST: {
00481 uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_STL_LIST, 0, FONT_HEIGHT_NORMAL);
00482 if (id_v >= this->stations.Length()) return;
00483
00484 const Station *st = this->stations[id_v];
00485
00486 assert(st->owner == (Owner)this->window_number || st->owner == OWNER_NONE);
00487
00488 if (_ctrl_pressed) {
00489 ShowExtraViewPortWindow(st->xy);
00490 } else {
00491 ScrollMainWindowToTile(st->xy);
00492 }
00493 break;
00494 }
00495
00496 case WID_STL_TRAIN:
00497 case WID_STL_TRUCK:
00498 case WID_STL_BUS:
00499 case WID_STL_AIRPLANE:
00500 case WID_STL_SHIP:
00501 if (_ctrl_pressed) {
00502 ToggleBit(this->facilities, widget - WID_STL_TRAIN);
00503 this->ToggleWidgetLoweredState(widget);
00504 } else {
00505 uint i;
00506 FOR_EACH_SET_BIT(i, this->facilities) {
00507 this->RaiseWidget(i + WID_STL_TRAIN);
00508 }
00509 this->facilities = 1 << (widget - WID_STL_TRAIN);
00510 this->LowerWidget(widget);
00511 }
00512 this->stations.ForceRebuild();
00513 this->SetDirty();
00514 break;
00515
00516 case WID_STL_FACILALL:
00517 for (uint i = WID_STL_TRAIN; i <= WID_STL_SHIP; i++) {
00518 this->LowerWidget(i);
00519 }
00520
00521 this->facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00522 this->stations.ForceRebuild();
00523 this->SetDirty();
00524 break;
00525
00526 case WID_STL_CARGOALL: {
00527 for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) {
00528 this->LowerWidget(WID_STL_CARGOSTART + i);
00529 }
00530 this->LowerWidget(WID_STL_NOCARGOWAITING);
00531
00532 this->cargo_filter = _cargo_mask;
00533 this->include_empty = true;
00534 this->stations.ForceRebuild();
00535 this->SetDirty();
00536 break;
00537 }
00538
00539 case WID_STL_SORTBY:
00540 this->stations.ToggleSortOrder();
00541 this->SetDirty();
00542 break;
00543
00544 case WID_STL_SORTDROPBTN:
00545 ShowDropDownMenu(this, this->sorter_names, this->stations.SortType(), WID_STL_SORTDROPBTN, 0, 0);
00546 break;
00547
00548 case WID_STL_NOCARGOWAITING:
00549 if (_ctrl_pressed) {
00550 this->include_empty = !this->include_empty;
00551 this->ToggleWidgetLoweredState(WID_STL_NOCARGOWAITING);
00552 } else {
00553 for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) {
00554 this->RaiseWidget(WID_STL_CARGOSTART + i);
00555 }
00556
00557 this->cargo_filter = 0;
00558 this->include_empty = true;
00559
00560 this->LowerWidget(WID_STL_NOCARGOWAITING);
00561 }
00562 this->stations.ForceRebuild();
00563 this->SetDirty();
00564 break;
00565
00566 default:
00567 if (widget >= WID_STL_CARGOSTART) {
00568
00569 const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART];
00570
00571 if (_ctrl_pressed) {
00572 ToggleBit(this->cargo_filter, cs->Index());
00573 this->ToggleWidgetLoweredState(widget);
00574 } else {
00575 for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) {
00576 this->RaiseWidget(WID_STL_CARGOSTART + i);
00577 }
00578 this->RaiseWidget(WID_STL_NOCARGOWAITING);
00579
00580 this->cargo_filter = 0;
00581 this->include_empty = false;
00582
00583 SetBit(this->cargo_filter, cs->Index());
00584 this->LowerWidget(widget);
00585 }
00586 this->stations.ForceRebuild();
00587 this->SetDirty();
00588 }
00589 break;
00590 }
00591 }
00592
00593 virtual void OnDropdownSelect(int widget, int index)
00594 {
00595 if (this->stations.SortType() != index) {
00596 this->stations.SetSortType(index);
00597
00598
00599 this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00600
00601 this->SetDirty();
00602 }
00603 }
00604
00605 virtual void OnTick()
00606 {
00607 if (_pause_mode != PM_UNPAUSED) return;
00608 if (this->stations.NeedResort()) {
00609 DEBUG(misc, 3, "Periodic rebuild station list company %d", this->window_number);
00610 this->SetDirty();
00611 }
00612 }
00613
00614 virtual void OnResize()
00615 {
00616 this->vscroll->SetCapacityFromWidget(this, WID_STL_LIST, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
00617 }
00618
00624 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00625 {
00626 if (data == 0) {
00627
00628 this->stations.ForceRebuild();
00629 } else {
00630 this->stations.ForceResort();
00631 }
00632 }
00633 };
00634
00635 Listing CompanyStationsWindow::last_sorting = {false, 0};
00636 byte CompanyStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00637 bool CompanyStationsWindow::include_empty = true;
00638 const uint32 CompanyStationsWindow::cargo_filter_max = UINT32_MAX;
00639 uint32 CompanyStationsWindow::cargo_filter = UINT32_MAX;
00640 const Station *CompanyStationsWindow::last_station = NULL;
00641
00642
00643 GUIStationList::SortFunction * const CompanyStationsWindow::sorter_funcs[] = {
00644 &StationNameSorter,
00645 &StationTypeSorter,
00646 &StationWaitingSorter,
00647 &StationRatingMaxSorter,
00648 &StationRatingMinSorter
00649 };
00650
00651
00652 const StringID CompanyStationsWindow::sorter_names[] = {
00653 STR_SORT_BY_NAME,
00654 STR_SORT_BY_FACILITY,
00655 STR_SORT_BY_WAITING,
00656 STR_SORT_BY_RATING_MAX,
00657 STR_SORT_BY_RATING_MIN,
00658 INVALID_STRING_ID
00659 };
00660
00666 static NWidgetBase *CargoWidgets(int *biggest_index)
00667 {
00668 NWidgetHorizontal *container = new NWidgetHorizontal();
00669
00670 for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) {
00671 NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_STL_CARGOSTART + i);
00672 panel->SetMinimalSize(14, 11);
00673 panel->SetResize(0, 0);
00674 panel->SetFill(0, 1);
00675 panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE);
00676 container->Add(panel);
00677 }
00678 *biggest_index = WID_STL_CARGOSTART + _sorted_standard_cargo_specs_size;
00679 return container;
00680 }
00681
00682 static const NWidgetPart _nested_company_stations_widgets[] = {
00683 NWidget(NWID_HORIZONTAL),
00684 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00685 NWidget(WWT_CAPTION, COLOUR_GREY, WID_STL_CAPTION), SetDataTip(STR_STATION_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00686 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00687 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00688 EndContainer(),
00689 NWidget(NWID_HORIZONTAL),
00690 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRAIN), SetMinimalSize(14, 11), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00691 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_TRUCK), SetMinimalSize(14, 11), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00692 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_BUS), SetMinimalSize(14, 11), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00693 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_SHIP), SetMinimalSize(14, 11), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00694 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_AIRPLANE), SetMinimalSize(14, 11), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00695 NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1),
00696 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(),
00697 NWidgetFunction(CargoWidgets),
00698 NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(),
00699 NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1),
00700 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00701 EndContainer(),
00702 NWidget(NWID_HORIZONTAL),
00703 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_STL_SORTBY), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
00704 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_STL_SORTDROPBTN), SetMinimalSize(163, 12), SetDataTip(STR_SORT_BY_NAME, STR_TOOLTIP_SORT_CRITERIA),
00705 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00706 EndContainer(),
00707 NWidget(NWID_HORIZONTAL),
00708 NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_LIST), SetMinimalSize(346, 125), SetResize(1, 10), SetDataTip(0x0, STR_STATION_LIST_TOOLTIP), SetScrollbar(WID_STL_SCROLLBAR), EndContainer(),
00709 NWidget(NWID_VERTICAL),
00710 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_STL_SCROLLBAR),
00711 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00712 EndContainer(),
00713 EndContainer(),
00714 };
00715
00716 static const WindowDesc _company_stations_desc(
00717 WDP_AUTO, 358, 162,
00718 WC_STATION_LIST, WC_NONE,
00719 0,
00720 _nested_company_stations_widgets, lengthof(_nested_company_stations_widgets)
00721 );
00722
00728 void ShowCompanyStations(CompanyID company)
00729 {
00730 if (!Company::IsValidID(company)) return;
00731
00732 AllocateWindowDescFront<CompanyStationsWindow>(&_company_stations_desc, company);
00733 }
00734
00735 static const NWidgetPart _nested_station_view_widgets[] = {
00736 NWidget(NWID_HORIZONTAL),
00737 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00738 NWidget(WWT_CAPTION, COLOUR_GREY, WID_SV_CAPTION), SetDataTip(STR_STATION_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00739 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00740 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00741 EndContainer(),
00742 NWidget(NWID_HORIZONTAL),
00743 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_SORT_ORDER), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
00744 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SV_SORT_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
00745 EndContainer(),
00746 NWidget(NWID_HORIZONTAL),
00747 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SV_GROUP), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_STATION_VIEW_GROUP, 0x0),
00748 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SV_GROUP_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_GROUP_ORDER),
00749 EndContainer(),
00750 NWidget(NWID_HORIZONTAL),
00751 NWidget(WWT_PANEL, COLOUR_GREY, WID_SV_WAITING), SetMinimalSize(237, 44), SetResize(1, 10), SetScrollbar(WID_SV_SCROLLBAR), EndContainer(),
00752 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SV_SCROLLBAR),
00753 EndContainer(),
00754 NWidget(WWT_PANEL, COLOUR_GREY, WID_SV_ACCEPT_RATING_LIST), SetMinimalSize(249, 23), SetResize(1, 0), EndContainer(),
00755 NWidget(NWID_HORIZONTAL),
00756 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
00757 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_LOCATION), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
00758 SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP),
00759 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ACCEPTS_RATINGS), SetMinimalSize(46, 12), SetResize(1, 0), SetFill(1, 1),
00760 SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP),
00761 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_RENAME), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
00762 SetDataTip(STR_BUTTON_RENAME, STR_STATION_VIEW_RENAME_TOOLTIP),
00763 EndContainer(),
00764 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SV_CLOSE_AIRPORT), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
00765 SetDataTip(STR_STATION_VIEW_CLOSE_AIRPORT, STR_STATION_VIEW_CLOSE_AIRPORT_TOOLTIP),
00766 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_TRAINS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP),
00767 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ROADVEHS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP),
00768 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_SHIPS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_SHIP, STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP),
00769 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_PLANES), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_PLANE, STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP),
00770 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00771 EndContainer(),
00772 };
00773
00784 static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
00785 {
00786 uint num = min((waiting + 5) / 10, (right - left) / 10);
00787 if (num == 0) return;
00788
00789 SpriteID sprite = CargoSpec::Get(i)->GetCargoIcon();
00790
00791 int x = _current_text_dir == TD_RTL ? left : right - num * 10;
00792 do {
00793 DrawSprite(sprite, PAL_NONE, x, y);
00794 x += 10;
00795 } while (--num);
00796 }
00797
00798 enum SortOrder {
00799 SO_DESCENDING,
00800 SO_ASCENDING
00801 };
00802
00803 class CargoDataEntry;
00804
00805 enum CargoSortType {
00806 ST_AS_GROUPING,
00807 ST_COUNT,
00808 ST_STATION_STRING,
00809 ST_STATION_ID,
00810 ST_CARGO_ID,
00811 };
00812
00817 class CargoSorter {
00818 public:
00819 CargoSorter(CargoSortType t = ST_STATION_ID, SortOrder o = SO_ASCENDING) : type(t), order(o) {}
00820 CargoSortType GetSortType() {return this->type;}
00821 bool operator()(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const;
00822
00823 private:
00824 CargoSortType type;
00825 SortOrder order;
00826
00827 template<class Tid>
00828 bool SortId(Tid st1, Tid st2) const;
00829 bool SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const;
00830 bool SortStation (StationID st1, StationID st2) const;
00831 };
00832
00833 typedef std::set<CargoDataEntry *, CargoSorter> CargoDataSet;
00834
00844 class CargoAndTransfer {
00845 CargoID cargo;
00846 bool transfer;
00847 public:
00848 operator CargoID() const { return this->cargo; }
00849
00856 CargoID operator=(CargoID cargo)
00857 {
00858 this->transfer = false;
00859 return this->cargo = cargo;
00860 }
00861
00862 bool HasTransfer() const { return this->transfer; }
00863 void SetTransfer(bool transfer) { this->transfer = transfer; }
00864 };
00865
00871 class CargoDataEntry {
00872 public:
00873 CargoDataEntry() : parent(NULL), station(INVALID_STATION),
00874 num_children(0), count(0),
00875 children(new CargoDataSet(CargoSorter(ST_CARGO_ID))) {}
00876
00877 ~CargoDataEntry()
00878 {
00879 this->Clear();
00880 delete this->children;
00881 }
00882
00888 CargoDataEntry *InsertOrRetrieve(StationID station)
00889 {
00890 return this->InsertOrRetrieve<StationID>(station);
00891 }
00892
00898 CargoDataEntry *InsertOrRetrieve(CargoID cargo)
00899 {
00900 return this->InsertOrRetrieve<CargoID>(cargo);
00901 }
00902
00903 void Update(uint count);
00904
00909 void Remove(StationID station)
00910 {
00911 CargoDataEntry t(station);
00912 this->Remove(&t);
00913 }
00914
00919 void Remove(CargoID cargo)
00920 {
00921 CargoDataEntry t(cargo);
00922 this->Remove(&t);
00923 }
00924
00930 CargoDataEntry *Retrieve(StationID station) const
00931 {
00932 CargoDataEntry t(station);
00933 return this->Retrieve(this->children->find(&t));
00934 }
00935
00941 CargoDataEntry *Retrieve(CargoID cargo) const
00942 {
00943 CargoDataEntry t(cargo);
00944 return this->Retrieve(this->children->find(&t));
00945 }
00946
00947 void Resort(CargoSortType type, SortOrder order);
00948
00952 StationID GetStation() const { return this->station; }
00953
00957 CargoID GetCargo() const { return this->cargo; }
00958
00962 uint GetCount() const { return this->count; }
00963
00967 CargoDataEntry *GetParent() const { return this->parent; }
00968
00972 uint GetNumChildren() const { return this->num_children; }
00973
00977 CargoDataSet::iterator Begin() const { return this->children->begin(); }
00978
00982 CargoDataSet::iterator End() const { return this->children->end(); }
00983
00989 bool HasTransfer() const { return this->cargo.HasTransfer(); }
00990
00997 void SetTransfer(bool transfer) { this->cargo.SetTransfer(transfer); }
00998
00999 void Clear();
01000 private:
01001
01002 CargoDataEntry(StationID station, uint count, CargoDataEntry *parent) :
01003 parent(parent), station(station), num_children(0), count(count),
01004 children(new CargoDataSet) {}
01005
01006 CargoDataEntry(CargoID cargo, uint count, CargoDataEntry *parent) :
01007 parent(parent), num_children(0), count(count), children(new CargoDataSet)
01008 {
01009 this->cargo = cargo;
01010 }
01011
01012 CargoDataEntry(StationID station) : parent(NULL), station(station),
01013 num_children(0), count(0), children(NULL) {}
01014
01015 CargoDataEntry(CargoID cargo) : parent(NULL), num_children(0),
01016 count(0), children(NULL)
01017 {
01018 this->cargo = cargo;
01019 }
01020
01021 CargoDataEntry *Retrieve(CargoDataSet::iterator i) const;
01022
01023 template<class Tid>
01024 CargoDataEntry *InsertOrRetrieve(Tid s);
01025
01026 void Remove(CargoDataEntry *comp);
01027 void IncrementSize();
01028
01029 CargoDataEntry *parent;
01030 const union {
01031 StationID station;
01032 CargoAndTransfer cargo;
01033 };
01034 uint num_children;
01035 uint count;
01036 CargoDataSet *children;
01037 };
01038
01042 void CargoDataEntry::Clear()
01043 {
01044 if (this->children != NULL) {
01045 for (CargoDataSet::iterator i = this->children->begin(); i != this->children->end(); ++i) {
01046 assert(*i != this);
01047 delete *i;
01048 }
01049 this->children->clear();
01050 }
01051 if (this->parent != NULL) this->parent->count -= this->count;
01052 this->count = 0;
01053 this->num_children = 0;
01054 }
01055
01062 void CargoDataEntry::Remove(CargoDataEntry *child)
01063 {
01064 CargoDataSet::iterator i = this->children->find(child);
01065 if (i != this->children->end()) {
01066 delete *i;
01067 this->children->erase(i);
01068 }
01069 }
01070
01077 template<class Tid>
01078 CargoDataEntry *CargoDataEntry::InsertOrRetrieve(Tid child_id)
01079 {
01080 CargoDataEntry tmp(child_id);
01081 CargoDataSet::iterator i = this->children->find(&tmp);
01082 if (i == this->children->end()) {
01083 this->IncrementSize();
01084 return *(this->children->insert(new CargoDataEntry(child_id, 0, this)).first);
01085 } else {
01086 CargoDataEntry *ret = *i;
01087 assert(this->children->value_comp().GetSortType() != ST_COUNT);
01088 return ret;
01089 }
01090 }
01091
01097 void CargoDataEntry::Update(uint count)
01098 {
01099 this->count += count;
01100 if (this->parent != NULL) this->parent->Update(count);
01101 }
01102
01106 void CargoDataEntry::IncrementSize()
01107 {
01108 ++this->num_children;
01109 if (this->parent != NULL) this->parent->IncrementSize();
01110 }
01111
01117 void CargoDataEntry::Resort(CargoSortType type, SortOrder order)
01118 {
01119 CargoDataSet *new_subs = new CargoDataSet(this->children->begin(), this->children->end(), CargoSorter(type, order));
01120 delete this->children;
01121 this->children = new_subs;
01122 }
01123
01130 CargoDataEntry *CargoDataEntry::Retrieve(CargoDataSet::iterator i) const
01131 {
01132 if (i == this->children->end()) {
01133 return NULL;
01134 } else {
01135 assert(this->children->value_comp().GetSortType() != ST_COUNT);
01136 return *i;
01137 }
01138 }
01139
01146 bool CargoSorter::operator()(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const
01147 {
01148 switch (this->type) {
01149 case ST_STATION_ID:
01150 return this->SortId<StationID>(cd1->GetStation(), cd2->GetStation());
01151 case ST_CARGO_ID:
01152 return this->SortId<CargoID>(cd1->GetCargo(), cd2->GetCargo());
01153 case ST_COUNT:
01154 return this->SortCount(cd1, cd2);
01155 case ST_STATION_STRING:
01156 return this->SortStation(cd1->GetStation(), cd2->GetStation());
01157 default:
01158 NOT_REACHED();
01159 }
01160 }
01161
01169 template<class Tid>
01170 bool CargoSorter::SortId(Tid id1, Tid id2) const
01171 {
01172 return (this->order == SO_ASCENDING) ? id1 < id2 : id2 < id1;
01173 }
01174
01181 bool CargoSorter::SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const
01182 {
01183 uint c1 = cd1->GetCount();
01184 uint c2 = cd2->GetCount();
01185 if (c1 == c2) {
01186 return this->SortStation(cd1->GetStation(), cd2->GetStation());
01187 } else if (this->order == SO_ASCENDING) {
01188 return c1 < c2;
01189 } else {
01190 return c2 < c1;
01191 }
01192 }
01193
01200 bool CargoSorter::SortStation(StationID st1, StationID st2) const
01201 {
01202 static char buf1[MAX_LENGTH_STATION_NAME_CHARS];
01203 static char buf2[MAX_LENGTH_STATION_NAME_CHARS];
01204
01205 if (!Station::IsValidID(st1)) {
01206 return Station::IsValidID(st2) ? this->order == SO_ASCENDING : this->SortId(st1, st2);
01207 } else if (!Station::IsValidID(st2)) {
01208 return order == SO_DESCENDING;
01209 }
01210
01211 SetDParam(0, st1);
01212 GetString(buf1, STR_STATION_NAME, lastof(buf1));
01213 SetDParam(0, st2);
01214 GetString(buf2, STR_STATION_NAME, lastof(buf2));
01215
01216 int res = strcmp(buf1, buf2);
01217 if (res == 0) {
01218 return this->SortId(st1, st2);
01219 } else {
01220 return (this->order == SO_ASCENDING) ? res < 0 : res > 0;
01221 }
01222 }
01223
01227 struct StationViewWindow : public Window {
01231 struct RowDisplay {
01232 RowDisplay(CargoDataEntry *f, StationID n) : filter(f), next_station(n) {}
01233 RowDisplay(CargoDataEntry *f, CargoID n) : filter(f), next_cargo(n) {}
01234
01238 CargoDataEntry *filter;
01239 union {
01243 StationID next_station;
01244
01248 CargoID next_cargo;
01249 };
01250 };
01251
01252 typedef std::vector<RowDisplay> CargoDataVector;
01253
01254 static const int NUM_COLUMNS = 4;
01255
01259 enum Invalidation {
01260 INV_FLOWS = 0x100,
01261 INV_CARGO = 0x200
01262 };
01263
01267 enum Grouping {
01268 GR_SOURCE,
01269 GR_NEXT,
01270 GR_DESTINATION,
01271 GR_CARGO,
01272 };
01273
01277 enum Mode {
01278 MODE_WAITING,
01279 MODE_PLANNED
01280 };
01281
01282 uint expand_shrink_width;
01283 int rating_lines;
01284 int accepts_lines;
01285 Scrollbar *vscroll;
01286
01288 enum AcceptListHeight {
01289 ALH_RATING = 13,
01290 ALH_ACCEPTS = 3,
01291 };
01292
01293 static const StringID _sort_names[];
01294 static const StringID _group_names[];
01295
01302 CargoSortType sortings[NUM_COLUMNS];
01303
01305 SortOrder sort_orders[NUM_COLUMNS];
01306
01307 int scroll_to_row;
01308 int grouping_index;
01309 Mode current_mode;
01310 Grouping groupings[NUM_COLUMNS];
01311
01312 CargoDataEntry expanded_rows;
01313 CargoDataEntry cached_destinations;
01314 CargoDataVector displayed_rows;
01315
01316 StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(),
01317 scroll_to_row(INT_MAX), grouping_index(0)
01318 {
01319 this->rating_lines = ALH_RATING;
01320 this->accepts_lines = ALH_ACCEPTS;
01321
01322 this->CreateNestedTree(desc);
01323 this->vscroll = this->GetScrollbar(WID_SV_SCROLLBAR);
01324
01325 this->FinishInitNested(desc, window_number);
01326
01327 this->groupings[0] = GR_CARGO;
01328 this->sortings[0] = ST_AS_GROUPING;
01329 this->SelectGroupBy(_settings_client.gui.station_gui_group_order);
01330 this->SelectSortBy(_settings_client.gui.station_gui_sort_by);
01331 this->sort_orders[0] = SO_ASCENDING;
01332 this->SelectSortOrder((SortOrder)_settings_client.gui.station_gui_sort_order);
01333 Owner owner = Station::Get(window_number)->owner;
01334 if (owner != OWNER_NONE) this->owner = owner;
01335 }
01336
01337 ~StationViewWindow()
01338 {
01339 Owner owner = Station::Get(this->window_number)->owner;
01340 DeleteWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, owner, this->window_number).Pack(), false);
01341 DeleteWindowById(WC_ROADVEH_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_ROAD, owner, this->window_number).Pack(), false);
01342 DeleteWindowById(WC_SHIPS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_SHIP, owner, this->window_number).Pack(), false);
01343 DeleteWindowById(WC_AIRCRAFT_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_AIRCRAFT, owner, this->window_number).Pack(), false);
01344 }
01345
01357 void ShowCargo(CargoDataEntry *data, CargoID cargo, StationID source, StationID next, StationID dest, uint count)
01358 {
01359 if (count == 0) return;
01360 bool auto_distributed = _settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL;
01361 const CargoDataEntry *expand = &this->expanded_rows;
01362 for (int i = 0; i < NUM_COLUMNS && expand != NULL; ++i) {
01363 switch (groupings[i]) {
01364 case GR_CARGO:
01365 assert(i == 0);
01366 data = data->InsertOrRetrieve(cargo);
01367 data->SetTransfer(source != this->window_number);
01368 expand = expand->Retrieve(cargo);
01369 break;
01370 case GR_SOURCE:
01371 if (auto_distributed || source != this->window_number) {
01372 data = data->InsertOrRetrieve(source);
01373 expand = expand->Retrieve(source);
01374 }
01375 break;
01376 case GR_NEXT:
01377 if (auto_distributed) {
01378 data = data->InsertOrRetrieve(next);
01379 expand = expand->Retrieve(next);
01380 }
01381 break;
01382 case GR_DESTINATION:
01383 if (auto_distributed) {
01384 data = data->InsertOrRetrieve(dest);
01385 expand = expand->Retrieve(dest);
01386 }
01387 break;
01388 }
01389 }
01390 data->Update(count);
01391 }
01392
01393 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01394 {
01395 switch (widget) {
01396 case WID_SV_WAITING:
01397 resize->height = FONT_HEIGHT_NORMAL;
01398 size->height = WD_FRAMERECT_TOP + 4 * resize->height + WD_FRAMERECT_BOTTOM;
01399 this->expand_shrink_width = max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01400 break;
01401
01402 case WID_SV_ACCEPT_RATING_LIST:
01403 size->height = WD_FRAMERECT_TOP + ((this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) ? this->accepts_lines : this->rating_lines) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
01404 break;
01405
01406 case WID_SV_CLOSE_AIRPORT:
01407 if (!(Station::Get(this->window_number)->facilities & FACIL_AIRPORT)) {
01408
01409 size->width = 0;
01410 resize->width = 0;
01411 fill->width = 0;
01412 }
01413 break;
01414 }
01415 }
01416
01417 virtual void OnPaint()
01418 {
01419 const Station *st = Station::Get(this->window_number);
01420 CargoDataEntry cargo;
01421 BuildCargoList(&cargo, st);
01422
01423 this->vscroll->SetCount(cargo.GetNumChildren());
01424
01425
01426 this->SetWidgetDisabledState(WID_SV_RENAME, st->owner != _local_company);
01427 this->SetWidgetDisabledState(WID_SV_TRAINS, !(st->facilities & FACIL_TRAIN));
01428 this->SetWidgetDisabledState(WID_SV_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
01429 this->SetWidgetDisabledState(WID_SV_SHIPS, !(st->facilities & FACIL_DOCK));
01430 this->SetWidgetDisabledState(WID_SV_PLANES, !(st->facilities & FACIL_AIRPORT));
01431 this->SetWidgetDisabledState(WID_SV_CLOSE_AIRPORT, !(st->facilities & FACIL_AIRPORT) || st->owner != _local_company || st->owner == OWNER_NONE);
01432 this->SetWidgetLoweredState(WID_SV_CLOSE_AIRPORT, (st->facilities & FACIL_AIRPORT) && (st->airport.flags & AIRPORT_CLOSED_block) != 0);
01433
01434 this->DrawWidgets();
01435
01436 if (!this->IsShaded()) {
01437
01438 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SV_ACCEPT_RATING_LIST);
01439 const Rect r = {wid->pos_x, wid->pos_y, wid->pos_x + wid->current_x - 1, wid->pos_y + wid->current_y - 1};
01440 if (this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
01441 int lines = this->DrawAcceptedCargo(r);
01442 if (lines > this->accepts_lines) {
01443 this->accepts_lines = lines;
01444 this->ReInit();
01445 return;
01446 }
01447 } else {
01448 int lines = this->DrawCargoRatings(r);
01449 if (lines > this->rating_lines) {
01450 this->rating_lines = lines;
01451 this->ReInit();
01452 return;
01453 }
01454 }
01455
01456
01457 this->DrawSortButtonState(WID_SV_SORT_ORDER, sort_orders[1] == SO_ASCENDING ? SBS_UP : SBS_DOWN);
01458
01459 int pos = this->vscroll->GetPosition();
01460
01461 int maxrows = this->vscroll->GetCapacity();
01462
01463 displayed_rows.clear();
01464
01465
01466 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_SV_WAITING);
01467 Rect waiting_rect = {nwi->pos_x, nwi->pos_y, nwi->pos_x + nwi->current_x - 1, nwi->pos_y + nwi->current_y - 1};
01468 this->DrawEntries(&cargo, waiting_rect, pos, maxrows, 0);
01469 scroll_to_row = INT_MAX;
01470 }
01471 }
01472
01473 virtual void SetStringParameters(int widget) const
01474 {
01475 const Station *st = Station::Get(this->window_number);
01476 SetDParam(0, st->index);
01477 SetDParam(1, st->facilities);
01478 }
01479
01486 void RecalcDestinations(CargoID cargo)
01487 {
01488 const Station *st = Station::Get(this->window_number);
01489 CargoDataEntry *cargo_entry = cached_destinations.InsertOrRetrieve(cargo);
01490 cargo_entry->Clear();
01491
01492 const FlowStatMap &flows = st->goods[cargo].flows;
01493 for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
01494 StationID from = it->first;
01495 CargoDataEntry *source_entry = cargo_entry->InsertOrRetrieve(from);
01496 const FlowStat::SharesMap *shares = it->second.GetShares();
01497 uint32 prev_count = 0;
01498 for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) {
01499 StationID via = flow_it->second;
01500 CargoDataEntry *via_entry = source_entry->InsertOrRetrieve(via);
01501 if (via == this->window_number) {
01502 via_entry->InsertOrRetrieve(via)->Update(flow_it->first - prev_count);
01503 } else {
01504 EstimateDestinations(cargo, from, via, flow_it->first - prev_count, via_entry);
01505 }
01506 prev_count = flow_it->first;
01507 }
01508 }
01509 }
01510
01521 void EstimateDestinations(CargoID cargo, StationID source, StationID next, uint count, CargoDataEntry *data)
01522 {
01523 if (Station::IsValidID(next) && Station::IsValidID(source)) {
01524 CargoDataEntry tmp;
01525 const FlowStatMap &flowmap = Station::Get(next)->goods[cargo].flows;
01526 FlowStatMap::const_iterator map_it = flowmap.find(source);
01527 if (map_it != flowmap.end()) {
01528 const FlowStat::SharesMap *shares = map_it->second.GetShares();
01529 uint32 prev_count = 0;
01530 for (FlowStat::SharesMap::const_iterator i = shares->begin(); i != shares->end(); ++i) {
01531 tmp.InsertOrRetrieve(i->second)->Update(i->first - prev_count);
01532 prev_count = i->first;
01533 }
01534 }
01535
01536 if (tmp.GetCount() == 0) {
01537 data->InsertOrRetrieve(INVALID_STATION)->Update(count);
01538 } else {
01539 uint sum_estimated = 0;
01540 while (sum_estimated < count) {
01541 for (CargoDataSet::iterator i = tmp.Begin(); i != tmp.End() && sum_estimated < count; ++i) {
01542 CargoDataEntry *child = *i;
01543 uint estimate = DivideApprox(child->GetCount() * count, tmp.GetCount());
01544 if (estimate == 0) estimate = 1;
01545
01546 sum_estimated += estimate;
01547 if (sum_estimated > count) {
01548 estimate -= sum_estimated - count;
01549 sum_estimated = count;
01550 }
01551
01552 if (estimate > 0) {
01553 if (child->GetStation() == next) {
01554 data->InsertOrRetrieve(next)->Update(estimate);
01555 } else {
01556 EstimateDestinations(cargo, source, child->GetStation(), estimate, data);
01557 }
01558 }
01559 }
01560
01561 }
01562 }
01563 } else {
01564 data->InsertOrRetrieve(INVALID_STATION)->Update(count);
01565 }
01566 }
01567
01574 void BuildFlowList(CargoID cargo, const FlowStatMap &flows, CargoDataEntry *data)
01575 {
01576 const CargoDataEntry *source_dest = this->cached_destinations.Retrieve(cargo);
01577 for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
01578 StationID from = it->first;
01579 const CargoDataEntry *source_entry = source_dest->Retrieve(from);
01580 const FlowStat::SharesMap *shares = it->second.GetShares();
01581 for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) {
01582 const CargoDataEntry *via_entry = source_entry->Retrieve(flow_it->second);
01583 for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
01584 CargoDataEntry *dest_entry = *dest_it;
01585 ShowCargo(data, cargo, from, flow_it->second, dest_entry->GetStation(), dest_entry->GetCount());
01586 }
01587 }
01588 }
01589 }
01590
01597 void BuildCargoList(CargoID cargo, const StationCargoList &packets, CargoDataEntry *data)
01598 {
01599 const CargoDataEntry *source_dest = this->cached_destinations.Retrieve(cargo);
01600 for (StationCargoList::ConstIterator it = packets.Packets()->begin(); it != packets.Packets()->end(); it++) {
01601 const CargoPacket *cp = *it;
01602 StationID next = it.GetKey();
01603
01604 const CargoDataEntry *source_entry = source_dest->Retrieve(cp->SourceStation());
01605 if (source_entry == NULL) {
01606 this->ShowCargo(data, cargo, cp->SourceStation(), next, INVALID_STATION, cp->Count());
01607 continue;
01608 }
01609
01610 const CargoDataEntry *via_entry = source_entry->Retrieve(next);
01611 if (via_entry == NULL) {
01612 this->ShowCargo(data, cargo, cp->SourceStation(), next, INVALID_STATION, cp->Count());
01613 continue;
01614 }
01615
01616 for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
01617 CargoDataEntry *dest_entry = *dest_it;
01618 uint val = DivideApprox(cp->Count() * dest_entry->GetCount(), via_entry->GetCount());
01619 this->ShowCargo(data, cargo, cp->SourceStation(), next, dest_entry->GetStation(), val);
01620 }
01621 }
01622 this->ShowCargo(data, cargo, NEW_STATION, NEW_STATION, NEW_STATION, packets.ReservedCount());
01623 }
01624
01630 void BuildCargoList(CargoDataEntry *data, const Station *st)
01631 {
01632 for (CargoID i = 0; i < NUM_CARGO; i++) {
01633
01634 if (this->cached_destinations.Retrieve(i) == NULL) {
01635 this->RecalcDestinations(i);
01636 }
01637
01638 if (this->current_mode == MODE_WAITING) {
01639 this->BuildCargoList(i, st->goods[i].cargo, data);
01640 } else if (_settings_game.linkgraph.GetDistributionType(i) != DT_MANUAL){
01641 this->BuildFlowList(i, st->goods[i].flows, data);
01642 }
01643 }
01644 }
01645
01650 void SetDisplayedRow(const CargoDataEntry *data)
01651 {
01652 std::list<StationID> stations;
01653 const CargoDataEntry *parent = data->GetParent();
01654 if (parent->GetParent() == NULL) {
01655 this->displayed_rows.push_back(RowDisplay(&this->expanded_rows, data->GetCargo()));
01656 return;
01657 }
01658
01659 StationID next = data->GetStation();
01660 while (parent->GetParent()->GetParent() != NULL) {
01661 stations.push_back(parent->GetStation());
01662 parent = parent->GetParent();
01663 }
01664
01665 CargoID cargo = parent->GetCargo();
01666 CargoDataEntry *filter = this->expanded_rows.Retrieve(cargo);
01667 while (!stations.empty()) {
01668 filter = filter->Retrieve(stations.back());
01669 stations.pop_back();
01670 }
01671
01672 this->displayed_rows.push_back(RowDisplay(filter, next));
01673 }
01674
01683 StringID GetEntryString(StationID station, StringID here, StringID other_station, StringID any)
01684 {
01685 if (station == this->window_number) {
01686 return here;
01687 } else if (station == INVALID_STATION) {
01688 return any;
01689 } else if (station == NEW_STATION) {
01690 return STR_STATION_VIEW_RESERVED;
01691 } else {
01692 SetDParam(2, station);
01693 return other_station;
01694 }
01695 }
01696
01704 StringID SearchNonStop(CargoDataEntry *cd, StationID station, int column)
01705 {
01706 CargoDataEntry *parent = cd->GetParent();
01707 for (int i = column - 1; i > 0; --i) {
01708 if (this->groupings[i] == GR_DESTINATION) {
01709 if (parent->GetStation() == station) {
01710 return STR_STATION_VIEW_NONSTOP;
01711 } else {
01712 return STR_STATION_VIEW_VIA;
01713 }
01714 }
01715 parent = parent->GetParent();
01716 }
01717
01718 if (this->groupings[column + 1] == GR_DESTINATION) {
01719 CargoDataSet::iterator begin = cd->Begin();
01720 CargoDataSet::iterator end = cd->End();
01721 if (begin != end && ++(cd->Begin()) == end && (*(begin))->GetStation() == station) {
01722 return STR_STATION_VIEW_NONSTOP;
01723 } else {
01724 return STR_STATION_VIEW_VIA;
01725 }
01726 }
01727
01728 return STR_STATION_VIEW_VIA;
01729 }
01730
01741 int DrawEntries(CargoDataEntry *data, Rect &r, int pos, int maxrows, int column, CargoID cargo = CT_INVALID)
01742 {
01743 if (this->sortings[column] == ST_AS_GROUPING) {
01744 if (this->groupings[column] != GR_CARGO) {
01745 data->Resort(ST_STATION_STRING, this->sort_orders[column]);
01746 }
01747 } else {
01748 data->Resort(ST_COUNT, this->sort_orders[column]);
01749 }
01750 for (CargoDataSet::iterator i = data->Begin(); i != data->End(); ++i) {
01751 CargoDataEntry *cd = *i;
01752
01753 Grouping grouping = this->groupings[column];
01754 if (grouping == GR_CARGO) cargo = cd->GetCargo();
01755 bool auto_distributed = _settings_game.linkgraph.GetDistributionType(cargo) != DT_MANUAL;
01756
01757 if (pos > -maxrows && pos <= 0) {
01758 StringID str = STR_EMPTY;
01759 int y = r.top + WD_FRAMERECT_TOP - pos * FONT_HEIGHT_NORMAL;
01760 SetDParam(0, cargo);
01761 SetDParam(1, cd->GetCount());
01762
01763 if (this->groupings[column] == GR_CARGO) {
01764 str = STR_STATION_VIEW_WAITING_CARGO;
01765 DrawCargoIcons(cd->GetCargo(), cd->GetCount(), r.left + WD_FRAMERECT_LEFT + this->expand_shrink_width, r.right - WD_FRAMERECT_RIGHT - this->expand_shrink_width, y);
01766 } else {
01767 if (!auto_distributed) grouping = GR_SOURCE;
01768 StationID station = cd->GetStation();
01769
01770 switch (grouping) {
01771 case GR_SOURCE:
01772 str = this->GetEntryString(station, STR_STATION_VIEW_FROM_HERE, STR_STATION_VIEW_FROM, STR_STATION_VIEW_FROM_ANY);
01773 break;
01774 case GR_NEXT:
01775 str = this->GetEntryString(station, STR_STATION_VIEW_VIA_HERE, STR_STATION_VIEW_VIA, STR_STATION_VIEW_VIA_ANY);
01776 if (str == STR_STATION_VIEW_VIA) str = this->SearchNonStop(cd, station, column);
01777 break;
01778 case GR_DESTINATION:
01779 str = this->GetEntryString(station, STR_STATION_VIEW_TO_HERE, STR_STATION_VIEW_TO, STR_STATION_VIEW_TO_ANY);
01780 break;
01781 default:
01782 NOT_REACHED();
01783 }
01784 if (pos == -this->scroll_to_row && Station::IsValidID(station)) {
01785 ScrollMainWindowToTile(Station::Get(station)->xy);
01786 }
01787 }
01788
01789 bool rtl = _current_text_dir == TD_RTL;
01790 int text_left = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT + column * this->expand_shrink_width;
01791 int text_right = rtl ? r.right - WD_FRAMERECT_LEFT - column * this->expand_shrink_width : r.right - this->expand_shrink_width;
01792 int shrink_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
01793 int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
01794
01795 DrawString(text_left, text_right, y, str);
01796
01797 if (column < NUM_COLUMNS - 1) {
01798 const char *sym = NULL;
01799 if (cd->GetNumChildren() > 0) {
01800 sym = "-";
01801 } else if (auto_distributed && str != STR_STATION_VIEW_RESERVED) {
01802 sym = "+";
01803 } else {
01804
01805 const StationCargoList &list = Station::Get(this->window_number)->goods[cargo].cargo;
01806 if (grouping == GR_CARGO && (list.ReservedCount() > 0 || cd->HasTransfer())) {
01807 sym = "+";
01808 }
01809 }
01810 if (sym) DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW);
01811 }
01812 this->SetDisplayedRow(cd);
01813 }
01814 --pos;
01815 if (auto_distributed || column == 0) {
01816 pos = this->DrawEntries(cd, r, pos, maxrows, column + 1, cargo);
01817 }
01818 }
01819 return pos;
01820 }
01821
01827 int DrawAcceptedCargo(const Rect &r) const
01828 {
01829 const Station *st = Station::Get(this->window_number);
01830
01831 uint32 cargo_mask = 0;
01832 for (CargoID i = 0; i < NUM_CARGO; i++) {
01833 if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE)) SetBit(cargo_mask, i);
01834 }
01835 SetDParam(0, cargo_mask);
01836 int bottom = DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, INT32_MAX, STR_STATION_VIEW_ACCEPTS_CARGO);
01837 return CeilDiv(bottom - r.top - WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL);
01838 }
01839
01845 int DrawCargoRatings(const Rect &r) const
01846 {
01847 const Station *st = Station::Get(this->window_number);
01848 int y = r.top + WD_FRAMERECT_TOP;
01849
01850 if (st->town->exclusive_counter > 0) {
01851 SetDParam(0, st->town->exclusivity);
01852 y = DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, r.bottom, st->town->exclusivity == st->owner ? STR_STATIOV_VIEW_EXCLUSIVE_RIGHTS_SELF : STR_STATIOV_VIEW_EXCLUSIVE_RIGHTS_COMPANY);
01853 y += WD_PAR_VSEP_WIDE;
01854 }
01855
01856 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_SUPPLY_RATINGS_TITLE);
01857 y += FONT_HEIGHT_NORMAL;
01858
01859 const CargoSpec *cs;
01860 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
01861 const GoodsEntry *ge = &st->goods[cs->Index()];
01862 if (!HasBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP)) continue;
01863
01864 SetDParam(0, cs->name);
01865 SetDParam(1, ge->supply);
01866 SetDParam(3, ToPercent8(ge->rating));
01867 SetDParam(2, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
01868 DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_SUPPLY_RATING);
01869 y += FONT_HEIGHT_NORMAL;
01870 }
01871 return CeilDiv(y - r.top - WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL);
01872 }
01873
01880 template<class Tid>
01881 void HandleCargoWaitingClick(CargoDataEntry *filter, Tid next)
01882 {
01883 if (filter->Retrieve(next) != NULL) {
01884 filter->Remove(next);
01885 } else {
01886 filter->InsertOrRetrieve(next);
01887 }
01888 }
01889
01894 void HandleCargoWaitingClick(int row)
01895 {
01896 if (row < 0 || (uint)row >= this->displayed_rows.size()) return;
01897 if (_ctrl_pressed) {
01898 this->scroll_to_row = row;
01899 } else {
01900 RowDisplay &display = this->displayed_rows[row];
01901 if (display.filter == &this->expanded_rows) {
01902 this->HandleCargoWaitingClick<CargoID>(display.filter, display.next_cargo);
01903 } else {
01904 this->HandleCargoWaitingClick<StationID>(display.filter, display.next_station);
01905 }
01906 }
01907 this->SetWidgetDirty(WID_SV_WAITING);
01908 }
01909
01910 virtual void OnClick(Point pt, int widget, int click_count)
01911 {
01912 switch (widget) {
01913 case WID_SV_WAITING:
01914 this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL) - this->vscroll->GetPosition());
01915 break;
01916
01917 case WID_SV_LOCATION:
01918 if (_ctrl_pressed) {
01919 ShowExtraViewPortWindow(Station::Get(this->window_number)->xy);
01920 } else {
01921 ScrollMainWindowToTile(Station::Get(this->window_number)->xy);
01922 }
01923 break;
01924
01925 case WID_SV_ACCEPTS_RATINGS: {
01926
01927 int height_change;
01928 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS);
01929 if (this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
01930 nwi->SetDataTip(STR_STATION_VIEW_ACCEPTS_BUTTON, STR_STATION_VIEW_ACCEPTS_TOOLTIP);
01931 height_change = this->rating_lines - this->accepts_lines;
01932 } else {
01933 nwi->SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP);
01934 height_change = this->accepts_lines - this->rating_lines;
01935 }
01936 this->ReInit(0, height_change * FONT_HEIGHT_NORMAL);
01937 break;
01938 }
01939
01940 case WID_SV_RENAME:
01941 SetDParam(0, this->window_number);
01942 ShowQueryString(STR_STATION_NAME, STR_STATION_VIEW_RENAME_STATION_CAPTION, MAX_LENGTH_STATION_NAME_CHARS,
01943 this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
01944 break;
01945
01946 case WID_SV_CLOSE_AIRPORT:
01947 DoCommandP(0, this->window_number, 0, CMD_OPEN_CLOSE_AIRPORT);
01948 break;
01949
01950 case WID_SV_TRAINS:
01951 case WID_SV_ROADVEHS:
01952 case WID_SV_SHIPS:
01953 case WID_SV_PLANES: {
01954 Owner owner = Station::Get(this->window_number)->owner;
01955 ShowVehicleListWindow(owner, (VehicleType)(widget - WID_SV_TRAINS), (StationID)this->window_number);
01956 break;
01957 }
01958
01959 case WID_SV_SORT_BY: {
01960 int sorting_index = this->current_mode * 2 + (this->sortings[1] == ST_AS_GROUPING ? 0 : 1);
01961 ShowDropDownMenu(this, _sort_names, sorting_index, WID_SV_SORT_BY, 0, 0);
01962 break;
01963 }
01964
01965 case WID_SV_GROUP_BY: {
01966 ShowDropDownMenu(this, _group_names, this->grouping_index, WID_SV_GROUP_BY, 0, 0);
01967 break;
01968 }
01969
01970 case WID_SV_SORT_ORDER: {
01971 this->SelectSortOrder(this->sort_orders[1] == SO_ASCENDING ? SO_DESCENDING : SO_ASCENDING);
01972 this->SetTimeout();
01973 this->LowerWidget(WID_SV_SORT_ORDER);
01974 break;
01975 }
01976 }
01977 }
01978
01983 void SelectSortOrder(SortOrder order)
01984 {
01985 this->sort_orders[1] = this->sort_orders[2] = this->sort_orders[3] = order;
01986 _settings_client.gui.station_gui_sort_order = this->sort_orders[1];
01987 this->SetDirty();
01988 }
01989
01994 void SelectSortBy(int index)
01995 {
01996 _settings_client.gui.station_gui_sort_by = index;
01997 switch (_sort_names[index]) {
01998 case STR_STATION_VIEW_WAITING_STATION:
01999 this->current_mode = MODE_WAITING;
02000 this->sortings[1] = this->sortings[2] = this->sortings[3] = ST_AS_GROUPING;
02001 break;
02002 case STR_STATION_VIEW_WAITING_AMOUNT:
02003 this->current_mode = MODE_WAITING;
02004 this->sortings[1] = this->sortings[2] = this->sortings[3] = ST_COUNT;
02005 break;
02006 case STR_STATION_VIEW_PLANNED_STATION:
02007 this->current_mode = MODE_PLANNED;
02008 this->sortings[1] = this->sortings[2] = this->sortings[3] = ST_AS_GROUPING;
02009 break;
02010 case STR_STATION_VIEW_PLANNED_AMOUNT:
02011 this->current_mode = MODE_PLANNED;
02012 this->sortings[1] = this->sortings[2] = this->sortings[3] = ST_COUNT;
02013 break;
02014 default:
02015 NOT_REACHED();
02016 }
02017
02018 this->GetWidget<NWidgetCore>(WID_SV_SORT_BY)->widget_data = _sort_names[index];
02019 this->SetDirty();
02020 }
02021
02026 void SelectGroupBy(int index)
02027 {
02028 this->grouping_index = index;
02029 _settings_client.gui.station_gui_group_order = index;
02030 this->GetWidget<NWidgetCore>(WID_SV_GROUP_BY)->widget_data = _group_names[index];
02031 switch (_group_names[index]) {
02032 case STR_STATION_VIEW_GROUP_S_V_D:
02033 this->groupings[1] = GR_SOURCE;
02034 this->groupings[2] = GR_NEXT;
02035 this->groupings[3] = GR_DESTINATION;
02036 break;
02037 case STR_STATION_VIEW_GROUP_S_D_V:
02038 this->groupings[1] = GR_SOURCE;
02039 this->groupings[2] = GR_DESTINATION;
02040 this->groupings[3] = GR_NEXT;
02041 break;
02042 case STR_STATION_VIEW_GROUP_V_S_D:
02043 this->groupings[1] = GR_NEXT;
02044 this->groupings[2] = GR_SOURCE;
02045 this->groupings[3] = GR_DESTINATION;
02046 break;
02047 case STR_STATION_VIEW_GROUP_V_D_S:
02048 this->groupings[1] = GR_NEXT;
02049 this->groupings[2] = GR_DESTINATION;
02050 this->groupings[3] = GR_SOURCE;
02051 break;
02052 case STR_STATION_VIEW_GROUP_D_S_V:
02053 this->groupings[1] = GR_DESTINATION;
02054 this->groupings[2] = GR_SOURCE;
02055 this->groupings[3] = GR_NEXT;
02056 break;
02057 case STR_STATION_VIEW_GROUP_D_V_S:
02058 this->groupings[1] = GR_DESTINATION;
02059 this->groupings[2] = GR_NEXT;
02060 this->groupings[3] = GR_SOURCE;
02061 break;
02062 }
02063 this->SetDirty();
02064 }
02065
02066 virtual void OnDropdownSelect(int widget, int index)
02067 {
02068 if (widget == WID_SV_SORT_BY) {
02069 this->SelectSortBy(index);
02070 } else {
02071 this->SelectGroupBy(index);
02072 }
02073 }
02074
02075 virtual void OnQueryTextFinished(char *str)
02076 {
02077 if (str == NULL) return;
02078
02079 DoCommandP(0, this->window_number, 0, CMD_RENAME_STATION | CMD_MSG(STR_ERROR_CAN_T_RENAME_STATION), NULL, str);
02080 }
02081
02082 virtual void OnResize()
02083 {
02084 this->vscroll->SetCapacityFromWidget(this, WID_SV_WAITING, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
02085 }
02086
02092 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
02093 {
02094 if (gui_scope) {
02095 if (data >= 0 && data < NUM_CARGO) {
02096 this->cached_destinations.Remove((CargoID)data);
02097 } else {
02098 this->ReInit();
02099 }
02100 }
02101 }
02102 };
02103
02107 const StringID StationViewWindow::_sort_names[] = {
02108 STR_STATION_VIEW_WAITING_STATION,
02109 STR_STATION_VIEW_WAITING_AMOUNT,
02110 STR_STATION_VIEW_PLANNED_STATION,
02111 STR_STATION_VIEW_PLANNED_AMOUNT,
02112 INVALID_STRING_ID
02113 };
02114
02118 const StringID StationViewWindow::_group_names[] = {
02119 STR_STATION_VIEW_GROUP_S_V_D,
02120 STR_STATION_VIEW_GROUP_S_D_V,
02121 STR_STATION_VIEW_GROUP_V_S_D,
02122 STR_STATION_VIEW_GROUP_V_D_S,
02123 STR_STATION_VIEW_GROUP_D_S_V,
02124 STR_STATION_VIEW_GROUP_D_V_S,
02125 INVALID_STRING_ID
02126 };
02127
02128 static const WindowDesc _station_view_desc(
02129 WDP_AUTO, 249, 117,
02130 WC_STATION_VIEW, WC_NONE,
02131 0,
02132 _nested_station_view_widgets, lengthof(_nested_station_view_widgets)
02133 );
02134
02140 void ShowStationViewWindow(StationID station)
02141 {
02142 AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station);
02143 }
02144
02146 struct TileAndStation {
02147 TileIndex tile;
02148 StationID station;
02149 };
02150
02151 static SmallVector<TileAndStation, 8> _deleted_stations_nearby;
02152 static SmallVector<StationID, 8> _stations_nearby_list;
02153
02161 template <class T>
02162 static bool AddNearbyStation(TileIndex tile, void *user_data)
02163 {
02164 TileArea *ctx = (TileArea *)user_data;
02165
02166
02167 for (uint i = 0; i < _deleted_stations_nearby.Length(); i++) {
02168 TileAndStation *ts = _deleted_stations_nearby.Get(i);
02169 if (ts->tile == tile) {
02170 *_stations_nearby_list.Append() = _deleted_stations_nearby[i].station;
02171 _deleted_stations_nearby.Erase(ts);
02172 i--;
02173 }
02174 }
02175
02176
02177 if (!IsTileType(tile, MP_STATION)) return false;
02178
02179 StationID sid = GetStationIndex(tile);
02180
02181
02182 if (!T::IsValidID(sid)) return false;
02183
02184 T *st = T::Get(sid);
02185 if (st->owner != _local_company || _stations_nearby_list.Contains(sid)) return false;
02186
02187 if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST).Succeeded()) {
02188 *_stations_nearby_list.Append() = sid;
02189 }
02190
02191 return false;
02192 }
02193
02203 template <class T>
02204 static const T *FindStationsNearby(TileArea ta, bool distant_join)
02205 {
02206 TileArea ctx = ta;
02207
02208 _stations_nearby_list.Clear();
02209 _deleted_stations_nearby.Clear();
02210
02211
02212 TILE_AREA_LOOP(t, ta) {
02213 if (t < MapSize() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return T::GetByTile(t);
02214 }
02215
02216
02217 const BaseStation *st;
02218 FOR_ALL_BASE_STATIONS(st) {
02219 if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company) {
02220
02221 if (max(DistanceMax(ta.tile, st->xy), DistanceMax(TILE_ADDXY(ta.tile, ta.w - 1, ta.h - 1), st->xy)) < _settings_game.station.station_spread) {
02222 TileAndStation *ts = _deleted_stations_nearby.Append();
02223 ts->tile = st->xy;
02224 ts->station = st->index;
02225
02226
02227 if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) &&
02228 IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) {
02229 AddNearbyStation<T>(st->xy, &ctx);
02230 }
02231 }
02232 }
02233 }
02234
02235
02236
02237
02238 if (distant_join && min(ta.w, ta.h) >= _settings_game.station.station_spread) return NULL;
02239 uint max_dist = distant_join ? _settings_game.station.station_spread - min(ta.w, ta.h) : 1;
02240
02241 TileIndex tile = TILE_ADD(ctx.tile, TileOffsByDir(DIR_N));
02242 CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
02243
02244 return NULL;
02245 }
02246
02247 static const NWidgetPart _nested_select_station_widgets[] = {
02248 NWidget(NWID_HORIZONTAL),
02249 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
02250 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_JS_CAPTION), SetDataTip(STR_JOIN_STATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
02251 EndContainer(),
02252 NWidget(NWID_HORIZONTAL),
02253 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_JS_PANEL), SetResize(1, 0), SetScrollbar(WID_JS_SCROLLBAR), EndContainer(),
02254 NWidget(NWID_VERTICAL),
02255 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_JS_SCROLLBAR),
02256 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
02257 EndContainer(),
02258 EndContainer(),
02259 };
02260
02265 template <class T>
02266 struct SelectStationWindow : Window {
02267 CommandContainer select_station_cmd;
02268 TileArea area;
02269 Scrollbar *vscroll;
02270
02271 SelectStationWindow(const WindowDesc *desc, CommandContainer cmd, TileArea ta) :
02272 Window(),
02273 select_station_cmd(cmd),
02274 area(ta)
02275 {
02276 this->CreateNestedTree(desc);
02277 this->vscroll = this->GetScrollbar(WID_JS_SCROLLBAR);
02278 this->GetWidget<NWidgetCore>(WID_JS_CAPTION)->widget_data = T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
02279 this->FinishInitNested(desc, 0);
02280 this->OnInvalidateData(0);
02281 }
02282
02283 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
02284 {
02285 if (widget != WID_JS_PANEL) return;
02286
02287
02288 Dimension d = GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
02289 for (uint i = 0; i < _stations_nearby_list.Length(); i++) {
02290 const T *st = T::Get(_stations_nearby_list[i]);
02291 SetDParam(0, st->index);
02292 SetDParam(1, st->facilities);
02293 d = maxdim(d, GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION));
02294 }
02295
02296 resize->height = d.height;
02297 d.height *= 5;
02298 d.width += WD_FRAMERECT_RIGHT + WD_FRAMERECT_LEFT;
02299 d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
02300 *size = d;
02301 }
02302
02303 virtual void DrawWidget(const Rect &r, int widget) const
02304 {
02305 if (widget != WID_JS_PANEL) return;
02306
02307 uint y = r.top + WD_FRAMERECT_TOP;
02308 if (this->vscroll->GetPosition() == 0) {
02309 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
02310 y += this->resize.step_height;
02311 }
02312
02313 for (uint i = max<uint>(1, this->vscroll->GetPosition()); i <= _stations_nearby_list.Length(); ++i, y += this->resize.step_height) {
02314
02315 if (i - this->vscroll->GetPosition() >= this->vscroll->GetCapacity()) break;
02316
02317 const T *st = T::Get(_stations_nearby_list[i - 1]);
02318 SetDParam(0, st->index);
02319 SetDParam(1, st->facilities);
02320 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION);
02321 }
02322 }
02323
02324 virtual void OnClick(Point pt, int widget, int click_count)
02325 {
02326 if (widget != WID_JS_PANEL) return;
02327
02328 uint st_index = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_JS_PANEL, WD_FRAMERECT_TOP);
02329 bool distant_join = (st_index > 0);
02330 if (distant_join) st_index--;
02331
02332 if (distant_join && st_index >= _stations_nearby_list.Length()) return;
02333
02334
02335 SB(this->select_station_cmd.p2, 16, 16,
02336 (distant_join ? _stations_nearby_list[st_index] : NEW_STATION));
02337
02338
02339 DoCommandP(&this->select_station_cmd);
02340
02341
02342 DeleteWindowById(WC_SELECT_STATION, 0);
02343 }
02344
02345 virtual void OnTick()
02346 {
02347 if (_thd.dirty & 2) {
02348 _thd.dirty &= ~2;
02349 this->SetDirty();
02350 }
02351 }
02352
02353 virtual void OnResize()
02354 {
02355 this->vscroll->SetCapacityFromWidget(this, WID_JS_PANEL, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
02356 }
02357
02363 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
02364 {
02365 if (!gui_scope) return;
02366 FindStationsNearby<T>(this->area, true);
02367 this->vscroll->SetCount(_stations_nearby_list.Length() + 1);
02368 this->SetDirty();
02369 }
02370 };
02371
02372 static const WindowDesc _select_station_desc(
02373 WDP_AUTO, 200, 180,
02374 WC_SELECT_STATION, WC_NONE,
02375 WDF_CONSTRUCTION,
02376 _nested_select_station_widgets, lengthof(_nested_select_station_widgets)
02377 );
02378
02379
02387 template <class T>
02388 static bool StationJoinerNeeded(CommandContainer cmd, TileArea ta)
02389 {
02390
02391 if (!_settings_game.station.distant_join_stations) return false;
02392
02393
02394
02395 Window *selection_window = FindWindowById(WC_SELECT_STATION, 0);
02396 if (selection_window != NULL) {
02397
02398 delete selection_window;
02399 UpdateTileSelection();
02400 }
02401
02402
02403 if (!_ctrl_pressed) return false;
02404
02405
02406 if (DoCommand(&cmd, CommandFlagsToDCFlags(GetCommandFlags(cmd.cmd))).Failed()) return false;
02407
02408
02409
02410
02411 const T *st = FindStationsNearby<T>(ta, false);
02412 return st == NULL && (_settings_game.station.adjacent_stations || _stations_nearby_list.Length() == 0);
02413 }
02414
02421 template <class T>
02422 void ShowSelectBaseStationIfNeeded(CommandContainer cmd, TileArea ta)
02423 {
02424 if (StationJoinerNeeded<T>(cmd, ta)) {
02425 if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
02426 new SelectStationWindow<T>(&_select_station_desc, cmd, ta);
02427 } else {
02428 DoCommandP(&cmd);
02429 }
02430 }
02431
02437 void ShowSelectStationIfNeeded(CommandContainer cmd, TileArea ta)
02438 {
02439 ShowSelectBaseStationIfNeeded<Station>(cmd, ta);
02440 }
02441
02447 void ShowSelectWaypointIfNeeded(CommandContainer cmd, TileArea ta)
02448 {
02449 ShowSelectBaseStationIfNeeded<Waypoint>(cmd, ta);
02450 }