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 <vector>
00039
00050 int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies)
00051 {
00052 TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
00053 uint32 cargo_mask = 0;
00054 if (_thd.drawstyle == HT_RECT && tile < MapSize()) {
00055 CargoArray cargoes;
00056 if (supplies) {
00057 cargoes = GetProductionAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00058 } else {
00059 cargoes = GetAcceptanceAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00060 }
00061
00062
00063 for (CargoID i = 0; i < NUM_CARGO; i++) {
00064 switch (sct) {
00065 case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00066 case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00067 case SCT_ALL: break;
00068 default: NOT_REACHED();
00069 }
00070 if (cargoes[i] >= (supplies ? 1U : 8U)) SetBit(cargo_mask, i);
00071 }
00072 }
00073 SetDParam(0, cargo_mask);
00074 return DrawStringMultiLine(left, right, top, INT32_MAX, supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO);
00075 }
00076
00082 void CheckRedrawStationCoverage(const Window *w)
00083 {
00084 if (_thd.dirty & 1) {
00085 _thd.dirty &= ~1;
00086 w->SetDirty();
00087 }
00088 }
00089
00105 static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating)
00106 {
00107 static const uint units_full = 576;
00108 static const uint rating_full = 224;
00109
00110 const CargoSpec *cs = CargoSpec::Get(type);
00111 if (!cs->IsValid()) return;
00112
00113 int colour = cs->rating_colour;
00114 TextColour tc = GetContrastColour(colour);
00115 uint w = (minu(amount, units_full) + 5) / 36;
00116
00117 int height = GetCharacterHeight(FS_SMALL);
00118
00119
00120 if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour);
00121
00122
00123
00124 if (w == 0) {
00125 uint rest = amount / 5;
00126 if (rest != 0) {
00127 w += left;
00128 GfxFillRect(w, y + height - rest, w, y + height, colour);
00129 }
00130 }
00131
00132 DrawString(left + 1, right, y, cs->abbrev, tc);
00133
00134
00135 y += height + 2;
00136 GfxFillRect(left + 1, y, left + 14, y, PC_RED);
00137 rating = minu(rating, rating_full) / 16;
00138 if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, PC_GREEN);
00139 }
00140
00141 typedef GUIList<const Station*> GUIStationList;
00142
00146 class CompanyStationsWindow : public Window
00147 {
00148 protected:
00149
00150 static Listing last_sorting;
00151 static byte facilities;
00152 static bool include_empty;
00153 static const uint32 cargo_filter_max;
00154 static uint32 cargo_filter;
00155 static const Station *last_station;
00156
00157
00158 static const StringID sorter_names[];
00159 static GUIStationList::SortFunction * const sorter_funcs[];
00160
00161 GUIStationList stations;
00162 Scrollbar *vscroll;
00163
00169 void BuildStationsList(const Owner owner)
00170 {
00171 if (!this->stations.NeedRebuild()) return;
00172
00173 DEBUG(misc, 3, "Building station list for company %d", owner);
00174
00175 this->stations.Clear();
00176
00177 const Station *st;
00178 FOR_ALL_STATIONS(st) {
00179 if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, true, owner))) {
00180 if (this->facilities & st->facilities) {
00181 int num_waiting_cargo = 0;
00182 for (CargoID j = 0; j < NUM_CARGO; j++) {
00183 if (HasBit(st->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) {
00184 num_waiting_cargo++;
00185 if (HasBit(this->cargo_filter, j)) {
00186 *this->stations.Append() = st;
00187 break;
00188 }
00189 }
00190 }
00191
00192 if (num_waiting_cargo == 0 && this->include_empty) {
00193 *this->stations.Append() = st;
00194 }
00195 }
00196 }
00197 }
00198
00199 this->stations.Compact();
00200 this->stations.RebuildDone();
00201
00202 this->vscroll->SetCount(this->stations.Length());
00203 }
00204
00206 static int CDECL StationNameSorter(const Station * const *a, const Station * const *b)
00207 {
00208 static char buf_cache[64];
00209 char buf[64];
00210
00211 SetDParam(0, (*a)->index);
00212 GetString(buf, STR_STATION_NAME, lastof(buf));
00213
00214 if (*b != last_station) {
00215 last_station = *b;
00216 SetDParam(0, (*b)->index);
00217 GetString(buf_cache, STR_STATION_NAME, lastof(buf_cache));
00218 }
00219
00220 return strcmp(buf, buf_cache);
00221 }
00222
00224 static int CDECL StationTypeSorter(const Station * const *a, const Station * const *b)
00225 {
00226 return (*a)->facilities - (*b)->facilities;
00227 }
00228
00230 static int CDECL StationWaitingSorter(const Station * const *a, const Station * const *b)
00231 {
00232 Money diff = 0;
00233
00234 CargoID j;
00235 FOR_EACH_SET_CARGO_ID(j, cargo_filter) {
00236 if (!(*a)->goods[j].cargo.Empty()) diff += GetTransportedGoodsIncome((*a)->goods[j].cargo.Count(), 20, 50, j);
00237 if (!(*b)->goods[j].cargo.Empty()) diff -= GetTransportedGoodsIncome((*b)->goods[j].cargo.Count(), 20, 50, j);
00238 }
00239
00240 return ClampToI32(diff);
00241 }
00242
00244 static int CDECL StationRatingMaxSorter(const Station * const *a, const Station * const *b)
00245 {
00246 byte maxr1 = 0;
00247 byte maxr2 = 0;
00248
00249 CargoID j;
00250 FOR_EACH_SET_CARGO_ID(j, cargo_filter) {
00251 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) maxr1 = max(maxr1, (*a)->goods[j].rating);
00252 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) maxr2 = max(maxr2, (*b)->goods[j].rating);
00253 }
00254
00255 return maxr1 - maxr2;
00256 }
00257
00259 static int CDECL StationRatingMinSorter(const Station * const *a, const Station * const *b)
00260 {
00261 byte minr1 = 255;
00262 byte minr2 = 255;
00263
00264 for (CargoID j = 0; j < NUM_CARGO; j++) {
00265 if (!HasBit(cargo_filter, j)) continue;
00266 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) minr1 = min(minr1, (*a)->goods[j].rating);
00267 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) minr2 = min(minr2, (*b)->goods[j].rating);
00268 }
00269
00270 return -(minr1 - minr2);
00271 }
00272
00274 void SortStationsList()
00275 {
00276 if (!this->stations.Sort()) return;
00277
00278
00279 this->last_station = NULL;
00280
00281
00282 this->SetWidgetDirty(WID_STL_LIST);
00283 }
00284
00285 public:
00286 CompanyStationsWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
00287 {
00288 this->stations.SetListing(this->last_sorting);
00289 this->stations.SetSortFuncs(this->sorter_funcs);
00290 this->stations.ForceRebuild();
00291 this->stations.NeedResort();
00292 this->SortStationsList();
00293
00294 this->CreateNestedTree(desc);
00295 this->vscroll = this->GetScrollbar(WID_STL_SCROLLBAR);
00296 this->FinishInitNested(desc, window_number);
00297 this->owner = (Owner)this->window_number;
00298
00299 const CargoSpec *cs;
00300 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
00301 if (!HasBit(this->cargo_filter, cs->Index())) continue;
00302 this->LowerWidget(WID_STL_CARGOSTART + index);
00303 }
00304
00305 if (this->cargo_filter == this->cargo_filter_max) this->cargo_filter = _cargo_mask;
00306
00307 for (uint i = 0; i < 5; i++) {
00308 if (HasBit(this->facilities, i)) this->LowerWidget(i + WID_STL_TRAIN);
00309 }
00310 this->SetWidgetLoweredState(WID_STL_NOCARGOWAITING, this->include_empty);
00311
00312 this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00313 }
00314
00315 ~CompanyStationsWindow()
00316 {
00317 this->last_sorting = this->stations.GetListing();
00318 }
00319
00320 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00321 {
00322 switch (widget) {
00323 case WID_STL_SORTBY: {
00324 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
00325 d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2;
00326 d.height += padding.height;
00327 *size = maxdim(*size, d);
00328 break;
00329 }
00330
00331 case WID_STL_SORTDROPBTN: {
00332 Dimension d = {0, 0};
00333 for (int i = 0; this->sorter_names[i] != INVALID_STRING_ID; i++) {
00334 d = maxdim(d, GetStringBoundingBox(this->sorter_names[i]));
00335 }
00336 d.width += padding.width;
00337 d.height += padding.height;
00338 *size = maxdim(*size, d);
00339 break;
00340 }
00341
00342 case WID_STL_LIST:
00343 resize->height = FONT_HEIGHT_NORMAL;
00344 size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
00345 break;
00346
00347 case WID_STL_TRAIN:
00348 case WID_STL_TRUCK:
00349 case WID_STL_BUS:
00350 case WID_STL_AIRPLANE:
00351 case WID_STL_SHIP:
00352 size->height = max<uint>(FONT_HEIGHT_SMALL, 10) + padding.height;
00353 break;
00354
00355 case WID_STL_CARGOALL:
00356 case WID_STL_FACILALL:
00357 case WID_STL_NOCARGOWAITING: {
00358 Dimension d = GetStringBoundingBox(widget == WID_STL_NOCARGOWAITING ? STR_ABBREV_NONE : STR_ABBREV_ALL);
00359 d.width += padding.width + 2;
00360 d.height += padding.height;
00361 *size = maxdim(*size, d);
00362 break;
00363 }
00364
00365 default:
00366 if (widget >= WID_STL_CARGOSTART) {
00367 Dimension d = GetStringBoundingBox(_sorted_cargo_specs[widget - WID_STL_CARGOSTART]->abbrev);
00368 d.width += padding.width + 2;
00369 d.height += padding.height;
00370 *size = maxdim(*size, d);
00371 }
00372 break;
00373 }
00374 }
00375
00376 virtual void OnPaint()
00377 {
00378 this->BuildStationsList((Owner)this->window_number);
00379 this->SortStationsList();
00380
00381 this->DrawWidgets();
00382 }
00383
00384 virtual void DrawWidget(const Rect &r, int widget) const
00385 {
00386 switch (widget) {
00387 case WID_STL_SORTBY:
00388
00389 this->DrawSortButtonState(WID_STL_SORTBY, this->stations.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
00390 break;
00391
00392 case WID_STL_LIST: {
00393 bool rtl = _current_text_dir == TD_RTL;
00394 int max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->stations.Length());
00395 int y = r.top + WD_FRAMERECT_TOP;
00396 for (int i = this->vscroll->GetPosition(); i < max; ++i) {
00397 const Station *st = this->stations[i];
00398 assert(st->xy != INVALID_TILE);
00399
00400
00401
00402 assert(st->owner == owner || st->owner == OWNER_NONE);
00403
00404 SetDParam(0, st->index);
00405 SetDParam(1, st->facilities);
00406 int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION);
00407 x += rtl ? -5 : 5;
00408
00409
00410 for (uint j = 0; j < _sorted_standard_cargo_specs_size; j++) {
00411 CargoID cid = _sorted_cargo_specs[j]->Index();
00412 if (!st->goods[cid].cargo.Empty()) {
00413
00414
00415
00416
00417 if (rtl) {
00418 x -= 20;
00419 if (x < r.left + WD_FRAMERECT_LEFT) break;
00420 }
00421 StationsWndShowStationRating(x, x + 16, y, cid, st->goods[cid].cargo.Count(), st->goods[cid].rating);
00422 if (!rtl) {
00423 x += 20;
00424 if (x > r.right - WD_FRAMERECT_RIGHT) break;
00425 }
00426 }
00427 }
00428 y += FONT_HEIGHT_NORMAL;
00429 }
00430
00431 if (this->vscroll->GetCount() == 0) {
00432 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_NONE);
00433 return;
00434 }
00435 break;
00436 }
00437
00438 case WID_STL_NOCARGOWAITING: {
00439 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00440 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER);
00441 break;
00442 }
00443
00444 case WID_STL_CARGOALL: {
00445 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00446 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER);
00447 break;
00448 }
00449
00450 case WID_STL_FACILALL: {
00451 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00452 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00453 break;
00454 }
00455
00456 default:
00457 if (widget >= WID_STL_CARGOSTART) {
00458 const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART];
00459 int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 2 : 1;
00460 GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour);
00461 TextColour tc = GetContrastColour(cs->rating_colour);
00462 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, cs->abbrev, tc, SA_HOR_CENTER);
00463 }
00464 break;
00465 }
00466 }
00467
00468 virtual void SetStringParameters(int widget) const
00469 {
00470 if (widget == WID_STL_CAPTION) {
00471 SetDParam(0, this->window_number);
00472 SetDParam(1, this->vscroll->GetCount());
00473 }
00474 }
00475
00476 virtual void OnClick(Point pt, int widget, int click_count)
00477 {
00478 switch (widget) {
00479 case WID_STL_LIST: {
00480 uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_STL_LIST, 0, FONT_HEIGHT_NORMAL);
00481 if (id_v >= this->stations.Length()) return;
00482
00483 const Station *st = this->stations[id_v];
00484
00485 assert(st->owner == (Owner)this->window_number || st->owner == OWNER_NONE);
00486
00487 if (_ctrl_pressed) {
00488 ShowExtraViewPortWindow(st->xy);
00489 } else {
00490 ScrollMainWindowToTile(st->xy);
00491 }
00492 break;
00493 }
00494
00495 case WID_STL_TRAIN:
00496 case WID_STL_TRUCK:
00497 case WID_STL_BUS:
00498 case WID_STL_AIRPLANE:
00499 case WID_STL_SHIP:
00500 if (_ctrl_pressed) {
00501 ToggleBit(this->facilities, widget - WID_STL_TRAIN);
00502 this->ToggleWidgetLoweredState(widget);
00503 } else {
00504 uint i;
00505 FOR_EACH_SET_BIT(i, this->facilities) {
00506 this->RaiseWidget(i + WID_STL_TRAIN);
00507 }
00508 this->facilities = 1 << (widget - WID_STL_TRAIN);
00509 this->LowerWidget(widget);
00510 }
00511 this->stations.ForceRebuild();
00512 this->SetDirty();
00513 break;
00514
00515 case WID_STL_FACILALL:
00516 for (uint i = WID_STL_TRAIN; i <= WID_STL_SHIP; i++) {
00517 this->LowerWidget(i);
00518 }
00519
00520 this->facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00521 this->stations.ForceRebuild();
00522 this->SetDirty();
00523 break;
00524
00525 case WID_STL_CARGOALL: {
00526 for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) {
00527 this->LowerWidget(WID_STL_CARGOSTART + i);
00528 }
00529 this->LowerWidget(WID_STL_NOCARGOWAITING);
00530
00531 this->cargo_filter = _cargo_mask;
00532 this->include_empty = true;
00533 this->stations.ForceRebuild();
00534 this->SetDirty();
00535 break;
00536 }
00537
00538 case WID_STL_SORTBY:
00539 this->stations.ToggleSortOrder();
00540 this->SetDirty();
00541 break;
00542
00543 case WID_STL_SORTDROPBTN:
00544 ShowDropDownMenu(this, this->sorter_names, this->stations.SortType(), WID_STL_SORTDROPBTN, 0, 0);
00545 break;
00546
00547 case WID_STL_NOCARGOWAITING:
00548 if (_ctrl_pressed) {
00549 this->include_empty = !this->include_empty;
00550 this->ToggleWidgetLoweredState(WID_STL_NOCARGOWAITING);
00551 } else {
00552 for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) {
00553 this->RaiseWidget(WID_STL_CARGOSTART + i);
00554 }
00555
00556 this->cargo_filter = 0;
00557 this->include_empty = true;
00558
00559 this->LowerWidget(WID_STL_NOCARGOWAITING);
00560 }
00561 this->stations.ForceRebuild();
00562 this->SetDirty();
00563 break;
00564
00565 default:
00566 if (widget >= WID_STL_CARGOSTART) {
00567
00568 const CargoSpec *cs = _sorted_cargo_specs[widget - WID_STL_CARGOSTART];
00569
00570 if (_ctrl_pressed) {
00571 ToggleBit(this->cargo_filter, cs->Index());
00572 this->ToggleWidgetLoweredState(widget);
00573 } else {
00574 for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) {
00575 this->RaiseWidget(WID_STL_CARGOSTART + i);
00576 }
00577 this->RaiseWidget(WID_STL_NOCARGOWAITING);
00578
00579 this->cargo_filter = 0;
00580 this->include_empty = false;
00581
00582 SetBit(this->cargo_filter, cs->Index());
00583 this->LowerWidget(widget);
00584 }
00585 this->stations.ForceRebuild();
00586 this->SetDirty();
00587 }
00588 break;
00589 }
00590 }
00591
00592 virtual void OnDropdownSelect(int widget, int index)
00593 {
00594 if (this->stations.SortType() != index) {
00595 this->stations.SetSortType(index);
00596
00597
00598 this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00599
00600 this->SetDirty();
00601 }
00602 }
00603
00604 virtual void OnTick()
00605 {
00606 if (_pause_mode != PM_UNPAUSED) return;
00607 if (this->stations.NeedResort()) {
00608 DEBUG(misc, 3, "Periodic rebuild station list company %d", this->window_number);
00609 this->SetDirty();
00610 }
00611 }
00612
00613 virtual void OnResize()
00614 {
00615 this->vscroll->SetCapacityFromWidget(this, WID_STL_LIST, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
00616 }
00617
00623 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00624 {
00625 if (data == 0) {
00626
00627 this->stations.ForceRebuild();
00628 } else {
00629 this->stations.ForceResort();
00630 }
00631 }
00632 };
00633
00634 Listing CompanyStationsWindow::last_sorting = {false, 0};
00635 byte CompanyStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00636 bool CompanyStationsWindow::include_empty = true;
00637 const uint32 CompanyStationsWindow::cargo_filter_max = UINT32_MAX;
00638 uint32 CompanyStationsWindow::cargo_filter = UINT32_MAX;
00639 const Station *CompanyStationsWindow::last_station = NULL;
00640
00641
00642 GUIStationList::SortFunction * const CompanyStationsWindow::sorter_funcs[] = {
00643 &StationNameSorter,
00644 &StationTypeSorter,
00645 &StationWaitingSorter,
00646 &StationRatingMaxSorter,
00647 &StationRatingMinSorter
00648 };
00649
00650
00651 const StringID CompanyStationsWindow::sorter_names[] = {
00652 STR_SORT_BY_NAME,
00653 STR_SORT_BY_FACILITY,
00654 STR_SORT_BY_WAITING,
00655 STR_SORT_BY_RATING_MAX,
00656 STR_SORT_BY_RATING_MIN,
00657 INVALID_STRING_ID
00658 };
00659
00665 static NWidgetBase *CargoWidgets(int *biggest_index)
00666 {
00667 NWidgetHorizontal *container = new NWidgetHorizontal();
00668
00669 for (uint i = 0; i < _sorted_standard_cargo_specs_size; i++) {
00670 NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_STL_CARGOSTART + i);
00671 panel->SetMinimalSize(14, 11);
00672 panel->SetResize(0, 0);
00673 panel->SetFill(0, 1);
00674 panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE);
00675 container->Add(panel);
00676 }
00677 *biggest_index = WID_STL_CARGOSTART + _sorted_standard_cargo_specs_size;
00678 return container;
00679 }
00680
00681 static const NWidgetPart _nested_company_stations_widgets[] = {
00682 NWidget(NWID_HORIZONTAL),
00683 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00684 NWidget(WWT_CAPTION, COLOUR_GREY, WID_STL_CAPTION), SetDataTip(STR_STATION_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00685 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00686 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00687 EndContainer(),
00688 NWidget(NWID_HORIZONTAL),
00689 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),
00690 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),
00691 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),
00692 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),
00693 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),
00694 NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1),
00695 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(),
00696 NWidgetFunction(CargoWidgets),
00697 NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(),
00698 NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1),
00699 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00700 EndContainer(),
00701 NWidget(NWID_HORIZONTAL),
00702 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_STL_SORTBY), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
00703 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_STL_SORTDROPBTN), SetMinimalSize(163, 12), SetDataTip(STR_SORT_BY_NAME, STR_TOOLTIP_SORT_CRITERIA),
00704 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00705 EndContainer(),
00706 NWidget(NWID_HORIZONTAL),
00707 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(),
00708 NWidget(NWID_VERTICAL),
00709 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_STL_SCROLLBAR),
00710 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00711 EndContainer(),
00712 EndContainer(),
00713 };
00714
00715 static const WindowDesc _company_stations_desc(
00716 WDP_AUTO, 358, 162,
00717 WC_STATION_LIST, WC_NONE,
00718 0,
00719 _nested_company_stations_widgets, lengthof(_nested_company_stations_widgets)
00720 );
00721
00727 void ShowCompanyStations(CompanyID company)
00728 {
00729 if (!Company::IsValidID(company)) return;
00730
00731 AllocateWindowDescFront<CompanyStationsWindow>(&_company_stations_desc, company);
00732 }
00733
00734 static const NWidgetPart _nested_station_view_widgets[] = {
00735 NWidget(NWID_HORIZONTAL),
00736 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00737 NWidget(WWT_CAPTION, COLOUR_GREY, WID_SV_CAPTION), SetDataTip(STR_STATION_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00738 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00739 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00740 EndContainer(),
00741 NWidget(NWID_HORIZONTAL),
00742 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_SORT_ORDER), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
00743 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SV_SORT_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
00744 EndContainer(),
00745 NWidget(NWID_HORIZONTAL),
00746 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SV_GROUP), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_STATION_VIEW_GROUP, 0x0),
00747 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SV_GROUP_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_GROUP_ORDER),
00748 EndContainer(),
00749 NWidget(NWID_HORIZONTAL),
00750 NWidget(WWT_PANEL, COLOUR_GREY, WID_SV_WAITING), SetMinimalSize(237, 44), SetResize(1, 10), SetScrollbar(WID_SV_SCROLLBAR), EndContainer(),
00751 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SV_SCROLLBAR),
00752 EndContainer(),
00753 NWidget(WWT_PANEL, COLOUR_GREY, WID_SV_ACCEPT_RATING_LIST), SetMinimalSize(249, 23), SetResize(1, 0), EndContainer(),
00754 NWidget(NWID_HORIZONTAL),
00755 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
00756 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_LOCATION), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
00757 SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP),
00758 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ACCEPTS_RATINGS), SetMinimalSize(46, 12), SetResize(1, 0), SetFill(1, 1),
00759 SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP),
00760 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_RENAME), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
00761 SetDataTip(STR_BUTTON_RENAME, STR_STATION_VIEW_RENAME_TOOLTIP),
00762 EndContainer(),
00763 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SV_CLOSE_AIRPORT), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
00764 SetDataTip(STR_STATION_VIEW_CLOSE_AIRPORT, STR_STATION_VIEW_CLOSE_AIRPORT_TOOLTIP),
00765 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_TRAINS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP),
00766 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ROADVEHS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP),
00767 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_SHIPS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_SHIP, STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP),
00768 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_PLANES), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_PLANE, STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP),
00769 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00770 EndContainer(),
00771 };
00772
00783 static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
00784 {
00785 uint num = min((waiting + 5) / 10, (right - left) / 10);
00786 if (num == 0) return;
00787
00788 SpriteID sprite = CargoSpec::Get(i)->GetCargoIcon();
00789
00790 int x = _current_text_dir == TD_RTL ? left : right - num * 10;
00791 do {
00792 DrawSprite(sprite, PAL_NONE, x, y);
00793 x += 10;
00794 } while (--num);
00795 }
00796
00797 CargoDataEntry::CargoDataEntry() :
00798 parent(NULL),
00799 station(INVALID_STATION),
00800 num_children(0),
00801 count(0),
00802 children(new CargoDataSet(CargoSorter(ST_CARGO_ID)))
00803 {}
00804
00805 CargoDataEntry::CargoDataEntry(CargoID cargo, uint count, CargoDataEntry *parent) :
00806 parent(parent),
00807 cargo(cargo),
00808 num_children(0),
00809 count(count),
00810 children(new CargoDataSet)
00811 {}
00812
00813 CargoDataEntry::CargoDataEntry(StationID station, uint count, CargoDataEntry *parent) :
00814 parent(parent),
00815 station(station),
00816 num_children(0),
00817 count(count),
00818 children(new CargoDataSet)
00819 {}
00820
00821 CargoDataEntry::CargoDataEntry(StationID station) :
00822 parent(NULL),
00823 station(station),
00824 num_children(0),
00825 count(0),
00826 children(NULL)
00827 {}
00828
00829 CargoDataEntry::CargoDataEntry(CargoID cargo) :
00830 parent(NULL),
00831 cargo(cargo),
00832 num_children(0),
00833 count(0),
00834 children(NULL)
00835 {}
00836
00837 CargoDataEntry::~CargoDataEntry()
00838 {
00839 this->Clear();
00840 delete this->children;
00841 }
00842
00846 void CargoDataEntry::Clear()
00847 {
00848 if (this->children != NULL) {
00849 for (CargoDataSet::iterator i = this->children->begin(); i != this->children->end(); ++i) {
00850 assert(*i != this);
00851 delete *i;
00852 }
00853 this->children->clear();
00854 }
00855 if (this->parent != NULL) this->parent->count -= this->count;
00856 this->count = 0;
00857 this->num_children = 0;
00858 }
00859
00866 void CargoDataEntry::Remove(CargoDataEntry *child)
00867 {
00868 CargoDataSet::iterator i = this->children->find(child);
00869 if (i != this->children->end()) {
00870 delete *i;
00871 this->children->erase(i);
00872 }
00873 }
00874
00881 template<class ID>
00882 CargoDataEntry *CargoDataEntry::InsertOrRetrieve(ID child_id)
00883 {
00884 CargoDataEntry tmp(child_id);
00885 CargoDataSet::iterator i = this->children->find(&tmp);
00886 if (i == this->children->end()) {
00887 IncrementSize();
00888 return *(this->children->insert(new CargoDataEntry(child_id, 0, this)).first);
00889 } else {
00890 CargoDataEntry *ret = *i;
00891 assert(this->children->value_comp().GetSortType() != ST_COUNT);
00892 return ret;
00893 }
00894 }
00895
00901 void CargoDataEntry::Update(uint count)
00902 {
00903 this->count += count;
00904 if (this->parent != NULL) this->parent->Update(count);
00905 }
00906
00910 void CargoDataEntry::IncrementSize()
00911 {
00912 ++this->num_children;
00913 if (this->parent != NULL) this->parent->IncrementSize();
00914 }
00915
00916 void CargoDataEntry::Resort(CargoSortType type, SortOrder order)
00917 {
00918 CargoDataSet *new_subs = new CargoDataSet(this->children->begin(), this->children->end(), CargoSorter(type, order));
00919 delete this->children;
00920 this->children = new_subs;
00921 }
00922
00923 CargoDataEntry *CargoDataEntry::Retrieve(CargoDataSet::iterator i) const
00924 {
00925 if (i == this->children->end()) {
00926 return NULL;
00927 } else {
00928 assert(this->children->value_comp().GetSortType() != ST_COUNT);
00929 return *i;
00930 }
00931 }
00932
00933 bool CargoSorter::operator()(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const
00934 {
00935 switch (this->type) {
00936 case ST_STATION_ID:
00937 return this->SortId<StationID>(cd1->GetStation(), cd2->GetStation());
00938 break;
00939 case ST_CARGO_ID:
00940 return this->SortId<CargoID>(cd1->GetCargo(), cd2->GetCargo());
00941 break;
00942 case ST_COUNT:
00943 return this->SortCount(cd1, cd2);
00944 break;
00945 case ST_STATION_STRING:
00946 return this->SortStation(cd1->GetStation(), cd2->GetStation());
00947 break;
00948 default:
00949 NOT_REACHED();
00950 }
00951 }
00952
00953 template<class ID>
00954 bool CargoSorter::SortId(ID st1, ID st2) const
00955 {
00956 return (this->order == SO_ASCENDING) ? st1 < st2 : st2 < st1;
00957 }
00958
00959 bool CargoSorter::SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const
00960 {
00961 uint c1 = cd1->GetCount();
00962 uint c2 = cd2->GetCount();
00963 if (c1 == c2) {
00964 return this->SortStation(cd1->GetStation(), cd2->GetStation());
00965 } else if (this->order == SO_ASCENDING) {
00966 return c1 < c2;
00967 } else {
00968 return c2 < c1;
00969 }
00970 }
00971
00972 bool CargoSorter::SortStation(StationID st1, StationID st2) const
00973 {
00974 static char buf1[MAX_LENGTH_STATION_NAME_CHARS];
00975 static char buf2[MAX_LENGTH_STATION_NAME_CHARS];
00976
00977 if (!Station::IsValidID(st1)) {
00978 return Station::IsValidID(st2) ? this->order == SO_ASCENDING : this->SortId(st1, st2);
00979 } else if (!Station::IsValidID(st2)) {
00980 return order == SO_DESCENDING;
00981 }
00982
00983 SetDParam(0, st1);
00984 GetString(buf1, STR_STATION_NAME, lastof(buf1));
00985 SetDParam(0, st2);
00986 GetString(buf2, STR_STATION_NAME, lastof(buf2));
00987
00988 int res = strcmp(buf1, buf2);
00989 if (res == 0) {
00990 return this->SortId(st1, st2);
00991 } else {
00992 return (this->order == SO_ASCENDING) ? res < 0 : res > 0;
00993 }
00994 }
00995
00999 struct StationViewWindow : public Window {
01003 struct RowDisplay {
01004 RowDisplay(CargoDataEntry *f, StationID n) : filter(f), next_station(n) {}
01005 RowDisplay(CargoDataEntry *f, CargoID n) : filter(f), next_cargo(n) {}
01006
01010 CargoDataEntry *filter;
01011 union {
01015 StationID next_station;
01016
01020 CargoID next_cargo;
01021 };
01022 };
01023
01024 typedef std::vector<RowDisplay> CargoDataVector;
01025
01026 static const int NUM_COLUMNS = 4;
01027
01031 enum Invalidation {
01032 INV_FLOWS = 0x100,
01033 INV_CARGO = 0x200
01034 };
01035
01039 enum Grouping {
01040 GR_SOURCE,
01041 GR_NEXT,
01042 GR_DESTINATION,
01043 GR_CARGO,
01044 };
01045
01049 enum Mode {
01050 MODE_WAITING,
01051 MODE_PLANNED
01052 };
01053
01054 uint expand_shrink_width;
01055 int rating_lines;
01056 int accepts_lines;
01057 Scrollbar *vscroll;
01058
01060 enum AcceptListHeight {
01061 ALH_RATING = 13,
01062 ALH_ACCEPTS = 3,
01063 };
01064
01065 static const StringID _sort_names[];
01066 static const StringID _group_names[];
01067
01074 CargoSortType sortings[NUM_COLUMNS];
01075
01077 SortOrder sort_orders[NUM_COLUMNS];
01078
01079 int scroll_to_row;
01080 int grouping_index;
01081 Mode current_mode;
01082 Grouping groupings[NUM_COLUMNS];
01083
01084 CargoDataEntry expanded_rows;
01085 CargoDataEntry cached_destinations;
01086 CargoDataVector displayed_rows;
01087
01088 StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(),
01089 scroll_to_row(INT_MAX), grouping_index(0)
01090 {
01091 this->rating_lines = ALH_RATING;
01092 this->accepts_lines = ALH_ACCEPTS;
01093
01094 this->CreateNestedTree(desc);
01095 this->vscroll = this->GetScrollbar(WID_SV_SCROLLBAR);
01096
01097 this->FinishInitNested(desc, window_number);
01098
01099 this->groupings[0] = GR_CARGO;
01100 this->sortings[0] = ST_AS_GROUPING;
01101 this->SelectGroupBy(_settings_client.gui.station_gui_group_order);
01102 this->SelectSortBy(_settings_client.gui.station_gui_sort_by);
01103 this->sort_orders[0] = SO_ASCENDING;
01104 this->SelectSortOrder((SortOrder)_settings_client.gui.station_gui_sort_order);
01105 Owner owner = Station::Get(window_number)->owner;
01106 if (owner != OWNER_NONE) this->owner = owner;
01107 }
01108
01109 ~StationViewWindow()
01110 {
01111 Owner owner = Station::Get(this->window_number)->owner;
01112 DeleteWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, owner, this->window_number).Pack(), false);
01113 DeleteWindowById(WC_ROADVEH_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_ROAD, owner, this->window_number).Pack(), false);
01114 DeleteWindowById(WC_SHIPS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_SHIP, owner, this->window_number).Pack(), false);
01115 DeleteWindowById(WC_AIRCRAFT_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_AIRCRAFT, owner, this->window_number).Pack(), false);
01116 }
01117
01128 void ShowCargo(CargoDataEntry *data, CargoID cargo, StationID source, StationID next, StationID dest, uint count)
01129 {
01130 if (count == 0) return;
01131 const CargoDataEntry *expand = &this->expanded_rows;
01132 for (int i = 0; i < NUM_COLUMNS && expand != NULL; ++i) {
01133 switch (groupings[i]) {
01134 case GR_CARGO:
01135 assert(i == 0);
01136 data = data->InsertOrRetrieve(cargo);
01137 expand = expand->Retrieve(cargo);
01138 break;
01139 case GR_SOURCE:
01140 data = data->InsertOrRetrieve(source);
01141 expand = expand->Retrieve(source);
01142 break;
01143 case GR_NEXT:
01144 data = data->InsertOrRetrieve(next);
01145 expand = expand->Retrieve(next);
01146 break;
01147 case GR_DESTINATION:
01148 data = data->InsertOrRetrieve(dest);
01149 expand = expand->Retrieve(dest);
01150 break;
01151 }
01152 }
01153 data->Update(count);
01154 }
01155
01156 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01157 {
01158 switch (widget) {
01159 case WID_SV_WAITING:
01160 resize->height = FONT_HEIGHT_NORMAL;
01161 size->height = WD_FRAMERECT_TOP + 4 * resize->height + WD_FRAMERECT_BOTTOM;
01162 this->expand_shrink_width = max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01163 break;
01164
01165 case WID_SV_ACCEPT_RATING_LIST:
01166 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;
01167 break;
01168
01169 case WID_SV_CLOSE_AIRPORT:
01170 if (!(Station::Get(this->window_number)->facilities & FACIL_AIRPORT)) {
01171
01172 size->width = 0;
01173 resize->width = 0;
01174 fill->width = 0;
01175 }
01176 break;
01177 }
01178 }
01179
01180 virtual void OnPaint()
01181 {
01182 const Station *st = Station::Get(this->window_number);
01183 CargoDataEntry cargo;
01184 BuildCargoList(&cargo, st);
01185
01186 this->vscroll->SetCount(cargo.GetNumChildren());
01187
01188
01189 this->SetWidgetDisabledState(WID_SV_RENAME, st->owner != _local_company);
01190 this->SetWidgetDisabledState(WID_SV_TRAINS, !(st->facilities & FACIL_TRAIN));
01191 this->SetWidgetDisabledState(WID_SV_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
01192 this->SetWidgetDisabledState(WID_SV_SHIPS, !(st->facilities & FACIL_DOCK));
01193 this->SetWidgetDisabledState(WID_SV_PLANES, !(st->facilities & FACIL_AIRPORT));
01194 this->SetWidgetDisabledState(WID_SV_CLOSE_AIRPORT, !(st->facilities & FACIL_AIRPORT) || st->owner != _local_company || st->owner == OWNER_NONE);
01195 this->SetWidgetLoweredState(WID_SV_CLOSE_AIRPORT, (st->facilities & FACIL_AIRPORT) && (st->airport.flags & AIRPORT_CLOSED_block) != 0);
01196
01197 SetDParam(0, st->index);
01198 SetDParam(1, st->facilities);
01199 this->DrawWidgets();
01200
01201 if (!this->IsShaded()) {
01202
01203 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SV_ACCEPT_RATING_LIST);
01204 const Rect r = {wid->pos_x, wid->pos_y, wid->pos_x + wid->current_x - 1, wid->pos_y + wid->current_y - 1};
01205 if (this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
01206 int lines = this->DrawAcceptedCargo(r);
01207 if (lines > this->accepts_lines) {
01208 this->accepts_lines = lines;
01209 this->ReInit();
01210 return;
01211 }
01212 } else {
01213 int lines = this->DrawCargoRatings(r);
01214 if (lines > this->rating_lines) {
01215 this->rating_lines = lines;
01216 this->ReInit();
01217 return;
01218 }
01219 }
01220
01221
01222 this->DrawSortButtonState(WID_SV_SORT_ORDER, sort_orders[1] == SO_ASCENDING ? SBS_UP : SBS_DOWN);
01223
01224 int pos = this->vscroll->GetPosition();
01225
01226 int maxrows = this->vscroll->GetCapacity();
01227
01228 displayed_rows.clear();
01229
01230
01231 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_SV_WAITING);
01232 Rect waiting_rect = {nwi->pos_x, nwi->pos_y, nwi->pos_x + nwi->current_x - 1, nwi->pos_y + nwi->current_y - 1};
01233 this->DrawEntries(&cargo, waiting_rect, pos, maxrows, 0);
01234 scroll_to_row = INT_MAX;
01235 }
01236 }
01237
01238 virtual void SetStringParameters(int widget) const
01239 {
01240 if (widget == WID_SV_CAPTION) {
01241 const Station *st = Station::Get(this->window_number);
01242 SetDParam(0, st->index);
01243 SetDParam(1, st->facilities);
01244 }
01245 }
01246
01252 void RecalcDestinations(CargoID i)
01253 {
01254 const Station *st = Station::Get(this->window_number);
01255 CargoDataEntry *cargo_entry = cached_destinations.InsertOrRetrieve(i);
01256 cargo_entry->Clear();
01257
01258 const FlowStatMap &flows = st->goods[i].flows;
01259 for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
01260 StationID from = it->first;
01261 CargoDataEntry *source_entry = cargo_entry->InsertOrRetrieve(from);
01262 const FlowStat::SharesMap *shares = it->second.GetShares();
01263 uint32 prev_count = 0;
01264 for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) {
01265 StationID via = flow_it->second;
01266 CargoDataEntry *via_entry = source_entry->InsertOrRetrieve(via);
01267 if (via == this->window_number) {
01268 via_entry->InsertOrRetrieve(via)->Update(flow_it->first - prev_count);
01269 } else {
01270 EstimateDestinations(i, from, via, flow_it->first - prev_count, via_entry);
01271 }
01272 prev_count = flow_it->first;
01273 }
01274 }
01275 }
01276
01286 void EstimateDestinations(CargoID cargo, StationID source, StationID next, uint count, CargoDataEntry *dest)
01287 {
01288 if (Station::IsValidID(next) && Station::IsValidID(source)) {
01289 CargoDataEntry tmp;
01290 const FlowStatMap &flowmap = Station::Get(next)->goods[cargo].flows;
01291 FlowStatMap::const_iterator map_it = flowmap.find(source);
01292 if (map_it != flowmap.end()) {
01293 const FlowStat::SharesMap *shares = map_it->second.GetShares();
01294 uint32 prev_count = 0;
01295 for (FlowStat::SharesMap::const_iterator i = shares->begin(); i != shares->end(); ++i) {
01296 tmp.InsertOrRetrieve(i->second)->Update(i->first - prev_count);
01297 prev_count = i->first;
01298 }
01299 }
01300
01301 if (tmp.GetCount() == 0) {
01302 dest->InsertOrRetrieve(INVALID_STATION)->Update(count);
01303 } else {
01304 uint sum_estimated = 0;
01305 while (sum_estimated < count) {
01306 for (CargoDataSet::iterator i = tmp.Begin(); i != tmp.End() && sum_estimated < count; ++i) {
01307 CargoDataEntry *child = *i;
01308 uint estimate = DivideApprox(child->GetCount() * count, tmp.GetCount());
01309 if (estimate == 0) estimate = 1;
01310
01311 sum_estimated += estimate;
01312 if (sum_estimated > count) {
01313 estimate -= sum_estimated - count;
01314 sum_estimated = count;
01315 }
01316
01317 if (estimate > 0) {
01318 if (child->GetStation() == next) {
01319 dest->InsertOrRetrieve(next)->Update(estimate);
01320 } else {
01321 EstimateDestinations(cargo, source, child->GetStation(), estimate, dest);
01322 }
01323 }
01324 }
01325
01326 }
01327 }
01328 } else {
01329 dest->InsertOrRetrieve(INVALID_STATION)->Update(count);
01330 }
01331 }
01332
01339 void BuildFlowList(CargoID i, const FlowStatMap &flows, CargoDataEntry *cargo)
01340 {
01341 const CargoDataEntry *source_dest = this->cached_destinations.Retrieve(i);
01342 for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
01343 StationID from = it->first;
01344 const CargoDataEntry *source_entry = source_dest->Retrieve(from);
01345 const FlowStat::SharesMap *shares = it->second.GetShares();
01346 for (FlowStat::SharesMap::const_iterator flow_it = shares->begin(); flow_it != shares->end(); ++flow_it) {
01347 const CargoDataEntry *via_entry = source_entry->Retrieve(flow_it->second);
01348 for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
01349 CargoDataEntry *dest_entry = *dest_it;
01350 ShowCargo(cargo, i, from, flow_it->second, dest_entry->GetStation(), dest_entry->GetCount());
01351 }
01352 }
01353 }
01354 }
01355
01362 void BuildCargoList(CargoID i, const StationCargoList &packets, CargoDataEntry *cargo)
01363 {
01364 const CargoDataEntry *source_dest = this->cached_destinations.Retrieve(i);
01365 for (StationCargoList::ConstIterator it = packets.Packets()->begin(); it != packets.Packets()->end(); it++) {
01366 const CargoPacket *cp = *it;
01367 StationID next = it.GetKey();
01368
01369 const CargoDataEntry *source_entry = source_dest->Retrieve(cp->SourceStation());
01370 if (source_entry == NULL) {
01371 ShowCargo(cargo, i, cp->SourceStation(), next, INVALID_STATION, cp->Count());
01372 continue;
01373 }
01374
01375 const CargoDataEntry *via_entry = source_entry->Retrieve(next);
01376 if (via_entry == NULL) {
01377 ShowCargo(cargo, i, cp->SourceStation(), next, INVALID_STATION, cp->Count());
01378 continue;
01379 }
01380
01381 for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
01382 CargoDataEntry *dest_entry = *dest_it;
01383 uint val = DivideApprox(cp->Count() * dest_entry->GetCount(), via_entry->GetCount());
01384 ShowCargo(cargo, i, cp->SourceStation(), next, dest_entry->GetStation(), val);
01385 }
01386 }
01387 }
01388
01394 void BuildCargoList(CargoDataEntry *cargo, const Station *st)
01395 {
01396 for (CargoID i = 0; i < NUM_CARGO; i++) {
01397
01398 if (this->cached_destinations.Retrieve(i) == NULL) {
01399 this->RecalcDestinations(i);
01400 }
01401
01402 if (this->current_mode == MODE_WAITING) {
01403 this->BuildCargoList(i, st->goods[i].cargo, cargo);
01404 } else {
01405 this->BuildFlowList(i, st->goods[i].flows, cargo);
01406 }
01407 }
01408 }
01409
01414 void SetDisplayedRow(const CargoDataEntry *data)
01415 {
01416 std::list<StationID> stations;
01417 const CargoDataEntry *parent = data->GetParent();
01418 if (parent->GetParent() == NULL) {
01419 this->displayed_rows.push_back(RowDisplay(&this->expanded_rows, data->GetCargo()));
01420 return;
01421 }
01422
01423 StationID next = data->GetStation();
01424 while (parent->GetParent()->GetParent() != NULL) {
01425 stations.push_back(parent->GetStation());
01426 parent = parent->GetParent();
01427 }
01428
01429 CargoID cargo = parent->GetCargo();
01430 CargoDataEntry *filter = this->expanded_rows.Retrieve(cargo);
01431 while (!stations.empty()) {
01432 filter = filter->Retrieve(stations.back());
01433 stations.pop_back();
01434 }
01435
01436 this->displayed_rows.push_back(RowDisplay(filter, next));
01437 }
01438
01447 StringID GetEntryString(StationID station, StringID here, StringID other_station, StringID any)
01448 {
01449 if (station == this->window_number) {
01450 return here;
01451 } else if (station != INVALID_STATION) {
01452 SetDParam(2, station);
01453 return other_station;
01454 } else {
01455 return any;
01456 }
01457 }
01458
01466 StringID SearchNonStop(CargoDataEntry *cd, StationID station, int column)
01467 {
01468 CargoDataEntry *parent = cd->GetParent();
01469 for (int i = column - 1; i > 0; --i) {
01470 if (this->groupings[i] == GR_DESTINATION) {
01471 if (parent->GetStation() == station) {
01472 return STR_STATION_VIEW_NONSTOP;
01473 } else {
01474 return STR_STATION_VIEW_VIA;
01475 }
01476 }
01477 parent = parent->GetParent();
01478 }
01479
01480 if (this->groupings[column + 1] == GR_DESTINATION) {
01481 CargoDataSet::iterator begin = cd->Begin();
01482 CargoDataSet::iterator end = cd->End();
01483 if (begin != end && ++(cd->Begin()) == end && (*(begin))->GetStation() == station) {
01484 return STR_STATION_VIEW_NONSTOP;
01485 } else {
01486 return STR_STATION_VIEW_VIA;
01487 }
01488 }
01489
01490 return STR_STATION_VIEW_VIA;
01491 }
01492
01503 int DrawEntries(CargoDataEntry *entry, Rect &r, int pos, int maxrows, int column, CargoID cargo = CT_INVALID)
01504 {
01505 if (this->sortings[column] == ST_AS_GROUPING) {
01506 if (this->groupings[column] != GR_CARGO) {
01507 entry->Resort(ST_STATION_STRING, this->sort_orders[column]);
01508 }
01509 } else {
01510 entry->Resort(ST_COUNT, this->sort_orders[column]);
01511 }
01512 for (CargoDataSet::iterator i = entry->Begin(); i != entry->End(); ++i) {
01513 CargoDataEntry *cd = *i;
01514
01515 if (this->groupings[column] == GR_CARGO) cargo = cd->GetCargo();
01516
01517 if (pos > -maxrows && pos <= 0) {
01518 StringID str = STR_EMPTY;
01519 int y = r.top + WD_FRAMERECT_TOP - pos * FONT_HEIGHT_NORMAL;
01520 SetDParam(0, cargo);
01521 SetDParam(1, cd->GetCount());
01522
01523 if (this->groupings[column] == GR_CARGO) {
01524 str = STR_STATION_VIEW_WAITING_CARGO;
01525 DrawCargoIcons(cd->GetCargo(), cd->GetCount(), r.left + WD_FRAMERECT_LEFT + this->expand_shrink_width, r.right - WD_FRAMERECT_RIGHT - this->expand_shrink_width, y);
01526 } else {
01527 StationID station = cd->GetStation();
01528
01529 switch (this->groupings[column]) {
01530 case GR_SOURCE:
01531 str = this->GetEntryString(station, STR_STATION_VIEW_FROM_HERE, STR_STATION_VIEW_FROM, STR_STATION_VIEW_FROM_ANY);
01532 break;
01533 case GR_NEXT:
01534 str = this->GetEntryString(station, STR_STATION_VIEW_VIA_HERE, STR_STATION_VIEW_VIA, STR_STATION_VIEW_VIA_ANY);
01535 if (str == STR_STATION_VIEW_VIA) str = SearchNonStop(cd, station, column);
01536 break;
01537 case GR_DESTINATION:
01538 str = this->GetEntryString(station, STR_STATION_VIEW_TO_HERE, STR_STATION_VIEW_TO, STR_STATION_VIEW_TO_ANY);
01539 break;
01540 default:
01541 NOT_REACHED();
01542 }
01543 if (pos == -this->scroll_to_row && Station::IsValidID(station)) {
01544 ScrollMainWindowToTile(Station::Get(station)->xy);
01545 }
01546 }
01547
01548 bool rtl = _current_text_dir == TD_RTL;
01549 int text_left = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT + column * this->expand_shrink_width;
01550 int text_right = rtl ? r.right - WD_FRAMERECT_LEFT - column * this->expand_shrink_width : r.right - this->expand_shrink_width;
01551 int shrink_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
01552 int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
01553
01554 DrawString(text_left, text_right, y, str);
01555
01556 if (column < NUM_COLUMNS - 1) {
01557 const char *sym = cd->GetNumChildren() > 0 ? "-" : "+";
01558 DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW);
01559 }
01560 SetDisplayedRow(cd);
01561 }
01562 pos = DrawEntries(cd, r, --pos, maxrows, column + 1, cargo);
01563 }
01564 return pos;
01565 }
01566
01572 int DrawAcceptedCargo(const Rect &r) const
01573 {
01574 const Station *st = Station::Get(this->window_number);
01575
01576 uint32 cargo_mask = 0;
01577 for (CargoID i = 0; i < NUM_CARGO; i++) {
01578 if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE)) SetBit(cargo_mask, i);
01579 }
01580 SetDParam(0, cargo_mask);
01581 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);
01582 return CeilDiv(bottom - r.top - WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL);
01583 }
01584
01590 int DrawCargoRatings(const Rect &r) const
01591 {
01592 const Station *st = Station::Get(this->window_number);
01593 int y = r.top + WD_FRAMERECT_TOP;
01594
01595 if (st->town->exclusive_counter > 0) {
01596 SetDParam(0, st->town->exclusivity);
01597 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);
01598 y += WD_PAR_VSEP_WIDE;
01599 }
01600
01601 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_CARGO_RATINGS_TITLE);
01602 y += FONT_HEIGHT_NORMAL;
01603
01604 const CargoSpec *cs;
01605 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
01606 const GoodsEntry *ge = &st->goods[cs->Index()];
01607 if (!HasBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP)) continue;
01608
01609 SetDParam(0, cs->name);
01610 SetDParam(1, ge->supply);
01611 SetDParam(3, ToPercent8(ge->rating));
01612 SetDParam(2, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
01613 DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_SUPPLY_RATING);
01614 y += FONT_HEIGHT_NORMAL;
01615 }
01616 return CeilDiv(y - r.top - WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL);
01617 }
01618
01624 template<class ID>
01625 void HandleCargoWaitingClick(CargoDataEntry *filter, ID next)
01626 {
01627 if (filter->Retrieve(next) != NULL) {
01628 filter->Remove(next);
01629 } else {
01630 filter->InsertOrRetrieve(next);
01631 }
01632 }
01633
01638 void HandleCargoWaitingClick(int row)
01639 {
01640 if (row < 0 || (uint)row >= this->displayed_rows.size()) return;
01641 if (_ctrl_pressed) {
01642 this->scroll_to_row = row;
01643 } else {
01644 RowDisplay &display = this->displayed_rows[row];
01645 if (display.filter == &this->expanded_rows) {
01646 this->HandleCargoWaitingClick<CargoID>(display.filter, display.next_cargo);
01647 } else {
01648 this->HandleCargoWaitingClick<StationID>(display.filter, display.next_station);
01649 }
01650 }
01651 this->SetWidgetDirty(WID_SV_WAITING);
01652 }
01653
01654 virtual void OnClick(Point pt, int widget, int click_count)
01655 {
01656 switch (widget) {
01657 case WID_SV_WAITING:
01658 this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL) - this->vscroll->GetPosition());
01659 break;
01660
01661 case WID_SV_LOCATION:
01662 if (_ctrl_pressed) {
01663 ShowExtraViewPortWindow(Station::Get(this->window_number)->xy);
01664 } else {
01665 ScrollMainWindowToTile(Station::Get(this->window_number)->xy);
01666 }
01667 break;
01668
01669 case WID_SV_ACCEPTS_RATINGS: {
01670
01671 int height_change;
01672 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS);
01673 if (this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
01674 nwi->SetDataTip(STR_STATION_VIEW_ACCEPTS_BUTTON, STR_STATION_VIEW_ACCEPTS_TOOLTIP);
01675 height_change = this->rating_lines - this->accepts_lines;
01676 } else {
01677 nwi->SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP);
01678 height_change = this->accepts_lines - this->rating_lines;
01679 }
01680 this->ReInit(0, height_change * FONT_HEIGHT_NORMAL);
01681 break;
01682 }
01683
01684 case WID_SV_RENAME:
01685 SetDParam(0, this->window_number);
01686 ShowQueryString(STR_STATION_NAME, STR_STATION_VIEW_RENAME_STATION_CAPTION, MAX_LENGTH_STATION_NAME_CHARS,
01687 this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
01688 break;
01689
01690 case WID_SV_CLOSE_AIRPORT:
01691 DoCommandP(0, this->window_number, 0, CMD_OPEN_CLOSE_AIRPORT);
01692 break;
01693
01694 case WID_SV_TRAINS:
01695 case WID_SV_ROADVEHS:
01696 case WID_SV_SHIPS:
01697 case WID_SV_PLANES: {
01698 Owner owner = Station::Get(this->window_number)->owner;
01699 ShowVehicleListWindow(owner, (VehicleType)(widget - WID_SV_TRAINS), (StationID)this->window_number);
01700 break;
01701 }
01702
01703 case WID_SV_SORT_BY: {
01704 ShowDropDownMenu(this, _sort_names, this->current_mode, WID_SV_SORT_BY, 0, 0);
01705 break;
01706 }
01707
01708 case WID_SV_GROUP_BY: {
01709 ShowDropDownMenu(this, _group_names, this->grouping_index, WID_SV_GROUP_BY, 0, 0);
01710 break;
01711 }
01712
01713 case WID_SV_SORT_ORDER: {
01714 this->SelectSortOrder(this->sort_orders[1] == SO_ASCENDING ? SO_DESCENDING : SO_ASCENDING);
01715 this->SetTimeout();
01716 this->LowerWidget(WID_SV_SORT_ORDER);
01717 break;
01718 }
01719 }
01720 }
01721
01726 void SelectSortOrder(SortOrder order)
01727 {
01728 this->sort_orders[1] = this->sort_orders[2] = this->sort_orders[3] = order;
01729 _settings_client.gui.station_gui_sort_order = this->sort_orders[1];
01730 this->SetDirty();
01731 }
01732
01737 void SelectSortBy(int index)
01738 {
01739 _settings_client.gui.station_gui_sort_by = index;
01740 switch (_sort_names[index]) {
01741 case STR_STATION_VIEW_WAITING_STATION:
01742 this->current_mode = MODE_WAITING;
01743 this->sortings[1] = this->sortings[2] = this->sortings[3] = ST_AS_GROUPING;
01744 break;
01745 case STR_STATION_VIEW_WAITING_AMOUNT:
01746 this->current_mode = MODE_WAITING;
01747 this->sortings[1] = this->sortings[2] = this->sortings[3] = ST_COUNT;
01748 break;
01749 case STR_STATION_VIEW_PLANNED_STATION:
01750 this->current_mode = MODE_PLANNED;
01751 this->sortings[1] = this->sortings[2] = this->sortings[3] = ST_AS_GROUPING;
01752 break;
01753 case STR_STATION_VIEW_PLANNED_AMOUNT:
01754 this->current_mode = MODE_PLANNED;
01755 this->sortings[1] = this->sortings[2] = this->sortings[3] = ST_COUNT;
01756 break;
01757 default:
01758 NOT_REACHED();
01759 }
01760
01761 this->GetWidget<NWidgetCore>(WID_SV_SORT_BY)->widget_data = _sort_names[index];
01762 this->SetDirty();
01763 }
01764
01769 void SelectGroupBy(int index)
01770 {
01771 this->grouping_index = index;
01772 _settings_client.gui.station_gui_group_order = index;
01773 this->GetWidget<NWidgetCore>(WID_SV_GROUP_BY)->widget_data = _group_names[index];
01774 switch (_group_names[index]) {
01775 case STR_STATION_VIEW_GROUP_S_V_D:
01776 this->groupings[1] = GR_SOURCE;
01777 this->groupings[2] = GR_NEXT;
01778 this->groupings[3] = GR_DESTINATION;
01779 break;
01780 case STR_STATION_VIEW_GROUP_S_D_V:
01781 this->groupings[1] = GR_SOURCE;
01782 this->groupings[2] = GR_DESTINATION;
01783 this->groupings[3] = GR_NEXT;
01784 break;
01785 case STR_STATION_VIEW_GROUP_V_S_D:
01786 this->groupings[1] = GR_NEXT;
01787 this->groupings[2] = GR_SOURCE;
01788 this->groupings[3] = GR_DESTINATION;
01789 break;
01790 case STR_STATION_VIEW_GROUP_V_D_S:
01791 this->groupings[1] = GR_NEXT;
01792 this->groupings[2] = GR_DESTINATION;
01793 this->groupings[3] = GR_SOURCE;
01794 break;
01795 case STR_STATION_VIEW_GROUP_D_S_V:
01796 this->groupings[1] = GR_DESTINATION;
01797 this->groupings[2] = GR_SOURCE;
01798 this->groupings[3] = GR_NEXT;
01799 break;
01800 case STR_STATION_VIEW_GROUP_D_V_S:
01801 this->groupings[1] = GR_DESTINATION;
01802 this->groupings[2] = GR_NEXT;
01803 this->groupings[3] = GR_SOURCE;
01804 break;
01805 }
01806 this->SetDirty();
01807 }
01808
01809 virtual void OnDropdownSelect(int widget, int index)
01810 {
01811 if (widget == WID_SV_SORT_BY) {
01812 this->SelectSortBy(index);
01813 } else {
01814 this->SelectGroupBy(index);
01815 }
01816 }
01817
01818 virtual void OnQueryTextFinished(char *str)
01819 {
01820 if (str == NULL) return;
01821
01822 DoCommandP(0, this->window_number, 0, CMD_RENAME_STATION | CMD_MSG(STR_ERROR_CAN_T_RENAME_STATION), NULL, str);
01823 }
01824
01825 virtual void OnResize()
01826 {
01827 this->vscroll->SetCapacityFromWidget(this, WID_SV_WAITING, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
01828 }
01829
01835 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01836 {
01837 if (gui_scope) {
01838 if (data >= 0 && data < NUM_CARGO) {
01839 this->cached_destinations.Remove((CargoID)data);
01840 } else {
01841 this->ReInit();
01842 }
01843 }
01844 }
01845 };
01846
01847 const StringID StationViewWindow::_sort_names[] = {
01848 STR_STATION_VIEW_WAITING_STATION,
01849 STR_STATION_VIEW_WAITING_AMOUNT,
01850 STR_STATION_VIEW_PLANNED_STATION,
01851 STR_STATION_VIEW_PLANNED_AMOUNT,
01852 INVALID_STRING_ID
01853 };
01854
01855 const StringID StationViewWindow::_group_names[] = {
01856 STR_STATION_VIEW_GROUP_S_V_D,
01857 STR_STATION_VIEW_GROUP_S_D_V,
01858 STR_STATION_VIEW_GROUP_V_S_D,
01859 STR_STATION_VIEW_GROUP_V_D_S,
01860 STR_STATION_VIEW_GROUP_D_S_V,
01861 STR_STATION_VIEW_GROUP_D_V_S,
01862 INVALID_STRING_ID
01863 };
01864
01865 static const WindowDesc _station_view_desc(
01866 WDP_AUTO, 249, 117,
01867 WC_STATION_VIEW, WC_NONE,
01868 0,
01869 _nested_station_view_widgets, lengthof(_nested_station_view_widgets)
01870 );
01871
01877 void ShowStationViewWindow(StationID station)
01878 {
01879 AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station);
01880 }
01881
01883 struct TileAndStation {
01884 TileIndex tile;
01885 StationID station;
01886 };
01887
01888 static SmallVector<TileAndStation, 8> _deleted_stations_nearby;
01889 static SmallVector<StationID, 8> _stations_nearby_list;
01890
01898 template <class T>
01899 static bool AddNearbyStation(TileIndex tile, void *user_data)
01900 {
01901 TileArea *ctx = (TileArea *)user_data;
01902
01903
01904 for (uint i = 0; i < _deleted_stations_nearby.Length(); i++) {
01905 TileAndStation *ts = _deleted_stations_nearby.Get(i);
01906 if (ts->tile == tile) {
01907 *_stations_nearby_list.Append() = _deleted_stations_nearby[i].station;
01908 _deleted_stations_nearby.Erase(ts);
01909 i--;
01910 }
01911 }
01912
01913
01914 if (!IsTileType(tile, MP_STATION)) return false;
01915
01916 StationID sid = GetStationIndex(tile);
01917
01918
01919 if (!T::IsValidID(sid)) return false;
01920
01921 T *st = T::Get(sid);
01922 if (st->owner != _local_company || _stations_nearby_list.Contains(sid)) return false;
01923
01924 if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST).Succeeded()) {
01925 *_stations_nearby_list.Append() = sid;
01926 }
01927
01928 return false;
01929 }
01930
01940 template <class T>
01941 static const T *FindStationsNearby(TileArea ta, bool distant_join)
01942 {
01943 TileArea ctx = ta;
01944
01945 _stations_nearby_list.Clear();
01946 _deleted_stations_nearby.Clear();
01947
01948
01949 TILE_AREA_LOOP(t, ta) {
01950 if (t < MapSize() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return T::GetByTile(t);
01951 }
01952
01953
01954 const BaseStation *st;
01955 FOR_ALL_BASE_STATIONS(st) {
01956 if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company) {
01957
01958 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) {
01959 TileAndStation *ts = _deleted_stations_nearby.Append();
01960 ts->tile = st->xy;
01961 ts->station = st->index;
01962
01963
01964 if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) &&
01965 IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) {
01966 AddNearbyStation<T>(st->xy, &ctx);
01967 }
01968 }
01969 }
01970 }
01971
01972
01973
01974
01975 if (distant_join && min(ta.w, ta.h) >= _settings_game.station.station_spread) return NULL;
01976 uint max_dist = distant_join ? _settings_game.station.station_spread - min(ta.w, ta.h) : 1;
01977
01978 TileIndex tile = TILE_ADD(ctx.tile, TileOffsByDir(DIR_N));
01979 CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
01980
01981 return NULL;
01982 }
01983
01984 static const NWidgetPart _nested_select_station_widgets[] = {
01985 NWidget(NWID_HORIZONTAL),
01986 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
01987 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_JS_CAPTION), SetDataTip(STR_JOIN_STATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01988 EndContainer(),
01989 NWidget(NWID_HORIZONTAL),
01990 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_JS_PANEL), SetResize(1, 0), SetScrollbar(WID_JS_SCROLLBAR), EndContainer(),
01991 NWidget(NWID_VERTICAL),
01992 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_JS_SCROLLBAR),
01993 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
01994 EndContainer(),
01995 EndContainer(),
01996 };
01997
02002 template <class T>
02003 struct SelectStationWindow : Window {
02004 CommandContainer select_station_cmd;
02005 TileArea area;
02006 Scrollbar *vscroll;
02007
02008 SelectStationWindow(const WindowDesc *desc, CommandContainer cmd, TileArea ta) :
02009 Window(),
02010 select_station_cmd(cmd),
02011 area(ta)
02012 {
02013 this->CreateNestedTree(desc);
02014 this->vscroll = this->GetScrollbar(WID_JS_SCROLLBAR);
02015 this->GetWidget<NWidgetCore>(WID_JS_CAPTION)->widget_data = T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
02016 this->FinishInitNested(desc, 0);
02017 this->OnInvalidateData(0);
02018 }
02019
02020 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
02021 {
02022 if (widget != WID_JS_PANEL) return;
02023
02024
02025 Dimension d = GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
02026 for (uint i = 0; i < _stations_nearby_list.Length(); i++) {
02027 const T *st = T::Get(_stations_nearby_list[i]);
02028 SetDParam(0, st->index);
02029 SetDParam(1, st->facilities);
02030 d = maxdim(d, GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION));
02031 }
02032
02033 resize->height = d.height;
02034 d.height *= 5;
02035 d.width += WD_FRAMERECT_RIGHT + WD_FRAMERECT_LEFT;
02036 d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
02037 *size = d;
02038 }
02039
02040 virtual void DrawWidget(const Rect &r, int widget) const
02041 {
02042 if (widget != WID_JS_PANEL) return;
02043
02044 uint y = r.top + WD_FRAMERECT_TOP;
02045 if (this->vscroll->GetPosition() == 0) {
02046 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);
02047 y += this->resize.step_height;
02048 }
02049
02050 for (uint i = max<uint>(1, this->vscroll->GetPosition()); i <= _stations_nearby_list.Length(); ++i, y += this->resize.step_height) {
02051
02052 if (i - this->vscroll->GetPosition() >= this->vscroll->GetCapacity()) break;
02053
02054 const T *st = T::Get(_stations_nearby_list[i - 1]);
02055 SetDParam(0, st->index);
02056 SetDParam(1, st->facilities);
02057 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);
02058 }
02059 }
02060
02061 virtual void OnClick(Point pt, int widget, int click_count)
02062 {
02063 if (widget != WID_JS_PANEL) return;
02064
02065 uint st_index = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_JS_PANEL, WD_FRAMERECT_TOP);
02066 bool distant_join = (st_index > 0);
02067 if (distant_join) st_index--;
02068
02069 if (distant_join && st_index >= _stations_nearby_list.Length()) return;
02070
02071
02072 SB(this->select_station_cmd.p2, 16, 16,
02073 (distant_join ? _stations_nearby_list[st_index] : NEW_STATION));
02074
02075
02076 DoCommandP(&this->select_station_cmd);
02077
02078
02079 DeleteWindowById(WC_SELECT_STATION, 0);
02080 }
02081
02082 virtual void OnTick()
02083 {
02084 if (_thd.dirty & 2) {
02085 _thd.dirty &= ~2;
02086 this->SetDirty();
02087 }
02088 }
02089
02090 virtual void OnResize()
02091 {
02092 this->vscroll->SetCapacityFromWidget(this, WID_JS_PANEL, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
02093 }
02094
02100 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
02101 {
02102 if (!gui_scope) return;
02103 FindStationsNearby<T>(this->area, true);
02104 this->vscroll->SetCount(_stations_nearby_list.Length() + 1);
02105 this->SetDirty();
02106 }
02107 };
02108
02109 static const WindowDesc _select_station_desc(
02110 WDP_AUTO, 200, 180,
02111 WC_SELECT_STATION, WC_NONE,
02112 WDF_CONSTRUCTION,
02113 _nested_select_station_widgets, lengthof(_nested_select_station_widgets)
02114 );
02115
02116
02124 template <class T>
02125 static bool StationJoinerNeeded(CommandContainer cmd, TileArea ta)
02126 {
02127
02128 if (!_settings_game.station.distant_join_stations) return false;
02129
02130
02131
02132 Window *selection_window = FindWindowById(WC_SELECT_STATION, 0);
02133 if (selection_window != NULL) {
02134
02135 delete selection_window;
02136 UpdateTileSelection();
02137 }
02138
02139
02140 if (!_ctrl_pressed) return false;
02141
02142
02143 if (DoCommand(&cmd, CommandFlagsToDCFlags(GetCommandFlags(cmd.cmd))).Failed()) return false;
02144
02145
02146
02147
02148 const T *st = FindStationsNearby<T>(ta, false);
02149 return st == NULL && (_settings_game.station.adjacent_stations || _stations_nearby_list.Length() == 0);
02150 }
02151
02158 template <class T>
02159 void ShowSelectBaseStationIfNeeded(CommandContainer cmd, TileArea ta)
02160 {
02161 if (StationJoinerNeeded<T>(cmd, ta)) {
02162 if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
02163 new SelectStationWindow<T>(&_select_station_desc, cmd, ta);
02164 } else {
02165 DoCommandP(&cmd);
02166 }
02167 }
02168
02174 void ShowSelectStationIfNeeded(CommandContainer cmd, TileArea ta)
02175 {
02176 ShowSelectBaseStationIfNeeded<Station>(cmd, ta);
02177 }
02178
02184 void ShowSelectWaypointIfNeeded(CommandContainer cmd, TileArea ta)
02185 {
02186 ShowSelectBaseStationIfNeeded<Waypoint>(cmd, ta);
02187 }