00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "openttd.h"
00014 #include "debug.h"
00015 #include "gui.h"
00016 #include "window_gui.h"
00017 #include "textbuf_gui.h"
00018 #include "company_func.h"
00019 #include "command_func.h"
00020 #include "vehicle_gui.h"
00021 #include "cargotype.h"
00022 #include "station_gui.h"
00023 #include "strings_func.h"
00024 #include "window_func.h"
00025 #include "viewport_func.h"
00026 #include "gfx_func.h"
00027 #include "widgets/dropdown_func.h"
00028 #include "station_base.h"
00029 #include "waypoint_base.h"
00030 #include "tilehighlight_func.h"
00031 #include "company_base.h"
00032 #include "sortlist_type.h"
00033
00034 #include "table/strings.h"
00035 #include "table/sprites.h"
00036
00052 static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating)
00053 {
00054 static const uint units_full = 576;
00055 static const uint rating_full = 224;
00056
00057 const CargoSpec *cs = CargoSpec::Get(type);
00058 if (!cs->IsValid()) return;
00059
00060 int colour = cs->rating_colour;
00061 uint w = (minu(amount, units_full) + 5) / 36;
00062
00063 int height = GetCharacterHeight(FS_SMALL);
00064
00065
00066 if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour);
00067
00068
00069
00070 if (w == 0) {
00071 uint rest = amount / 5;
00072 if (rest != 0) {
00073 w += left;
00074 GfxFillRect(w, y + height - rest, w, y + height, colour);
00075 }
00076 }
00077
00078 DrawString(left + 1, right, y, cs->abbrev, TC_BLACK);
00079
00080
00081 y += height + 2;
00082 GfxFillRect(left + 1, y, left + 14, y, 0xB8);
00083 rating = minu(rating, rating_full) / 16;
00084 if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, 0xD0);
00085 }
00086
00087 typedef GUIList<const Station*> GUIStationList;
00088
00090 enum StationListWidgets {
00091 SLW_CAPTION,
00092 SLW_LIST,
00093 SLW_SCROLLBAR,
00094
00095 SLW_TRAIN,
00096 SLW_TRUCK,
00097 SLW_BUS,
00098 SLW_AIRPLANE,
00099 SLW_SHIP,
00100 SLW_FACILALL,
00101
00102 SLW_NOCARGOWAITING,
00103 SLW_CARGOALL,
00104
00105 SLW_SORTBY,
00106 SLW_SORTDROPBTN,
00107
00108 SLW_CARGOSTART,
00109 };
00110
00114 class CompanyStationsWindow : public Window
00115 {
00116 protected:
00117
00118 static Listing last_sorting;
00119 static byte facilities;
00120 static bool include_empty;
00121 static const uint32 cargo_filter_max;
00122 static uint32 cargo_filter;
00123 static const Station *last_station;
00124
00125
00126 static const StringID sorter_names[];
00127 static GUIStationList::SortFunction * const sorter_funcs[];
00128
00129 GUIStationList stations;
00130
00131
00137 void BuildStationsList(const Owner owner)
00138 {
00139 if (!this->stations.NeedRebuild()) return;
00140
00141 DEBUG(misc, 3, "Building station list for company %d", owner);
00142
00143 this->stations.Clear();
00144
00145 const Station *st;
00146 FOR_ALL_STATIONS(st) {
00147 if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, owner))) {
00148 if (this->facilities & st->facilities) {
00149 int num_waiting_cargo = 0;
00150 for (CargoID j = 0; j < NUM_CARGO; j++) {
00151 if (!st->goods[j].cargo.Empty()) {
00152 num_waiting_cargo++;
00153 if (HasBit(this->cargo_filter, j)) {
00154 *this->stations.Append() = st;
00155 break;
00156 }
00157 }
00158 }
00159
00160 if (num_waiting_cargo == 0 && this->include_empty) {
00161 *this->stations.Append() = st;
00162 }
00163 }
00164 }
00165 }
00166
00167 this->stations.Compact();
00168 this->stations.RebuildDone();
00169
00170 this->vscroll.SetCount(this->stations.Length());
00171 }
00172
00174 static int CDECL StationNameSorter(const Station * const *a, const Station * const *b)
00175 {
00176 static char buf_cache[64];
00177 char buf[64];
00178
00179 SetDParam(0, (*a)->index);
00180 GetString(buf, STR_STATION_NAME, lastof(buf));
00181
00182 if (*b != last_station) {
00183 last_station = *b;
00184 SetDParam(0, (*b)->index);
00185 GetString(buf_cache, STR_STATION_NAME, lastof(buf_cache));
00186 }
00187
00188 return strcmp(buf, buf_cache);
00189 }
00190
00192 static int CDECL StationTypeSorter(const Station * const *a, const Station * const *b)
00193 {
00194 return (*a)->facilities - (*b)->facilities;
00195 }
00196
00198 static int CDECL StationWaitingSorter(const Station * const *a, const Station * const *b)
00199 {
00200 Money diff = 0;
00201
00202 for (CargoID j = 0; j < NUM_CARGO; j++) {
00203 if (!HasBit(cargo_filter, j)) continue;
00204 if (!(*a)->goods[j].cargo.Empty()) diff += GetTransportedGoodsIncome((*a)->goods[j].cargo.Count(), 20, 50, j);
00205 if (!(*b)->goods[j].cargo.Empty()) diff -= GetTransportedGoodsIncome((*b)->goods[j].cargo.Count(), 20, 50, j);
00206 }
00207
00208 return ClampToI32(diff);
00209 }
00210
00212 static int CDECL StationRatingMaxSorter(const Station * const *a, const Station * const *b)
00213 {
00214 byte maxr1 = 0;
00215 byte maxr2 = 0;
00216
00217 for (CargoID j = 0; j < NUM_CARGO; j++) {
00218 if (!HasBit(cargo_filter, j)) continue;
00219 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr1 = max(maxr1, (*a)->goods[j].rating);
00220 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr2 = max(maxr2, (*b)->goods[j].rating);
00221 }
00222
00223 return maxr1 - maxr2;
00224 }
00225
00227 static int CDECL StationRatingMinSorter(const Station * const *a, const Station * const *b)
00228 {
00229 byte minr1 = 255;
00230 byte minr2 = 255;
00231
00232 for (CargoID j = 0; j < NUM_CARGO; j++) {
00233 if (!HasBit(cargo_filter, j)) continue;
00234 if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) minr1 = min(minr1, (*a)->goods[j].rating);
00235 if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) minr2 = min(minr2, (*b)->goods[j].rating);
00236 }
00237
00238 return -(minr1 - minr2);
00239 }
00240
00242 void SortStationsList()
00243 {
00244 if (!this->stations.Sort()) return;
00245
00246
00247 this->last_station = NULL;
00248
00249
00250 this->SetWidgetDirty(SLW_LIST);
00251 }
00252
00253 public:
00254 CompanyStationsWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
00255 {
00256 this->stations.SetListing(this->last_sorting);
00257 this->stations.SetSortFuncs(this->sorter_funcs);
00258 this->stations.ForceRebuild();
00259 this->stations.NeedResort();
00260 this->SortStationsList();
00261
00262 this->InitNested(desc, window_number);
00263 this->owner = (Owner)this->window_number;
00264
00265 for (uint i = 0; i < NUM_CARGO; i++) {
00266 const CargoSpec *cs = CargoSpec::Get(i);
00267 if (cs->IsValid() && HasBit(this->cargo_filter, i)) this->LowerWidget(SLW_CARGOSTART + i);
00268 }
00269
00270 if (this->cargo_filter == this->cargo_filter_max) this->cargo_filter = _cargo_mask;
00271
00272 for (uint i = 0; i < 5; i++) {
00273 if (HasBit(this->facilities, i)) this->LowerWidget(i + SLW_TRAIN);
00274 }
00275 this->SetWidgetLoweredState(SLW_FACILALL, this->facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00276 this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00277 this->SetWidgetLoweredState(SLW_NOCARGOWAITING, this->include_empty);
00278
00279 this->GetWidget<NWidgetCore>(SLW_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00280 }
00281
00282 ~CompanyStationsWindow()
00283 {
00284 this->last_sorting = this->stations.GetListing();
00285 }
00286
00287 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00288 {
00289 switch (widget) {
00290 case SLW_SORTBY: {
00291 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
00292 d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2;
00293 d.height += padding.height;
00294 *size = maxdim(*size, d);
00295 break;
00296 }
00297
00298 case SLW_SORTDROPBTN: {
00299 Dimension d = {0, 0};
00300 for (int i = 0; this->sorter_names[i] != INVALID_STRING_ID; i++) {
00301 d = maxdim(d, GetStringBoundingBox(this->sorter_names[i]));
00302 }
00303 d.width += padding.width;
00304 d.height += padding.height;
00305 *size = maxdim(*size, d);
00306 break;
00307 }
00308
00309 case SLW_LIST:
00310 resize->height = FONT_HEIGHT_NORMAL;
00311 size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
00312 break;
00313
00314 case SLW_TRAIN:
00315 case SLW_TRUCK:
00316 case SLW_BUS:
00317 case SLW_AIRPLANE:
00318 case SLW_SHIP:
00319 size->height = max<uint>(FONT_HEIGHT_SMALL, 10) + padding.height;
00320 break;
00321
00322 case SLW_CARGOALL:
00323 case SLW_FACILALL:
00324 case SLW_NOCARGOWAITING: {
00325 Dimension d = GetStringBoundingBox(widget == SLW_NOCARGOWAITING ? STR_ABBREV_NONE : STR_ABBREV_ALL);
00326 d.width += padding.width + 2;
00327 d.height += padding.height;
00328 *size = maxdim(*size, d);
00329 break;
00330 }
00331
00332 default:
00333 if (widget >= SLW_CARGOSTART) {
00334 const CargoSpec *cs = CargoSpec::Get(widget - SLW_CARGOSTART);
00335 if (cs->IsValid()) {
00336 Dimension d = GetStringBoundingBox(cs->abbrev);
00337 d.width += padding.width + 2;
00338 d.height += padding.height;
00339 *size = maxdim(*size, d);
00340 }
00341 }
00342 break;
00343 }
00344 }
00345
00346 virtual void OnPaint()
00347 {
00348 this->BuildStationsList((Owner)this->window_number);
00349 this->SortStationsList();
00350
00351 this->DrawWidgets();
00352 }
00353
00354 virtual void DrawWidget(const Rect &r, int widget) const
00355 {
00356 switch (widget) {
00357 case SLW_SORTBY:
00358
00359 this->DrawSortButtonState(SLW_SORTBY, this->stations.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
00360 break;
00361
00362 case SLW_LIST: {
00363 bool rtl = _dynlang.text_dir == TD_RTL;
00364 int max = min(this->vscroll.GetPosition() + this->vscroll.GetCapacity(), this->stations.Length());
00365 int y = r.top + WD_FRAMERECT_TOP;
00366 for (int i = this->vscroll.GetPosition(); i < max; ++i) {
00367 const Station *st = this->stations[i];
00368 assert(st->xy != INVALID_TILE);
00369
00370
00371
00372 assert(st->owner == owner || st->owner == OWNER_NONE);
00373
00374 SetDParam(0, st->index);
00375 SetDParam(1, st->facilities);
00376 int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION);
00377 x += rtl ? -5 : 5;
00378
00379
00380 for (CargoID j = 0; j < NUM_CARGO; j++) {
00381 if (!st->goods[j].cargo.Empty()) {
00382
00383
00384
00385
00386 if (rtl) {
00387 x -= 20;
00388 if (x < r.left + WD_FRAMERECT_LEFT) break;
00389 }
00390 StationsWndShowStationRating(x, x + 16, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
00391 if (!rtl) {
00392 x += 20;
00393 if (x > r.right - WD_FRAMERECT_RIGHT) break;
00394 }
00395 }
00396 }
00397 y += FONT_HEIGHT_NORMAL;
00398 }
00399
00400 if (this->vscroll.GetCount() == 0) {
00401 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_NONE);
00402 return;
00403 }
00404 break;
00405 }
00406
00407 case SLW_NOCARGOWAITING: {
00408 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00409 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_CENTER);
00410 break;
00411 }
00412
00413 case SLW_CARGOALL: {
00414 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00415 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_CENTER);
00416 break;
00417 }
00418
00419 case SLW_FACILALL: {
00420 int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00421 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
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 int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 2 : 1;
00430 GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour);
00431 DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, cs->abbrev, TC_BLACK, SA_CENTER);
00432 }
00433 }
00434 break;
00435 }
00436 }
00437
00438 virtual void SetStringParameters(int widget) const
00439 {
00440 if (widget == SLW_CAPTION) {
00441 SetDParam(0, this->window_number);
00442 SetDParam(1, this->vscroll.GetCount());
00443 }
00444 }
00445
00446 virtual void OnClick(Point pt, int widget)
00447 {
00448 switch (widget) {
00449 case SLW_LIST: {
00450 uint32 id_v = (pt.y - this->GetWidget<NWidgetBase>(SLW_LIST)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL;
00451
00452 if (id_v >= this->vscroll.GetCapacity()) return;
00453
00454 id_v += this->vscroll.GetPosition();
00455
00456 if (id_v >= this->stations.Length()) return;
00457
00458 const Station *st = this->stations[id_v];
00459
00460 assert(st->owner == (Owner)this->window_number || st->owner == OWNER_NONE);
00461
00462 if (_ctrl_pressed) {
00463 ShowExtraViewPortWindow(st->xy);
00464 } else {
00465 ScrollMainWindowToTile(st->xy);
00466 }
00467 break;
00468 }
00469
00470 case SLW_TRAIN:
00471 case SLW_TRUCK:
00472 case SLW_BUS:
00473 case SLW_AIRPLANE:
00474 case SLW_SHIP:
00475 if (_ctrl_pressed) {
00476 ToggleBit(this->facilities, widget - SLW_TRAIN);
00477 this->ToggleWidgetLoweredState(widget);
00478 } else {
00479 uint i;
00480 FOR_EACH_SET_BIT(i, this->facilities) {
00481 this->RaiseWidget(i + SLW_TRAIN);
00482 }
00483 SetBit(this->facilities, widget - SLW_TRAIN);
00484 this->LowerWidget(widget);
00485 }
00486 this->SetWidgetLoweredState(SLW_FACILALL, this->facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00487 this->stations.ForceRebuild();
00488 this->SetDirty();
00489 break;
00490
00491 case SLW_FACILALL:
00492 for (uint i = 0; i < 5; i++) {
00493 this->LowerWidget(i + SLW_TRAIN);
00494 }
00495 this->LowerWidget(SLW_FACILALL);
00496
00497 this->facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00498 this->stations.ForceRebuild();
00499 this->SetDirty();
00500 break;
00501
00502 case SLW_CARGOALL: {
00503 for (uint i = 0; i < NUM_CARGO; i++) {
00504 const CargoSpec *cs = CargoSpec::Get(i);
00505 if (cs->IsValid()) this->LowerWidget(SLW_CARGOSTART + i);
00506 }
00507 this->LowerWidget(SLW_NOCARGOWAITING);
00508 this->LowerWidget(SLW_CARGOALL);
00509
00510 this->cargo_filter = _cargo_mask;
00511 this->include_empty = true;
00512 this->stations.ForceRebuild();
00513 this->SetDirty();
00514 break;
00515 }
00516
00517 case SLW_SORTBY:
00518 this->stations.ToggleSortOrder();
00519 this->flags4 |= WF_TIMEOUT_BEGIN;
00520 this->LowerWidget(SLW_SORTBY);
00521 this->SetDirty();
00522 break;
00523
00524 case SLW_SORTDROPBTN:
00525 ShowDropDownMenu(this, this->sorter_names, this->stations.SortType(), SLW_SORTDROPBTN, 0, 0);
00526 break;
00527
00528 case SLW_NOCARGOWAITING:
00529 if (_ctrl_pressed) {
00530 this->include_empty = !this->include_empty;
00531 this->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
00532 } else {
00533 for (uint i = 0; i < NUM_CARGO; i++) {
00534 const CargoSpec *cs = CargoSpec::Get(i);
00535 if (cs->IsValid()) this->RaiseWidget(SLW_CARGOSTART + i);
00536 }
00537
00538 this->cargo_filter = 0;
00539 this->include_empty = true;
00540
00541 this->LowerWidget(SLW_NOCARGOWAITING);
00542 }
00543 this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00544 this->stations.ForceRebuild();
00545 this->SetDirty();
00546 break;
00547
00548 default:
00549 if (widget >= SLW_CARGOSTART) {
00550
00551 const CargoSpec *cs = CargoSpec::Get(widget - SLW_CARGOSTART);
00552 if (!cs->IsValid()) break;
00553
00554 if (_ctrl_pressed) {
00555 ToggleBit(this->cargo_filter, cs->Index());
00556 this->ToggleWidgetLoweredState(widget);
00557 } else {
00558 for (uint i = 0; i < NUM_CARGO; i++) {
00559 const CargoSpec *cs = CargoSpec::Get(i);
00560 if (cs->IsValid()) this->RaiseWidget(SLW_CARGOSTART + i);
00561 }
00562 this->RaiseWidget(SLW_NOCARGOWAITING);
00563
00564 this->cargo_filter = 0;
00565 this->include_empty = false;
00566
00567 SetBit(this->cargo_filter, cs->Index());
00568 this->LowerWidget(widget);
00569 }
00570 this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00571 this->stations.ForceRebuild();
00572 this->SetDirty();
00573 }
00574 break;
00575 }
00576 }
00577
00578 virtual void OnDropdownSelect(int widget, int index)
00579 {
00580 if (this->stations.SortType() != index) {
00581 this->stations.SetSortType(index);
00582
00583
00584 this->GetWidget<NWidgetCore>(SLW_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00585
00586 this->SetDirty();
00587 }
00588 }
00589
00590 virtual void OnTick()
00591 {
00592 if (_pause_mode != PM_UNPAUSED) return;
00593 if (this->stations.NeedResort()) {
00594 DEBUG(misc, 3, "Periodic rebuild station list company %d", this->window_number);
00595 this->SetDirty();
00596 }
00597 }
00598
00599 virtual void OnTimeout()
00600 {
00601 this->RaiseWidget(SLW_SORTBY);
00602 this->SetDirty();
00603 }
00604
00605 virtual void OnResize()
00606 {
00607 this->vscroll.SetCapacityFromWidget(this, SLW_LIST, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
00608 }
00609
00610 virtual void OnInvalidateData(int data)
00611 {
00612 if (data == 0) {
00613 this->stations.ForceRebuild();
00614 } else {
00615 this->stations.ForceResort();
00616 }
00617 }
00618 };
00619
00620 Listing CompanyStationsWindow::last_sorting = {false, 0};
00621 byte CompanyStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00622 bool CompanyStationsWindow::include_empty = true;
00623 const uint32 CompanyStationsWindow::cargo_filter_max = UINT32_MAX;
00624 uint32 CompanyStationsWindow::cargo_filter = UINT32_MAX;
00625 const Station *CompanyStationsWindow::last_station = NULL;
00626
00627
00628 GUIStationList::SortFunction * const CompanyStationsWindow::sorter_funcs[] = {
00629 &StationNameSorter,
00630 &StationTypeSorter,
00631 &StationWaitingSorter,
00632 &StationRatingMaxSorter,
00633 &StationRatingMinSorter
00634 };
00635
00636
00637 const StringID CompanyStationsWindow::sorter_names[] = {
00638 STR_SORT_BY_NAME,
00639 STR_SORT_BY_FACILITY,
00640 STR_SORT_BY_WAITING,
00641 STR_SORT_BY_RATING_MAX,
00642 STR_SORT_BY_RATING_MIN,
00643 INVALID_STRING_ID
00644 };
00645
00650 static NWidgetBase *CargoWidgets(int *biggest_index)
00651 {
00652 NWidgetHorizontal *container = new NWidgetHorizontal();
00653
00654 for (uint i = 0; i < NUM_CARGO; i++) {
00655 const CargoSpec *cs = CargoSpec::Get(i);
00656 if (cs->IsValid()) {
00657 NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, SLW_CARGOSTART + i);
00658 panel->SetMinimalSize(14, 11);
00659 panel->SetResize(0, 0);
00660 panel->SetFill(0, 1);
00661 panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE);
00662 container->Add(panel);
00663 } else {
00664 NWidgetLeaf *nwi = new NWidgetLeaf(WWT_EMPTY, COLOUR_GREY, SLW_CARGOSTART + i, 0x0, STR_NULL);
00665 nwi->SetMinimalSize(0, 11);
00666 nwi->SetResize(0, 0);
00667 nwi->SetFill(0, 1);
00668 container->Add(nwi);
00669 }
00670 }
00671 *biggest_index = SLW_CARGOSTART + NUM_CARGO;
00672 return container;
00673 }
00674
00675 static const NWidgetPart _nested_company_stations_widgets[] = {
00676 NWidget(NWID_HORIZONTAL),
00677 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00678 NWidget(WWT_CAPTION, COLOUR_GREY, SLW_CAPTION), SetDataTip(STR_STATION_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00679 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00680 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00681 EndContainer(),
00682 NWidget(NWID_HORIZONTAL),
00683 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_TRAIN), SetMinimalSize(14, 11), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00684 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_TRUCK), SetMinimalSize(14, 11), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00685 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_BUS), SetMinimalSize(14, 11), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00686 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_AIRPLANE), SetMinimalSize(14, 11), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00687 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_SHIP), SetMinimalSize(14, 11), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00688 NWidget(WWT_PANEL, COLOUR_GREY, SLW_FACILALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1), EndContainer(),
00689 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(),
00690 NWidgetFunction(CargoWidgets),
00691 NWidget(WWT_PANEL, COLOUR_GREY, SLW_NOCARGOWAITING), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(),
00692 NWidget(WWT_PANEL, COLOUR_GREY, SLW_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1), EndContainer(),
00693 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00694 EndContainer(),
00695 NWidget(NWID_HORIZONTAL),
00696 NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_SORTBY), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
00697 NWidget(WWT_DROPDOWN, COLOUR_GREY, SLW_SORTDROPBTN), SetMinimalSize(163, 12), SetDataTip(STR_SORT_BY_NAME, STR_TOOLTIP_SORT_CRITERIAP),
00698 NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00699 EndContainer(),
00700 NWidget(NWID_HORIZONTAL),
00701 NWidget(WWT_PANEL, COLOUR_GREY, SLW_LIST), SetMinimalSize(346, 125), SetResize(1, 10), SetDataTip(0x0, STR_STATION_LIST_TOOLTIP), EndContainer(),
00702 NWidget(NWID_VERTICAL),
00703 NWidget(WWT_SCROLLBAR, COLOUR_GREY, SLW_SCROLLBAR),
00704 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00705 EndContainer(),
00706 EndContainer(),
00707 };
00708
00709 static const WindowDesc _company_stations_desc(
00710 WDP_AUTO, 358, 162,
00711 WC_STATION_LIST, WC_NONE,
00712 0,
00713 _nested_company_stations_widgets, lengthof(_nested_company_stations_widgets)
00714 );
00715
00721 void ShowCompanyStations(CompanyID company)
00722 {
00723 if (!Company::IsValidID(company)) return;
00724
00725 AllocateWindowDescFront<CompanyStationsWindow>(&_company_stations_desc, company);
00726 }
00727
00728 static const NWidgetPart _nested_station_view_widgets[] = {
00729 NWidget(NWID_HORIZONTAL),
00730 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00731 NWidget(WWT_CAPTION, COLOUR_GREY, SVW_CAPTION), SetDataTip(STR_STATION_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00732 NWidget(WWT_SHADEBOX, COLOUR_GREY),
00733 NWidget(WWT_STICKYBOX, COLOUR_GREY),
00734 EndContainer(),
00735 NWidget(NWID_HORIZONTAL),
00736 NWidget(WWT_PANEL, COLOUR_GREY, SVW_WAITING), SetMinimalSize(237, 52), SetResize(1, 10), EndContainer(),
00737 NWidget(WWT_SCROLLBAR, COLOUR_GREY, SVW_SCROLLBAR),
00738 EndContainer(),
00739 NWidget(WWT_PANEL, COLOUR_GREY, SVW_ACCEPTLIST), SetMinimalSize(249, 32), SetResize(1, 0), EndContainer(),
00740 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
00741 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_LOCATION), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1),
00742 SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP),
00743 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_ACCEPTS), SetMinimalSize(61, 12), SetResize(1, 0), SetFill(1, 1),
00744 SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP),
00745 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_RENAME), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1),
00746 SetDataTip(STR_BUTTON_RENAME, STR_STATION_VIEW_RENAME_TOOLTIP),
00747 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_TRAINS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP),
00748 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_ROADVEHS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP),
00749 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_PLANES), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_PLANE, STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP),
00750 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_SHIPS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_SHIP, STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP),
00751 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00752 EndContainer(),
00753 };
00754
00765 static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
00766 {
00767 uint num = min((waiting + 5) / 10, (right - left) / 10);
00768 if (num == 0) return;
00769
00770 SpriteID sprite = CargoSpec::Get(i)->GetCargoIcon();
00771
00772 int x = _dynlang.text_dir == TD_RTL ? right - num * 10 : left;
00773 do {
00774 DrawSprite(sprite, PAL_NONE, x, y);
00775 x += 10;
00776 } while (--num);
00777 }
00778
00779 struct CargoData {
00780 CargoID cargo;
00781 StationID source;
00782 uint count;
00783
00784 CargoData(CargoID cargo, StationID source, uint count) :
00785 cargo(cargo),
00786 source(source),
00787 count(count)
00788 { }
00789 };
00790
00791 typedef std::list<CargoData> CargoDataList;
00792
00796 struct StationViewWindow : public Window {
00797 uint32 cargo;
00798 uint16 cargo_rows[NUM_CARGO];
00799 uint expand_shrink_width;
00800
00802 enum AcceptListHeight {
00803 ALH_RATING = 13,
00804 ALH_ACCEPTS = 3,
00805 };
00806
00807 StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
00808 {
00809 this->CreateNestedTree(desc);
00810
00811 this->FinishInitNested(desc, window_number);
00812
00813 Owner owner = Station::Get(window_number)->owner;
00814 if (owner != OWNER_NONE) this->owner = owner;
00815 }
00816
00817 ~StationViewWindow()
00818 {
00819 WindowNumber wno = (this->window_number << 16) | VLW_STATION_LIST | Station::Get(this->window_number)->owner;
00820
00821 DeleteWindowById(WC_TRAINS_LIST, wno | (VEH_TRAIN << 11), false);
00822 DeleteWindowById(WC_ROADVEH_LIST, wno | (VEH_ROAD << 11), false);
00823 DeleteWindowById(WC_SHIPS_LIST, wno | (VEH_SHIP << 11), false);
00824 DeleteWindowById(WC_AIRCRAFT_LIST, wno | (VEH_AIRCRAFT << 11), false);
00825 }
00826
00827 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00828 {
00829 switch (widget) {
00830 case SVW_WAITING:
00831 resize->height = FONT_HEIGHT_NORMAL;
00832 size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
00833 this->expand_shrink_width = max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
00834 break;
00835
00836 case SVW_ACCEPTLIST:
00837 size->height = WD_FRAMERECT_TOP + ((this->GetWidget<NWidgetCore>(SVW_ACCEPTS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) ? ALH_ACCEPTS : ALH_RATING) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
00838 break;
00839 }
00840 }
00841
00842 virtual void OnPaint()
00843 {
00844 CargoDataList cargolist;
00845 uint32 transfers = 0;
00846 this->OrderWaitingCargo(&cargolist, &transfers);
00847
00848 this->vscroll.SetCount((int)cargolist.size() + 1);
00849
00850
00851 const Station *st = Station::Get(this->window_number);
00852 this->SetWidgetDisabledState(SVW_RENAME, st->owner != _local_company);
00853 this->SetWidgetDisabledState(SVW_TRAINS, !(st->facilities & FACIL_TRAIN));
00854 this->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
00855 this->SetWidgetDisabledState(SVW_PLANES, !(st->facilities & FACIL_AIRPORT));
00856 this->SetWidgetDisabledState(SVW_SHIPS, !(st->facilities & FACIL_DOCK));
00857
00858 this->DrawWidgets();
00859
00860 if (!this->IsShaded()) {
00861 NWidgetBase *nwi = this->GetWidget<NWidgetBase>(SVW_WAITING);
00862 Rect waiting_rect = {nwi->pos_x, nwi->pos_y, nwi->pos_x + nwi->current_x - 1, nwi->pos_y + nwi->current_y - 1};
00863 this->DrawWaitingCargo(waiting_rect, cargolist, transfers);
00864 }
00865 }
00866
00867 virtual void DrawWidget(const Rect &r, int widget) const
00868 {
00869 if (widget != SVW_ACCEPTLIST) return;
00870
00871 if (this->GetWidget<NWidgetCore>(SVW_ACCEPTS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
00872 this->DrawAcceptedCargo(r);
00873 } else {
00874 this->DrawCargoRatings(r);
00875 }
00876 }
00877
00878 virtual void SetStringParameters(int widget) const
00879 {
00880 if (widget == SVW_CAPTION) {
00881 const Station *st = Station::Get(this->window_number);
00882 SetDParam(0, st->index);
00883 SetDParam(1, st->facilities);
00884 }
00885 }
00886
00892 void OrderWaitingCargo(CargoDataList *cargolist, uint32 *transfers)
00893 {
00894 assert(cargolist->size() == 0);
00895 *transfers = 0;
00896
00897 StationID station_id = this->window_number;
00898 const Station *st = Station::Get(station_id);
00899
00900
00901 for (CargoID i = 0; i < NUM_CARGO; i++) {
00902 if (st->goods[i].cargo.Empty()) {
00903 this->cargo_rows[i] = 0;
00904 } else {
00905
00906 cargolist->push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
00907
00908
00909 this->cargo_rows[i] = (uint16)cargolist->size();
00910
00911
00912 const StationCargoList::List *packets = st->goods[i].cargo.Packets();
00913 for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
00914 const CargoPacket *cp = *it;
00915 if (cp->SourceStation() != station_id) {
00916 bool added = false;
00917
00918
00919 SetBit(*transfers, i);
00920
00921
00922 if (!HasBit(this->cargo, i)) break;
00923
00924
00925 for (CargoDataList::iterator jt(cargolist->begin()); jt != cargolist->end(); jt++) {
00926 CargoData *cd = &(*jt);
00927 if (cd->cargo == i && cd->source == cp->SourceStation()) {
00928 cd->count += cp->Count();
00929 added = true;
00930 break;
00931 }
00932 }
00933
00934 if (!added) cargolist->push_back(CargoData(i, cp->SourceStation(), cp->Count()));
00935 }
00936 }
00937 }
00938 }
00939 }
00940
00946 void DrawWaitingCargo(const Rect &r, const CargoDataList &cargolist, uint32 transfers) const
00947 {
00948 int y = r.top + WD_FRAMERECT_TOP;
00949 int pos = this->vscroll.GetPosition();
00950
00951 const Station *st = Station::Get(this->window_number);
00952 if (--pos < 0) {
00953 StringID str = STR_JUST_NOTHING;
00954 for (CargoID i = 0; i < NUM_CARGO; i++) {
00955 if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
00956 }
00957 SetDParam(0, str);
00958 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_TITLE);
00959 y += FONT_HEIGHT_NORMAL;
00960 }
00961
00962 bool rtl = _dynlang.text_dir == TD_RTL;
00963 int text_left = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT;
00964 int text_right = rtl ? r.right - WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width;
00965 int shrink_left = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
00966 int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
00967
00968
00969 int maxrows = this->vscroll.GetCapacity();
00970 for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
00971 if (--pos < 0) {
00972 const CargoData *cd = &(*it);
00973 if (cd->source == INVALID_STATION) {
00974
00975 DrawCargoIcons(cd->cargo, cd->count, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y);
00976 SetDParam(0, cd->cargo);
00977 SetDParam(1, cd->count);
00978 if (HasBit(transfers, cd->cargo)) {
00979
00980 const char *sym = HasBit(this->cargo, cd->cargo) ? "-" : "+";
00981 DrawString(text_left, text_right, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
00982 DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW, SA_RIGHT);
00983 } else {
00984 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
00985 }
00986 } else {
00987 SetDParam(0, cd->cargo);
00988 SetDParam(1, cd->count);
00989 SetDParam(2, cd->source);
00990 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_EN_ROUTE_FROM, TC_FROMSTRING, SA_RIGHT);
00991 }
00992
00993 y += FONT_HEIGHT_NORMAL;
00994 }
00995 }
00996 }
00997
01001 void DrawAcceptedCargo(const Rect &r) const
01002 {
01003 const Station *st = Station::Get(this->window_number);
01004
01005 uint32 cargo_mask = 0;
01006 for (CargoID i = 0; i < NUM_CARGO; i++) {
01007 if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) SetBit(cargo_mask, i);
01008 }
01009 Rect s = {r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, r.right - WD_FRAMERECT_RIGHT, r.bottom - WD_FRAMERECT_BOTTOM};
01010 DrawCargoListText(cargo_mask, s, STR_STATION_VIEW_ACCEPTS_CARGO);
01011 }
01012
01016 void DrawCargoRatings(const Rect &r) const
01017 {
01018 const Station *st = Station::Get(this->window_number);
01019 int y = r.top + WD_FRAMERECT_TOP;
01020
01021 DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_CARGO_RATINGS_TITLE);
01022 y += FONT_HEIGHT_NORMAL;
01023
01024 const CargoSpec *cs;
01025 FOR_ALL_CARGOSPECS(cs) {
01026 const GoodsEntry *ge = &st->goods[cs->Index()];
01027 if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
01028
01029 SetDParam(0, cs->name);
01030 SetDParam(2, ToPercent8(ge->rating));
01031 SetDParam(1, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
01032 DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_RATING);
01033 y += FONT_HEIGHT_NORMAL;
01034 }
01035 }
01036
01037 void HandleCargoWaitingClick(int row)
01038 {
01039 if (row == 0) return;
01040
01041 for (CargoID c = 0; c < NUM_CARGO; c++) {
01042 if (this->cargo_rows[c] == row) {
01043 ToggleBit(this->cargo, c);
01044 this->SetWidgetDirty(SVW_WAITING);
01045 break;
01046 }
01047 }
01048 }
01049
01050 virtual void OnClick(Point pt, int widget)
01051 {
01052 switch (widget) {
01053 case SVW_WAITING:
01054 this->HandleCargoWaitingClick((pt.y - this->GetWidget<NWidgetBase>(SVW_WAITING)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL + this->vscroll.GetPosition());
01055 break;
01056
01057 case SVW_LOCATION:
01058 if (_ctrl_pressed) {
01059 ShowExtraViewPortWindow(Station::Get(this->window_number)->xy);
01060 } else {
01061 ScrollMainWindowToTile(Station::Get(this->window_number)->xy);
01062 }
01063 break;
01064
01065 case SVW_RATINGS: {
01066
01067 int height_change;
01068 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(SVW_RATINGS);
01069 if (this->GetWidget<NWidgetCore>(SVW_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
01070 nwi->SetDataTip(STR_STATION_VIEW_ACCEPTS_BUTTON, STR_STATION_VIEW_ACCEPTS_TOOLTIP);
01071 height_change = ALH_RATING - ALH_ACCEPTS;
01072 } else {
01073 nwi->SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP);
01074 height_change = ALH_ACCEPTS - ALH_RATING;
01075 }
01076 this->ReInit(0, height_change * FONT_HEIGHT_NORMAL);
01077 break;
01078 }
01079
01080 case SVW_RENAME:
01081 SetDParam(0, this->window_number);
01082 ShowQueryString(STR_STATION_NAME, STR_STATION_VIEW_RENAME_STATION_CAPTION, MAX_LENGTH_STATION_NAME_BYTES, MAX_LENGTH_STATION_NAME_PIXELS,
01083 this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT);
01084 break;
01085
01086 case SVW_TRAINS: {
01087 const Station *st = Station::Get(this->window_number);
01088 ShowVehicleListWindow(st->owner, VEH_TRAIN, (StationID)this->window_number);
01089 break;
01090 }
01091
01092 case SVW_ROADVEHS: {
01093 const Station *st = Station::Get(this->window_number);
01094 ShowVehicleListWindow(st->owner, VEH_ROAD, (StationID)this->window_number);
01095 break;
01096 }
01097
01098 case SVW_PLANES: {
01099 const Station *st = Station::Get(this->window_number);
01100
01101 Owner owner = (st->owner == OWNER_NONE) ? _local_company : st->owner;
01102 ShowVehicleListWindow(owner, VEH_AIRCRAFT, (StationID)this->window_number);
01103 break;
01104 }
01105
01106 case SVW_SHIPS: {
01107 const Station *st = Station::Get(this->window_number);
01108
01109 Owner owner = (st->owner == OWNER_NONE) ? _local_company : st->owner;
01110 ShowVehicleListWindow(owner, VEH_SHIP, (StationID)this->window_number);
01111 break;
01112 }
01113 }
01114 }
01115
01116 virtual void OnQueryTextFinished(char *str)
01117 {
01118 if (str == NULL) return;
01119
01120 DoCommandP(0, this->window_number, 0, CMD_RENAME_STATION | CMD_MSG(STR_ERROR_CAN_T_RENAME_STATION), NULL, str);
01121 }
01122
01123 virtual void OnResize()
01124 {
01125 this->vscroll.SetCapacityFromWidget(this, SVW_WAITING, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
01126 }
01127 };
01128
01129
01130 static const WindowDesc _station_view_desc(
01131 WDP_AUTO, 249, 110,
01132 WC_STATION_VIEW, WC_NONE,
01133 WDF_UNCLICK_BUTTONS,
01134 _nested_station_view_widgets, lengthof(_nested_station_view_widgets)
01135 );
01136
01142 void ShowStationViewWindow(StationID station)
01143 {
01144 AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station);
01145 }
01146
01148 struct TileAndStation {
01149 TileIndex tile;
01150 StationID station;
01151 };
01152
01153 static SmallVector<TileAndStation, 8> _deleted_stations_nearby;
01154 static SmallVector<StationID, 8> _stations_nearby_list;
01155
01163 template <class T>
01164 static bool AddNearbyStation(TileIndex tile, void *user_data)
01165 {
01166 TileArea *ctx = (TileArea *)user_data;
01167
01168
01169 for (uint i = 0; i < _deleted_stations_nearby.Length(); i++) {
01170 TileAndStation *ts = _deleted_stations_nearby.Get(i);
01171 if (ts->tile == tile) {
01172 *_stations_nearby_list.Append() = _deleted_stations_nearby[i].station;
01173 _deleted_stations_nearby.Erase(ts);
01174 i--;
01175 }
01176 }
01177
01178
01179 if (!IsTileType(tile, MP_STATION)) return false;
01180
01181 StationID sid = GetStationIndex(tile);
01182
01183
01184 if (!T::IsValidID(sid)) return false;
01185
01186 T *st = T::Get(sid);
01187 if (st->owner != _local_company || _stations_nearby_list.Contains(sid)) return false;
01188
01189 if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST)) {
01190 *_stations_nearby_list.Append() = sid;
01191 }
01192
01193 return false;
01194 }
01195
01205 template <class T>
01206 static const T *FindStationsNearby(TileArea ta, bool distant_join)
01207 {
01208 TileArea ctx = ta;
01209
01210 _stations_nearby_list.Clear();
01211 _deleted_stations_nearby.Clear();
01212
01213
01214 TILE_LOOP(t, ta.w, ta.h, ta.tile) {
01215 if (t < MapSize() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return T::GetByTile(t);
01216 }
01217
01218
01219 const BaseStation *st;
01220 FOR_ALL_BASE_STATIONS(st) {
01221 if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company) {
01222
01223 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) {
01224 TileAndStation *ts = _deleted_stations_nearby.Append();
01225 ts->tile = st->xy;
01226 ts->station = st->index;
01227
01228
01229 if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) &&
01230 IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) {
01231 AddNearbyStation<T>(st->xy, &ctx);
01232 }
01233 }
01234 }
01235 }
01236
01237
01238
01239
01240 if (distant_join && min(ta.w, ta.h) >= _settings_game.station.station_spread) return NULL;
01241 uint max_dist = distant_join ? _settings_game.station.station_spread - min(ta.w, ta.h) : 1;
01242
01243 TileIndex tile = TILE_ADD(ctx.tile, TileOffsByDir(DIR_N));
01244 CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
01245
01246 return NULL;
01247 }
01248
01249 enum JoinStationWidgets {
01250 JSW_WIDGET_CAPTION,
01251 JSW_PANEL,
01252 JSW_SCROLLBAR,
01253 };
01254
01255 static const NWidgetPart _nested_select_station_widgets[] = {
01256 NWidget(NWID_HORIZONTAL),
01257 NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
01258 NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, JSW_WIDGET_CAPTION), SetDataTip(STR_JOIN_STATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01259 EndContainer(),
01260 NWidget(NWID_HORIZONTAL),
01261 NWidget(WWT_PANEL, COLOUR_DARK_GREEN, JSW_PANEL), SetResize(1, 0), EndContainer(),
01262 NWidget(NWID_VERTICAL),
01263 NWidget(WWT_SCROLLBAR, COLOUR_DARK_GREEN, JSW_SCROLLBAR),
01264 NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
01265 EndContainer(),
01266 EndContainer(),
01267 };
01268
01273 template <class T>
01274 struct SelectStationWindow : Window {
01275 CommandContainer select_station_cmd;
01276 TileArea area;
01277
01278 SelectStationWindow(const WindowDesc *desc, CommandContainer cmd, TileArea ta) :
01279 Window(),
01280 select_station_cmd(cmd),
01281 area(ta)
01282 {
01283 this->CreateNestedTree(desc);
01284 this->GetWidget<NWidgetCore>(JSW_WIDGET_CAPTION)->widget_data = T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
01285 this->FinishInitNested(desc, 0);
01286 this->OnInvalidateData(0);
01287 }
01288
01289 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01290 {
01291 if (widget != JSW_PANEL) return;
01292
01293
01294 Dimension d = GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
01295 for (uint i = 0; i < _stations_nearby_list.Length(); i++) {
01296 const T *st = T::Get(_stations_nearby_list[i]);
01297 SetDParam(0, st->index);
01298 SetDParam(1, st->facilities);
01299 d = maxdim(d, GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION));
01300 }
01301
01302 resize->height = d.height;
01303 d.height *= 5;
01304 d.width += WD_FRAMERECT_RIGHT + WD_FRAMERECT_LEFT;
01305 d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
01306 *size = d;
01307 }
01308
01309 virtual void OnPaint()
01310 {
01311 this->DrawWidgets();
01312 }
01313
01314 virtual void DrawWidget(const Rect &r, int widget) const
01315 {
01316 if (widget != JSW_PANEL) return;
01317
01318 uint y = r.top + WD_FRAMERECT_TOP;
01319 if (this->vscroll.GetPosition() == 0) {
01320 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);
01321 y += this->resize.step_height;
01322 }
01323
01324 for (uint i = max<uint>(1, this->vscroll.GetPosition()); i <= _stations_nearby_list.Length(); ++i, y += this->resize.step_height) {
01325
01326 if (i - this->vscroll.GetPosition() >= this->vscroll.GetCapacity()) break;
01327
01328 const T *st = T::Get(_stations_nearby_list[i - 1]);
01329 SetDParam(0, st->index);
01330 SetDParam(1, st->facilities);
01331 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);
01332 }
01333 }
01334
01335 virtual void OnClick(Point pt, int widget)
01336 {
01337 if (widget != JSW_PANEL) return;
01338
01339 uint32 st_index = (pt.y - this->GetWidget<NWidgetBase>(JSW_PANEL)->pos_y - WD_FRAMERECT_TOP) / this->resize.step_height;
01340 bool distant_join = (st_index > 0);
01341 if (distant_join) st_index--;
01342
01343 if (distant_join && st_index >= _stations_nearby_list.Length()) return;
01344
01345
01346 SB(this->select_station_cmd.p2, 16, 16,
01347 (distant_join ? _stations_nearby_list[st_index] : NEW_STATION));
01348
01349
01350 DoCommandP(&this->select_station_cmd);
01351
01352
01353 DeleteWindowById(WC_SELECT_STATION, 0);
01354 }
01355
01356 virtual void OnTick()
01357 {
01358 if (_thd.dirty & 2) {
01359 _thd.dirty &= ~2;
01360 this->SetDirty();
01361 }
01362 }
01363
01364 virtual void OnResize()
01365 {
01366 this->vscroll.SetCapacityFromWidget(this, JSW_PANEL, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
01367 }
01368
01369 virtual void OnInvalidateData(int data)
01370 {
01371 FindStationsNearby<T>(this->area, true);
01372 this->vscroll.SetCount(_stations_nearby_list.Length() + 1);
01373 this->SetDirty();
01374 }
01375 };
01376
01377 static const WindowDesc _select_station_desc(
01378 WDP_AUTO, 200, 180,
01379 WC_SELECT_STATION, WC_NONE,
01380 WDF_CONSTRUCTION,
01381 _nested_select_station_widgets, lengthof(_nested_select_station_widgets)
01382 );
01383
01384
01392 template <class T>
01393 static bool StationJoinerNeeded(CommandContainer cmd, TileArea ta)
01394 {
01395
01396 if (!_settings_game.station.distant_join_stations) return false;
01397
01398
01399
01400 Window *selection_window = FindWindowById(WC_SELECT_STATION, 0);
01401 if (selection_window != NULL) {
01402 if (!_ctrl_pressed) return true;
01403
01404
01405 delete selection_window;
01406 UpdateTileSelection();
01407 }
01408
01409
01410 if (!_ctrl_pressed) return false;
01411
01412
01413 if (CmdFailed(DoCommand(&cmd, CommandFlagsToDCFlags(GetCommandFlags(cmd.cmd))))) return false;
01414
01415
01416
01417
01418 const T *st = FindStationsNearby<T>(ta, false);
01419 return st == NULL && (_settings_game.station.adjacent_stations || _stations_nearby_list.Length() == 0);
01420 }
01421
01428 template <class T>
01429 void ShowSelectBaseStationIfNeeded(CommandContainer cmd, TileArea ta)
01430 {
01431 if (StationJoinerNeeded<T>(cmd, ta)) {
01432 if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
01433 if (BringWindowToFrontById(WC_SELECT_STATION, 0)) return;
01434 new SelectStationWindow<T>(&_select_station_desc, cmd, ta);
01435 } else {
01436 DoCommandP(&cmd);
01437 }
01438 }
01439
01445 void ShowSelectStationIfNeeded(CommandContainer cmd, TileArea ta)
01446 {
01447 ShowSelectBaseStationIfNeeded<Station>(cmd, ta);
01448 }
01449
01455 void ShowSelectWaypointIfNeeded(CommandContainer cmd, TileArea ta)
01456 {
01457 ShowSelectBaseStationIfNeeded<Waypoint>(cmd, ta);
01458 }