station_gui.cpp

Go to the documentation of this file.
00001 /* $Id: station_gui.cpp 14919 2009-01-08 16:35:45Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "gui.h"
00009 #include "window_gui.h"
00010 #include "textbuf_gui.h"
00011 #include "station_base.h"
00012 #include "company_func.h"
00013 #include "economy_func.h"
00014 #include "town.h"
00015 #include "command_func.h"
00016 #include "variables.h"
00017 #include "vehicle_gui.h"
00018 #include "cargotype.h"
00019 #include "station_gui.h"
00020 #include "station_func.h"
00021 #include "strings_func.h"
00022 #include "core/alloc_func.hpp"
00023 #include "window_func.h"
00024 #include "viewport_func.h"
00025 #include "gfx_func.h"
00026 #include "widgets/dropdown_func.h"
00027 #include "newgrf_cargo.h"
00028 #include "map_func.h"
00029 #include "settings_type.h"
00030 #include "tile_map.h"
00031 #include "station_map.h"
00032 #include "tilehighlight_func.h"
00033 #include "core/smallvec_type.hpp"
00034 #include "core/smallmap_type.hpp"
00035 #include "string_func.h"
00036 #include "company_base.h"
00037 #include "sortlist_type.h"
00038 
00039 #include "table/strings.h"
00040 #include "table/sprites.h"
00041 
00056 static void StationsWndShowStationRating(int x, int y, CargoID type, uint amount, byte rating)
00057 {
00058   static const uint units_full  = 576; 
00059   static const uint rating_full = 224; 
00060 
00061   const CargoSpec *cs = GetCargo(type);
00062   if (!cs->IsValid()) return;
00063 
00064   int colour = cs->rating_colour;
00065   uint w = (minu(amount, units_full) + 5) / 36;
00066 
00067   /* Draw total cargo (limited) on station (fits into 16 pixels) */
00068   if (w != 0) GfxFillRect(x, y, x + w - 1, y + 6, colour);
00069 
00070   /* Draw a one pixel-wide bar of additional cargo meter, useful
00071    * for stations with only a small amount (<=30) */
00072   if (w == 0) {
00073     uint rest = amount / 5;
00074     if (rest != 0) {
00075       w += x;
00076       GfxFillRect(w, y + 6 - rest, w, y + 6, colour);
00077     }
00078   }
00079 
00080   DrawString(x + 1, y, cs->abbrev, TC_BLACK);
00081 
00082   /* Draw green/red ratings bar (fits into 14 pixels) */
00083   y += 8;
00084   GfxFillRect(x + 1, y, x + 14, y, 0xB8);
00085   rating = minu(rating, rating_full) / 16;
00086   if (rating != 0) GfxFillRect(x + 1, y, x + rating, y, 0xD0);
00087 }
00088 
00089 typedef GUIList<const Station*> GUIStationList;
00090 
00094 class CompanyStationsWindow : public Window
00095 {
00096 protected:
00097   /* Runtime saved values */
00098   static Listing last_sorting;
00099   static byte facilities;               // types of stations of interest
00100   static bool include_empty;            // whether we should include stations without waiting cargo
00101   static const uint32 cargo_filter_max;
00102   static uint32 cargo_filter;           // bitmap of cargo types to include
00103   static const Station *last_station;
00104 
00105   /* Constants for sorting stations */
00106   static const StringID sorter_names[];
00107   static GUIStationList::SortFunction *const sorter_funcs[];
00108 
00109   GUIStationList stations;
00110 
00111 
00117   void BuildStationsList(const Owner owner)
00118   {
00119     if (!this->stations.NeedRebuild()) return;
00120 
00121     DEBUG(misc, 3, "Building station list for company %d", owner);
00122 
00123     this->stations.Clear();
00124 
00125     const Station *st;
00126     FOR_ALL_STATIONS(st) {
00127       if (st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy() && HasStationInUse(st->index, owner))) {
00128         if (this->facilities & st->facilities) { // only stations with selected facilities
00129           int num_waiting_cargo = 0;
00130           for (CargoID j = 0; j < NUM_CARGO; j++) {
00131             if (!st->goods[j].cargo.Empty()) {
00132               num_waiting_cargo++; // count number of waiting cargo
00133               if (HasBit(this->cargo_filter, j)) {
00134                 *this->stations.Append() = st;
00135                 break;
00136               }
00137             }
00138           }
00139           /* stations without waiting cargo */
00140           if (num_waiting_cargo == 0 && this->include_empty) {
00141             *this->stations.Append() = st;
00142           }
00143         }
00144       }
00145     }
00146 
00147     this->stations.Compact();
00148     this->stations.RebuildDone();
00149   }
00150 
00152   static int CDECL StationNameSorter(const Station* const *a, const Station* const *b)
00153   {
00154     static char buf_cache[64];
00155     char buf[64];
00156 
00157     SetDParam(0, (*a)->index);
00158     GetString(buf, STR_STATION, lastof(buf));
00159 
00160     if (*b != last_station) {
00161       last_station = *b;
00162       SetDParam(0, (*b)->index);
00163       GetString(buf_cache, STR_STATION, lastof(buf_cache));
00164     }
00165 
00166     return strcmp(buf, buf_cache);
00167   }
00168 
00170   static int CDECL StationTypeSorter(const Station* const *a, const Station* const *b)
00171   {
00172     return (*a)->facilities - (*b)->facilities;
00173   }
00174 
00176   static int CDECL StationWaitingSorter(const Station* const *a, const Station* const *b)
00177   {
00178     Money diff = 0;
00179 
00180     for (CargoID j = 0; j < NUM_CARGO; j++) {
00181       if (!HasBit(cargo_filter, j)) continue;
00182       if (!(*a)->goods[j].cargo.Empty()) diff += GetTransportedGoodsIncome((*a)->goods[j].cargo.Count(), 20, 50, j);
00183       if (!(*b)->goods[j].cargo.Empty()) diff -= GetTransportedGoodsIncome((*b)->goods[j].cargo.Count(), 20, 50, j);
00184     }
00185 
00186     return ClampToI32(diff);
00187   }
00188 
00190   static int CDECL StationRatingMaxSorter(const Station* const *a, const Station* const *b)
00191   {
00192     byte maxr1 = 0;
00193     byte maxr2 = 0;
00194 
00195     for (CargoID j = 0; j < NUM_CARGO; j++) {
00196       if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr1 = max(maxr1, (*a)->goods[j].rating);
00197       if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr2 = max(maxr2, (*b)->goods[j].rating);
00198     }
00199 
00200     return maxr1 - maxr2;
00201   }
00202 
00204   void SortStationsList()
00205   {
00206     if (!this->stations.Sort()) return;
00207 
00208     /* Reset name sorter sort cache */
00209     this->last_station = NULL;
00210 
00211     /* Set the modified widget dirty */
00212     this->InvalidateWidget(SLW_LIST);
00213   }
00214 
00215 public:
00216   CompanyStationsWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
00217   {
00218     this->caption_color = (byte)this->window_number;
00219     this->vscroll.cap = 12;
00220     this->resize.step_height = 10;
00221     this->resize.height = this->height - 10 * 7; // minimum if 5 in the list
00222 
00223     /* Add cargo filter buttons */
00224     uint num_active = 0;
00225     for (CargoID c = 0; c < NUM_CARGO; c++) {
00226       if (GetCargo(c)->IsValid()) num_active++;
00227     }
00228 
00229     this->widget_count += num_active;
00230     this->widget = ReallocT(this->widget, this->widget_count + 1);
00231     this->widget[this->widget_count].type = WWT_LAST;
00232 
00233     uint i = 0;
00234     for (CargoID c = 0; c < NUM_CARGO; c++) {
00235       if (!GetCargo(c)->IsValid()) continue;
00236 
00237       Widget *wi = &this->widget[SLW_CARGOSTART + i];
00238       wi->type     = WWT_PANEL;
00239       wi->display_flags = RESIZE_NONE;
00240       wi->color    = COLOUR_GREY;
00241       wi->left     = 89 + i * 14;
00242       wi->right    = wi->left + 13;
00243       wi->top      = 14;
00244       wi->bottom   = 24;
00245       wi->data     = 0;
00246       wi->tooltips = STR_USE_CTRL_TO_SELECT_MORE;
00247 
00248       if (HasBit(this->cargo_filter, c)) this->LowerWidget(SLW_CARGOSTART + i);
00249       i++;
00250     }
00251 
00252     this->widget[SLW_NOCARGOWAITING].left += num_active * 14;
00253     this->widget[SLW_NOCARGOWAITING].right += num_active * 14;
00254     this->widget[SLW_CARGOALL].left += num_active * 14;
00255     this->widget[SLW_CARGOALL].right += num_active * 14;
00256     this->widget[SLW_PAN_RIGHT].left += num_active * 14;
00257 
00258     if (num_active > 15) {
00259       /* Resize and fix the minimum width, if necessary */
00260       ResizeWindow(this, (num_active - 15) * 14, 0);
00261       this->resize.width = this->width;
00262     }
00263 
00264     if (this->cargo_filter == this->cargo_filter_max) this->cargo_filter = _cargo_mask;
00265 
00266     for (uint i = 0; i < 5; i++) {
00267       if (HasBit(this->facilities, i)) this->LowerWidget(i + SLW_TRAIN);
00268     }
00269     this->SetWidgetLoweredState(SLW_FACILALL, this->facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00270     this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00271     this->SetWidgetLoweredState(SLW_NOCARGOWAITING, this->include_empty);
00272 
00273     this->stations.SetListing(this->last_sorting);
00274     this->stations.SetSortFuncs(this->sorter_funcs);
00275     this->stations.ForceRebuild();
00276     this->stations.NeedResort();
00277     this->SortStationsList();
00278 
00279     this->widget[SLW_SORTDROPBTN].data = this->sorter_names[this->stations.SortType()];
00280 
00281     this->FindWindowPlacementAndResize(desc);
00282   }
00283 
00284   ~CompanyStationsWindow()
00285   {
00286     this->last_sorting = this->stations.GetListing();
00287   }
00288 
00289   virtual void OnPaint()
00290   {
00291     const Owner owner = (Owner)this->window_number;
00292 
00293     this->BuildStationsList(owner);
00294     this->SortStationsList();
00295 
00296     SetVScrollCount(this, this->stations.Length());
00297 
00298     /* draw widgets, with company's name in the caption */
00299     SetDParam(0, owner);
00300     SetDParam(1, this->vscroll.count);
00301 
00302     this->DrawWidgets();
00303 
00304     /* draw arrow pointing up/down for ascending/descending sorting */
00305     this->DrawSortButtonState(SLW_SORTBY, this->stations.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
00306 
00307     int cg_ofst;
00308     int x = 89;
00309     int y = 14;
00310     int xb = 2; 
00311 
00312     uint i = 0;
00313     for (CargoID c = 0; c < NUM_CARGO; c++) {
00314       const CargoSpec *cs = GetCargo(c);
00315       if (!cs->IsValid()) continue;
00316 
00317       cg_ofst = HasBit(this->cargo_filter, c) ? 2 : 1;
00318       GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, cs->rating_colour);
00319       DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, cs->abbrev, TC_BLACK);
00320       x += 14;
00321       i++;
00322     }
00323 
00324     x += 6;
00325     cg_ofst = this->IsWidgetLowered(SLW_NOCARGOWAITING) ? 2 : 1;
00326     DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, TC_BLACK);
00327     x += 14;
00328     cg_ofst = this->IsWidgetLowered(SLW_CARGOALL) ? 2 : 1;
00329     DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00330 
00331     cg_ofst = this->IsWidgetLowered(SLW_FACILALL) ? 2 : 1;
00332     DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00333 
00334     if (this->vscroll.count == 0) { // company has no stations
00335       DrawString(xb, 40, STR_304A_NONE, TC_FROMSTRING);
00336       return;
00337     }
00338 
00339     int max = min(this->vscroll.pos + this->vscroll.cap, this->stations.Length());
00340     y = 40; // start of the list-widget
00341 
00342     for (int i = this->vscroll.pos; i < max; ++i) { // do until max number of stations of owner
00343       const Station *st = this->stations[i];
00344       int x;
00345 
00346       assert(st->xy != INVALID_TILE);
00347 
00348       /* Do not do the complex check HasStationInUse here, it may be even false
00349         * when the order had been removed and the station list hasn't been removed yet */
00350       assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
00351 
00352       SetDParam(0, st->index);
00353       SetDParam(1, st->facilities);
00354       x = DrawString(xb, y, STR_3049_0, TC_FROMSTRING) + 5;
00355 
00356       /* show cargo waiting and station ratings */
00357       for (CargoID j = 0; j < NUM_CARGO; j++) {
00358         if (!st->goods[j].cargo.Empty()) {
00359           StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
00360           x += 20;
00361         }
00362       }
00363       y += 10;
00364     }
00365   }
00366 
00367   virtual void OnClick(Point pt, int widget)
00368   {
00369     switch (widget) {
00370       case SLW_LIST: {
00371         uint32 id_v = (pt.y - 41) / 10;
00372 
00373         if (id_v >= this->vscroll.cap) return; // click out of bounds
00374 
00375         id_v += this->vscroll.pos;
00376 
00377         if (id_v >= this->stations.Length()) return; // click out of list bound
00378 
00379         const Station *st = this->stations[id_v];
00380         /* do not check HasStationInUse - it is slow and may be invalid */
00381         assert(st->owner == (Owner)this->window_number || (st->owner == OWNER_NONE && !st->IsBuoy()));
00382 
00383         if (_ctrl_pressed) {
00384           ShowExtraViewPortWindow(st->xy);
00385         } else {
00386           ScrollMainWindowToTile(st->xy);
00387         }
00388         break;
00389       }
00390 
00391       case SLW_TRAIN:
00392       case SLW_TRUCK:
00393       case SLW_BUS:
00394       case SLW_AIRPLANE:
00395       case SLW_SHIP:
00396         if (_ctrl_pressed) {
00397           ToggleBit(this->facilities, widget - SLW_TRAIN);
00398           this->ToggleWidgetLoweredState(widget);
00399         } else {
00400           uint i;
00401           FOR_EACH_SET_BIT(i, this->facilities) {
00402             this->RaiseWidget(i + SLW_TRAIN);
00403           }
00404           SetBit(this->facilities, widget - SLW_TRAIN);
00405           this->LowerWidget(widget);
00406         }
00407         this->SetWidgetLoweredState(SLW_FACILALL, this->facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00408         this->stations.ForceRebuild();
00409         this->SetDirty();
00410         break;
00411 
00412       case SLW_FACILALL:
00413         for (uint i = 0; i < 5; i++) {
00414           this->LowerWidget(i + SLW_TRAIN);
00415         }
00416         this->LowerWidget(SLW_FACILALL);
00417 
00418         this->facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00419         this->stations.ForceRebuild();
00420         this->SetDirty();
00421         break;
00422 
00423       case SLW_CARGOALL: {
00424         uint i = 0;
00425         for (CargoID c = 0; c < NUM_CARGO; c++) {
00426           if (!GetCargo(c)->IsValid()) continue;
00427           this->LowerWidget(i + SLW_CARGOSTART);
00428           i++;
00429         }
00430         this->LowerWidget(SLW_NOCARGOWAITING);
00431         this->LowerWidget(SLW_CARGOALL);
00432 
00433         this->cargo_filter = _cargo_mask;
00434         this->include_empty = true;
00435         this->stations.ForceRebuild();
00436         this->SetDirty();
00437         break;
00438       }
00439 
00440       case SLW_SORTBY: // flip sorting method asc/desc
00441         this->stations.ToggleSortOrder();
00442         this->flags4 |= WF_TIMEOUT_BEGIN;
00443         this->LowerWidget(SLW_SORTBY);
00444         this->SetDirty();
00445         break;
00446 
00447       case SLW_SORTDROPBTN: // select sorting criteria dropdown menu
00448         ShowDropDownMenu(this, this->sorter_names, this->stations.SortType(), SLW_SORTDROPBTN, 0, 0);
00449         break;
00450 
00451       case SLW_NOCARGOWAITING:
00452         if (_ctrl_pressed) {
00453           this->include_empty = !this->include_empty;
00454           this->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
00455         } else {
00456           for (uint i = SLW_CARGOSTART; i < this->widget_count; i++) {
00457             this->RaiseWidget(i);
00458           }
00459 
00460           this->cargo_filter = 0;
00461           this->include_empty = true;
00462 
00463           this->LowerWidget(SLW_NOCARGOWAITING);
00464         }
00465         this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00466         this->stations.ForceRebuild();
00467         this->SetDirty();
00468         break;
00469 
00470       default:
00471         if (widget >= SLW_CARGOSTART) { // change cargo_filter
00472           /* Determine the selected cargo type */
00473           CargoID c;
00474           int i = 0;
00475           for (c = 0; c < NUM_CARGO; c++) {
00476             if (!GetCargo(c)->IsValid()) continue;
00477             if (widget - SLW_CARGOSTART == i) break;
00478             i++;
00479           }
00480 
00481           if (_ctrl_pressed) {
00482             ToggleBit(this->cargo_filter, c);
00483             this->ToggleWidgetLoweredState(widget);
00484           } else {
00485             for (uint i = SLW_CARGOSTART; i < this->widget_count; i++) {
00486               this->RaiseWidget(i);
00487             }
00488             this->RaiseWidget(SLW_NOCARGOWAITING);
00489 
00490             this->cargo_filter = 0;
00491             this->include_empty = false;
00492 
00493             SetBit(this->cargo_filter, c);
00494             this->LowerWidget(widget);
00495           }
00496           this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00497           this->stations.ForceRebuild();
00498           this->SetDirty();
00499         }
00500         break;
00501     }
00502   }
00503 
00504   virtual void OnDropdownSelect(int widget, int index)
00505   {
00506     if (this->stations.SortType() != index) {
00507       this->stations.SetSortType(index);
00508 
00509       /* Display the current sort variant */
00510       this->widget[SLW_SORTDROPBTN].data = this->sorter_names[this->stations.SortType()];
00511 
00512       this->SetDirty();
00513     }
00514   }
00515 
00516   virtual void OnTick()
00517   {
00518     if (_pause_game != 0) return;
00519     if (this->stations.NeedResort()) {
00520       DEBUG(misc, 3, "Periodic rebuild station list company %d", this->window_number);
00521       this->SetDirty();
00522     }
00523   }
00524 
00525   virtual void OnTimeout()
00526   {
00527     this->RaiseWidget(SLW_SORTBY);
00528     this->SetDirty();
00529   }
00530 
00531   virtual void OnResize(Point new_size, Point delta)
00532   {
00533     this->vscroll.cap += delta.y / 10;
00534   }
00535 
00536   virtual void OnInvalidateData(int data)
00537   {
00538     if (data == 0) {
00539       this->stations.ForceRebuild();
00540     } else {
00541       this->stations.ForceResort();
00542     }
00543   }
00544 };
00545 
00546 Listing CompanyStationsWindow::last_sorting = {false, 0};
00547 byte CompanyStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00548 bool CompanyStationsWindow::include_empty = true;
00549 const uint32 CompanyStationsWindow::cargo_filter_max = UINT32_MAX;
00550 uint32 CompanyStationsWindow::cargo_filter = UINT32_MAX;
00551 const Station *CompanyStationsWindow::last_station = NULL;
00552 
00553 /* Availible station sorting functions */
00554 GUIStationList::SortFunction *const CompanyStationsWindow::sorter_funcs[] = {
00555   &StationNameSorter,
00556   &StationTypeSorter,
00557   &StationWaitingSorter,
00558   &StationRatingMaxSorter
00559 };
00560 
00561 /* Names of the sorting functions */
00562 const StringID CompanyStationsWindow::sorter_names[] = {
00563   STR_SORT_BY_DROPDOWN_NAME,
00564   STR_SORT_BY_FACILITY,
00565   STR_SORT_BY_WAITING,
00566   STR_SORT_BY_RATING_MAX,
00567   INVALID_STRING_ID
00568 };
00569 
00570 
00571 static const Widget _company_stations_widgets[] = {
00572 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},            // SLW_CLOSEBOX
00573 {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   345,     0,    13, STR_3048_STATIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},
00574 {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_GREY,   346,   357,     0,    13, 0x0,               STR_STICKY_BUTTON},
00575 {      WWT_PANEL,     RESIZE_RB,  COLOUR_GREY,     0,   345,    37,   161, 0x0,               STR_3057_STATION_NAMES_CLICK_ON},  // SLW_LIST
00576 {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY,   346,   357,    37,   149, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
00577 {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   346,   357,   150,   161, 0x0,               STR_RESIZE_BUTTON},
00578 
00579 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,     0,    13,    14,    24, STR_TRAIN,         STR_USE_CTRL_TO_SELECT_MORE},      // SLW_TRAIN
00580 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,    14,    27,    14,    24, STR_LORRY,         STR_USE_CTRL_TO_SELECT_MORE},      // SLW_TRUCK
00581 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,    28,    41,    14,    24, STR_BUS,           STR_USE_CTRL_TO_SELECT_MORE},      // SLW_BUS
00582 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,    42,    55,    14,    24, STR_PLANE,         STR_USE_CTRL_TO_SELECT_MORE},      // SLW_AIRPLANE
00583 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,    56,    69,    14,    24, STR_SHIP,          STR_USE_CTRL_TO_SELECT_MORE},      // SLW_SHIP
00584 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,    70,    83,    14,    24, 0x0,               STR_SELECT_ALL_FACILITIES},        // SLW_FACILALL
00585 
00586 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,    83,    88,    14,    24, 0x0,               STR_NULL},                         // SLW_PAN_BETWEEN
00587 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,    89,   102,    14,    24, 0x0,               STR_NO_WAITING_CARGO},             // SLW_NOCARGOWAITING
00588 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,   103,   116,    14,    24, 0x0,               STR_SELECT_ALL_TYPES},             // SLW_CARGOALL
00589 {      WWT_PANEL,  RESIZE_RIGHT,  COLOUR_GREY,   117,   357,    14,    24, 0x0,               STR_NULL},                         // SLW_PAN_RIGHT
00590 
00591 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,     0,    80,    25,    36, STR_SORT_BY,       STR_SORT_ORDER_TIP},               // SLW_SORTBY
00592 {   WWT_DROPDOWN,   RESIZE_NONE,  COLOUR_GREY,    81,   243,    25,    36, 0x0,               STR_SORT_CRITERIA_TIP},            // SLW_SORTDROPBTN
00593 {      WWT_PANEL,  RESIZE_RIGHT,  COLOUR_GREY,   244,   357,    25,    36, 0x0,               STR_NULL},                         // SLW_PAN_SORT_RIGHT
00594 {   WIDGETS_END},
00595 };
00596 
00597 static const WindowDesc _company_stations_desc = {
00598   WDP_AUTO, WDP_AUTO, 358, 162, 358, 162,
00599   WC_STATION_LIST, WC_NONE,
00600   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00601   _company_stations_widgets,
00602 };
00603 
00609 void ShowCompanyStations(CompanyID company)
00610 {
00611   if (!IsValidCompanyID(company)) return;
00612 
00613   AllocateWindowDescFront<CompanyStationsWindow>(&_company_stations_desc, company);
00614 }
00615 
00616 static const Widget _station_view_widgets[] = {
00617 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},                // SVW_CLOSEBOX
00618 {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   236,     0,    13, STR_300A_0,        STR_018C_WINDOW_TITLE_DRAG_THIS},
00619 {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_GREY,   237,   248,     0,    13, 0x0,               STR_STICKY_BUTTON},
00620 {      WWT_PANEL,     RESIZE_RB,  COLOUR_GREY,     0,   236,    14,    65, 0x0,               STR_NULL},                             // SVW_WAITING
00621 {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY,   237,   248,    14,    65, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
00622 {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,     0,   248,    66,    97, 0x0,               STR_NULL},                             // SVW_ACCEPTLIST / SVW_RATINGLIST
00623 { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,     0,    59,    98,   109, STR_00E4_LOCATION, STR_3053_CENTER_MAIN_VIEW_ON_STATION}, // SVW_LOCATION
00624 { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,    60,   120,    98,   109, STR_3032_RATINGS,  STR_3054_SHOW_STATION_RATINGS},        // SVW_RATINGS / SVW_ACCEPTS
00625 { WWT_PUSHTXTBTN,    RESIZE_RTB,  COLOUR_GREY,   121,   180,    98,   109, STR_0130_RENAME,   STR_3055_CHANGE_NAME_OF_STATION},      // SVW_RENAME
00626 { WWT_PUSHTXTBTN,   RESIZE_LRTB,  COLOUR_GREY,   181,   194,    98,   109, STR_TRAIN,         STR_SCHEDULED_TRAINS_TIP },            // SVW_TRAINS
00627 { WWT_PUSHTXTBTN,   RESIZE_LRTB,  COLOUR_GREY,   195,   208,    98,   109, STR_LORRY,         STR_SCHEDULED_ROAD_VEHICLES_TIP },     // SVW_ROADVEHS
00628 { WWT_PUSHTXTBTN,   RESIZE_LRTB,  COLOUR_GREY,   209,   222,    98,   109, STR_PLANE,         STR_SCHEDULED_AIRCRAFT_TIP },          // SVW_PLANES
00629 { WWT_PUSHTXTBTN,   RESIZE_LRTB,  COLOUR_GREY,   223,   236,    98,   109, STR_SHIP,          STR_SCHEDULED_SHIPS_TIP },             // SVW_SHIPS
00630 {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   237,   248,    98,   109, 0x0,               STR_RESIZE_BUTTON},
00631 {   WIDGETS_END},
00632 };
00633 
00634 SpriteID GetCargoSprite(CargoID i)
00635 {
00636   const CargoSpec *cs = GetCargo(i);
00637   SpriteID sprite;
00638 
00639   if (cs->sprite == 0xFFFF) {
00640     /* A value of 0xFFFF indicates we should draw a custom icon */
00641     sprite = GetCustomCargoSprite(cs);
00642   } else {
00643     sprite = cs->sprite;
00644   }
00645 
00646   if (sprite == 0) sprite = SPR_CARGO_GOODS;
00647 
00648   return sprite;
00649 }
00650 
00659 static void DrawCargoIcons(CargoID i, uint waiting, int x, int y, uint width)
00660 {
00661   uint num = min((waiting + 5) / 10, width / 10); // maximum is width / 10 icons so it won't overflow
00662   if (num == 0) return;
00663 
00664   SpriteID sprite = GetCargoSprite(i);
00665 
00666   do {
00667     DrawSprite(sprite, PAL_NONE, x, y);
00668     x += 10;
00669   } while (--num);
00670 }
00671 
00672 struct CargoData {
00673   CargoID cargo;
00674   StationID source;
00675   uint count;
00676 
00677   CargoData(CargoID cargo, StationID source, uint count) :
00678     cargo(cargo),
00679     source(source),
00680     count(count)
00681   { }
00682 };
00683 
00684 typedef std::list<CargoData> CargoDataList;
00685 
00689 struct StationViewWindow : public Window {
00690   uint32 cargo;                 
00691   uint16 cargo_rows[NUM_CARGO]; 
00692 
00693   StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
00694   {
00695     Owner owner = GetStation(window_number)->owner;
00696     if (owner != OWNER_NONE) this->caption_color = owner;
00697     this->vscroll.cap = 5;
00698     this->resize.step_height = 10;
00699 
00700     this->FindWindowPlacementAndResize(desc);
00701   }
00702 
00703   ~StationViewWindow()
00704   {
00705     WindowNumber wno =
00706       (this->window_number << 16) | VLW_STATION_LIST | GetStation(this->window_number)->owner;
00707 
00708     DeleteWindowById(WC_TRAINS_LIST, wno | (VEH_TRAIN << 11), false);
00709     DeleteWindowById(WC_ROADVEH_LIST, wno | (VEH_ROAD << 11), false);
00710     DeleteWindowById(WC_SHIPS_LIST, wno | (VEH_SHIP << 11), false);
00711     DeleteWindowById(WC_AIRCRAFT_LIST, wno | (VEH_AIRCRAFT << 11), false);
00712   }
00713 
00714   virtual void OnPaint()
00715   {
00716     StationID station_id = this->window_number;
00717     const Station *st = GetStation(station_id);
00718     CargoDataList cargolist;
00719     uint32 transfers = 0;
00720 
00721     /* count types of cargos waiting in station */
00722     for (CargoID i = 0; i < NUM_CARGO; i++) {
00723       if (st->goods[i].cargo.Empty()) {
00724         this->cargo_rows[i] = 0;
00725       } else {
00726         /* Add an entry for total amount of cargo of this type waiting. */
00727         cargolist.push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
00728 
00729         /* Set the row for this cargo entry for the expand/hide button */
00730         this->cargo_rows[i] = (uint16)cargolist.size();
00731 
00732         /* Add an entry for each distinct cargo source. */
00733         const CargoList::List *packets = st->goods[i].cargo.Packets();
00734         for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
00735           const CargoPacket *cp = *it;
00736           if (cp->source != station_id) {
00737             bool added = false;
00738 
00739             /* Enable the expand/hide button for this cargo type */
00740             SetBit(transfers, i);
00741 
00742             /* Don't add cargo lines if not expanded */
00743             if (!HasBit(this->cargo, i)) break;
00744 
00745             /* Check if we already have this source in the list */
00746             for (CargoDataList::iterator jt = cargolist.begin(); jt != cargolist.end(); jt++) {
00747               CargoData *cd = &(*jt);
00748               if (cd->cargo == i && cd->source == cp->source) {
00749                 cd->count += cp->count;
00750                 added = true;
00751                 break;
00752               }
00753             }
00754 
00755             if (!added) cargolist.push_back(CargoData(i, cp->source, cp->count));
00756           }
00757         }
00758       }
00759     }
00760     SetVScrollCount(this, (int)cargolist.size() + 1); // update scrollbar
00761 
00762     /* disable some buttons */
00763     this->SetWidgetDisabledState(SVW_RENAME,   st->owner != _local_company);
00764     this->SetWidgetDisabledState(SVW_TRAINS,   !(st->facilities & FACIL_TRAIN));
00765     this->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
00766     this->SetWidgetDisabledState(SVW_PLANES,   !(st->facilities & FACIL_AIRPORT));
00767     this->SetWidgetDisabledState(SVW_SHIPS,    !(st->facilities & FACIL_DOCK));
00768 
00769     SetDParam(0, st->index);
00770     SetDParam(1, st->facilities);
00771     this->DrawWidgets();
00772 
00773     int x = 2;  
00774     int y = 15;
00775     int pos = this->vscroll.pos; 
00776 
00777     uint width = this->widget[SVW_WAITING].right - this->widget[SVW_WAITING].left - 4;
00778     int maxrows = this->vscroll.cap;
00779 
00780     StringID str;
00781 
00782     if (--pos < 0) {
00783       str = STR_00D0_NOTHING;
00784       for (CargoID i = 0; i < NUM_CARGO; i++) {
00785         if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
00786       }
00787       SetDParam(0, str);
00788       DrawString(x, y, STR_0008_WAITING, TC_FROMSTRING);
00789       y += 10;
00790     }
00791 
00792     for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
00793       if (--pos < 0) {
00794         const CargoData *cd = &(*it);
00795         if (cd->source == INVALID_STATION) {
00796           /* Heading */
00797           DrawCargoIcons(cd->cargo, cd->count, x, y, width);
00798           SetDParam(0, cd->cargo);
00799           SetDParam(1, cd->count);
00800           if (HasBit(transfers, cd->cargo)) {
00801             /* This cargo has transfers waiting so show the expand or shrink 'button' */
00802             const char *sym = HasBit(this->cargo, cd->cargo) ? "-" : "+";
00803             DrawStringRightAligned(x + width - 8, y, STR_0009, TC_FROMSTRING);
00804             DoDrawString(sym, x + width - 6, y, TC_YELLOW);
00805           } else {
00806             DrawStringRightAligned(x + width, y, STR_0009, TC_FROMSTRING);
00807           }
00808         } else {
00809           SetDParam(0, cd->cargo);
00810           SetDParam(1, cd->count);
00811           SetDParam(2, cd->source);
00812           DrawStringRightAlignedTruncated(x + width, y, STR_EN_ROUTE_FROM, TC_FROMSTRING, width);
00813         }
00814 
00815         y += 10;
00816       }
00817     }
00818 
00819     if (this->widget[SVW_ACCEPTS].data == STR_3032_RATINGS) { // small window with list of accepted cargo
00820       char string[512];
00821       char *b = string;
00822       bool first = true;
00823 
00824       b = InlineString(b, STR_000C_ACCEPTS);
00825 
00826       for (CargoID i = 0; i < NUM_CARGO; i++) {
00827         if (b >= lastof(string) - (1 + 2 * 4)) break; // ',' or ' ' and two calls to Utf8Encode()
00828         if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
00829           if (first) {
00830             first = false;
00831           } else {
00832             /* Add a comma if this is not the first item */
00833             *b++ = ',';
00834             *b++ = ' ';
00835           }
00836           b = InlineString(b, GetCargo(i)->name);
00837         }
00838       }
00839 
00840       /* If first is still true then no cargo is accepted */
00841       if (first) b = InlineString(b, STR_00D0_NOTHING);
00842 
00843       *b = '\0';
00844 
00845       /* Make sure we detect any buffer overflow */
00846       assert(b < endof(string));
00847 
00848       SetDParamStr(0, string);
00849       DrawStringMultiLine(2, this->widget[SVW_ACCEPTLIST].top + 1, STR_JUST_RAW_STRING, this->widget[SVW_ACCEPTLIST].right - this->widget[SVW_ACCEPTLIST].left);
00850     } else { // extended window with list of cargo ratings
00851       y = this->widget[SVW_RATINGLIST].top + 1;
00852 
00853       DrawString(2, y, STR_3034_LOCAL_RATING_OF_TRANSPORT, TC_FROMSTRING);
00854       y += 10;
00855 
00856       for (CargoID i = 0; i < NUM_CARGO; i++) {
00857         const CargoSpec *cs = GetCargo(i);
00858         if (!cs->IsValid())