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 "cargodest_base.h"
00033 #include "industry.h"
00034 #include "town.h"
00035
00036 #include "table/strings.h"
00037
00045 static int DrawCargoListText(uint32 cargo_mask, const Rect &r, StringID prefix)
00046 {
00047 bool first = true;
00048 char string[512];
00049 char *b = string;
00050
00051 CargoID i;
00052 FOR_EACH_SET_CARGO_ID(i, cargo_mask) {
00053 if (b >= lastof(string) - (1 + 2 * 4)) break;
00054
00055 if (first) {
00056 first = false;
00057 } else {
00058
00059 *b++ = ',';
00060 *b++ = ' ';
00061 }
00062 b = InlineString(b, CargoSpec::Get(i)->name);
00063 }
00064
00065
00066 if (first) b = InlineString(b, STR_JUST_NOTHING);
00067
00068 *b = '\0';
00069
00070
00071 assert(b < endof(string));
00072
00073 SetDParamStr(0, string);
00074 return DrawStringMultiLine(r.left, r.right, r.top, r.bottom, prefix);
00075 }
00076
00087 int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies)
00088 {
00089 TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
00090 if (tile < MapSize()) {
00091 CargoArray cargos;
00092 if (supplies) {
00093 cargos = GetProductionAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00094 } else {
00095 cargos = GetAcceptanceAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00096 }
00097
00098
00099 uint32 cargo_mask = 0;
00100 for (CargoID i = 0; i < NUM_CARGO; i++) {
00101 switch (sct) {
00102 case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00103 case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00104 case SCT_ALL: break;
00105 default: NOT_REACHED();
00106 }
00107 if (cargos[i] >= (supplies ? 1U : 8U)) SetBit(cargo_mask, i);
00108 }
00109 Rect r = {left, top, right, INT32_MAX};
00110 return DrawCargoListText(cargo_mask, r, supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO);
00111 }
00112
00113 return top;
00114 }
00115
00121 void CheckRedrawStationCoverage(const Window *w)
00122 {
00123 if (_thd.dirty & 1) {
00124 _thd.dirty &= ~1;
00125 w->SetDirty();
00126 }
00127 }
00128
00144 static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating)
00145 {
00146 static const uint units_full = 576;
00147 static const uint rating_full = 224;
00148
00149 const CargoSpec *cs = CargoSpec::Get(type);
00150 if (!cs->IsValid()) return;
00151
00152 int colour = cs->rating_colour;
00153 uint w = (minu(amount, units_full) + 5) / 36;
00154
00155 int height = GetCharacterHeight(FS_SMALL);
00156
00157
00158 if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour);
00159
00160
00161
00162 if (w == 0) {
00163 uint rest = amount / 5;
00164 if (rest != 0) {
00165 w += left;
00166 GfxFillRect(w, y + height - rest, w, y + height, colour);
00167 }
00168 }
00169
00170 DrawString(left + 1, right, y, cs->abbrev, TC_BLACK);
00171
00172
00173 y += height + 2;
00174 GfxFillRect(left + 1, y, left + 14, y, PC_RED);
00175 rating = minu(rating, rating_full) / 16;
00176 if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, PC_GREEN);
00177 }
00178
00179 typedef GUIList<const Station*> GUIStationList;
00180
00182 enum StationListWidgets {
00183 SLW_CAPTION,
00184 SLW_LIST,
00185 SLW_SCROLLBAR,
00186
00187
00188 SLW_TRAIN,
00189 SLW_TRUCK,
00190 SLW_BUS,
00191 SLW_AIRPLANE,
00192 SLW_SHIP,
00193 SLW_FACILALL,
00194
00195 SLW_NOCARGOWAITING,
00196 SLW_CARGOALL,
00197
00198 SLW_SORTBY,
00199 SLW_SORTDROPBTN,
00200
00201 SLW_CARGOSTART,
00202 };
00203
00207 class CompanyStationsWindow : public Window
00208 {
00209 protected:
00210
00211 static Listing last_sorting;
00212 static byte facilities;
00213 static bool include_empty;
00214 static const uint32 cargo_filter_max;
00215 static uint32 cargo_filter;
00216 static const Station *last_station;
00217
00218
00219 static const StringID sorter_names[];
00220 static GUIStationList::SortFunction * const sorter_funcs[];
00221
00222 GUIStationList stations;
00223 Scrollbar *vscroll;
00224
00230 void BuildStationsList(const Owner owner)
00231 {
00232 if (!this->stations.NeedRebuild()) return;
00233
00234 DEBUG(misc, 3, "Building station list for company %d", owner);
00235
00236 this->stations.Clear();
00237
00238 const Station *st;
00239 FOR_ALL_STATIONS(st) {
00240 if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, true, owner))) {
00241 if (this->facilities & st->facilities) {
00242 int num_waiting_cargo = 0;
00243 for (CargoID j = 0; j < NUM_CARGO; j++) {
00244 if (HasBit(st->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) {
00245 num_waiting_cargo++;
00246 if (HasBit(this->cargo_filter, j)) {
00247 *this->stations.Append() = st;
00248 break;
00249 }
00250 }
00251 }
00252
00253 if (num_waiting_cargo == 0 && this->include_empty) {
00254 *this->stations.Append() = st;
00255 }
00256 }
00257 }
00258 }
00259
00260 this->stations.Compact();
00261 this->stations.RebuildDone();
00262
00263 this->vscroll->SetCount(this->stations.Length());
00264 }
00265
00267 static int CDECL StationNameSorter(const Station * const *a, const Station * const *b)
00268 {
00269 static char buf_cache[64];
00270 char buf[64];
00271
00272 SetDParam(0, (*a)->index);
00273 GetString(buf, STR_STATION_NAME, lastof(buf));
00274
00275 if (*b != last_station) {
00276 last_station = *b;
00277 SetDParam(0, (*b)->index);
00278 GetString(buf_cache, STR_STATION_NAME, lastof(buf_cache));
00279 }
00280
00281 return strcmp(buf, buf_cache);
00282 }
00283
00285 static int CDECL StationTypeSorter(const Station * const *a, const Station * const *b)
00286 {
00287 return (*a)->facilities - (*b)->facilities;
00288 }
00289
00291 static int CDECL StationWaitingSorter(const Station * const *a, const Station * const *b)
00292 {
00293 Money diff = 0;
00294
00295 CargoID j;
00296 FOR_EACH_SET_CARGO_ID(j, cargo_filter) {
00297 if (!(*a)->goods[j].cargo.Empty()) diff += GetTransportedGoodsIncome((*a)->goods[j].cargo.Count(), 20, 50, j);
00298 if (!(*b)->goods[j].cargo.Empty()) diff -= GetTransportedGoodsIncome((*b)->goods[j].cargo.Count(), 20, 50, j);
00299 }
00300
00301 return ClampToI32(diff);
00302 }
00303
00305 static int CDECL StationRatingMaxSorter(const Station * const *a, const Station * const *b)
00306 {
00307 byte maxr1 = 0;
00308 byte maxr2 = 0;
00309
00310 CargoID j;
00311 FOR_EACH_SET_CARGO_ID(j, cargo_filter) {
00312 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr1 = max(maxr1, (*a)->goods[j].rating);
00313 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr2 = max(maxr2, (*b)->goods[j].rating);
00314 }
00315
00316 return maxr1 - maxr2;
00317 }
00318
00320 static int CDECL StationRatingMinSorter(const Station * const *a, const Station * const *b)
00321 {
00322 byte minr1 = 255;
00323 byte minr2 = 255;
00324
00325 for (CargoID j = 0; j < NUM_CARGO; j++) {
00326 if (!HasBit(cargo_filter, j)) continue;
00327 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) minr1 = min(minr1, (*a)->goods[j].rating);
00328 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) minr2 = min(minr2, (*b)->goods[j].rating);
00329 }
00330
00331 return -(minr1 - minr2);
00332 }
00333
00335 void SortStationsList()
00336 {
00337 if (!this->stations.Sort()) return;
00338
00339
00340 this->last_station = NULL;
00341
00342
00343 this->SetWidgetDirty(SLW_LIST);
00344 }
00345
00346 public:
00347 CompanyStationsWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
00348 {
00349 this->stations.SetListing(this->last_sorting);
00350 this->stations.SetSortFuncs(this->sorter_funcs);
00351 this->stations.ForceRebuild();
00352 this->stations.NeedResort();
00353 this->SortStationsList();
00354
00355 this->CreateNestedTree(desc);
00356 this->vscroll = this->GetScrollbar(SLW_SCROLLBAR);
00357 this->FinishInitNested(desc, window_number);
00358 this->owner = (Owner)this->window_number;
00359
00360 CargoID cid;
00361 FOR_EACH_SET_CARGO_ID(cid, this->cargo_filter) {
00362 if (CargoSpec::Get(cid)->IsValid()) this->LowerWidget(SLW_CARGOSTART + cid);
00363 }
00364
00365 if (this->cargo_filter == this->cargo_filter_max) this->cargo_filter = _cargo_mask;
00366
00367 for (uint i = 0; i < 5; i++) {
00368 if (HasBit(this->facilities, i)) this->LowerWidget(i + SLW_TRAIN);
00369 }
00370 this->SetWidgetLoweredState(SLW_NOCARGOWAITING, this->include_empty);
00371
00372 this->GetWidget<NWidgetCore>(SLW_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00373 }
00374
00375 ~CompanyStationsWindow()
00376 {
00377 this->last_sorting = this->stations.GetListing();
00378 }
00379
00380 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00381 {
00382 switch (widget) {
00383 case SLW_SORTBY: {
00384 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
00385 d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2;
00386 d.height += padding.height;
00387 *size = maxdim(*size, d);
00388 break;
00389 }
00390
00391 case SLW_SORTDROPBTN: {
00392 Dimension d = {0, 0};
00393 for (int i = 0; this->sorter_names[i] != INVALID_STRING_ID; i++) {
00394 d = maxdim(d, GetStringBoundingBox(this->sorter_names[i]));
00395 }
00396 d.width += padding.width;
00397 d.height += padding.height;
00398 *size = maxdim(*size, d);
00399 break;
00400 }
00401
00402 case SLW_LIST:
00403 resize->height = FONT_HEIGHT_NORMAL;
00404 size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
00405 break;
00406
00407 case SLW_TRAIN:
00408 case SLW_TRUCK:
00409 case SLW_BUS:
00410 case SLW_AIRPLANE:
00411 case SLW_SHIP:
00412 size->height = max<uint>(FONT_HEIGHT_SMALL, 10) + padding.height;
00413 break;
00414
00415 case SLW_CARGOALL:
00416 case SLW_FACILALL:
00417 case SLW_NOCARGOWAITING: {
00418 Dimension d = GetStringBoundingBox(widget == SLW_NOCARGOWAITING ? STR_ABBREV_NONE : STR_ABBREV_ALL);
00419 d.width += padding.width + 2;
00420 d.height += padding.height;
00421 *size = maxdim(*size, d);
00422 break;
00423 }
00424
00425 default:
00426 if (widget >= SLW_CARGOSTART) {
00427 const CargoSpec *cs = CargoSpec::Get(widget - SLW_CARGOSTART);
00428 if (cs->IsValid()) {
00429 Dimension d = GetStringBoundingBox(cs->abbrev);
00430 d.width += padding.width + 2;
00431 d.height += padding.height;
00432 *size = maxdim(*size, d);
00433 }
00434 }
00435 break;
00436 }
00437 }
00438
00439 virtual void OnPaint()
00440 {
00441 this->BuildStationsList((Owner)this->window_number);
00442 this->SortStationsList();
00443
00444 this->DrawWidgets();
00445 }
00446
00447 virtual void DrawWidget(const Rect &r, int widget) const
00448 {
00449 switch (widget) {
00450 case SLW_SORTBY:
00451
00452 this->DrawSortButtonState(SLW_SORTBY, this->stations.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
00453 break;
00454
00455 case SLW_LIST: {
00456 bool rtl = _current_text_dir == TD_RTL;
00457 int max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->stations.Length());
00458 int y = r.top + WD_FRAMERECT_TOP;
00459 for (int i = this->vscroll->GetPosition(); i < max; ++i) {
00460 const Station *st = this->stations[i];
00461 assert(st->xy != INVALID_TILE);
00462
00463
00464
00465 assert(st->owner == owner || st->owner == OWNER_NONE);
00466
00467 SetDParam(0, st->index);
00468 SetDParam(1, st->facilities);
00469 int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION);
00470 x += rtl ? -5 : 5;
00471
00472
00473 for (CargoID j = 0; j < NUM_CARGO; j++) {
00474 if (!st->goods[j].cargo.Empty()) {
00475
00476
00477
00478
00479 if (rtl) {
00480 x -= 20;
00481 if (x < r.left + WD_FRAMERECT_LEFT) break;
00482 }
00483 StationsWndShowStationRating(x, x + 16, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
00484 if (!rtl) {
00485 x += 20;
00486 if (x > r.right - WD_FRAMERECT_RIGHT) break;
00487 }
00488 }
00489 }
00490 y += FONT_HEIGHT_NORMAL;
00491 }
00492
00493 if (this->vscroll->GetCount() == 0) {
00494 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_NONE);
00495 return;
00496 }
00497 break;
00498 }
00499
00500 case SLW_NOCARGOWAITING: {
00501 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00502 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER);
00503 break;
00504 }
00505
00506 case SLW_CARGOALL: {
00507 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00508 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER);
00509 break;
00510 }
00511
00512 case SLW_FACILALL: {
00513 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00514 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00515 break;
00516 }
00517
00518 default:
00519 if (widget >= SLW_CARGOSTART) {
00520 const CargoSpec *cs = CargoSpec::Get(widget - SLW_CARGOSTART);
00521 if (cs->IsValid()) {
00522 int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 2 : 1;
00523 GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour);
00524 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, cs->abbrev, TC_BLACK, SA_HOR_CENTER);
00525 }
00526 }
00527 break;
00528 }
00529 }
00530
00531 virtual void SetStringParameters(int widget) const
00532 {
00533 if (widget == SLW_CAPTION) {
00534 SetDParam(0, this->window_number);
00535 SetDParam(1, this->vscroll->GetCount());
00536 }
00537 }
00538
00539 virtual void OnClick(Point pt, int widget, int click_count)
00540 {
00541 switch (widget) {
00542 case SLW_LIST: {
00543 uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, SLW_LIST, 0, FONT_HEIGHT_NORMAL);
00544 if (id_v >= this->stations.Length()) return;
00545
00546 const Station *st = this->stations[id_v];
00547
00548 assert(st->owner == (Owner)this->window_number || st->owner == OWNER_NONE);
00549
00550 if (_ctrl_pressed) {
00551 ShowExtraViewPortWindow(st->xy);
00552 } else {
00553 ScrollMainWindowToTile(st->xy);
00554 }
00555 break;
00556 }
00557
00558 case SLW_TRAIN:
00559 case SLW_TRUCK:
00560 case SLW_BUS:
00561 case SLW_AIRPLANE:
00562 case SLW_SHIP:
00563 if (_ctrl_pressed) {
00564 ToggleBit(this->facilities, widget - SLW_TRAIN);
00565 this->ToggleWidgetLoweredState(widget);
00566 } else {
00567 uint i;
00568 FOR_EACH_SET_BIT(i, this->facilities) {
00569 this->RaiseWidget(i + SLW_TRAIN);
00570 }
00571 this->facilities = 1 << (widget - SLW_TRAIN);
00572 this->LowerWidget(widget);
00573 }
00574 this->stations.ForceRebuild();
00575 this->SetDirty();
00576 break;
00577
00578 case SLW_FACILALL:
00579 for (uint i = SLW_TRAIN; i <= SLW_SHIP; i++) {
00580 this->LowerWidget(i);
00581 }
00582
00583 this->facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00584 this->stations.ForceRebuild();
00585 this->SetDirty();
00586 break;
00587
00588 case SLW_CARGOALL: {
00589 for (uint i = 0; i < NUM_CARGO; i++) {
00590 const CargoSpec *cs = CargoSpec::Get(i);
00591 if (cs->IsValid()) this->LowerWidget(SLW_CARGOSTART + i);
00592 }
00593 this->LowerWidget(SLW_NOCARGOWAITING);
00594
00595 this->cargo_filter = _cargo_mask;
00596 this->include_empty = true;
00597 this->stations.ForceRebuild();
00598 this->SetDirty();
00599 break;
00600 }
00601
00602 case SLW_SORTBY:
00603 this->stations.ToggleSortOrder();
00604 this->flags4 |= WF_TIMEOUT_BEGIN;
00605 this->LowerWidget(SLW_SORTBY);
00606 this->SetDirty();
00607 break;
00608
00609 case SLW_SORTDROPBTN:
00610 ShowDropDownMenu(this, this->sorter_names, this->stations.SortType(), SLW_SORTDROPBTN, 0, 0);
00611 break;
00612
00613 case SLW_NOCARGOWAITING:
00614 if (_ctrl_pressed) {
00615 this->include_empty = !this->include_empty;
00616 this->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
00617 } else {
00618 for (uint i = 0; i < NUM_CARGO; i++) {
00619 const CargoSpec *cs = CargoSpec::Get(i);
00620 if (cs->IsValid()) this->RaiseWidget(SLW_CARGOSTART + i);
00621 }
00622
00623 this->cargo_filter = 0;
00624 this->include_empty = true;
00625
00626 this->LowerWidget(SLW_NOCARGOWAITING);
00627 }
00628 this->stations.ForceRebuild();
00629 this->SetDirty();
00630 break;
00631
00632 default:
00633 if (widget >= SLW_CARGOSTART) {
00634
00635 const CargoSpec *cs = CargoSpec::Get(widget - SLW_CARGOSTART);
00636 if (!cs->IsValid()) break;
00637
00638 if (_ctrl_pressed) {
00639 ToggleBit(this->cargo_filter, cs->Index());
00640 this->ToggleWidgetLoweredState(widget);
00641 } else {
00642 for (uint i = 0; i < NUM_CARGO; i++) {
00643 const CargoSpec *cs = CargoSpec::Get(i);
00644 if (cs->IsValid()) this->RaiseWidget(SLW_CARGOSTART + i);
00645 }
00646 this->RaiseWidget(SLW_NOCARGOWAITING);
00647
00648 this->cargo_filter = 0;
00649 this->include_empty = false;
00650
00651 SetBit(this->cargo_filter, cs->Index());
00652 this->LowerWidget(widget);
00653 }
00654 this->stations.ForceRebuild();
00655 this->SetDirty();
00656 }
00657 break;
00658 }
00659 }
00660
00661 virtual void OnDropdownSelect(int widget, int index)
00662 {
00663 if (this->stations.SortType() != index) {
00664 this->stations.SetSortType(index);
00665
00666
00667 this->GetWidget<NWidgetCore>(SLW_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00668
00669 this->SetDirty();
00670 }
00671 }
00672
00673 virtual void OnTick()
00674 {
00675 if (_pause_mode != PM_UNPAUSED) return;
00676 if (this->stations.NeedResort()) {
00677 DEBUG(misc, 3, "Periodic rebuild station list company %d", this->window_number);
00678 this->SetDirty();
00679 }
00680 }
00681
00682 virtual void OnTimeout()
00683 {
00684 this->RaiseWidget(SLW_SORTBY);
00685 this->SetDirty();
00686 }
00687
00688 virtual void OnResize()
00689 {
00690 this->vscroll->SetCapacityFromWidget(this, SLW_LIST, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
00691 }
00692
00698 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00699 {
00700 if (data == 0) {
00701
00702 this->stations.ForceRebuild();
00703 } else {
00704 this->stations.ForceResort();
00705 }
00706 }
00707 };
00708
00709 Listing CompanyStationsWindow::last_sorting = {false, 0};
00710 byte CompanyStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00711 bool CompanyStationsWindow::include_empty = true;
00712 const uint32 CompanyStationsWindow::cargo_filter_max = UINT32_MAX;
00713 uint32 CompanyStationsWindow::cargo_filter = UINT32_MAX;
00714 const Station *CompanyStationsWindow::last_station = NULL;
00715
00716
00717 GUIStationList::SortFunction * const CompanyStationsWindow::sorter_funcs[] = {
00718 &StationNameSorter,
00719 &StationTypeSorter,
00720 &StationWaitingSorter,
00721 &StationRatingMaxSorter,
00722 &StationRatingMinSorter
00723 };
00724
00725
00726 const StringID CompanyStationsWindow::sorter_names[] = {
00727 STR_SORT_BY_NAME,
00728 STR_SORT_BY_FACILITY,
00729 STR_SORT_BY_WAITING,
00730 STR_SORT_BY_RATING_MAX,
00731 STR_SORT_BY_RATING_MIN,
00732 INVALID_STRING_ID
00733 };
00734
00740 static NWidgetBase *CargoWidgets(int *biggest_index)
00741 {
00742 NWidgetHorizontal *container = new NWidgetHorizontal();
00743
00744 for (uint i = 0; i < NUM_CARGO; i++) {
00745 const CargoSpec *cs = CargoSpec::Get(i);
00746 if (cs->IsValid()) {
00747 NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, SLW_CARGOSTART + i);
00748 panel->SetMinimalSize(14, 11);
00749 panel->SetResize(0, 0);
00750 panel->SetFill(0, 1);
00751 panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE);
00752 container->Add(panel);
00753 } else {
00754 NWidgetLeaf *nwi = new NWidgetLeaf(WWT_EMPTY, COLOUR_GREY, SLW_CARGOSTART + i, 0x0, STR_NULL);
00755 nwi->SetMinimalSize(0, 11);
00756 nwi->SetResize(0, 0);
00757 nwi->SetFill(0, 1);
00758 container->Add(nwi);
00759 }
00760 }
00761 *biggest_index = SLW_CARGOSTART + NUM_CARGO;
00762 return container;
00763 }
00764
00765 static const NWidgetPart _nested_company_stations_widgets[] = {
00766 NWidget(NWID_HORIZONTAL),
00767 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00768 NWidget(WWT_CAPTION, COLOUR_GREY, SLW_CAPTION), SetDataTip(STR_STATION_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00769 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00770 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00771 EndContainer(),
00772 NWidget(NWID_HORIZONTAL),
00773 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_TRAIN), SetMinimalSize(14, 11), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00774 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_TRUCK), SetMinimalSize(14, 11), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00775 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_BUS), SetMinimalSize(14, 11), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00776 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_SHIP), SetMinimalSize(14, 11), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00777 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_AIRPLANE), SetMinimalSize(14, 11), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00778 NWidget(WWT_PUSHBTN, COLOUR_GREY, SLW_FACILALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1),
00779 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(),
00780 NWidgetFunction(CargoWidgets),
00781 NWidget(WWT_PANEL, COLOUR_GREY, SLW_NOCARGOWAITING), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(),
00782 NWidget(WWT_PUSHBTN, COLOUR_GREY, SLW_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1),
00783 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00784 EndContainer(),
00785 NWidget(NWID_HORIZONTAL),
00786 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_SORTBY), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
00787 NWidget(WWT_DROPDOWN, COLOUR_GREY, SLW_SORTDROPBTN), SetMinimalSize(163, 12), SetDataTip(STR_SORT_BY_NAME, STR_TOOLTIP_SORT_CRITERIA),
00788 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00789 EndContainer(),
00790 NWidget(NWID_HORIZONTAL),
00791 NWidget(WWT_PANEL, COLOUR_GREY, SLW_LIST), SetMinimalSize(346, 125), SetResize(1, 10), SetDataTip(0x0, STR_STATION_LIST_TOOLTIP), SetScrollbar(SLW_SCROLLBAR), EndContainer(),
00792 NWidget(NWID_VERTICAL),
00793 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, SLW_SCROLLBAR),
00794 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00795 EndContainer(),
00796 EndContainer(),
00797 };
00798
00799 static const WindowDesc _company_stations_desc(
00800 WDP_AUTO, 358, 162,
00801 WC_STATION_LIST, WC_NONE,
00802 WDF_UNCLICK_BUTTONS,
00803 _nested_company_stations_widgets, lengthof(_nested_company_stations_widgets)
00804 );
00805
00811 void ShowCompanyStations(CompanyID company)
00812 {
00813 if (!Company::IsValidID(company)) return;
00814
00815 AllocateWindowDescFront<CompanyStationsWindow>(&_company_stations_desc, company);
00816 }
00817
00818 static const NWidgetPart _nested_station_view_widgets[] = {
00819 NWidget(NWID_HORIZONTAL),
00820 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00821 NWidget(WWT_CAPTION, COLOUR_GREY, SVW_CAPTION), SetDataTip(STR_STATION_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00822 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00823 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00824 EndContainer(),
00825 NWidget(NWID_HORIZONTAL),
00826 NWidget(WWT_PANEL, COLOUR_GREY, SVW_WAITING), SetMinimalSize(237, 52), SetResize(1, 10), SetScrollbar(SVW_SCROLLBAR), EndContainer(),
00827 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, SVW_SCROLLBAR),
00828 EndContainer(),
00829 NWidget(WWT_PANEL, COLOUR_GREY, SVW_ACCEPTLIST), SetMinimalSize(249, 32), SetResize(1, 0), EndContainer(),
00830 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
00831 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_LOCATION), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1),
00832 SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP),
00833 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_ACCEPTS), SetMinimalSize(61, 12), SetResize(1, 0), SetFill(1, 1),
00834 SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP),
00835 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_CARGO_VIA), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1),
00836 SetDataTip(STR_STATION_VIEW_WAITING_VIA_BUTTON, STR_STATION_VIEW_WAITING_VIA_TOOLTIP),
00837 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_RENAME), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1),
00838 SetDataTip(STR_BUTTON_RENAME, STR_STATION_VIEW_RENAME_TOOLTIP),
00839 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_TRAINS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP),
00840 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_ROADVEHS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP),
00841 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_SHIPS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_SHIP, STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP),
00842 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_PLANES), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_PLANE, STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP),
00843 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00844 EndContainer(),
00845 };
00846
00857 static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
00858 {
00859 uint num = min((waiting + 5) / 10, (right - left) / 10);
00860 if (num == 0) return;
00861
00862 SpriteID sprite = CargoSpec::Get(i)->GetCargoIcon();
00863
00864 int x = _current_text_dir == TD_RTL ? right - num * 10 : left;
00865 do {
00866 DrawSprite(sprite, PAL_NONE, x, y);
00867 x += 10;
00868 } while (--num);
00869 }
00870
00871 struct CargoData {
00872 CargoID cargo;
00873 union {
00874 StationID station;
00875 SourceID css;
00876 };
00877 uint count;
00878 SourceType type;
00879
00880 CargoData(CargoID cargo, StationID station, uint count, SourceType type = ST_INDUSTRY) :
00881 cargo(cargo),
00882 station(station),
00883 count(count),
00884 type(type)
00885 { }
00886 };
00887
00888 typedef std::list<CargoData> CargoDataList;
00889
00891 struct CargoDestEntry {
00892 typedef std::list<CargoDestEntry> List;
00893
00895 enum Type {
00896 FINAL_DEST,
00897 NEXT_HOP,
00898 TRANSFER_HOP
00899 };
00900
00901 List children;
00902 CargoData data;
00903 Type type;
00904 uint16 start_row;
00905 bool expanded;
00906
00907 CargoDestEntry(Type type, StationID station, uint count, SourceType st = ST_INDUSTRY) :
00908 data(INVALID_CARGO, station, count, st),
00909 type(type),
00910 start_row(0),
00911 expanded(false)
00912 { }
00913
00915 void Zero()
00916 {
00917 for (List::iterator i = this->children.begin(); i != this->children.end(); ++i ) {
00918 i->Zero();
00919 }
00920 this->data.count = 0;
00921 this->start_row = 0;
00922 }
00923
00925 void RemoveEmpty()
00926 {
00927 for (List::iterator i = this->children.begin(); i != this->children.end(); ) {
00928 if (i->data.count > 0) {
00929 i->RemoveEmpty();
00930 ++i;
00931 } else {
00932 i = this->children.erase(i);
00933 }
00934 }
00935 }
00936
00938 int UpdateRowCount(int row)
00939 {
00940 this->start_row = ++row;
00941 if (this->expanded) {
00942 for (List::iterator i = this->children.begin(); i != this->children.end(); ++i) {
00943 row = i->UpdateRowCount(row);
00944 }
00945 }
00946 return row;
00947 }
00948 };
00949
00956 static StationID GetNextHopStation(const GoodsEntry &ge, const CargoPacket *cp)
00957 {
00958 StationID next = INVALID_STATION;
00959 for (RouteLinkList::const_iterator i = ge.routes.begin(); i != ge.routes.end(); ++i) {
00960 if ((*i)->GetOriginOrderId() == cp->NextHop()) {
00961 next = (*i)->GetDestination();
00962 break;
00963 }
00964 }
00965 return next;
00966 }
00967
00976 static CargoDestEntry *AddCargoPacketToList(CargoDestEntry::List &list, CargoDestEntry::Type type, const CargoPacket *cp, const GoodsEntry &ge)
00977 {
00978 assert_compile(INVALID_STATION == INVALID_SOURCE);
00979
00980
00981 uint16 sort_val;
00982 switch (type) {
00983 case CargoDestEntry::FINAL_DEST:
00984 sort_val = cp->DestinationID();
00985 break;
00986 case CargoDestEntry::NEXT_HOP:
00987 sort_val = GetNextHopStation(ge, cp);
00988 break;
00989 case CargoDestEntry::TRANSFER_HOP:
00990 sort_val = cp->NextStation();
00991 break;
00992 default:
00993 NOT_REACHED();
00994 }
00995
00996 if (sort_val == INVALID_STATION) return NULL;
00997
00998
00999 for (CargoDestEntry::List::iterator i = list.begin(); i != list.end(); ++i) {
01000 if (type == CargoDestEntry::FINAL_DEST ? i->data.css == sort_val && i->data.type == cp->DestinationType() : i->data.station == sort_val) {
01001 i->data.count += cp->Count();
01002 return &*i;
01003 }
01004 }
01005
01006
01007 list.push_back(CargoDestEntry(type, sort_val, cp->Count(), cp->DestinationType()));
01008 return &list.back();
01009 }
01010
01011
01015 struct StationViewWindow : public Window {
01016 uint32 cargo;
01017 uint16 cargo_rows[NUM_CARGO];
01018 uint expand_shrink_width;
01019 int rating_lines;
01020 int accepts_lines;
01021 Scrollbar *vscroll;
01022 CargoDestEntry::List cargodest_list[NUM_CARGO];
01023
01024 static StringID last_cargo_from_str;
01025 static StringID last_cargo_from_tooltip;
01026
01028 enum AcceptListHeight {
01029 ALH_RATING = 13,
01030 ALH_ACCEPTS = 3,
01031 };
01032
01033 StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
01034 {
01035 this->rating_lines = ALH_RATING;
01036 this->accepts_lines = ALH_ACCEPTS;
01037
01038 this->CreateNestedTree(desc);
01039 this->vscroll = this->GetScrollbar(SVW_SCROLLBAR);
01040 this->GetWidget<NWidgetCore>(SVW_CARGO_FROM)->SetDataTip(StationViewWindow::last_cargo_from_str, StationViewWindow::last_cargo_from_tooltip);
01041
01042 this->FinishInitNested(desc, window_number);
01043
01044 Owner owner = Station::Get(window_number)->owner;
01045 if (owner != OWNER_NONE) this->owner = owner;
01046 }
01047
01048 ~StationViewWindow()
01049 {
01050 Owner owner = Station::Get(this->window_number)->owner;
01051 if (!Company::IsValidID(owner)) owner = _local_company;
01052 if (!Company::IsValidID(owner)) return;
01053 DeleteWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, owner, this->window_number).Pack(), false);
01054 DeleteWindowById(WC_ROADVEH_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_ROAD, owner, this->window_number).Pack(), false);
01055 DeleteWindowById(WC_SHIPS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_SHIP, owner, this->window_number).Pack(), false);
01056 DeleteWindowById(WC_AIRCRAFT_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_AIRCRAFT, owner, this->window_number).Pack(), false);
01057 }
01058
01059 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01060 {
01061 switch (widget) {
01062 case SVW_WAITING:
01063 resize->height = FONT_HEIGHT_NORMAL;
01064 size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
01065 this->expand_shrink_width = max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01066 break;
01067
01068 case SVW_ACCEPTLIST:
01069 size->height = WD_FRAMERECT_TOP + ((this->GetWidget<NWidgetCore>(SVW_ACCEPTS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) ? this->accepts_lines : this->rating_lines) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
01070 break;
01071 }
01072 }
01073
01074 virtual void OnPaint()
01075 {
01076 CargoDataList cargolist;
01077 uint32 transfers = 0;
01078
01079 NWidgetCore *cargo_btn = this->GetWidget<NWidgetCore>(SVW_CARGO_FROM);
01080 if (cargo_btn->widget_data == STR_STATION_VIEW_WAITING_TO_BUTTON) {
01081 this->OrderWaitingCargo(&cargolist, &transfers);
01082 this->vscroll->SetCount((int)cargolist.size() + 1);
01083 } else {
01084
01085 CargoDestEntry::Type dest_type;
01086 switch (cargo_btn->widget_data) {
01087 case STR_STATION_VIEW_WAITING_VIA_BUTTON:
01088 dest_type = CargoDestEntry::FINAL_DEST;
01089 break;
01090 case STR_STATION_VIEW_WAITING_TRANSFER_BUTTON:
01091 dest_type = CargoDestEntry::NEXT_HOP;
01092 break;
01093 case STR_STATION_VIEW_WAITING_BUTTON:
01094 dest_type = CargoDestEntry::TRANSFER_HOP;
01095 break;
01096 default:
01097 NOT_REACHED();
01098 }
01099 int num = this->FillCargodestList(dest_type, this->cargodest_list);
01100 this->vscroll->SetCount(num + 1);
01101 }
01102
01103
01104 const Station *st = Station::Get(this->window_number);
01105 this->SetWidgetDisabledState(SVW_RENAME, st->owner != _local_company);
01106 this->SetWidgetDisabledState(SVW_TRAINS, !(st->facilities & FACIL_TRAIN));
01107 this->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
01108 this->SetWidgetDisabledState(SVW_SHIPS, !(st->facilities & FACIL_DOCK));
01109 this->SetWidgetDisabledState(SVW_PLANES, !(st->facilities & FACIL_AIRPORT));
01110
01111 this->DrawWidgets();
01112
01113 if (!this->IsShaded()) {
01114
01115 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SVW_ACCEPTLIST);
01116 const Rect r = {wid->pos_x, wid->pos_y, wid->pos_x + wid->current_x - 1, wid->pos_y + wid->current_y - 1};
01117 if (this->GetWidget<NWidgetCore>(SVW_ACCEPTS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
01118 int lines = this->DrawAcceptedCargo(r);
01119 if (lines > this->accepts_lines) {
01120 this->accepts_lines = lines;
01121 this->ReInit();
01122 return;
01123 }
01124 } else {
01125 int lines = this->DrawCargoRatings(r);
01126 if (lines > this->rating_lines) {
01127 this->rating_lines = lines;
01128 this->ReInit();
01129 return;
01130 }
01131 }
01132
01133
01134 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(SVW_WAITING);
01135 Rect waiting_rect = {nwi->pos_x, nwi->pos_y, nwi->pos_x + nwi->current_x - 1, nwi->pos_y + nwi->current_y - 1};
01136 if (cargo_btn->widget_data == STR_STATION_VIEW_WAITING_TO_BUTTON) {
01137 this->DrawWaitingCargo(waiting_rect, cargolist, transfers);
01138 } else {
01139 this->DrawWaitingCargoByDest(waiting_rect, this->cargodest_list);
01140 }
01141 }
01142 }
01143
01144 virtual void SetStringParameters(int widget) const
01145 {
01146 if (widget == SVW_CAPTION) {
01147 const Station *st = Station::Get(this->window_number);
01148 SetDParam(0, st->index);
01149 SetDParam(1, st->facilities);
01150 }
01151 }
01152
01159 void OrderWaitingCargo(CargoDataList *cargolist, uint32 *transfers)
01160 {
01161 assert(cargolist->size() == 0);
01162 *transfers = 0;
01163
01164 StationID station_id = this->window_number;
01165 const Station *st = Station::Get(station_id);
01166
01167
01168 for (CargoID i = 0; i < NUM_CARGO; i++) {
01169 if (st->goods[i].cargo.Empty()) {
01170 this->cargo_rows[i] = 0;
01171 } else {
01172
01173 cargolist->push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
01174
01175
01176 this->cargo_rows[i] = (uint16)cargolist->size();
01177
01178
01179 const StationCargoList::List *packets = st->goods[i].cargo.Packets();
01180 for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
01181 const CargoPacket *cp = *it;
01182 if (cp->SourceStation() != station_id) {
01183 bool added = false;
01184
01185
01186 SetBit(*transfers, i);
01187
01188
01189 if (!HasBit(this->cargo, i)) break;
01190
01191
01192 for (CargoDataList::iterator jt(cargolist->begin()); jt != cargolist->end(); jt++) {
01193 CargoData *cd = &(*jt);
01194 if (cd->cargo == i && cd->station == cp->SourceStation()) {
01195 cd->count += cp->Count();
01196 added = true;
01197 break;
01198 }
01199 }
01200
01201 if (!added) cargolist->push_back(CargoData(i, cp->SourceStation(), cp->Count()));
01202 }
01203 }
01204 }
01205 }
01206 }
01207
01214 int FillCargodestList(CargoDestEntry::Type sort_by, CargoDestEntry::List *list)
01215 {
01216 StationID station_id = this->window_number;
01217 const Station *st = Station::Get(station_id);
01218
01219 int lines = 0;
01220
01221
01222 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01223
01224 for (CargoDestEntry::List::iterator i = list[cid].begin(); i != list[cid].end(); ++i) {
01225 i->Zero();
01226 }
01227
01228
01229 if (st->goods[cid].cargo.Empty()) {
01230 this->cargo_rows[cid] = 0;
01231 list[cid].clear();
01232 continue;
01233 }
01234
01235
01236 this->cargo_rows[cid] = ++lines;
01237
01238
01239 const StationCargoList::List *packets = st->goods[cid].cargo.Packets();
01240 for (StationCargoList::ConstIterator it = packets->begin(); it != packets->end(); ++it) {
01241 const CargoPacket *cp = *it;
01242
01243
01244 static const CargoDestEntry::Type sort_types[][3] = {
01245 {CargoDestEntry::FINAL_DEST, CargoDestEntry::NEXT_HOP, CargoDestEntry::TRANSFER_HOP},
01246 {CargoDestEntry::NEXT_HOP, CargoDestEntry::TRANSFER_HOP, CargoDestEntry::FINAL_DEST},
01247 {CargoDestEntry::TRANSFER_HOP, CargoDestEntry::NEXT_HOP, CargoDestEntry::FINAL_DEST}
01248 };
01249
01250 CargoDestEntry *entry = AddCargoPacketToList(list[cid], sort_types[sort_by][0], cp, st->goods[cid]);
01251 if (entry != NULL) {
01252 entry = AddCargoPacketToList(entry->children, sort_types[sort_by][1], cp, st->goods[cid]);
01253 if (entry != NULL) AddCargoPacketToList(entry->children, sort_types[sort_by][2], cp, st->goods[cid]);
01254 }
01255 }
01256
01257
01258 for (CargoDestEntry::List::iterator i = list[cid].begin(); i != list[cid].end(); ) {
01259 if (i->data.count > 0) {
01260 i->RemoveEmpty();
01261 if (HasBit(this->cargo, cid)) lines = i->UpdateRowCount(lines);
01262 ++i;
01263 } else {
01264 i = list[cid].erase(i);
01265 }
01266 }
01267 }
01268
01269 return lines;
01270 }
01271
01278 void DrawWaitingCargo(const Rect &r, const CargoDataList &cargolist, uint32 transfers) const
01279 {
01280 int y = r.top + WD_FRAMERECT_TOP;
01281 int pos = this->vscroll->GetPosition();
01282
01283 const Station *st = Station::Get(this->window_number);
01284 if (--pos < 0) {
01285 StringID str = STR_JUST_NOTHING;
01286 for (CargoID i = 0; i < NUM_CARGO; i++) {
01287 if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
01288 }
01289 SetDParam(0, str);
01290 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_TITLE);
01291 y += FONT_HEIGHT_NORMAL;
01292 }
01293
01294 bool rtl = _current_text_dir == TD_RTL;
01295 int text_left = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT;
01296 int text_right = rtl ? r.right - WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width;
01297 int shrink_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
01298 int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
01299
01300
01301 int maxrows = this->vscroll->GetCapacity();
01302 for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
01303 if (--pos < 0) {
01304 const CargoData *cd = &(*it);
01305 if (cd->station == INVALID_STATION) {
01306
01307 DrawCargoIcons(cd->cargo, cd->count, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y);
01308 SetDParam(0, cd->cargo);
01309 SetDParam(1, cd->count);
01310 if (HasBit(transfers, cd->cargo)) {
01311
01312 const char *sym = HasBit(this->cargo, cd->cargo) ? "-" : "+";
01313 DrawString(text_left, text_right, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
01314 DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW, SA_RIGHT);
01315 } else {
01316 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
01317 }
01318 } else {
01319 SetDParam(0, cd->cargo);
01320 SetDParam(1, cd->count);
01321 SetDParam(2, cd->station);
01322 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_EN_ROUTE_FROM, TC_FROMSTRING, SA_RIGHT);
01323 }
01324
01325 y += FONT_HEIGHT_NORMAL;
01326 }
01327 }
01328 }
01329
01345 int DrawSingleDestEntry(CargoID cid, int *pos, int maxrows, int left, int right, int shrink_left, int shrink_right, int offs_left, int offs_right, int y, const CargoDestEntry &entry) const
01346 {
01347 if (--(*pos) < 0) {
01348
01349 StringID str;
01350
01351 SetDParam(0, cid);
01352 SetDParam(1, entry.data.count);
01353 if (entry.type == CargoDestEntry::FINAL_DEST) {
01354 SetDParam(2, entry.data.type == ST_INDUSTRY ? STR_INDUSTRY_NAME : (entry.data.type == ST_TOWN ? STR_TOWN_NAME : STR_COMPANY_NAME));
01355 SetDParam(3, entry.data.css);
01356 str = STR_STATION_VIEW_WAITING_TO;
01357 } else {
01358 SetDParam(2, entry.data.station);
01359 str = (entry.type == CargoDestEntry::NEXT_HOP) ? STR_STATION_VIEW_WAITING_VIA : STR_STATION_VIEW_WAITING_TRANSFER;
01360 }
01361 DrawString(left, right, y, str);
01362 y += FONT_HEIGHT_NORMAL;
01363
01364 if (!entry.children.empty()) {
01365
01366 DrawString(shrink_left, shrink_right, y - FONT_HEIGHT_NORMAL, entry.expanded ? "-" : "+", TC_YELLOW, SA_RIGHT);
01367
01368 if (entry.expanded) {
01369
01370 for (CargoDestEntry::List::const_iterator i = entry.children.begin(); i != entry.children.end() && *pos > -maxrows; ++i) {
01371 y = this->DrawSingleDestEntry(cid, pos, maxrows, left + offs_left, right + offs_right, shrink_left, shrink_right, offs_left, offs_right, y, *i);
01372 }
01373 }
01374 }
01375 }
01376
01377 return y;
01378 }
01379
01385 void DrawWaitingCargoByDest(const Rect &r, const CargoDestEntry::List *list) const
01386 {
01387 int y = r.top + WD_FRAMERECT_TOP;
01388 int pos = this->vscroll->GetPosition();
01389
01390 const Station *st = Station::Get(this->window_number);
01391 if (--pos < 0) {
01392 StringID str = STR_JUST_NOTHING;
01393 for (CargoID i = 0; i < NUM_CARGO; i++) {
01394 if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
01395 }
01396 SetDParam(0, str);
01397 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_TITLE);
01398 y += FONT_HEIGHT_NORMAL;
01399 }
01400
01401 bool rtl = _current_text_dir == TD_RTL;
01402 int text_left = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT;
01403 int text_right = rtl ? r.right - WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width;
01404 int shrink_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
01405 int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
01406
01407 int offs_left = rtl ? 0 : this->expand_shrink_width;
01408 int offs_right = rtl ? this->expand_shrink_width : 0;
01409
01410 int maxrows = this->vscroll->GetCapacity();
01411 for (CargoID cid = 0; cid < NUM_CARGO && pos > -maxrows; cid++) {
01412 if (st->goods[cid].cargo.Empty()) continue;
01413
01414 if (--pos < 0) {
01415
01416 DrawCargoIcons(cid, st->goods[cid].cargo.Count(), r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMERECT_RIGHT, y);
01417 SetDParam(0, cid);
01418 SetDParam(1, st->goods[cid].cargo.Count());
01419 DrawString(text_left, text_right, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
01420 if (!list[cid].empty()) {
01421 DrawString(shrink_left, shrink_right, y, HasBit(this->cargo, cid) ? "-" : "+", TC_YELLOW, SA_RIGHT);
01422 }
01423 y += FONT_HEIGHT_NORMAL;
01424 }
01425
01426
01427 if (HasBit(this->cargo, cid)) {
01428 for (CargoDestEntry::List::const_iterator i = list[cid].begin(); i != list[cid].end() && pos > -maxrows; ++i) {
01429 y = this->DrawSingleDestEntry(cid, &pos, maxrows, text_left + offs_left, text_right + offs_right, shrink_left, shrink_right, offs_left, offs_right, y, *i);
01430 }
01431 }
01432 }
01433 }
01434
01440 int DrawAcceptedCargo(const Rect &r) const
01441 {
01442 const Station *st = Station::Get(this->window_number);
01443
01444 uint32 cargo_mask = 0;
01445 for (CargoID i = 0; i < NUM_CARGO; i++) {
01446 if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) SetBit(cargo_mask, i);
01447 }
01448 Rect s = {r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, r.right - WD_FRAMERECT_RIGHT, INT32_MAX};
01449 int bottom = DrawCargoListText(cargo_mask, s, STR_STATION_VIEW_ACCEPTS_CARGO);
01450 return CeilDiv(bottom - r.top - WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL);
01451 }
01452
01458 int DrawCargoRatings(const Rect &r) const
01459 {
01460 const Station *st = Station::Get(this->window_number);
01461 int y = r.top + WD_FRAMERECT_TOP;
01462
01463 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_CARGO_RATINGS_TITLE);
01464 y += FONT_HEIGHT_NORMAL;
01465
01466 const CargoSpec *cs;
01467 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
01468 const GoodsEntry *ge = &st->goods[cs->Index()];
01469 if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
01470
01471 SetDParam(0, cs->name);
01472 SetDParam(2, ToPercent8(ge->rating));
01473 SetDParam(1, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
01474 DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_RATING);
01475 y += FONT_HEIGHT_NORMAL;
01476 }
01477 return CeilDiv(y - r.top - WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL);
01478 }
01479
01486 bool HandleCargoDestEntryClick(CargoDestEntry &entry, int row)
01487 {
01488 if (entry.start_row == row) {
01489 if (_ctrl_pressed) {
01490
01491 TileIndex dest_tile = 0;
01492 switch (entry.type) {
01493 case CargoDestEntry::FINAL_DEST:
01494 switch (entry.data.type) {
01495 case ST_INDUSTRY:
01496 dest_tile = Industry::Get(entry.data.css)->location.tile;
01497 break;
01498 case ST_TOWN:
01499 dest_tile = Town::Get(entry.data.css)->xy;
01500 break;
01501 case ST_HEADQUARTERS:
01502 dest_tile = Company::Get(entry.data.css)->location_of_HQ;
01503 break;
01504
01505 default:
01506 NOT_REACHED();
01507 }
01508 break;
01509
01510 case CargoDestEntry::NEXT_HOP:
01511 case CargoDestEntry::TRANSFER_HOP:
01512 dest_tile = Station::Get(entry.data.station)->xy;
01513 break;
01514
01515 default:
01516 NOT_REACHED();
01517 }
01518 ScrollMainWindowToTile(dest_tile);
01519 } else if (!entry.children.empty()) {
01520
01521 entry.expanded = !entry.expanded;
01522 this->SetWidgetDirty(SVW_WAITING);
01523 this->SetWidgetDirty(SVW_SCROLLBAR);
01524 }
01525 }
01526
01527 if (entry.start_row < row) {
01528
01529 for (CargoDestEntry::List::iterator i = entry.children.begin(); i != entry.children.end(); ++i) {
01530 if (!this->HandleCargoDestEntryClick(*i, row)) return false;
01531 }
01532 return true;
01533 }
01534
01535 return false;
01536 }
01537
01538 void HandleCargoWaitingClick(int row)
01539 {
01540 if (row == 0) return;
01541
01542 bool dest_view = this->GetWidget<NWidgetCore>(SVW_CARGO_FROM)->widget_data != STR_STATION_VIEW_WAITING_TO_BUTTON;
01543
01544 for (CargoID c = 0; c < NUM_CARGO; c++) {
01545
01546 if (this->cargo_rows[c] == row) {
01547 ToggleBit(this->cargo, c);
01548 this->SetWidgetDirty(SVW_WAITING);
01549 this->SetWidgetDirty(SVW_SCROLLBAR);
01550 break;
01551 }
01552
01553 if (dest_view) {
01554
01555 for (CargoDestEntry::List::iterator i = this->cargodest_list[c].begin(); i != this->cargodest_list[c].end(); ++i) {
01556 if (!this->HandleCargoDestEntryClick(*i, row)) break;
01557 }
01558 }
01559 }
01560 }
01561
01563 void ClearCargodestList()
01564 {
01565 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01566 this->cargodest_list[cid].clear();
01567 }
01568 }
01569
01570 virtual void OnClick(Point pt, int widget, int click_count)
01571 {
01572 switch (widget) {
01573 case SVW_WAITING:
01574 this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, SVW_WAITING, WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL));
01575 break;
01576
01577 case SVW_LOCATION:
01578 if (_ctrl_pressed) {
01579 ShowExtraViewPortWindow(Station::Get(this->window_number)->xy);
01580 } else {
01581 ScrollMainWindowToTile(Station::Get(this->window_number)->xy);
01582 }
01583 break;
01584
01585 case SVW_RATINGS: {
01586
01587 int height_change;
01588 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(SVW_RATINGS);
01589 if (this->GetWidget<NWidgetCore>(SVW_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
01590 nwi->SetDataTip(STR_STATION_VIEW_ACCEPTS_BUTTON, STR_STATION_VIEW_ACCEPTS_TOOLTIP);
01591 height_change = this->rating_lines - this->accepts_lines;
01592 } else {
01593 nwi->SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP);
01594 height_change = this->accepts_lines - this->rating_lines;
01595 }
01596 this->ReInit(0, height_change * FONT_HEIGHT_NORMAL);
01597 break;
01598 }
01599
01600 case SVW_CARGO_FROM: {
01601
01602
01603 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(SVW_CARGO_FROM);
01604 switch (nwi->widget_data) {
01605 case STR_STATION_VIEW_WAITING_BUTTON:
01606 StationViewWindow::last_cargo_from_str = STR_STATION_VIEW_WAITING_TO_BUTTON;
01607 StationViewWindow::last_cargo_from_tooltip = STR_STATION_VIEW_WAITING_TO_TOOLTIP;
01608 break;
01609 case STR_STATION_VIEW_WAITING_TO_BUTTON:
01610 StationViewWindow::last_cargo_from_str = STR_STATION_VIEW_WAITING_VIA_BUTTON;
01611 StationViewWindow::last_cargo_from_tooltip = STR_STATION_VIEW_WAITING_VIA_TOOLTIP;
01612 break;
01613 case STR_STATION_VIEW_WAITING_VIA_BUTTON:
01614 StationViewWindow::last_cargo_from_str = STR_STATION_VIEW_WAITING_TRANSFER_BUTTON;
01615 StationViewWindow::last_cargo_from_tooltip = STR_STATION_VIEW_WAITING_TRANSFER_TOOLTIP;
01616 break;
01617 case STR_STATION_VIEW_WAITING_TRANSFER_BUTTON:
01618 StationViewWindow::last_cargo_from_str = STR_STATION_VIEW_WAITING_BUTTON;
01619 StationViewWindow::last_cargo_from_tooltip = STR_STATION_VIEW_WAITING_TOOLTIP;
01620 break;
01621 default:
01622 NOT_REACHED();
01623 }
01624 nwi->SetDataTip(StationViewWindow::last_cargo_from_str, StationViewWindow::last_cargo_from_tooltip);
01625 this->ClearCargodestList();
01626 this->SetWidgetDirty(SVW_CARGO_FROM);
01627 this->SetWidgetDirty(SVW_WAITING);
01628 this->SetWidgetDirty(SVW_SCROLLBAR);
01629 break;
01630 }
01631
01632 case SVW_RENAME:
01633 SetDParam(0, this->window_number);
01634 ShowQueryString(STR_STATION_NAME, STR_STATION_VIEW_RENAME_STATION_CAPTION, MAX_LENGTH_STATION_NAME_CHARS,
01635 this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
01636 break;
01637
01638 case SVW_TRAINS:
01639 case SVW_ROADVEHS:
01640 case SVW_SHIPS:
01641 case SVW_PLANES:
01642 ShowVehicleListWindow(this->owner, (VehicleType)(widget - SVW_TRAINS), (StationID)this->window_number);
01643 break;
01644 }
01645 }
01646
01647 virtual void OnQueryTextFinished(char *str)
01648 {
01649 if (str == NULL) return;
01650
01651 DoCommandP(0, this->window_number, 0, CMD_RENAME_STATION | CMD_MSG(STR_ERROR_CAN_T_RENAME_STATION), NULL, str);
01652 }
01653
01654 virtual void OnResize()
01655 {
01656 this->vscroll->SetCapacityFromWidget(this, SVW_WAITING, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
01657 }
01658 };
01659
01660 StringID StationViewWindow::last_cargo_from_str = STR_STATION_VIEW_WAITING_VIA_BUTTON;
01661 StringID StationViewWindow::last_cargo_from_tooltip = STR_STATION_VIEW_WAITING_VIA_TOOLTIP;
01662
01663 static const WindowDesc _station_view_desc(
01664 WDP_AUTO, 249, 110,
01665 WC_STATION_VIEW, WC_NONE,
01666 WDF_UNCLICK_BUTTONS,
01667 _nested_station_view_widgets, lengthof(_nested_station_view_widgets)
01668 );
01669
01675 void ShowStationViewWindow(StationID station)
01676 {
01677 AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station);
01678 }
01679
01681 struct TileAndStation {
01682 TileIndex tile;
01683 StationID station;
01684 };
01685
01686 static SmallVector<TileAndStation, 8> _deleted_stations_nearby;
01687 static SmallVector<StationID, 8> _stations_nearby_list;
01688
01696 template <class T>
01697 static bool AddNearbyStation(TileIndex tile, void *user_data)
01698 {
01699 TileArea *ctx = (TileArea *)user_data;
01700
01701
01702 for (uint i = 0; i < _deleted_stations_nearby.Length(); i++) {
01703 TileAndStation *ts = _deleted_stations_nearby.Get(i);
01704 if (ts->tile == tile) {
01705 *_stations_nearby_list.Append() = _deleted_stations_nearby[i].station;
01706 _deleted_stations_nearby.Erase(ts);
01707 i--;
01708 }
01709 }
01710
01711
01712 if (!IsTileType(tile, MP_STATION)) return false;
01713
01714 StationID sid = GetStationIndex(tile);
01715
01716
01717 if (!T::IsValidID(sid)) return false;
01718
01719 T *st = T::Get(sid);
01720 if (st->owner != _local_company || _stations_nearby_list.Contains(sid)) return false;
01721
01722 if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST).Succeeded()) {
01723 *_stations_nearby_list.Append() = sid;
01724 }
01725
01726 return false;
01727 }
01728
01738 template <class T>
01739 static const T *FindStationsNearby(TileArea ta, bool distant_join)
01740 {
01741 TileArea ctx = ta;
01742
01743 _stations_nearby_list.Clear();
01744 _deleted_stations_nearby.Clear();
01745
01746
01747 TILE_AREA_LOOP(t, ta) {
01748 if (t < MapSize() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return T::GetByTile(t);
01749 }
01750
01751
01752 const BaseStation *st;
01753 FOR_ALL_BASE_STATIONS(st) {
01754 if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company) {
01755
01756 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) {
01757 TileAndStation *ts = _deleted_stations_nearby.Append();
01758 ts->tile = st->xy;
01759 ts->station = st->index;
01760
01761
01762 if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) &&
01763 IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) {
01764 AddNearbyStation<T>(st->xy, &ctx);
01765 }
01766 }
01767 }
01768 }
01769
01770
01771
01772
01773 if (distant_join && min(ta.w, ta.h) >= _settings_game.station.station_spread) return NULL;
01774 uint max_dist = distant_join ? _settings_game.station.station_spread - min(ta.w, ta.h) : 1;
01775
01776 TileIndex tile = TILE_ADD(ctx.tile, TileOffsByDir(DIR_N));
01777 CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
01778
01779 return NULL;
01780 }
01781
01782 enum JoinStationWidgets {
01783 JSW_WIDGET_CAPTION,
01784 JSW_PANEL,
01785 JSW_SCROLLBAR,
01786 };
01787
01788 static const NWidgetPart _nested_select_station_widgets[] = {
01789 NWidget(NWID_HORIZONTAL),
01790 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
01791 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, JSW_WIDGET_CAPTION), SetDataTip(STR_JOIN_STATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01792 EndContainer(),
01793 NWidget(NWID_HORIZONTAL),
01794 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, JSW_PANEL), SetResize(1, 0), SetScrollbar(JSW_SCROLLBAR), EndContainer(),
01795 NWidget(NWID_VERTICAL),
01796 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, JSW_SCROLLBAR),
01797 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
01798 EndContainer(),
01799 EndContainer(),
01800 };
01801
01806 template <class T>
01807 struct SelectStationWindow : Window {
01808 CommandContainer select_station_cmd;
01809 TileArea area;
01810 Scrollbar *vscroll;
01811
01812 SelectStationWindow(const WindowDesc *desc, CommandContainer cmd, TileArea ta) :
01813 Window(),
01814 select_station_cmd(cmd),
01815 area(ta)
01816 {
01817 this->CreateNestedTree(desc);
01818 this->vscroll = this->GetScrollbar(JSW_SCROLLBAR);
01819 this->GetWidget<NWidgetCore>(JSW_WIDGET_CAPTION)->widget_data = T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
01820 this->FinishInitNested(desc, 0);
01821 this->OnInvalidateData(0);
01822 }
01823
01824 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01825 {
01826 if (widget != JSW_PANEL) return;
01827
01828
01829 Dimension d = GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
01830 for (uint i = 0; i < _stations_nearby_list.Length(); i++) {
01831 const T *st = T::Get(_stations_nearby_list[i]);
01832 SetDParam(0, st->index);
01833 SetDParam(1, st->facilities);
01834 d = maxdim(d, GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION));
01835 }
01836
01837 resize->height = d.height;
01838 d.height *= 5;
01839 d.width += WD_FRAMERECT_RIGHT + WD_FRAMERECT_LEFT;
01840 d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
01841 *size = d;
01842 }
01843
01844 virtual void DrawWidget(const Rect &r, int widget) const
01845 {
01846 if (widget != JSW_PANEL) return;
01847
01848 uint y = r.top + WD_FRAMERECT_TOP;
01849 if (this->vscroll->GetPosition() == 0) {
01850 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);
01851 y += this->resize.step_height;
01852 }
01853
01854 for (uint i = max<uint>(1, this->vscroll->GetPosition()); i <= _stations_nearby_list.Length(); ++i, y += this->resize.step_height) {
01855
01856 if (i - this->vscroll->GetPosition() >= this->vscroll->GetCapacity()) break;
01857
01858 const T *st = T::Get(_stations_nearby_list[i - 1]);
01859 SetDParam(0, st->index);
01860 SetDParam(1, st->facilities);
01861 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);
01862 }
01863 }
01864
01865 virtual void OnClick(Point pt, int widget, int click_count)
01866 {
01867 if (widget != JSW_PANEL) return;
01868
01869 uint st_index = this->vscroll->GetScrolledRowFromWidget(pt.y, this, JSW_PANEL, WD_FRAMERECT_TOP);
01870 bool distant_join = (st_index > 0);
01871 if (distant_join) st_index--;
01872
01873 if (distant_join && st_index >= _stations_nearby_list.Length()) return;
01874
01875
01876 SB(this->select_station_cmd.p2, 16, 16,
01877 (distant_join ? _stations_nearby_list[st_index] : NEW_STATION));
01878
01879
01880 DoCommandP(&this->select_station_cmd);
01881
01882
01883 DeleteWindowById(WC_SELECT_STATION, 0);
01884 }
01885
01886 virtual void OnTick()
01887 {
01888 if (_thd.dirty & 2) {
01889 _thd.dirty &= ~2;
01890 this->SetDirty();
01891 }
01892 }
01893
01894 virtual void OnResize()
01895 {
01896 this->vscroll->SetCapacityFromWidget(this, JSW_PANEL, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
01897 }
01898
01904 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01905 {
01906 if (!gui_scope) return;
01907 FindStationsNearby<T>(this->area, true);
01908 this->vscroll->SetCount(_stations_nearby_list.Length() + 1);
01909 this->SetDirty();
01910 }
01911 };
01912
01913 static const WindowDesc _select_station_desc(
01914 WDP_AUTO, 200, 180,
01915 WC_SELECT_STATION, WC_NONE,
01916 WDF_CONSTRUCTION,
01917 _nested_select_station_widgets, lengthof(_nested_select_station_widgets)
01918 );
01919
01920
01928 template <class T>
01929 static bool StationJoinerNeeded(CommandContainer cmd, TileArea ta)
01930 {
01931
01932 if (!_settings_game.station.distant_join_stations) return false;
01933
01934
01935
01936 Window *selection_window = FindWindowById(WC_SELECT_STATION, 0);
01937 if (selection_window != NULL) {
01938
01939 delete selection_window;
01940 UpdateTileSelection();
01941 }
01942
01943
01944 if (!_ctrl_pressed) return false;
01945
01946
01947 if (DoCommand(&cmd, CommandFlagsToDCFlags(GetCommandFlags(cmd.cmd))).Failed()) return false;
01948
01949
01950
01951
01952 const T *st = FindStationsNearby<T>(ta, false);
01953 return st == NULL && (_settings_game.station.adjacent_stations || _stations_nearby_list.Length() == 0);
01954 }
01955
01962 template <class T>
01963 void ShowSelectBaseStationIfNeeded(CommandContainer cmd, TileArea ta)
01964 {
01965 if (StationJoinerNeeded<T>(cmd, ta)) {
01966 if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
01967 new SelectStationWindow<T>(&_select_station_desc, cmd, ta);
01968 } else {
01969 DoCommandP(&cmd);
01970 }
01971 }
01972
01978 void ShowSelectStationIfNeeded(CommandContainer cmd, TileArea ta)
01979 {
01980 ShowSelectBaseStationIfNeeded<Station>(cmd, ta);
01981 }
01982
01988 void ShowSelectWaypointIfNeeded(CommandContainer cmd, TileArea ta)
01989 {
01990 ShowSelectBaseStationIfNeeded<Waypoint>(cmd, ta);
01991 }