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
00048 int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies)
00049 {
00050 TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
00051 uint32 cargo_mask = 0;
00052 if (_thd.drawstyle == HT_RECT && tile < MapSize()) {
00053 CargoArray cargoes;
00054 if (supplies) {
00055 cargoes = GetProductionAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00056 } else {
00057 cargoes = GetAcceptanceAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00058 }
00059
00060
00061 for (CargoID i = 0; i < NUM_CARGO; i++) {
00062 switch (sct) {
00063 case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00064 case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00065 case SCT_ALL: break;
00066 default: NOT_REACHED();
00067 }
00068 if (cargoes[i] >= (supplies ? 1U : 8U)) SetBit(cargo_mask, i);
00069 }
00070 }
00071 SetDParam(0, cargo_mask);
00072 return DrawStringMultiLine(left, right, top, INT32_MAX, supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO);
00073 }
00074
00080 void CheckRedrawStationCoverage(const Window *w)
00081 {
00082 if (_thd.dirty & 1) {
00083 _thd.dirty &= ~1;
00084 w->SetDirty();
00085 }
00086 }
00087
00103 static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating)
00104 {
00105 static const uint units_full = 576;
00106 static const uint rating_full = 224;
00107
00108 const CargoSpec *cs = CargoSpec::Get(type);
00109 if (!cs->IsValid()) return;
00110
00111 int colour = cs->rating_colour;
00112 uint w = (minu(amount, units_full) + 5) / 36;
00113
00114 int height = GetCharacterHeight(FS_SMALL);
00115
00116
00117 if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour);
00118
00119
00120
00121 if (w == 0) {
00122 uint rest = amount / 5;
00123 if (rest != 0) {
00124 w += left;
00125 GfxFillRect(w, y + height - rest, w, y + height, colour);
00126 }
00127 }
00128
00129 DrawString(left + 1, right, y, cs->abbrev, TC_BLACK);
00130
00131
00132 y += height + 2;
00133 GfxFillRect(left + 1, y, left + 14, y, PC_RED);
00134 rating = minu(rating, rating_full) / 16;
00135 if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, PC_GREEN);
00136 }
00137
00138 typedef GUIList<const Station*> GUIStationList;
00139
00143 class CompanyStationsWindow : public Window
00144 {
00145 protected:
00146
00147 static Listing last_sorting;
00148 static byte facilities;
00149 static bool include_empty;
00150 static const uint32 cargo_filter_max;
00151 static uint32 cargo_filter;
00152 static const Station *last_station;
00153
00154
00155 static const StringID sorter_names[];
00156 static GUIStationList::SortFunction * const sorter_funcs[];
00157
00158 GUIStationList stations;
00159 Scrollbar *vscroll;
00160
00166 void BuildStationsList(const Owner owner)
00167 {
00168 if (!this->stations.NeedRebuild()) return;
00169
00170 DEBUG(misc, 3, "Building station list for company %d", owner);
00171
00172 this->stations.Clear();
00173
00174 const Station *st;
00175 FOR_ALL_STATIONS(st) {
00176 if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, true, owner))) {
00177 if (this->facilities & st->facilities) {
00178 int num_waiting_cargo = 0;
00179 for (CargoID j = 0; j < NUM_CARGO; j++) {
00180 if (HasBit(st->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) {
00181 num_waiting_cargo++;
00182 if (HasBit(this->cargo_filter, j)) {
00183 *this->stations.Append() = st;
00184 break;
00185 }
00186 }
00187 }
00188
00189 if (num_waiting_cargo == 0 && this->include_empty) {
00190 *this->stations.Append() = st;
00191 }
00192 }
00193 }
00194 }
00195
00196 this->stations.Compact();
00197 this->stations.RebuildDone();
00198
00199 this->vscroll->SetCount(this->stations.Length());
00200 }
00201
00203 static int CDECL StationNameSorter(const Station * const *a, const Station * const *b)
00204 {
00205 static char buf_cache[64];
00206 char buf[64];
00207
00208 SetDParam(0, (*a)->index);
00209 GetString(buf, STR_STATION_NAME, lastof(buf));
00210
00211 if (*b != last_station) {
00212 last_station = *b;
00213 SetDParam(0, (*b)->index);
00214 GetString(buf_cache, STR_STATION_NAME, lastof(buf_cache));
00215 }
00216
00217 return strcmp(buf, buf_cache);
00218 }
00219
00221 static int CDECL StationTypeSorter(const Station * const *a, const Station * const *b)
00222 {
00223 return (*a)->facilities - (*b)->facilities;
00224 }
00225
00227 static int CDECL StationWaitingSorter(const Station * const *a, const Station * const *b)
00228 {
00229 Money diff = 0;
00230
00231 CargoID j;
00232 FOR_EACH_SET_CARGO_ID(j, cargo_filter) {
00233 if (!(*a)->goods[j].cargo.Empty()) diff += GetTransportedGoodsIncome((*a)->goods[j].cargo.Count(), 20, 50, j);
00234 if (!(*b)->goods[j].cargo.Empty()) diff -= GetTransportedGoodsIncome((*b)->goods[j].cargo.Count(), 20, 50, j);
00235 }
00236
00237 return ClampToI32(diff);
00238 }
00239
00241 static int CDECL StationRatingMaxSorter(const Station * const *a, const Station * const *b)
00242 {
00243 byte maxr1 = 0;
00244 byte maxr2 = 0;
00245
00246 CargoID j;
00247 FOR_EACH_SET_CARGO_ID(j, cargo_filter) {
00248 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) maxr1 = max(maxr1, (*a)->goods[j].rating);
00249 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) maxr2 = max(maxr2, (*b)->goods[j].rating);
00250 }
00251
00252 return maxr1 - maxr2;
00253 }
00254
00256 static int CDECL StationRatingMinSorter(const Station * const *a, const Station * const *b)
00257 {
00258 byte minr1 = 255;
00259 byte minr2 = 255;
00260
00261 for (CargoID j = 0; j < NUM_CARGO; j++) {
00262 if (!HasBit(cargo_filter, j)) continue;
00263 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) minr1 = min(minr1, (*a)->goods[j].rating);
00264 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::GES_PICKUP)) minr2 = min(minr2, (*b)->goods[j].rating);
00265 }
00266
00267 return -(minr1 - minr2);
00268 }
00269
00271 void SortStationsList()
00272 {
00273 if (!this->stations.Sort()) return;
00274
00275
00276 this->last_station = NULL;
00277
00278
00279 this->SetWidgetDirty(WID_STL_LIST);
00280 }
00281
00282 public:
00283 CompanyStationsWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
00284 {
00285 this->stations.SetListing(this->last_sorting);
00286 this->stations.SetSortFuncs(this->sorter_funcs);
00287 this->stations.ForceRebuild();
00288 this->stations.NeedResort();
00289 this->SortStationsList();
00290
00291 this->CreateNestedTree(desc);
00292 this->vscroll = this->GetScrollbar(WID_STL_SCROLLBAR);
00293 this->FinishInitNested(desc, window_number);
00294 this->owner = (Owner)this->window_number;
00295
00296 CargoID cid;
00297 FOR_EACH_SET_CARGO_ID(cid, this->cargo_filter) {
00298 if (CargoSpec::Get(cid)->IsValid()) this->LowerWidget(WID_STL_CARGOSTART + cid);
00299 }
00300
00301 if (this->cargo_filter == this->cargo_filter_max) this->cargo_filter = _cargo_mask;
00302
00303 for (uint i = 0; i < 5; i++) {
00304 if (HasBit(this->facilities, i)) this->LowerWidget(i + WID_STL_TRAIN);
00305 }
00306 this->SetWidgetLoweredState(WID_STL_NOCARGOWAITING, this->include_empty);
00307
00308 this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00309 }
00310
00311 ~CompanyStationsWindow()
00312 {
00313 this->last_sorting = this->stations.GetListing();
00314 }
00315
00316 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00317 {
00318 switch (widget) {
00319 case WID_STL_SORTBY: {
00320 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
00321 d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2;
00322 d.height += padding.height;
00323 *size = maxdim(*size, d);
00324 break;
00325 }
00326
00327 case WID_STL_SORTDROPBTN: {
00328 Dimension d = {0, 0};
00329 for (int i = 0; this->sorter_names[i] != INVALID_STRING_ID; i++) {
00330 d = maxdim(d, GetStringBoundingBox(this->sorter_names[i]));
00331 }
00332 d.width += padding.width;
00333 d.height += padding.height;
00334 *size = maxdim(*size, d);
00335 break;
00336 }
00337
00338 case WID_STL_LIST:
00339 resize->height = FONT_HEIGHT_NORMAL;
00340 size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
00341 break;
00342
00343 case WID_STL_TRAIN:
00344 case WID_STL_TRUCK:
00345 case WID_STL_BUS:
00346 case WID_STL_AIRPLANE:
00347 case WID_STL_SHIP:
00348 size->height = max<uint>(FONT_HEIGHT_SMALL, 10) + padding.height;
00349 break;
00350
00351 case WID_STL_CARGOALL:
00352 case WID_STL_FACILALL:
00353 case WID_STL_NOCARGOWAITING: {
00354 Dimension d = GetStringBoundingBox(widget == WID_STL_NOCARGOWAITING ? STR_ABBREV_NONE : STR_ABBREV_ALL);
00355 d.width += padding.width + 2;
00356 d.height += padding.height;
00357 *size = maxdim(*size, d);
00358 break;
00359 }
00360
00361 default:
00362 if (widget >= WID_STL_CARGOSTART) {
00363 const CargoSpec *cs = CargoSpec::Get(widget - WID_STL_CARGOSTART);
00364 if (cs->IsValid()) {
00365 Dimension d = GetStringBoundingBox(cs->abbrev);
00366 d.width += padding.width + 2;
00367 d.height += padding.height;
00368 *size = maxdim(*size, d);
00369 }
00370 }
00371 break;
00372 }
00373 }
00374
00375 virtual void OnPaint()
00376 {
00377 this->BuildStationsList((Owner)this->window_number);
00378 this->SortStationsList();
00379
00380 this->DrawWidgets();
00381 }
00382
00383 virtual void DrawWidget(const Rect &r, int widget) const
00384 {
00385 switch (widget) {
00386 case WID_STL_SORTBY:
00387
00388 this->DrawSortButtonState(WID_STL_SORTBY, this->stations.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
00389 break;
00390
00391 case WID_STL_LIST: {
00392 bool rtl = _current_text_dir == TD_RTL;
00393 int max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->stations.Length());
00394 int y = r.top + WD_FRAMERECT_TOP;
00395 for (int i = this->vscroll->GetPosition(); i < max; ++i) {
00396 const Station *st = this->stations[i];
00397 assert(st->xy != INVALID_TILE);
00398
00399
00400
00401 assert(st->owner == owner || st->owner == OWNER_NONE);
00402
00403 SetDParam(0, st->index);
00404 SetDParam(1, st->facilities);
00405 int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION);
00406 x += rtl ? -5 : 5;
00407
00408
00409 for (CargoID j = 0; j < NUM_CARGO; j++) {
00410 if (!st->goods[j].cargo.Empty()) {
00411
00412
00413
00414
00415 if (rtl) {
00416 x -= 20;
00417 if (x < r.left + WD_FRAMERECT_LEFT) break;
00418 }
00419 StationsWndShowStationRating(x, x + 16, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
00420 if (!rtl) {
00421 x += 20;
00422 if (x > r.right - WD_FRAMERECT_RIGHT) break;
00423 }
00424 }
00425 }
00426 y += FONT_HEIGHT_NORMAL;
00427 }
00428
00429 if (this->vscroll->GetCount() == 0) {
00430 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_NONE);
00431 return;
00432 }
00433 break;
00434 }
00435
00436 case WID_STL_NOCARGOWAITING: {
00437 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00438 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_HOR_CENTER);
00439 break;
00440 }
00441
00442 case WID_STL_CARGOALL: {
00443 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00444 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_HOR_CENTER);
00445 break;
00446 }
00447
00448 case WID_STL_FACILALL: {
00449 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00450 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00451 break;
00452 }
00453
00454 default:
00455 if (widget >= WID_STL_CARGOSTART) {
00456 const CargoSpec *cs = CargoSpec::Get(widget - WID_STL_CARGOSTART);
00457 if (cs->IsValid()) {
00458 int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 2 : 1;
00459 GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour);
00460 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, cs->abbrev, TC_BLACK, SA_HOR_CENTER);
00461 }
00462 }
00463 break;
00464 }
00465 }
00466
00467 virtual void SetStringParameters(int widget) const
00468 {
00469 if (widget == WID_STL_CAPTION) {
00470 SetDParam(0, this->window_number);
00471 SetDParam(1, this->vscroll->GetCount());
00472 }
00473 }
00474
00475 virtual void OnClick(Point pt, int widget, int click_count)
00476 {
00477 switch (widget) {
00478 case WID_STL_LIST: {
00479 uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_STL_LIST, 0, FONT_HEIGHT_NORMAL);
00480 if (id_v >= this->stations.Length()) return;
00481
00482 const Station *st = this->stations[id_v];
00483
00484 assert(st->owner == (Owner)this->window_number || st->owner == OWNER_NONE);
00485
00486 if (_ctrl_pressed) {
00487 ShowExtraViewPortWindow(st->xy);
00488 } else {
00489 ScrollMainWindowToTile(st->xy);
00490 }
00491 break;
00492 }
00493
00494 case WID_STL_TRAIN:
00495 case WID_STL_TRUCK:
00496 case WID_STL_BUS:
00497 case WID_STL_AIRPLANE:
00498 case WID_STL_SHIP:
00499 if (_ctrl_pressed) {
00500 ToggleBit(this->facilities, widget - WID_STL_TRAIN);
00501 this->ToggleWidgetLoweredState(widget);
00502 } else {
00503 uint i;
00504 FOR_EACH_SET_BIT(i, this->facilities) {
00505 this->RaiseWidget(i + WID_STL_TRAIN);
00506 }
00507 this->facilities = 1 << (widget - WID_STL_TRAIN);
00508 this->LowerWidget(widget);
00509 }
00510 this->stations.ForceRebuild();
00511 this->SetDirty();
00512 break;
00513
00514 case WID_STL_FACILALL:
00515 for (uint i = WID_STL_TRAIN; i <= WID_STL_SHIP; i++) {
00516 this->LowerWidget(i);
00517 }
00518
00519 this->facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00520 this->stations.ForceRebuild();
00521 this->SetDirty();
00522 break;
00523
00524 case WID_STL_CARGOALL: {
00525 for (uint i = 0; i < NUM_CARGO; i++) {
00526 const CargoSpec *cs = CargoSpec::Get(i);
00527 if (cs->IsValid()) 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->SetTimeout();
00541 this->LowerWidget(WID_STL_SORTBY);
00542 this->SetDirty();
00543 break;
00544
00545 case WID_STL_SORTDROPBTN:
00546 ShowDropDownMenu(this, this->sorter_names, this->stations.SortType(), WID_STL_SORTDROPBTN, 0, 0);
00547 break;
00548
00549 case WID_STL_NOCARGOWAITING:
00550 if (_ctrl_pressed) {
00551 this->include_empty = !this->include_empty;
00552 this->ToggleWidgetLoweredState(WID_STL_NOCARGOWAITING);
00553 } else {
00554 for (uint i = 0; i < NUM_CARGO; i++) {
00555 const CargoSpec *cs = CargoSpec::Get(i);
00556 if (cs->IsValid()) this->RaiseWidget(WID_STL_CARGOSTART + i);
00557 }
00558
00559 this->cargo_filter = 0;
00560 this->include_empty = true;
00561
00562 this->LowerWidget(WID_STL_NOCARGOWAITING);
00563 }
00564 this->stations.ForceRebuild();
00565 this->SetDirty();
00566 break;
00567
00568 default:
00569 if (widget >= WID_STL_CARGOSTART) {
00570
00571 const CargoSpec *cs = CargoSpec::Get(widget - WID_STL_CARGOSTART);
00572 if (!cs->IsValid()) break;
00573
00574 if (_ctrl_pressed) {
00575 ToggleBit(this->cargo_filter, cs->Index());
00576 this->ToggleWidgetLoweredState(widget);
00577 } else {
00578 for (uint i = 0; i < NUM_CARGO; i++) {
00579 const CargoSpec *cs = CargoSpec::Get(i);
00580 if (cs->IsValid()) this->RaiseWidget(WID_STL_CARGOSTART + i);
00581 }
00582 this->RaiseWidget(WID_STL_NOCARGOWAITING);
00583
00584 this->cargo_filter = 0;
00585 this->include_empty = false;
00586
00587 SetBit(this->cargo_filter, cs->Index());
00588 this->LowerWidget(widget);
00589 }
00590 this->stations.ForceRebuild();
00591 this->SetDirty();
00592 }
00593 break;
00594 }
00595 }
00596
00597 virtual void OnDropdownSelect(int widget, int index)
00598 {
00599 if (this->stations.SortType() != index) {
00600 this->stations.SetSortType(index);
00601
00602
00603 this->GetWidget<NWidgetCore>(WID_STL_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00604
00605 this->SetDirty();
00606 }
00607 }
00608
00609 virtual void OnTick()
00610 {
00611 if (_pause_mode != PM_UNPAUSED) return;
00612 if (this->stations.NeedResort()) {
00613 DEBUG(misc, 3, "Periodic rebuild station list company %d", this->window_number);
00614 this->SetDirty();
00615 }
00616 }
00617
00618 virtual void OnTimeout()
00619 {
00620 this->RaiseWidget(WID_STL_SORTBY);
00621 this->SetDirty();
00622 }
00623
00624 virtual void OnResize()
00625 {
00626 this->vscroll->SetCapacityFromWidget(this, WID_STL_LIST, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
00627 }
00628
00634 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00635 {
00636 if (data == 0) {
00637
00638 this->stations.ForceRebuild();
00639 } else {
00640 this->stations.ForceResort();
00641 }
00642 }
00643 };
00644
00645 Listing CompanyStationsWindow::last_sorting = {false, 0};
00646 byte CompanyStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00647 bool CompanyStationsWindow::include_empty = true;
00648 const uint32 CompanyStationsWindow::cargo_filter_max = UINT32_MAX;
00649 uint32 CompanyStationsWindow::cargo_filter = UINT32_MAX;
00650 const Station *CompanyStationsWindow::last_station = NULL;
00651
00652
00653 GUIStationList::SortFunction * const CompanyStationsWindow::sorter_funcs[] = {
00654 &StationNameSorter,
00655 &StationTypeSorter,
00656 &StationWaitingSorter,
00657 &StationRatingMaxSorter,
00658 &StationRatingMinSorter
00659 };
00660
00661
00662 const StringID CompanyStationsWindow::sorter_names[] = {
00663 STR_SORT_BY_NAME,
00664 STR_SORT_BY_FACILITY,
00665 STR_SORT_BY_WAITING,
00666 STR_SORT_BY_RATING_MAX,
00667 STR_SORT_BY_RATING_MIN,
00668 INVALID_STRING_ID
00669 };
00670
00676 static NWidgetBase *CargoWidgets(int *biggest_index)
00677 {
00678 NWidgetHorizontal *container = new NWidgetHorizontal();
00679
00680 for (uint i = 0; i < NUM_CARGO; i++) {
00681 const CargoSpec *cs = CargoSpec::Get(i);
00682 if (cs->IsValid()) {
00683 NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, WID_STL_CARGOSTART + i);
00684 panel->SetMinimalSize(14, 11);
00685 panel->SetResize(0, 0);
00686 panel->SetFill(0, 1);
00687 panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE);
00688 container->Add(panel);
00689 } else {
00690 NWidgetLeaf *nwi = new NWidgetLeaf(WWT_EMPTY, COLOUR_GREY, WID_STL_CARGOSTART + i, 0x0, STR_NULL);
00691 nwi->SetMinimalSize(0, 11);
00692 nwi->SetResize(0, 0);
00693 nwi->SetFill(0, 1);
00694 container->Add(nwi);
00695 }
00696 }
00697 *biggest_index = WID_STL_CARGOSTART + NUM_CARGO;
00698 return container;
00699 }
00700
00701 static const NWidgetPart _nested_company_stations_widgets[] = {
00702 NWidget(NWID_HORIZONTAL),
00703 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00704 NWidget(WWT_CAPTION, COLOUR_GREY, WID_STL_CAPTION), SetDataTip(STR_STATION_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00705 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00706 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00707 EndContainer(),
00708 NWidget(NWID_HORIZONTAL),
00709 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),
00710 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),
00711 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),
00712 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),
00713 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),
00714 NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_FACILALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1),
00715 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(),
00716 NWidgetFunction(CargoWidgets),
00717 NWidget(WWT_PANEL, COLOUR_GREY, WID_STL_NOCARGOWAITING), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(),
00718 NWidget(WWT_PUSHBTN, COLOUR_GREY, WID_STL_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1),
00719 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00720 EndContainer(),
00721 NWidget(NWID_HORIZONTAL),
00722 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_STL_SORTBY), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
00723 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_STL_SORTDROPBTN), SetMinimalSize(163, 12), SetDataTip(STR_SORT_BY_NAME, STR_TOOLTIP_SORT_CRITERIA),
00724 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00725 EndContainer(),
00726 NWidget(NWID_HORIZONTAL),
00727 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(),
00728 NWidget(NWID_VERTICAL),
00729 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_STL_SCROLLBAR),
00730 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00731 EndContainer(),
00732 EndContainer(),
00733 };
00734
00735 static const WindowDesc _company_stations_desc(
00736 WDP_AUTO, 358, 162,
00737 WC_STATION_LIST, WC_NONE,
00738 WDF_UNCLICK_BUTTONS,
00739 _nested_company_stations_widgets, lengthof(_nested_company_stations_widgets)
00740 );
00741
00747 void ShowCompanyStations(CompanyID company)
00748 {
00749 if (!Company::IsValidID(company)) return;
00750
00751 AllocateWindowDescFront<CompanyStationsWindow>(&_company_stations_desc, company);
00752 }
00753
00754 static const NWidgetPart _nested_station_view_widgets[] = {
00755 NWidget(NWID_HORIZONTAL),
00756 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00757 NWidget(WWT_CAPTION, COLOUR_GREY, WID_SV_CAPTION), SetDataTip(STR_STATION_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00758 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00759 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00760 EndContainer(),
00761 NWidget(NWID_HORIZONTAL),
00762 NWidget(WWT_PANEL, COLOUR_GREY, WID_SV_WAITING), SetMinimalSize(237, 52), SetResize(1, 10), SetScrollbar(WID_SV_SCROLLBAR), EndContainer(),
00763 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SV_SCROLLBAR),
00764 EndContainer(),
00765 NWidget(WWT_PANEL, COLOUR_GREY, WID_SV_ACCEPT_RATING_LIST), SetMinimalSize(249, 32), SetResize(1, 0), EndContainer(),
00766 NWidget(NWID_HORIZONTAL),
00767 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
00768 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_LOCATION), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
00769 SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP),
00770 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ACCEPTS_RATINGS), SetMinimalSize(46, 12), SetResize(1, 0), SetFill(1, 1),
00771 SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP),
00772 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_RENAME), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
00773 SetDataTip(STR_BUTTON_RENAME, STR_STATION_VIEW_RENAME_TOOLTIP),
00774 EndContainer(),
00775 NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SV_CLOSE_AIRPORT), SetMinimalSize(45, 12), SetResize(1, 0), SetFill(1, 1),
00776 SetDataTip(STR_STATION_VIEW_CLOSE_AIRPORT, STR_STATION_VIEW_CLOSE_AIRPORT_TOOLTIP),
00777 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_TRAINS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP),
00778 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_ROADVEHS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP),
00779 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_SHIPS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_SHIP, STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP),
00780 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SV_PLANES), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_PLANE, STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP),
00781 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00782 EndContainer(),
00783 };
00784
00795 static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
00796 {
00797 uint num = min((waiting + 5) / 10, (right - left) / 10);
00798 if (num == 0) return;
00799
00800 SpriteID sprite = CargoSpec::Get(i)->GetCargoIcon();
00801
00802 int x = _current_text_dir == TD_RTL ? right - num * 10 : left;
00803 do {
00804 DrawSprite(sprite, PAL_NONE, x, y);
00805 x += 10;
00806 } while (--num);
00807 }
00808
00809 struct CargoData {
00810 CargoID cargo;
00811 StationID source;
00812 uint count;
00813
00814 CargoData(CargoID cargo, StationID source, uint count) :
00815 cargo(cargo),
00816 source(source),
00817 count(count)
00818 { }
00819 };
00820
00821 typedef std::list<CargoData> CargoDataList;
00822
00826 struct StationViewWindow : public Window {
00827 uint32 cargo;
00828 uint16 cargo_rows[NUM_CARGO];
00829 uint expand_shrink_width;
00830 int rating_lines;
00831 int accepts_lines;
00832 Scrollbar *vscroll;
00833
00835 enum AcceptListHeight {
00836 ALH_RATING = 13,
00837 ALH_ACCEPTS = 3,
00838 };
00839
00840 StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
00841 {
00842 this->rating_lines = ALH_RATING;
00843 this->accepts_lines = ALH_ACCEPTS;
00844
00845 this->CreateNestedTree(desc);
00846 this->vscroll = this->GetScrollbar(WID_SV_SCROLLBAR);
00847
00848 this->FinishInitNested(desc, window_number);
00849
00850 Owner owner = Station::Get(window_number)->owner;
00851 if (owner != OWNER_NONE) this->owner = owner;
00852 }
00853
00854 ~StationViewWindow()
00855 {
00856 Owner owner = Station::Get(this->window_number)->owner;
00857 DeleteWindowById(WC_TRAINS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_TRAIN, owner, this->window_number).Pack(), false);
00858 DeleteWindowById(WC_ROADVEH_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_ROAD, owner, this->window_number).Pack(), false);
00859 DeleteWindowById(WC_SHIPS_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_SHIP, owner, this->window_number).Pack(), false);
00860 DeleteWindowById(WC_AIRCRAFT_LIST, VehicleListIdentifier(VL_STATION_LIST, VEH_AIRCRAFT, owner, this->window_number).Pack(), false);
00861 }
00862
00863 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00864 {
00865 switch (widget) {
00866 case WID_SV_WAITING:
00867 resize->height = FONT_HEIGHT_NORMAL;
00868 size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
00869 this->expand_shrink_width = max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
00870 break;
00871
00872 case WID_SV_ACCEPT_RATING_LIST:
00873 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;
00874 break;
00875
00876 case WID_SV_CLOSE_AIRPORT:
00877 if (!(Station::Get(this->window_number)->facilities & FACIL_AIRPORT)) {
00878
00879 size->width = 0;
00880 resize->width = 0;
00881 fill->width = 0;
00882 }
00883 break;
00884 }
00885 }
00886
00887 virtual void OnPaint()
00888 {
00889 CargoDataList cargolist;
00890 uint32 transfers = 0;
00891 this->OrderWaitingCargo(&cargolist, &transfers);
00892
00893 this->vscroll->SetCount((int)cargolist.size() + 1);
00894
00895
00896 const Station *st = Station::Get(this->window_number);
00897 this->SetWidgetDisabledState(WID_SV_RENAME, st->owner != _local_company);
00898 this->SetWidgetDisabledState(WID_SV_TRAINS, !(st->facilities & FACIL_TRAIN));
00899 this->SetWidgetDisabledState(WID_SV_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
00900 this->SetWidgetDisabledState(WID_SV_SHIPS, !(st->facilities & FACIL_DOCK));
00901 this->SetWidgetDisabledState(WID_SV_PLANES, !(st->facilities & FACIL_AIRPORT));
00902 this->SetWidgetDisabledState(WID_SV_CLOSE_AIRPORT, !(st->facilities & FACIL_AIRPORT) || st->owner != _local_company);
00903 this->SetWidgetLoweredState(WID_SV_CLOSE_AIRPORT, (st->facilities & FACIL_AIRPORT) && (st->airport.flags & AIRPORT_CLOSED_block) != 0);
00904
00905 this->DrawWidgets();
00906
00907 if (!this->IsShaded()) {
00908
00909 const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_SV_ACCEPT_RATING_LIST);
00910 const Rect r = {wid->pos_x, wid->pos_y, wid->pos_x + wid->current_x - 1, wid->pos_y + wid->current_y - 1};
00911 if (this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
00912 int lines = this->DrawAcceptedCargo(r);
00913 if (lines > this->accepts_lines) {
00914 this->accepts_lines = lines;
00915 this->ReInit();
00916 return;
00917 }
00918 } else {
00919 int lines = this->DrawCargoRatings(r);
00920 if (lines > this->rating_lines) {
00921 this->rating_lines = lines;
00922 this->ReInit();
00923 return;
00924 }
00925 }
00926
00927
00928 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(WID_SV_WAITING);
00929 Rect waiting_rect = {nwi->pos_x, nwi->pos_y, nwi->pos_x + nwi->current_x - 1, nwi->pos_y + nwi->current_y - 1};
00930 this->DrawWaitingCargo(waiting_rect, cargolist, transfers);
00931 }
00932 }
00933
00934 virtual void SetStringParameters(int widget) const
00935 {
00936 if (widget == WID_SV_CAPTION) {
00937 const Station *st = Station::Get(this->window_number);
00938 SetDParam(0, st->index);
00939 SetDParam(1, st->facilities);
00940 }
00941 }
00942
00949 void OrderWaitingCargo(CargoDataList *cargolist, uint32 *transfers)
00950 {
00951 assert(cargolist->size() == 0);
00952 *transfers = 0;
00953
00954 StationID station_id = this->window_number;
00955 const Station *st = Station::Get(station_id);
00956
00957
00958 for (CargoID i = 0; i < NUM_CARGO; i++) {
00959 if (st->goods[i].cargo.Empty()) {
00960 this->cargo_rows[i] = 0;
00961 } else {
00962
00963 cargolist->push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
00964
00965
00966 this->cargo_rows[i] = (uint16)cargolist->size();
00967
00968
00969 const StationCargoList::List *packets = st->goods[i].cargo.Packets();
00970 for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
00971 const CargoPacket *cp = *it;
00972 if (cp->SourceStation() != station_id) {
00973 bool added = false;
00974
00975
00976 SetBit(*transfers, i);
00977
00978
00979 if (!HasBit(this->cargo, i)) break;
00980
00981
00982 for (CargoDataList::iterator jt(cargolist->begin()); jt != cargolist->end(); jt++) {
00983 CargoData *cd = &(*jt);
00984 if (cd->cargo == i && cd->source == cp->SourceStation()) {
00985 cd->count += cp->Count();
00986 added = true;
00987 break;
00988 }
00989 }
00990
00991 if (!added) cargolist->push_back(CargoData(i, cp->SourceStation(), cp->Count()));
00992 }
00993 }
00994 }
00995 }
00996 }
00997
01004 void DrawWaitingCargo(const Rect &r, const CargoDataList &cargolist, uint32 transfers) const
01005 {
01006 int y = r.top + WD_FRAMERECT_TOP;
01007 int pos = this->vscroll->GetPosition();
01008
01009 const Station *st = Station::Get(this->window_number);
01010 if (--pos < 0) {
01011 StringID str = STR_JUST_NOTHING;
01012 for (CargoID i = 0; i < NUM_CARGO; i++) {
01013 if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
01014 }
01015 SetDParam(0, str);
01016 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_TITLE);
01017 y += FONT_HEIGHT_NORMAL;
01018 }
01019
01020 bool rtl = _current_text_dir == TD_RTL;
01021 int text_left = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT;
01022 int text_right = rtl ? r.right - WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width;
01023 int shrink_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
01024 int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
01025
01026
01027 int maxrows = this->vscroll->GetCapacity();
01028 for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
01029 if (--pos < 0) {
01030 const CargoData *cd = &(*it);
01031 if (cd->source == INVALID_STATION) {
01032
01033 DrawCargoIcons(cd->cargo, cd->count, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y);
01034 SetDParam(0, cd->cargo);
01035 SetDParam(1, cd->count);
01036 if (HasBit(transfers, cd->cargo)) {
01037
01038 const char *sym = HasBit(this->cargo, cd->cargo) ? "-" : "+";
01039 DrawString(text_left, text_right, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
01040 DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW, SA_RIGHT);
01041 } else {
01042 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
01043 }
01044 } else {
01045 SetDParam(0, cd->cargo);
01046 SetDParam(1, cd->count);
01047 SetDParam(2, cd->source);
01048 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_EN_ROUTE_FROM, TC_FROMSTRING, SA_RIGHT);
01049 }
01050
01051 y += FONT_HEIGHT_NORMAL;
01052 }
01053 }
01054 }
01055
01061 int DrawAcceptedCargo(const Rect &r) const
01062 {
01063 const Station *st = Station::Get(this->window_number);
01064
01065 uint32 cargo_mask = 0;
01066 for (CargoID i = 0; i < NUM_CARGO; i++) {
01067 if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE)) SetBit(cargo_mask, i);
01068 }
01069 SetDParam(0, cargo_mask);
01070 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);
01071 return CeilDiv(bottom - r.top - WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL);
01072 }
01073
01079 int DrawCargoRatings(const Rect &r) const
01080 {
01081 const Station *st = Station::Get(this->window_number);
01082 int y = r.top + WD_FRAMERECT_TOP;
01083
01084 if (st->town->exclusive_counter > 0) {
01085 SetDParam(0, st->town->exclusivity);
01086 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);
01087 y += WD_PAR_VSEP_WIDE;
01088 }
01089
01090 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_CARGO_RATINGS_TITLE);
01091 y += FONT_HEIGHT_NORMAL;
01092
01093 const CargoSpec *cs;
01094 FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
01095 const GoodsEntry *ge = &st->goods[cs->Index()];
01096 if (!HasBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP)) continue;
01097
01098 SetDParam(0, cs->name);
01099 SetDParam(2, ToPercent8(ge->rating));
01100 SetDParam(1, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
01101 DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_RATING);
01102 y += FONT_HEIGHT_NORMAL;
01103 }
01104 return CeilDiv(y - r.top - WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL);
01105 }
01106
01107 void HandleCargoWaitingClick(int row)
01108 {
01109 if (row == 0) return;
01110
01111 for (CargoID c = 0; c < NUM_CARGO; c++) {
01112 if (this->cargo_rows[c] == row) {
01113 ToggleBit(this->cargo, c);
01114 this->SetWidgetDirty(WID_SV_WAITING);
01115 break;
01116 }
01117 }
01118 }
01119
01120 virtual void OnClick(Point pt, int widget, int click_count)
01121 {
01122 switch (widget) {
01123 case WID_SV_WAITING:
01124 this->HandleCargoWaitingClick(this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SV_WAITING, WD_FRAMERECT_TOP, FONT_HEIGHT_NORMAL));
01125 break;
01126
01127 case WID_SV_LOCATION:
01128 if (_ctrl_pressed) {
01129 ShowExtraViewPortWindow(Station::Get(this->window_number)->xy);
01130 } else {
01131 ScrollMainWindowToTile(Station::Get(this->window_number)->xy);
01132 }
01133 break;
01134
01135 case WID_SV_ACCEPTS_RATINGS: {
01136
01137 int height_change;
01138 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS);
01139 if (this->GetWidget<NWidgetCore>(WID_SV_ACCEPTS_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
01140 nwi->SetDataTip(STR_STATION_VIEW_ACCEPTS_BUTTON, STR_STATION_VIEW_ACCEPTS_TOOLTIP);
01141 height_change = this->rating_lines - this->accepts_lines;
01142 } else {
01143 nwi->SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP);
01144 height_change = this->accepts_lines - this->rating_lines;
01145 }
01146 this->ReInit(0, height_change * FONT_HEIGHT_NORMAL);
01147 break;
01148 }
01149
01150 case WID_SV_RENAME:
01151 SetDParam(0, this->window_number);
01152 ShowQueryString(STR_STATION_NAME, STR_STATION_VIEW_RENAME_STATION_CAPTION, MAX_LENGTH_STATION_NAME_CHARS,
01153 this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
01154 break;
01155
01156 case WID_SV_CLOSE_AIRPORT:
01157 DoCommandP(0, this->window_number, 0, CMD_OPEN_CLOSE_AIRPORT);
01158 break;
01159
01160 case WID_SV_TRAINS:
01161 case WID_SV_ROADVEHS:
01162 case WID_SV_SHIPS:
01163 case WID_SV_PLANES: {
01164 Owner owner = Station::Get(this->window_number)->owner;
01165 ShowVehicleListWindow(owner, (VehicleType)(widget - WID_SV_TRAINS), (StationID)this->window_number);
01166 break;
01167 }
01168 }
01169 }
01170
01171 virtual void OnQueryTextFinished(char *str)
01172 {
01173 if (str == NULL) return;
01174
01175 DoCommandP(0, this->window_number, 0, CMD_RENAME_STATION | CMD_MSG(STR_ERROR_CAN_T_RENAME_STATION), NULL, str);
01176 }
01177
01178 virtual void OnResize()
01179 {
01180 this->vscroll->SetCapacityFromWidget(this, WID_SV_WAITING, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
01181 }
01182
01188 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01189 {
01190 if (gui_scope) this->ReInit();
01191 }
01192 };
01193
01194
01195 static const WindowDesc _station_view_desc(
01196 WDP_AUTO, 249, 110,
01197 WC_STATION_VIEW, WC_NONE,
01198 WDF_UNCLICK_BUTTONS,
01199 _nested_station_view_widgets, lengthof(_nested_station_view_widgets)
01200 );
01201
01207 void ShowStationViewWindow(StationID station)
01208 {
01209 AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station);
01210 }
01211
01213 struct TileAndStation {
01214 TileIndex tile;
01215 StationID station;
01216 };
01217
01218 static SmallVector<TileAndStation, 8> _deleted_stations_nearby;
01219 static SmallVector<StationID, 8> _stations_nearby_list;
01220
01228 template <class T>
01229 static bool AddNearbyStation(TileIndex tile, void *user_data)
01230 {
01231 TileArea *ctx = (TileArea *)user_data;
01232
01233
01234 for (uint i = 0; i < _deleted_stations_nearby.Length(); i++) {
01235 TileAndStation *ts = _deleted_stations_nearby.Get(i);
01236 if (ts->tile == tile) {
01237 *_stations_nearby_list.Append() = _deleted_stations_nearby[i].station;
01238 _deleted_stations_nearby.Erase(ts);
01239 i--;
01240 }
01241 }
01242
01243
01244 if (!IsTileType(tile, MP_STATION)) return false;
01245
01246 StationID sid = GetStationIndex(tile);
01247
01248
01249 if (!T::IsValidID(sid)) return false;
01250
01251 T *st = T::Get(sid);
01252 if (st->owner != _local_company || _stations_nearby_list.Contains(sid)) return false;
01253
01254 if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST).Succeeded()) {
01255 *_stations_nearby_list.Append() = sid;
01256 }
01257
01258 return false;
01259 }
01260
01270 template <class T>
01271 static const T *FindStationsNearby(TileArea ta, bool distant_join)
01272 {
01273 TileArea ctx = ta;
01274
01275 _stations_nearby_list.Clear();
01276 _deleted_stations_nearby.Clear();
01277
01278
01279 TILE_AREA_LOOP(t, ta) {
01280 if (t < MapSize() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return T::GetByTile(t);
01281 }
01282
01283
01284 const BaseStation *st;
01285 FOR_ALL_BASE_STATIONS(st) {
01286 if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company) {
01287
01288 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) {
01289 TileAndStation *ts = _deleted_stations_nearby.Append();
01290 ts->tile = st->xy;
01291 ts->station = st->index;
01292
01293
01294 if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) &&
01295 IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) {
01296 AddNearbyStation<T>(st->xy, &ctx);
01297 }
01298 }
01299 }
01300 }
01301
01302
01303
01304
01305 if (distant_join && min(ta.w, ta.h) >= _settings_game.station.station_spread) return NULL;
01306 uint max_dist = distant_join ? _settings_game.station.station_spread - min(ta.w, ta.h) : 1;
01307
01308 TileIndex tile = TILE_ADD(ctx.tile, TileOffsByDir(DIR_N));
01309 CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
01310
01311 return NULL;
01312 }
01313
01314 static const NWidgetPart _nested_select_station_widgets[] = {
01315 NWidget(NWID_HORIZONTAL),
01316 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
01317 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_JS_CAPTION), SetDataTip(STR_JOIN_STATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01318 EndContainer(),
01319 NWidget(NWID_HORIZONTAL),
01320 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_JS_PANEL), SetResize(1, 0), SetScrollbar(WID_JS_SCROLLBAR), EndContainer(),
01321 NWidget(NWID_VERTICAL),
01322 NWidget(NWID_VSCROLLBAR, COLOUR_DARK_GREEN, WID_JS_SCROLLBAR),
01323 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
01324 EndContainer(),
01325 EndContainer(),
01326 };
01327
01332 template <class T>
01333 struct SelectStationWindow : Window {
01334 CommandContainer select_station_cmd;
01335 TileArea area;
01336 Scrollbar *vscroll;
01337
01338 SelectStationWindow(const WindowDesc *desc, CommandContainer cmd, TileArea ta) :
01339 Window(),
01340 select_station_cmd(cmd),
01341 area(ta)
01342 {
01343 this->CreateNestedTree(desc);
01344 this->vscroll = this->GetScrollbar(WID_JS_SCROLLBAR);
01345 this->GetWidget<NWidgetCore>(WID_JS_CAPTION)->widget_data = T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
01346 this->FinishInitNested(desc, 0);
01347 this->OnInvalidateData(0);
01348 }
01349
01350 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01351 {
01352 if (widget != WID_JS_PANEL) return;
01353
01354
01355 Dimension d = GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
01356 for (uint i = 0; i < _stations_nearby_list.Length(); i++) {
01357 const T *st = T::Get(_stations_nearby_list[i]);
01358 SetDParam(0, st->index);
01359 SetDParam(1, st->facilities);
01360 d = maxdim(d, GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION));
01361 }
01362
01363 resize->height = d.height;
01364 d.height *= 5;
01365 d.width += WD_FRAMERECT_RIGHT + WD_FRAMERECT_LEFT;
01366 d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
01367 *size = d;
01368 }
01369
01370 virtual void DrawWidget(const Rect &r, int widget) const
01371 {
01372 if (widget != WID_JS_PANEL) return;
01373
01374 uint y = r.top + WD_FRAMERECT_TOP;
01375 if (this->vscroll->GetPosition() == 0) {
01376 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);
01377 y += this->resize.step_height;
01378 }
01379
01380 for (uint i = max<uint>(1, this->vscroll->GetPosition()); i <= _stations_nearby_list.Length(); ++i, y += this->resize.step_height) {
01381
01382 if (i - this->vscroll->GetPosition() >= this->vscroll->GetCapacity()) break;
01383
01384 const T *st = T::Get(_stations_nearby_list[i - 1]);
01385 SetDParam(0, st->index);
01386 SetDParam(1, st->facilities);
01387 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);
01388 }
01389 }
01390
01391 virtual void OnClick(Point pt, int widget, int click_count)
01392 {
01393 if (widget != WID_JS_PANEL) return;
01394
01395 uint st_index = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_JS_PANEL, WD_FRAMERECT_TOP);
01396 bool distant_join = (st_index > 0);
01397 if (distant_join) st_index--;
01398
01399 if (distant_join && st_index >= _stations_nearby_list.Length()) return;
01400
01401
01402 SB(this->select_station_cmd.p2, 16, 16,
01403 (distant_join ? _stations_nearby_list[st_index] : NEW_STATION));
01404
01405
01406 DoCommandP(&this->select_station_cmd);
01407
01408
01409 DeleteWindowById(WC_SELECT_STATION, 0);
01410 }
01411
01412 virtual void OnTick()
01413 {
01414 if (_thd.dirty & 2) {
01415 _thd.dirty &= ~2;
01416 this->SetDirty();
01417 }
01418 }
01419
01420 virtual void OnResize()
01421 {
01422 this->vscroll->SetCapacityFromWidget(this, WID_JS_PANEL, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
01423 }
01424
01430 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01431 {
01432 if (!gui_scope) return;
01433 FindStationsNearby<T>(this->area, true);
01434 this->vscroll->SetCount(_stations_nearby_list.Length() + 1);
01435 this->SetDirty();
01436 }
01437 };
01438
01439 static const WindowDesc _select_station_desc(
01440 WDP_AUTO, 200, 180,
01441 WC_SELECT_STATION, WC_NONE,
01442 WDF_CONSTRUCTION,
01443 _nested_select_station_widgets, lengthof(_nested_select_station_widgets)
01444 );
01445
01446
01454 template <class T>
01455 static bool StationJoinerNeeded(CommandContainer cmd, TileArea ta)
01456 {
01457
01458 if (!_settings_game.station.distant_join_stations) return false;
01459
01460
01461
01462 Window *selection_window = FindWindowById(WC_SELECT_STATION, 0);
01463 if (selection_window != NULL) {
01464
01465 delete selection_window;
01466 UpdateTileSelection();
01467 }
01468
01469
01470 if (!_ctrl_pressed) return false;
01471
01472
01473 if (DoCommand(&cmd, CommandFlagsToDCFlags(GetCommandFlags(cmd.cmd))).Failed()) return false;
01474
01475
01476
01477
01478 const T *st = FindStationsNearby<T>(ta, false);
01479 return st == NULL && (_settings_game.station.adjacent_stations || _stations_nearby_list.Length() == 0);
01480 }
01481
01488 template <class T>
01489 void ShowSelectBaseStationIfNeeded(CommandContainer cmd, TileArea ta)
01490 {
01491 if (StationJoinerNeeded<T>(cmd, ta)) {
01492 if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
01493 new SelectStationWindow<T>(&_select_station_desc, cmd, ta);
01494 } else {
01495 DoCommandP(&cmd);
01496 }
01497 }
01498
01504 void ShowSelectStationIfNeeded(CommandContainer cmd, TileArea ta)
01505 {
01506 ShowSelectBaseStationIfNeeded<Station>(cmd, ta);
01507 }
01508
01514 void ShowSelectWaypointIfNeeded(CommandContainer cmd, TileArea ta)
01515 {
01516 ShowSelectBaseStationIfNeeded<Waypoint>(cmd, ta);
01517 }