station_gui.cpp

Go to the documentation of this file.
00001 /* $Id: station_gui.cpp 19129 2010-02-14 15:35:44Z alberth $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
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 
00032 #include "table/strings.h"
00033 #include "table/sprites.h"
00034 
00042 static int DrawCargoListText(uint32 cargo_mask, const Rect &r, StringID prefix)
00043 {
00044   bool first = true;
00045   char string[512];
00046   char *b = string;
00047 
00048   for (CargoID i = 0; i < NUM_CARGO; i++) {
00049     if (b >= lastof(string) - (1 + 2 * 4)) break; // ',' or ' ' and two calls to Utf8Encode()
00050     if (HasBit(cargo_mask, i)) {
00051       if (first) {
00052         first = false;
00053       } else {
00054         /* Add a comma if this is not the first item */
00055         *b++ = ',';
00056         *b++ = ' ';
00057       }
00058       b = InlineString(b, CargoSpec::Get(i)->name);
00059     }
00060   }
00061 
00062   /* If first is still true then no cargo is accepted */
00063   if (first) b = InlineString(b, STR_JUST_NOTHING);
00064 
00065   *b = '\0';
00066 
00067   /* Make sure we detect any buffer overflow */
00068   assert(b < endof(string));
00069 
00070   SetDParamStr(0, string);
00071   return DrawStringMultiLine(r.left, r.right, r.top, r.bottom, prefix);
00072 }
00073 
00084 int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageType sct, int rad, bool supplies)
00085 {
00086   TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
00087   if (tile < MapSize()) {
00088     CargoArray cargos;
00089     if (supplies) {
00090       cargos = GetProductionAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00091     } else {
00092       cargos = GetAcceptanceAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
00093     }
00094 
00095     /* Convert cargo counts to a set of cargo bits, and draw the result. */
00096     uint32 cargo_mask = 0;
00097     for (CargoID i = 0; i < NUM_CARGO; i++) {
00098       switch (sct) {
00099         case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00100         case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break;
00101         case SCT_ALL: break;
00102         default: NOT_REACHED();
00103       }
00104       if (cargos[i] >= (supplies ? 1U : 8U)) SetBit(cargo_mask, i);
00105     }
00106     Rect r = {left, top, right, INT32_MAX};
00107     return DrawCargoListText(cargo_mask, r, supplies ? STR_STATION_BUILD_SUPPLIES_CARGO : STR_STATION_BUILD_ACCEPTS_CARGO);
00108   }
00109 
00110   return top;
00111 }
00112 
00118 void CheckRedrawStationCoverage(const Window *w)
00119 {
00120   if (_thd.dirty & 1) {
00121     _thd.dirty &= ~1;
00122     w->SetDirty();
00123   }
00124 }
00125 
00141 static void StationsWndShowStationRating(int left, int right, int y, CargoID type, uint amount, byte rating)
00142 {
00143   static const uint units_full  = 576; 
00144   static const uint rating_full = 224; 
00145 
00146   const CargoSpec *cs = CargoSpec::Get(type);
00147   if (!cs->IsValid()) return;
00148 
00149   int colour = cs->rating_colour;
00150   uint w = (minu(amount, units_full) + 5) / 36;
00151 
00152   int height = GetCharacterHeight(FS_SMALL);
00153 
00154   /* Draw total cargo (limited) on station (fits into 16 pixels) */
00155   if (w != 0) GfxFillRect(left, y, left + w - 1, y + height, colour);
00156 
00157   /* Draw a one pixel-wide bar of additional cargo meter, useful
00158    * for stations with only a small amount (<=30) */
00159   if (w == 0) {
00160     uint rest = amount / 5;
00161     if (rest != 0) {
00162       w += left;
00163       GfxFillRect(w, y + height - rest, w, y + height, colour);
00164     }
00165   }
00166 
00167   DrawString(left + 1, right, y, cs->abbrev, TC_BLACK);
00168 
00169   /* Draw green/red ratings bar (fits into 14 pixels) */
00170   y += height + 2;
00171   GfxFillRect(left + 1, y, left + 14, y, 0xB8);
00172   rating = minu(rating, rating_full) / 16;
00173   if (rating != 0) GfxFillRect(left + 1, y, left + rating, y, 0xD0);
00174 }
00175 
00176 typedef GUIList<const Station*> GUIStationList;
00177 
00179 enum StationListWidgets {
00180   SLW_CAPTION,        
00181   SLW_LIST,           
00182   SLW_SCROLLBAR,      
00183 
00184   SLW_TRAIN,          
00185   SLW_TRUCK,          
00186   SLW_BUS,            
00187   SLW_AIRPLANE,       
00188   SLW_SHIP,           
00189   SLW_FACILALL,       
00190 
00191   SLW_NOCARGOWAITING, 
00192   SLW_CARGOALL,       
00193 
00194   SLW_SORTBY,         
00195   SLW_SORTDROPBTN,    
00196 
00197   SLW_CARGOSTART,     
00198 };
00199 
00203 class CompanyStationsWindow : public Window
00204 {
00205 protected:
00206   /* Runtime saved values */
00207   static Listing last_sorting;
00208   static byte facilities;               // types of stations of interest
00209   static bool include_empty;            // whether we should include stations without waiting cargo
00210   static const uint32 cargo_filter_max;
00211   static uint32 cargo_filter;           // bitmap of cargo types to include
00212   static const Station *last_station;
00213 
00214   /* Constants for sorting stations */
00215   static const StringID sorter_names[];
00216   static GUIStationList::SortFunction * const sorter_funcs[];
00217 
00218   GUIStationList stations;
00219 
00220 
00226   void BuildStationsList(const Owner owner)
00227   {
00228     if (!this->stations.NeedRebuild()) return;
00229 
00230     DEBUG(misc, 3, "Building station list for company %d", owner);
00231 
00232     this->stations.Clear();
00233 
00234     const Station *st;
00235     FOR_ALL_STATIONS(st) {
00236       if (st->owner == owner || (st->owner == OWNER_NONE && HasStationInUse(st->index, owner))) {
00237         if (this->facilities & st->facilities) { // only stations with selected facilities
00238           int num_waiting_cargo = 0;
00239           for (CargoID j = 0; j < NUM_CARGO; j++) {
00240             if (!st->goods[j].cargo.Empty()) {
00241               num_waiting_cargo++; // count number of waiting cargo
00242               if (HasBit(this->cargo_filter, j)) {
00243                 *this->stations.Append() = st;
00244                 break;
00245               }
00246             }
00247           }
00248           /* stations without waiting cargo */
00249           if (num_waiting_cargo == 0 && this->include_empty) {
00250             *this->stations.Append() = st;
00251           }
00252         }
00253       }
00254     }
00255 
00256     this->stations.Compact();
00257     this->stations.RebuildDone();
00258 
00259     this->vscroll.SetCount(this->stations.Length()); // Update the scrollbar
00260   }
00261 
00263   static int CDECL StationNameSorter(const Station * const *a, const Station * const *b)
00264   {
00265     static char buf_cache[64];
00266     char buf[64];
00267 
00268     SetDParam(0, (*a)->index);
00269     GetString(buf, STR_STATION_NAME, lastof(buf));
00270 
00271     if (*b != last_station) {
00272       last_station = *b;
00273       SetDParam(0, (*b)->index);
00274       GetString(buf_cache, STR_STATION_NAME, lastof(buf_cache));
00275     }
00276 
00277     return strcmp(buf, buf_cache);
00278   }
00279 
00281   static int CDECL StationTypeSorter(const Station * const *a, const Station * const *b)
00282   {
00283     return (*a)->facilities - (*b)->facilities;
00284   }
00285 
00287   static int CDECL StationWaitingSorter(const Station * const *a, const Station * const *b)
00288   {
00289     Money diff = 0;
00290 
00291     for (CargoID j = 0; j < NUM_CARGO; j++) {
00292       if (!HasBit(cargo_filter, j)) continue;
00293       if (!(*a)->goods[j].cargo.Empty()) diff += GetTransportedGoodsIncome((*a)->goods[j].cargo.Count(), 20, 50, j);
00294       if (!(*b)->goods[j].cargo.Empty()) diff -= GetTransportedGoodsIncome((*b)->goods[j].cargo.Count(), 20, 50, j);
00295     }
00296 
00297     return ClampToI32(diff);
00298   }
00299 
00301   static int CDECL StationRatingMaxSorter(const Station * const *a, const Station * const *b)
00302   {
00303     byte maxr1 = 0;
00304     byte maxr2 = 0;
00305 
00306     for (CargoID j = 0; j < NUM_CARGO; j++) {
00307       if (!HasBit(cargo_filter, j)) continue;
00308       if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr1 = max(maxr1, (*a)->goods[j].rating);
00309       if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr2 = max(maxr2, (*b)->goods[j].rating);
00310     }
00311 
00312     return maxr1 - maxr2;
00313   }
00314 
00316   static int CDECL StationRatingMinSorter(const Station * const *a, const Station * const *b)
00317   {
00318     byte minr1 = 255;
00319     byte minr2 = 255;
00320 
00321     for (CargoID j = 0; j < NUM_CARGO; j++) {
00322       if (!HasBit(cargo_filter, j)) continue;
00323       if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) minr1 = min(minr1, (*a)->goods[j].rating);
00324       if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) minr2 = min(minr2, (*b)->goods[j].rating);
00325     }
00326 
00327     return -(minr1 - minr2);
00328   }
00329 
00331   void SortStationsList()
00332   {
00333     if (!this->stations.Sort()) return;
00334 
00335     /* Reset name sorter sort cache */
00336     this->last_station = NULL;
00337 
00338     /* Set the modified widget dirty */
00339     this->SetWidgetDirty(SLW_LIST);
00340   }
00341 
00342 public:
00343   CompanyStationsWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
00344   {
00345     this->stations.SetListing(this->last_sorting);
00346     this->stations.SetSortFuncs(this->sorter_funcs);
00347     this->stations.ForceRebuild();
00348     this->stations.NeedResort();
00349     this->SortStationsList();
00350 
00351     this->InitNested(desc, window_number);
00352     this->owner = (Owner)this->window_number;
00353 
00354     for (uint i = 0; i < NUM_CARGO; i++) {
00355       const CargoSpec *cs = CargoSpec::Get(i);
00356       if (cs->IsValid() && HasBit(this->cargo_filter, i)) this->LowerWidget(SLW_CARGOSTART + i);
00357     }
00358 
00359     if (this->cargo_filter == this->cargo_filter_max) this->cargo_filter = _cargo_mask;
00360 
00361     for (uint i = 0; i < 5; i++) {
00362       if (HasBit(this->facilities, i)) this->LowerWidget(i + SLW_TRAIN);
00363     }
00364     this->SetWidgetLoweredState(SLW_FACILALL, this->facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00365     this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00366     this->SetWidgetLoweredState(SLW_NOCARGOWAITING, this->include_empty);
00367 
00368     this->GetWidget<NWidgetCore>(SLW_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00369   }
00370 
00371   ~CompanyStationsWindow()
00372   {
00373     this->last_sorting = this->stations.GetListing();
00374   }
00375 
00376   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00377   {
00378     switch (widget) {
00379       case SLW_SORTBY: {
00380         Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
00381         d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the word is centered, also looks nice.
00382         d.height += padding.height;
00383         *size = maxdim(*size, d);
00384         break;
00385       }
00386 
00387       case SLW_SORTDROPBTN: {
00388         Dimension d = {0, 0};
00389         for (int i = 0; this->sorter_names[i] != INVALID_STRING_ID; i++) {
00390           d = maxdim(d, GetStringBoundingBox(this->sorter_names[i]));
00391         }
00392         d.width += padding.width;
00393         d.height += padding.height;
00394         *size = maxdim(*size, d);
00395         break;
00396       }
00397 
00398       case SLW_LIST:
00399         resize->height = FONT_HEIGHT_NORMAL;
00400         size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
00401         break;
00402 
00403       case SLW_TRAIN:
00404       case SLW_TRUCK:
00405       case SLW_BUS:
00406       case SLW_AIRPLANE:
00407       case SLW_SHIP:
00408         size->height = max<uint>(FONT_HEIGHT_SMALL, 10) + padding.height;
00409         break;
00410 
00411       case SLW_CARGOALL:
00412       case SLW_FACILALL:
00413       case SLW_NOCARGOWAITING: {
00414         Dimension d = GetStringBoundingBox(widget == SLW_NOCARGOWAITING ? STR_ABBREV_NONE : STR_ABBREV_ALL);
00415         d.width  += padding.width + 2;
00416         d.height += padding.height;
00417         *size = maxdim(*size, d);
00418         break;
00419       }
00420 
00421       default:
00422         if (widget >= SLW_CARGOSTART) {
00423           const CargoSpec *cs = CargoSpec::Get(widget - SLW_CARGOSTART);
00424           if (cs->IsValid()) {
00425             Dimension d = GetStringBoundingBox(cs->abbrev);
00426             d.width  += padding.width + 2;
00427             d.height += padding.height;
00428             *size = maxdim(*size, d);
00429           }
00430         }
00431         break;
00432     }
00433   }
00434 
00435   virtual void OnPaint()
00436   {
00437     this->BuildStationsList((Owner)this->window_number);
00438     this->SortStationsList();
00439 
00440     this->DrawWidgets();
00441   }
00442 
00443   virtual void DrawWidget(const Rect &r, int widget) const
00444   {
00445     switch (widget) {
00446       case SLW_SORTBY:
00447         /* draw arrow pointing up/down for ascending/descending sorting */
00448         this->DrawSortButtonState(SLW_SORTBY, this->stations.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
00449         break;
00450 
00451       case SLW_LIST: {
00452         bool rtl = _dynlang.text_dir == TD_RTL;
00453         int max = min(this->vscroll.GetPosition() + this->vscroll.GetCapacity(), this->stations.Length());
00454         int y = r.top + WD_FRAMERECT_TOP;
00455         for (int i = this->vscroll.GetPosition(); i < max; ++i) { // do until max number of stations of owner
00456           const Station *st = this->stations[i];
00457           assert(st->xy != INVALID_TILE);
00458 
00459           /* Do not do the complex check HasStationInUse here, it may be even false
00460            * when the order had been removed and the station list hasn't been removed yet */
00461           assert(st->owner == owner || st->owner == OWNER_NONE);
00462 
00463           SetDParam(0, st->index);
00464           SetDParam(1, st->facilities);
00465           int x = DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_STATION);
00466           x += rtl ? -5 : 5;
00467 
00468           /* show cargo waiting and station ratings */
00469           for (CargoID j = 0; j < NUM_CARGO; j++) {
00470             if (!st->goods[j].cargo.Empty()) {
00471               /* For RTL we work in exactly the opposite direction. So
00472                * decrement the space needed first, then draw to the left
00473                * instead of drawing to the left and then incrementing
00474                * the space. */
00475               if (rtl) {
00476                 x -= 20;
00477                 if (x < r.left + WD_FRAMERECT_LEFT) break;
00478               }
00479               StationsWndShowStationRating(x, x + 16, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
00480               if (!rtl) {
00481                 x += 20;
00482                 if (x > r.right - WD_FRAMERECT_RIGHT) break;
00483               }
00484             }
00485           }
00486           y += FONT_HEIGHT_NORMAL;
00487         }
00488 
00489         if (this->vscroll.GetCount() == 0) { // company has no stations
00490           DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_LIST_NONE);
00491           return;
00492         }
00493         break;
00494       }
00495 
00496       case SLW_NOCARGOWAITING: {
00497         int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00498         DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_NONE, TC_BLACK, SA_CENTER);
00499         break;
00500       }
00501 
00502       case SLW_CARGOALL: {
00503         int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00504         DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK, SA_CENTER);
00505         break;
00506       }
00507 
00508       case SLW_FACILALL: {
00509         int cg_ofst = this->IsWidgetLowered(widget) ? 2 : 1;
00510         DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00511         break;
00512       }
00513 
00514       default:
00515         if (widget >= SLW_CARGOSTART) {
00516           const CargoSpec *cs = CargoSpec::Get(widget - SLW_CARGOSTART);
00517           if (cs->IsValid()) {
00518             int cg_ofst = HasBit(this->cargo_filter, cs->Index()) ? 2 : 1;
00519             GfxFillRect(r.left + cg_ofst, r.top + cg_ofst, r.right - 2 + cg_ofst, r.bottom - 2 + cg_ofst, cs->rating_colour);
00520             DrawString(r.left + cg_ofst, r.right + cg_ofst, r.top + cg_ofst, cs->abbrev, TC_BLACK, SA_CENTER);
00521           }
00522         }
00523         break;
00524     }
00525   }
00526 
00527   virtual void SetStringParameters(int widget) const
00528   {
00529     if (widget == SLW_CAPTION) {
00530       SetDParam(0, this->window_number);
00531       SetDParam(1, this->vscroll.GetCount());
00532     }
00533   }
00534 
00535   virtual void OnClick(Point pt, int widget, int click_count)
00536   {
00537     switch (widget) {
00538       case SLW_LIST: {
00539         uint32 id_v = (pt.y - this->GetWidget<NWidgetBase>(SLW_LIST)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL;
00540 
00541         if (id_v >= this->vscroll.GetCapacity()) return; // click out of bounds
00542 
00543         id_v += this->vscroll.GetPosition();
00544 
00545         if (id_v >= this->stations.Length()) return; // click out of list bound
00546 
00547         const Station *st = this->stations[id_v];
00548         /* do not check HasStationInUse - it is slow and may be invalid */
00549         assert(st->owner == (Owner)this->window_number || st->owner == OWNER_NONE);
00550 
00551         if (_ctrl_pressed) {
00552           ShowExtraViewPortWindow(st->xy);
00553         } else {
00554           ScrollMainWindowToTile(st->xy);
00555         }
00556         break;
00557       }
00558 
00559       case SLW_TRAIN:
00560       case SLW_TRUCK:
00561       case SLW_BUS:
00562       case SLW_AIRPLANE:
00563       case SLW_SHIP:
00564         if (_ctrl_pressed) {
00565           ToggleBit(this->facilities, widget - SLW_TRAIN);
00566           this->ToggleWidgetLoweredState(widget);
00567         } else {
00568           uint i;
00569           FOR_EACH_SET_BIT(i, this->facilities) {
00570             this->RaiseWidget(i + SLW_TRAIN);
00571           }
00572           SetBit(this->facilities, widget - SLW_TRAIN);
00573           this->LowerWidget(widget);
00574         }
00575         this->SetWidgetLoweredState(SLW_FACILALL, this->facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00576         this->stations.ForceRebuild();
00577         this->SetDirty();
00578         break;
00579 
00580       case SLW_FACILALL:
00581         for (uint i = 0; i < 5; i++) {
00582           this->LowerWidget(i + SLW_TRAIN);
00583         }
00584         this->LowerWidget(SLW_FACILALL);
00585 
00586         this->facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00587         this->stations.ForceRebuild();
00588         this->SetDirty();
00589         break;
00590 
00591       case SLW_CARGOALL: {
00592         for (uint i = 0; i < NUM_CARGO; i++) {
00593           const CargoSpec *cs = CargoSpec::Get(i);
00594           if (cs->IsValid()) this->LowerWidget(SLW_CARGOSTART + i);
00595         }
00596         this->LowerWidget(SLW_NOCARGOWAITING);
00597         this->LowerWidget(SLW_CARGOALL);
00598 
00599         this->cargo_filter = _cargo_mask;
00600         this->include_empty = true;
00601         this->stations.ForceRebuild();
00602         this->SetDirty();
00603         break;
00604       }
00605 
00606       case SLW_SORTBY: // flip sorting method asc/desc
00607         this->stations.ToggleSortOrder();
00608         this->flags4 |= WF_TIMEOUT_BEGIN;
00609         this->LowerWidget(SLW_SORTBY);
00610         this->SetDirty();
00611         break;
00612 
00613       case SLW_SORTDROPBTN: // select sorting criteria dropdown menu
00614         ShowDropDownMenu(this, this->sorter_names, this->stations.SortType(), SLW_SORTDROPBTN, 0, 0);
00615         break;
00616 
00617       case SLW_NOCARGOWAITING:
00618         if (_ctrl_pressed) {
00619           this->include_empty = !this->include_empty;
00620           this->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
00621         } else {
00622           for (uint i = 0; i < NUM_CARGO; i++) {
00623             const CargoSpec *cs = CargoSpec::Get(i);
00624             if (cs->IsValid()) this->RaiseWidget(SLW_CARGOSTART + i);
00625           }
00626 
00627           this->cargo_filter = 0;
00628           this->include_empty = true;
00629 
00630           this->LowerWidget(SLW_NOCARGOWAITING);
00631         }
00632         this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00633         this->stations.ForceRebuild();
00634         this->SetDirty();
00635         break;
00636 
00637       default:
00638         if (widget >= SLW_CARGOSTART) { // change cargo_filter
00639           /* Determine the selected cargo type */
00640           const CargoSpec *cs = CargoSpec::Get(widget - SLW_CARGOSTART);
00641           if (!cs->IsValid()) break;
00642 
00643           if (_ctrl_pressed) {
00644             ToggleBit(this->cargo_filter, cs->Index());
00645             this->ToggleWidgetLoweredState(widget);
00646           } else {
00647             for (uint i = 0; i < NUM_CARGO; i++) {
00648               const CargoSpec *cs = CargoSpec::Get(i);
00649               if (cs->IsValid()) this->RaiseWidget(SLW_CARGOSTART + i);
00650             }
00651             this->RaiseWidget(SLW_NOCARGOWAITING);
00652 
00653             this->cargo_filter = 0;
00654             this->include_empty = false;
00655 
00656             SetBit(this->cargo_filter, cs->Index());
00657             this->LowerWidget(widget);
00658           }
00659           this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00660           this->stations.ForceRebuild();
00661           this->SetDirty();
00662         }
00663         break;
00664     }
00665   }
00666 
00667   virtual void OnDropdownSelect(int widget, int index)
00668   {
00669     if (this->stations.SortType() != index) {
00670       this->stations.SetSortType(index);
00671 
00672       /* Display the current sort variant */
00673       this->GetWidget<NWidgetCore>(SLW_SORTDROPBTN)->widget_data = this->sorter_names[this->stations.SortType()];
00674 
00675       this->SetDirty();
00676     }
00677   }
00678 
00679   virtual void OnTick()
00680   {
00681     if (_pause_mode != PM_UNPAUSED) return;
00682     if (this->stations.NeedResort()) {
00683       DEBUG(misc, 3, "Periodic rebuild station list company %d", this->window_number);
00684       this->SetDirty();
00685     }
00686   }
00687 
00688   virtual void OnTimeout()
00689   {
00690     this->RaiseWidget(SLW_SORTBY);
00691     this->SetDirty();
00692   }
00693 
00694   virtual void OnResize()
00695   {
00696     this->vscroll.SetCapacityFromWidget(this, SLW_LIST, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
00697   }
00698 
00699   virtual void OnInvalidateData(int data)
00700   {
00701     if (data == 0) {
00702       this->stations.ForceRebuild();
00703     } else {
00704       this->stations.ForceResort();
00705     }
00706   }
00707 };
00708 
00709 Listing CompanyStationsWindow::last_sorting = {false, 0};
00710 byte CompanyStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00711 bool CompanyStationsWindow::include_empty = true;
00712 const uint32 CompanyStationsWindow::cargo_filter_max = UINT32_MAX;
00713 uint32 CompanyStationsWindow::cargo_filter = UINT32_MAX;
00714 const Station *CompanyStationsWindow::last_station = NULL;
00715 
00716 /* Availible station sorting functions */
00717 GUIStationList::SortFunction * const CompanyStationsWindow::sorter_funcs[] = {
00718   &StationNameSorter,
00719   &StationTypeSorter,
00720   &StationWaitingSorter,
00721   &StationRatingMaxSorter,
00722   &StationRatingMinSorter
00723 };
00724 
00725 /* Names of the sorting functions */
00726 const StringID CompanyStationsWindow::sorter_names[] = {
00727   STR_SORT_BY_NAME,
00728   STR_SORT_BY_FACILITY,
00729   STR_SORT_BY_WAITING,
00730   STR_SORT_BY_RATING_MAX,
00731   STR_SORT_BY_RATING_MIN,
00732   INVALID_STRING_ID
00733 };
00734 
00739 static NWidgetBase *CargoWidgets(int *biggest_index)
00740 {
00741   NWidgetHorizontal *container = new NWidgetHorizontal();
00742 
00743   for (uint i = 0; i < NUM_CARGO; i++) {
00744     const CargoSpec *cs = CargoSpec::Get(i);
00745     if (cs->IsValid()) {
00746       NWidgetBackground *panel = new NWidgetBackground(WWT_PANEL, COLOUR_GREY, SLW_CARGOSTART + i);
00747       panel->SetMinimalSize(14, 11);
00748       panel->SetResize(0, 0);
00749       panel->SetFill(0, 1);
00750       panel->SetDataTip(0, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE);
00751       container->Add(panel);
00752     } else {
00753       NWidgetLeaf *nwi = new NWidgetLeaf(WWT_EMPTY, COLOUR_GREY, SLW_CARGOSTART + i, 0x0, STR_NULL);
00754       nwi->SetMinimalSize(0, 11);
00755       nwi->SetResize(0, 0);
00756       nwi->SetFill(0, 1);
00757       container->Add(nwi);
00758     }
00759   }
00760   *biggest_index = SLW_CARGOSTART + NUM_CARGO;
00761   return container;
00762 }
00763 
00764 static const NWidgetPart _nested_company_stations_widgets[] = {
00765   NWidget(NWID_HORIZONTAL),
00766     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00767     NWidget(WWT_CAPTION, COLOUR_GREY, SLW_CAPTION), SetDataTip(STR_STATION_LIST_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00768     NWidget(WWT_SHADEBOX, COLOUR_GREY),
00769     NWidget(WWT_STICKYBOX, COLOUR_GREY),
00770   EndContainer(),
00771   NWidget(NWID_HORIZONTAL),
00772     NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_TRAIN), SetMinimalSize(14, 11), SetDataTip(STR_TRAIN, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00773     NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_TRUCK), SetMinimalSize(14, 11), SetDataTip(STR_LORRY, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00774     NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_BUS), SetMinimalSize(14, 11), SetDataTip(STR_BUS, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00775     NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_AIRPLANE), SetMinimalSize(14, 11), SetDataTip(STR_PLANE, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00776     NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_SHIP), SetMinimalSize(14, 11), SetDataTip(STR_SHIP, STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE), SetFill(0, 1),
00777     NWidget(WWT_PANEL, COLOUR_GREY, SLW_FACILALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_FACILITIES), SetFill(0, 1), EndContainer(),
00778     NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(5, 11), SetFill(0, 1), EndContainer(),
00779     NWidgetFunction(CargoWidgets),
00780     NWidget(WWT_PANEL, COLOUR_GREY, SLW_NOCARGOWAITING), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_NO_WAITING_CARGO), SetFill(0, 1), EndContainer(),
00781     NWidget(WWT_PANEL, COLOUR_GREY, SLW_CARGOALL), SetMinimalSize(14, 11), SetDataTip(0x0, STR_STATION_LIST_SELECT_ALL_TYPES), SetFill(0, 1), EndContainer(),
00782     NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00783   EndContainer(),
00784   NWidget(NWID_HORIZONTAL),
00785     NWidget(WWT_TEXTBTN, COLOUR_GREY, SLW_SORTBY), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
00786     NWidget(WWT_DROPDOWN, COLOUR_GREY, SLW_SORTDROPBTN), SetMinimalSize(163, 12), SetDataTip(STR_SORT_BY_NAME, STR_TOOLTIP_SORT_CRITERIA), // widget_data gets overwritten.
00787     NWidget(WWT_PANEL, COLOUR_GREY), SetDataTip(0x0, STR_NULL), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00788   EndContainer(),
00789   NWidget(NWID_HORIZONTAL),
00790     NWidget(WWT_PANEL, COLOUR_GREY, SLW_LIST), SetMinimalSize(346, 125), SetResize(1, 10), SetDataTip(0x0, STR_STATION_LIST_TOOLTIP), EndContainer(),
00791     NWidget(NWID_VERTICAL),
00792       NWidget(WWT_SCROLLBAR, COLOUR_GREY, SLW_SCROLLBAR),
00793       NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00794     EndContainer(),
00795   EndContainer(),
00796 };
00797 
00798 static const WindowDesc _company_stations_desc(
00799   WDP_AUTO, 358, 162,
00800   WC_STATION_LIST, WC_NONE,
00801   0,
00802   _nested_company_stations_widgets, lengthof(_nested_company_stations_widgets)
00803 );
00804 
00810 void ShowCompanyStations(CompanyID company)
00811 {
00812   if (!Company::IsValidID(company)) return;
00813 
00814   AllocateWindowDescFront<CompanyStationsWindow>(&_company_stations_desc, company);
00815 }
00816 
00817 static const NWidgetPart _nested_station_view_widgets[] = {
00818   NWidget(NWID_HORIZONTAL),
00819     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00820     NWidget(WWT_CAPTION, COLOUR_GREY, SVW_CAPTION), SetDataTip(STR_STATION_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00821     NWidget(WWT_SHADEBOX, COLOUR_GREY),
00822     NWidget(WWT_STICKYBOX, COLOUR_GREY),
00823   EndContainer(),
00824   NWidget(NWID_HORIZONTAL),
00825     NWidget(WWT_PANEL, COLOUR_GREY, SVW_WAITING), SetMinimalSize(237, 52), SetResize(1, 10), EndContainer(),
00826     NWidget(WWT_SCROLLBAR, COLOUR_GREY, SVW_SCROLLBAR),
00827   EndContainer(),
00828   NWidget(WWT_PANEL, COLOUR_GREY, SVW_ACCEPTLIST), SetMinimalSize(249, 32), SetResize(1, 0), EndContainer(),
00829   NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
00830     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_LOCATION), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1),
00831         SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP),
00832     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_ACCEPTS), SetMinimalSize(61, 12), SetResize(1, 0), SetFill(1, 1),
00833         SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP),
00834     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_RENAME), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1),
00835         SetDataTip(STR_BUTTON_RENAME, STR_STATION_VIEW_RENAME_TOOLTIP),
00836     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_TRAINS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_TRAIN, STR_STATION_VIEW_SCHEDULED_TRAINS_TOOLTIP),
00837     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_ROADVEHS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_LORRY, STR_STATION_VIEW_SCHEDULED_ROAD_VEHICLES_TOOLTIP),
00838     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_PLANES),  SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_PLANE, STR_STATION_VIEW_SCHEDULED_AIRCRAFT_TOOLTIP),
00839     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_SHIPS), SetMinimalSize(14, 12), SetFill(0, 1), SetDataTip(STR_SHIP, STR_STATION_VIEW_SCHEDULED_SHIPS_TOOLTIP),
00840     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00841   EndContainer(),
00842 };
00843 
00854 static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
00855 {
00856   uint num = min((waiting + 5) / 10, (right - left) / 10); // maximum is width / 10 icons so it won't overflow
00857   if (num == 0) return;
00858 
00859   SpriteID sprite = CargoSpec::Get(i)->GetCargoIcon();
00860 
00861   int x = _dynlang.text_dir == TD_RTL ? right - num * 10 : left;
00862   do {
00863     DrawSprite(sprite, PAL_NONE, x, y);
00864     x += 10;
00865   } while (--num);
00866 }
00867 
00868 struct CargoData {
00869   CargoID cargo;
00870   StationID source;
00871   uint count;
00872 
00873   CargoData(CargoID cargo, StationID source, uint count) :
00874     cargo(cargo),
00875     source(source),
00876     count(count)
00877   { }
00878 };
00879 
00880 typedef std::list<CargoData> CargoDataList;
00881 
00885 struct StationViewWindow : public Window {
00886   uint32 cargo;                 
00887   uint16 cargo_rows[NUM_CARGO]; 
00888   uint expand_shrink_width;     
00889   int rating_lines;             
00890   int accepts_lines;            
00891 
00893   enum AcceptListHeight {
00894     ALH_RATING  = 13, 
00895     ALH_ACCEPTS = 3,  
00896   };
00897 
00898   StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
00899   {
00900     this->rating_lines  = ALH_RATING;
00901     this->accepts_lines = ALH_ACCEPTS;
00902 
00903     this->CreateNestedTree(desc);
00904     /* Nested widget tree creation is done in two steps to ensure that this->GetWidget<NWidgetCore>(SVW_ACCEPTS) exists in UpdateWidgetSize(). */
00905     this->FinishInitNested(desc, window_number);
00906 
00907     Owner owner = Station::Get(window_number)->owner;
00908     if (owner != OWNER_NONE) this->owner = owner;
00909   }
00910 
00911   ~StationViewWindow()
00912   {
00913     WindowNumber wno = (this->window_number << 16) | VLW_STATION_LIST | Station::Get(this->window_number)->owner;
00914 
00915     DeleteWindowById(WC_TRAINS_LIST, wno | (VEH_TRAIN << 11), false);
00916     DeleteWindowById(WC_ROADVEH_LIST, wno | (VEH_ROAD << 11), false);
00917     DeleteWindowById(WC_SHIPS_LIST, wno | (VEH_SHIP << 11), false);
00918     DeleteWindowById(WC_AIRCRAFT_LIST, wno | (VEH_AIRCRAFT << 11), false);
00919   }
00920 
00921   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00922   {
00923     switch (widget) {
00924       case SVW_WAITING:
00925         resize->height = FONT_HEIGHT_NORMAL;
00926         size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
00927         this->expand_shrink_width = max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
00928         break;
00929 
00930       case SVW_ACCEPTLIST:
00931         size->height = WD_FRAMERECT_TOP + ((this->GetWidget<NWidgetCore>(SVW_ACCEPTS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) ? this->accepts_lines : this->rating_lines) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
00932         break;
00933     }
00934   }
00935 
00936   virtual void OnPaint()
00937   {
00938     CargoDataList cargolist;
00939     uint32 transfers = 0;
00940     this->OrderWaitingCargo(&cargolist, &transfers);
00941 
00942     this->vscroll.SetCount((int)cargolist.size() + 1); // update scrollbar
00943 
00944     /* disable some buttons */
00945     const Station *st = Station::Get(this->window_number);
00946     this->SetWidgetDisabledState(SVW_RENAME,   st->owner != _local_company);
00947     this->SetWidgetDisabledState(SVW_TRAINS,   !(st->facilities & FACIL_TRAIN));
00948     this->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
00949     this->SetWidgetDisabledState(SVW_PLANES,   !(st->facilities & FACIL_AIRPORT));
00950     this->SetWidgetDisabledState(SVW_SHIPS,    !(st->facilities & FACIL_DOCK));
00951 
00952     this->DrawWidgets();
00953 
00954     if (!this->IsShaded()) {
00955       /* Draw 'accepted cargo' or 'cargo ratings'. */
00956       const NWidgetBase *wid = this->GetWidget<NWidgetBase>(SVW_ACCEPTLIST);
00957       const Rect r = {wid->pos_x, wid->pos_y, wid->pos_x + wid->current_x - 1, wid->pos_y + wid->current_y - 1};
00958       if (this->GetWidget<NWidgetCore>(SVW_ACCEPTS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
00959         int lines = this->DrawAcceptedCargo(r);
00960         if (lines > this->accepts_lines) { // Resize the widget, and perform re-initialization of the window.
00961           this->accepts_lines = lines;
00962           this->ReInit();
00963           return;
00964         }
00965       } else {
00966         int lines = this->DrawCargoRatings(r);
00967         if (lines > this->rating_lines) { // Resize the widget, and perform re-initialization of the window.
00968           this->rating_lines = lines;
00969           this->ReInit();
00970           return;
00971         }
00972       }
00973 
00974       /* Draw waiting cargo. */
00975       NWidgetBase *nwi = this->GetWidget<NWidgetBase>(SVW_WAITING);
00976       Rect waiting_rect = {nwi->pos_x, nwi->pos_y, nwi->pos_x + nwi->current_x - 1, nwi->pos_y + nwi->current_y - 1};
00977       this->DrawWaitingCargo(waiting_rect, cargolist, transfers);
00978     }
00979   }
00980 
00981   virtual void SetStringParameters(int widget) const
00982   {
00983     if (widget == SVW_CAPTION) {
00984       const Station *st = Station::Get(this->window_number);
00985       SetDParam(0, st->index);
00986       SetDParam(1, st->facilities);
00987     }
00988   }
00989 
00995   void OrderWaitingCargo(CargoDataList *cargolist, uint32 *transfers)
00996   {
00997     assert(cargolist->size() == 0);
00998     *transfers = 0;
00999 
01000     StationID station_id = this->window_number;
01001     const Station *st = Station::Get(station_id);
01002 
01003     /* count types of cargos waiting in station */
01004     for (CargoID i = 0; i < NUM_CARGO; i++) {
01005       if (st->goods[i].cargo.Empty()) {
01006         this->cargo_rows[i] = 0;
01007       } else {
01008         /* Add an entry for total amount of cargo of this type waiting. */
01009         cargolist->push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
01010 
01011         /* Set the row for this cargo entry for the expand/hide button */
01012         this->cargo_rows[i] = (uint16)cargolist->size();
01013 
01014         /* Add an entry for each distinct cargo source. */
01015         const StationCargoList::List *packets = st->goods[i].cargo.Packets();
01016         for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
01017           const CargoPacket *cp = *it;
01018           if (cp->SourceStation() != station_id) {
01019             bool added = false;
01020 
01021             /* Enable the expand/hide button for this cargo type */
01022             SetBit(*transfers, i);
01023 
01024             /* Don't add cargo lines if not expanded */
01025             if (!HasBit(this->cargo, i)) break;
01026 
01027             /* Check if we already have this source in the list */
01028             for (CargoDataList::iterator jt(cargolist->begin()); jt != cargolist->end(); jt++) {
01029               CargoData *cd = &(*jt);
01030               if (cd->cargo == i && cd->source == cp->SourceStation()) {
01031                 cd->count += cp->Count();
01032                 added = true;
01033                 break;
01034               }
01035             }
01036 
01037             if (!added) cargolist->push_back(CargoData(i, cp->SourceStation(), cp->Count()));
01038           }
01039         }
01040       }
01041     }
01042   }
01043 
01049   void DrawWaitingCargo(const Rect &r, const CargoDataList &cargolist, uint32 transfers) const
01050   {
01051     int y = r.top + WD_FRAMERECT_TOP;
01052     int pos = this->vscroll.GetPosition();
01053 
01054     const Station *st = Station::Get(this->window_number);
01055     if (--pos < 0) {
01056       StringID str = STR_JUST_NOTHING;
01057       for (CargoID i = 0; i < NUM_CARGO; i++) {
01058         if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
01059       }
01060       SetDParam(0, str);
01061       DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_TITLE);
01062       y += FONT_HEIGHT_NORMAL;
01063     }
01064 
01065     bool rtl = _dynlang.text_dir == TD_RTL;
01066     int text_left    = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT;
01067     int text_right   = rtl ? r.right - WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width;
01068     int shrink_left  = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
01069     int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
01070 
01071 
01072     int maxrows = this->vscroll.GetCapacity();
01073     for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
01074       if (--pos < 0) {
01075         const CargoData *cd = &(*it);
01076         if (cd->source == INVALID_STATION) {
01077           /* Heading */
01078           DrawCargoIcons(cd->cargo, cd->count, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y);
01079           SetDParam(0, cd->cargo);
01080           SetDParam(1, cd->count);
01081           if (HasBit(transfers, cd->cargo)) {
01082             /* This cargo has transfers waiting so show the expand or shrink 'button' */
01083             const char *sym = HasBit(this->cargo, cd->cargo) ? "-" : "+";
01084             DrawString(text_left, text_right, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
01085             DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW, SA_RIGHT);
01086           } else {
01087             DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
01088           }
01089         } else {
01090           SetDParam(0, cd->cargo);
01091           SetDParam(1, cd->count);
01092           SetDParam(2, cd->source);
01093           DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_EN_ROUTE_FROM, TC_FROMSTRING, SA_RIGHT);
01094         }
01095 
01096         y += FONT_HEIGHT_NORMAL;
01097       }
01098     }
01099   }
01100 
01105   int DrawAcceptedCargo(const Rect &r) const
01106   {
01107     const Station *st = Station::Get(this->window_number);
01108 
01109     uint32 cargo_mask = 0;
01110     for (CargoID i = 0; i < NUM_CARGO; i++) {
01111       if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) SetBit(cargo_mask, i);
01112     }
01113     Rect s = {r.left + WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP, r.right - WD_FRAMERECT_RIGHT, INT32_MAX};
01114     int bottom = DrawCargoListText(cargo_mask, s, STR_STATION_VIEW_ACCEPTS_CARGO);
01115     return (bottom - r.top - WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL - 1) / FONT_HEIGHT_NORMAL;
01116   }
01117 
01122   int DrawCargoRatings(const Rect &r) const
01123   {
01124     const Station *st = Station::Get(this->window_number);
01125     int y = r.top + WD_FRAMERECT_TOP;
01126 
01127     DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_CARGO_RATINGS_TITLE);
01128     y += FONT_HEIGHT_NORMAL;
01129 
01130     const CargoSpec *cs;
01131     FOR_ALL_CARGOSPECS(cs) {
01132       const GoodsEntry *ge = &st->goods[cs->Index()];
01133       if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
01134 
01135       SetDParam(0, cs->name);
01136       SetDParam(2, ToPercent8(ge->rating));
01137       SetDParam(1, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
01138       DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_RATING);
01139       y += FONT_HEIGHT_NORMAL;
01140     }
01141     return (y - r.top - WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL - 1) / FONT_HEIGHT_NORMAL;
01142   }
01143 
01144   void HandleCargoWaitingClick(int row)
01145   {
01146     if (row == 0) return;
01147 
01148     for (CargoID c = 0; c < NUM_CARGO; c++) {
01149       if (this->cargo_rows[c] == row) {
01150         ToggleBit(this->cargo, c);
01151         this->SetWidgetDirty(SVW_WAITING);
01152         break;
01153       }
01154     }
01155   }
01156 
01157   virtual void OnClick(Point pt, int widget, int click_count)
01158   {
01159     switch (widget) {
01160       case SVW_WAITING:
01161         this->HandleCargoWaitingClick((pt.y - this->GetWidget<NWidgetBase>(SVW_WAITING)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL + this->vscroll.GetPosition());
01162         break;
01163 
01164       case SVW_LOCATION:
01165         if (_ctrl_pressed) {
01166           ShowExtraViewPortWindow(Station::Get(this->window_number)->xy);
01167         } else {
01168           ScrollMainWindowToTile(Station::Get(this->window_number)->xy);
01169         }
01170         break;
01171 
01172       case SVW_RATINGS: {
01173         /* Swap between 'accepts' and 'ratings' view. */
01174         int height_change;
01175         NWidgetCore *nwi = this->GetWidget<NWidgetCore>(SVW_RATINGS);
01176         if (this->GetWidget<NWidgetCore>(SVW_RATINGS)->widget_data == STR_STATION_VIEW_RATINGS_BUTTON) {
01177           nwi->SetDataTip(STR_STATION_VIEW_ACCEPTS_BUTTON, STR_STATION_VIEW_ACCEPTS_TOOLTIP); // Switch to accepts view.
01178           height_change = this->rating_lines - this->accepts_lines;
01179         } else {
01180           nwi->SetDataTip(STR_STATION_VIEW_RATINGS_BUTTON, STR_STATION_VIEW_RATINGS_TOOLTIP); // Switch to ratings view.
01181           height_change = this->accepts_lines - this->rating_lines;
01182         }
01183         this->ReInit(0, height_change * FONT_HEIGHT_NORMAL);
01184         break;
01185       }
01186 
01187       case SVW_RENAME:
01188         SetDParam(0, this->window_number);
01189         ShowQueryString(STR_STATION_NAME, STR_STATION_VIEW_RENAME_STATION_CAPTION, MAX_LENGTH_STATION_NAME_BYTES, MAX_LENGTH_STATION_NAME_PIXELS,
01190             this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT);
01191         break;
01192 
01193       case SVW_TRAINS: { // Show a list of scheduled trains to this station
01194         const Station *st = Station::Get(this->window_number);
01195         ShowVehicleListWindow(st->owner, VEH_TRAIN, (StationID)this->window_number);
01196         break;
01197       }
01198 
01199       case SVW_ROADVEHS: { // Show a list of scheduled road-vehicles to this station
01200         const Station *st = Station::Get(this->window_number);
01201         ShowVehicleListWindow(st->owner, VEH_ROAD, (StationID)this->window_number);
01202         break;
01203       }
01204 
01205       case SVW_PLANES: { // Show a list of scheduled aircraft to this station
01206         const Station *st = Station::Get(this->window_number);
01207         /* Since oilrigs have no owners, show the scheduled aircraft of local company */
01208         Owner owner = (st->owner == OWNER_NONE) ? _local_company : st->owner;
01209         ShowVehicleListWindow(owner, VEH_AIRCRAFT, (StationID)this->window_number);
01210         break;
01211       }
01212 
01213       case SVW_SHIPS: { // Show a list of scheduled ships to this station
01214         const Station *st = Station::Get(this->window_number);
01215         /* Since oilrigs/bouys have no owners, show the scheduled ships of local company */
01216         Owner owner = (st->owner == OWNER_NONE) ? _local_company : st->owner;
01217         ShowVehicleListWindow(owner, VEH_SHIP, (StationID)this->window_number);
01218         break;
01219       }
01220     }
01221   }
01222 
01223   virtual void OnQueryTextFinished(char *str)
01224   {
01225     if (str == NULL) return;
01226 
01227     DoCommandP(0, this->window_number, 0, CMD_RENAME_STATION | CMD_MSG(STR_ERROR_CAN_T_RENAME_STATION), NULL, str);
01228   }
01229 
01230   virtual void OnResize()
01231   {
01232     this->vscroll.SetCapacityFromWidget(this, SVW_WAITING, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
01233   }
01234 };
01235 
01236 
01237 static const WindowDesc _station_view_desc(
01238   WDP_AUTO, 249, 110,
01239   WC_STATION_VIEW, WC_NONE,
01240   WDF_UNCLICK_BUTTONS,
01241   _nested_station_view_widgets, lengthof(_nested_station_view_widgets)
01242 );
01243 
01249 void ShowStationViewWindow(StationID station)
01250 {
01251   AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station);
01252 }
01253 
01255 struct TileAndStation {
01256   TileIndex tile;    
01257   StationID station; 
01258 };
01259 
01260 static SmallVector<TileAndStation, 8> _deleted_stations_nearby;
01261 static SmallVector<StationID, 8> _stations_nearby_list;
01262 
01270 template <class T>
01271 static bool AddNearbyStation(TileIndex tile, void *user_data)
01272 {
01273   TileArea *ctx = (TileArea *)user_data;
01274 
01275   /* First check if there were deleted stations here */
01276   for (uint i = 0; i < _deleted_stations_nearby.Length(); i++) {
01277     TileAndStation *ts = _deleted_stations_nearby.Get(i);
01278     if (ts->tile == tile) {
01279       *_stations_nearby_list.Append() = _deleted_stations_nearby[i].station;
01280       _deleted_stations_nearby.Erase(ts);
01281       i--;
01282     }
01283   }
01284 
01285   /* Check if own station and if we stay within station spread */
01286   if (!IsTileType(tile, MP_STATION)) return false;
01287 
01288   StationID sid = GetStationIndex(tile);
01289 
01290   /* This station is (likely) a waypoint */
01291   if (!T::IsValidID(sid)) return false;
01292 
01293   T *st = T::Get(sid);
01294   if (st->owner != _local_company || _stations_nearby_list.Contains(sid)) return false;
01295 
01296   if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST)) {
01297     *_stations_nearby_list.Append() = sid;
01298   }
01299 
01300   return false; // We want to include *all* nearby stations
01301 }
01302 
01312 template <class T>
01313 static const T *FindStationsNearby(TileArea ta, bool distant_join)
01314 {
01315   TileArea ctx = ta;
01316 
01317   _stations_nearby_list.Clear();
01318   _deleted_stations_nearby.Clear();
01319 
01320   /* Check the inside, to return, if we sit on another station */
01321   TILE_AREA_LOOP(t, ta) {
01322     if (t < MapSize() && IsTileType(t, MP_STATION) && T::IsValidID(GetStationIndex(t))) return T::GetByTile(t);
01323   }
01324 
01325   /* Look for deleted stations */
01326   const BaseStation *st;
01327   FOR_ALL_BASE_STATIONS(st) {
01328     if (T::IsExpected(st) && !st->IsInUse() && st->owner == _local_company) {
01329       /* Include only within station spread (yes, it is strictly less than) */
01330       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) {
01331         TileAndStation *ts = _deleted_stations_nearby.Append();
01332         ts->tile = st->xy;
01333         ts->station = st->index;
01334 
01335         /* Add the station when it's within where we're going to build */
01336         if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) &&
01337             IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) {
01338           AddNearbyStation<T>(st->xy, &ctx);
01339         }
01340       }
01341     }
01342   }
01343 
01344   /* Only search tiles where we have a chance to stay within the station spread.
01345    * The complete check needs to be done in the callback as we don't know the
01346    * extent of the found station, yet. */
01347   if (distant_join && min(ta.w, ta.h) >= _settings_game.station.station_spread) return NULL;
01348   uint max_dist = distant_join ? _settings_game.station.station_spread - min(ta.w, ta.h) : 1;
01349 
01350   TileIndex tile = TILE_ADD(ctx.tile, TileOffsByDir(DIR_N));
01351   CircularTileSearch(&tile, max_dist, ta.w, ta.h, AddNearbyStation<T>, &ctx);
01352 
01353   return NULL;
01354 }
01355 
01356 enum JoinStationWidgets {
01357   JSW_WIDGET_CAPTION,
01358   JSW_PANEL,
01359   JSW_SCROLLBAR,
01360 };
01361 
01362 static const NWidgetPart _nested_select_station_widgets[] = {
01363   NWidget(NWID_HORIZONTAL),
01364     NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
01365     NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, JSW_WIDGET_CAPTION), SetDataTip(STR_JOIN_STATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01366   EndContainer(),
01367   NWidget(NWID_HORIZONTAL),
01368     NWidget(WWT_PANEL, COLOUR_DARK_GREEN, JSW_PANEL), SetResize(1, 0), EndContainer(),
01369     NWidget(NWID_VERTICAL),
01370       NWidget(WWT_SCROLLBAR, COLOUR_DARK_GREEN, JSW_SCROLLBAR),
01371       NWidget(WWT_RESIZEBOX, COLOUR_DARK_GREEN),
01372     EndContainer(),
01373   EndContainer(),
01374 };
01375 
01380 template <class T>
01381 struct SelectStationWindow : Window {
01382   CommandContainer select_station_cmd; 
01383   TileArea area; 
01384 
01385   SelectStationWindow(const WindowDesc *desc, CommandContainer cmd, TileArea ta) :
01386     Window(),
01387     select_station_cmd(cmd),
01388     area(ta)
01389   {
01390     this->CreateNestedTree(desc);
01391     this->GetWidget<NWidgetCore>(JSW_WIDGET_CAPTION)->widget_data = T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CAPTION : STR_JOIN_STATION_CAPTION;
01392     this->FinishInitNested(desc, 0);
01393     this->OnInvalidateData(0);
01394   }
01395 
01396   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01397   {
01398     if (widget != JSW_PANEL) return;
01399 
01400     /* Determine the widest string */
01401     Dimension d = GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_JOIN_WAYPOINT_CREATE_SPLITTED_WAYPOINT : STR_JOIN_STATION_CREATE_SPLITTED_STATION);
01402     for (uint i = 0; i < _stations_nearby_list.Length(); i++) {
01403       const T *st = T::Get(_stations_nearby_list[i]);
01404       SetDParam(0, st->index);
01405       SetDParam(1, st->facilities);
01406       d = maxdim(d, GetStringBoundingBox(T::EXPECTED_FACIL == FACIL_WAYPOINT ? STR_STATION_LIST_WAYPOINT : STR_STATION_LIST_STATION));
01407     }
01408 
01409     resize->height = d.height;
01410     d.height *= 5;
01411     d.width += WD_FRAMERECT_RIGHT + WD_FRAMERECT_LEFT;
01412     d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
01413     *size = d;
01414   }
01415 
01416   virtual void OnPaint()
01417   {
01418     this->DrawWidgets();
01419   }
01420 
01421   virtual void DrawWidget(const Rect &r, int widget) const
01422   {
01423     if (widget != JSW_PANEL) return;
01424 
01425     uint y = r.top + WD_FRAMERECT_TOP;
01426     if (this->vscroll.GetPosition() == 0) {
01427       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);
01428       y += this->resize.step_height;
01429     }
01430 
01431     for (uint i = max<uint>(1, this->vscroll.GetPosition()); i <= _stations_nearby_list.Length(); ++i, y += this->resize.step_height) {
01432       /* Don't draw anything if it extends past the end of the window. */
01433       if (i - this->vscroll.GetPosition() >= this->vscroll.GetCapacity()) break;
01434 
01435       const T *st = T::Get(_stations_nearby_list[i - 1]);
01436       SetDParam(0, st->index);
01437       SetDParam(1, st->facilities);
01438       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);
01439     }
01440   }
01441 
01442   virtual void OnClick(Point pt, int widget, int click_count)
01443   {
01444     if (widget != JSW_PANEL) return;
01445 
01446     uint32 st_index = (pt.y - this->GetWidget<NWidgetBase>(JSW_PANEL)->pos_y - WD_FRAMERECT_TOP) / this->resize.step_height + this->vscroll.GetPosition();
01447     bool distant_join = (st_index > 0);
01448     if (distant_join) st_index--;
01449 
01450     if (distant_join && st_index >= _stations_nearby_list.Length()) return;
01451 
01452     /* Insert station to be joined into stored command */
01453     SB(this->select_station_cmd.p2, 16, 16,
01454        (distant_join ? _stations_nearby_list[st_index] : NEW_STATION));
01455 
01456     /* Execute stored Command */
01457     DoCommandP(&this->select_station_cmd);
01458 
01459     /* Close Window; this might cause double frees! */
01460     DeleteWindowById(WC_SELECT_STATION, 0);
01461   }
01462 
01463   virtual void OnTick()
01464   {
01465     if (_thd.dirty & 2) {
01466       _thd.dirty &= ~2;
01467       this->SetDirty();
01468     }
01469   }
01470 
01471   virtual void OnResize()
01472   {
01473     this->vscroll.SetCapacityFromWidget(this, JSW_PANEL, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM);
01474   }
01475 
01476   virtual void OnInvalidateData(int data)
01477   {
01478     FindStationsNearby<T>(this->area, true);
01479     this->vscroll.SetCount(_stations_nearby_list.Length() + 1);
01480     this->SetDirty();
01481   }
01482 };
01483 
01484 static const WindowDesc _select_station_desc(
01485   WDP_AUTO, 200, 180,
01486   WC_SELECT_STATION, WC_NONE,
01487   WDF_CONSTRUCTION,
01488   _nested_select_station_widgets, lengthof(_nested_select_station_widgets)
01489 );
01490 
01491 
01499 template <class T>
01500 static bool StationJoinerNeeded(CommandContainer cmd, TileArea ta)
01501 {
01502   /* Only show selection if distant join is enabled in the settings */
01503   if (!_settings_game.station.distant_join_stations) return false;
01504 
01505   /* If a window is already opened and we didn't ctrl-click,
01506    * return true (i.e. just flash the old window) */
01507   Window *selection_window = FindWindowById(WC_SELECT_STATION, 0);
01508   if (selection_window != NULL) {
01509     if (!_ctrl_pressed) return true;
01510 
01511     /* Abort current distant-join and start new one */
01512     delete selection_window;
01513     UpdateTileSelection();
01514   }
01515 
01516   /* only show the popup, if we press ctrl */
01517   if (!_ctrl_pressed) return false;
01518 
01519   /* Now check if we could build there */
01520   if (DoCommand(&cmd, CommandFlagsToDCFlags(GetCommandFlags(cmd.cmd))).Failed()) return false;
01521 
01522   /* Test for adjacent station or station below selection.
01523    * If adjacent-stations is disabled and we are building next to a station, do not show the selection window.
01524    * but join the other station immediatelly. */
01525   const T *st = FindStationsNearby<T>(ta, false);
01526   return st == NULL && (_settings_game.station.adjacent_stations || _stations_nearby_list.Length() == 0);
01527 }
01528 
01535 template <class T>
01536 void ShowSelectBaseStationIfNeeded(CommandContainer cmd, TileArea ta)
01537 {
01538   if (StationJoinerNeeded<T>(cmd, ta)) {
01539     if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
01540     if (BringWindowToFrontById(WC_SELECT_STATION, 0)) return;
01541     new SelectStationWindow<T>(&_select_station_desc, cmd, ta);
01542   } else {
01543     DoCommandP(&cmd);
01544   }
01545 }
01546 
01552 void ShowSelectStationIfNeeded(CommandContainer cmd, TileArea ta)
01553 {
01554   ShowSelectBaseStationIfNeeded<Station>(cmd, ta);
01555 }
01556 
01562 void ShowSelectWaypointIfNeeded(CommandContainer cmd, TileArea ta)
01563 {
01564   ShowSelectBaseStationIfNeeded<Waypoint>(cmd, ta);
01565 }

Generated on Wed Mar 3 23:32:27 2010 for OpenTTD by  doxygen 1.6.1