station_gui.cpp

Go to the documentation of this file.
00001 /* $Id: station_gui.cpp 16741 2009-07-04 17:20:48Z 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 "company_func.h"
00012 #include "command_func.h"
00013 #include "vehicle_gui.h"
00014 #include "cargotype.h"
00015 #include "station_gui.h"
00016 #include "strings_func.h"
00017 #include "window_func.h"
00018 #include "viewport_func.h"
00019 #include "gfx_func.h"
00020 #include "widgets/dropdown_func.h"
00021 #include "newgrf_cargo.h"
00022 #include "station_map.h"
00023 #include "tilehighlight_func.h"
00024 #include "core/smallmap_type.hpp"
00025 #include "company_base.h"
00026 #include "sortlist_type.h"
00027 #include "settings_type.h"
00028 
00029 #include "table/strings.h"
00030 #include "table/sprites.h"
00031 
00046 static void StationsWndShowStationRating(int x, int y, CargoID type, uint amount, byte rating)
00047 {
00048   static const uint units_full  = 576; 
00049   static const uint rating_full = 224; 
00050 
00051   const CargoSpec *cs = GetCargo(type);
00052   if (!cs->IsValid()) return;
00053 
00054   int colour = cs->rating_colour;
00055   uint w = (minu(amount, units_full) + 5) / 36;
00056 
00057   /* Draw total cargo (limited) on station (fits into 16 pixels) */
00058   if (w != 0) GfxFillRect(x, y, x + w - 1, y + 6, colour);
00059 
00060   /* Draw a one pixel-wide bar of additional cargo meter, useful
00061    * for stations with only a small amount (<=30) */
00062   if (w == 0) {
00063     uint rest = amount / 5;
00064     if (rest != 0) {
00065       w += x;
00066       GfxFillRect(w, y + 6 - rest, w, y + 6, colour);
00067     }
00068   }
00069 
00070   DrawString(x + 1, y, cs->abbrev, TC_BLACK);
00071 
00072   /* Draw green/red ratings bar (fits into 14 pixels) */
00073   y += 8;
00074   GfxFillRect(x + 1, y, x + 14, y, 0xB8);
00075   rating = minu(rating, rating_full) / 16;
00076   if (rating != 0) GfxFillRect(x + 1, y, x + rating, y, 0xD0);
00077 }
00078 
00079 typedef GUIList<const Station*> GUIStationList;
00080 
00084 class CompanyStationsWindow : public Window
00085 {
00087   enum StationListWidgets {
00088     SLW_CLOSEBOX =  0,  
00089     SLW_CAPTION,        
00090     SLW_STICKY,         
00091     SLW_LIST,           
00092     SLW_SCROLLBAR,      
00093     SLW_RESIZE,         
00094 
00095     SLW_TRAIN,          
00096     SLW_TRUCK,          
00097     SLW_BUS,            
00098     SLW_AIRPLANE,       
00099     SLW_SHIP,           
00100     SLW_FACILALL,       
00101 
00102     SLW_PAN_BETWEEN,    
00103     SLW_NOCARGOWAITING, 
00104     SLW_CARGOALL,       
00105     SLW_PAN_RIGHT,      
00106 
00107     SLW_SORTBY,         
00108     SLW_SORTDROPBTN,    
00109     SLW_PAN_SORT_RIGHT, 
00110 
00111     SLW_CARGOSTART,     
00112   };
00113 
00114 protected:
00115   /* Runtime saved values */
00116   static Listing last_sorting;
00117   static byte facilities;               // types of stations of interest
00118   static bool include_empty;            // whether we should include stations without waiting cargo
00119   static const uint32 cargo_filter_max;
00120   static uint32 cargo_filter;           // bitmap of cargo types to include
00121   static const Station *last_station;
00122 
00123   /* Constants for sorting stations */
00124   static const StringID sorter_names[];
00125   static GUIStationList::SortFunction * const sorter_funcs[];
00126 
00127   GUIStationList stations;
00128 
00129 
00135   void BuildStationsList(const Owner owner)
00136   {
00137     if (!this->stations.NeedRebuild()) return;
00138 
00139     DEBUG(misc, 3, "Building station list for company %d", owner);
00140 
00141     this->stations.Clear();
00142 
00143     const Station *st;
00144     FOR_ALL_STATIONS(st) {
00145       if (st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy() && HasStationInUse(st->index, owner))) {
00146         if (this->facilities & st->facilities) { // only stations with selected facilities
00147           int num_waiting_cargo = 0;
00148           for (CargoID j = 0; j < NUM_CARGO; j++) {
00149             if (!st->goods[j].cargo.Empty()) {
00150               num_waiting_cargo++; // count number of waiting cargo
00151               if (HasBit(this->cargo_filter, j)) {
00152                 *this->stations.Append() = st;
00153                 break;
00154               }
00155             }
00156           }
00157           /* stations without waiting cargo */
00158           if (num_waiting_cargo == 0 && this->include_empty) {
00159             *this->stations.Append() = st;
00160           }
00161         }
00162       }
00163     }
00164 
00165     this->stations.Compact();
00166     this->stations.RebuildDone();
00167   }
00168 
00170   static int CDECL StationNameSorter(const Station * const *a, const Station * const *b)
00171   {
00172     static char buf_cache[64];
00173     char buf[64];
00174 
00175     SetDParam(0, (*a)->index);
00176     GetString(buf, STR_STATION, lastof(buf));
00177 
00178     if (*b != last_station) {
00179       last_station = *b;
00180       SetDParam(0, (*b)->index);
00181       GetString(buf_cache, STR_STATION, lastof(buf_cache));
00182     }
00183 
00184     return strcmp(buf, buf_cache);
00185   }
00186 
00188   static int CDECL StationTypeSorter(const Station * const *a, const Station * const *b)
00189   {
00190     return (*a)->facilities - (*b)->facilities;
00191   }
00192 
00194   static int CDECL StationWaitingSorter(const Station * const *a, const Station * const *b)
00195   {
00196     Money diff = 0;
00197 
00198     for (CargoID j = 0; j < NUM_CARGO; j++) {
00199       if (!HasBit(cargo_filter, j)) continue;
00200       if (!(*a)->goods[j].cargo.Empty()) diff += GetTransportedGoodsIncome((*a)->goods[j].cargo.Count(), 20, 50, j);
00201       if (!(*b)->goods[j].cargo.Empty()) diff -= GetTransportedGoodsIncome((*b)->goods[j].cargo.Count(), 20, 50, j);
00202     }
00203 
00204     return ClampToI32(diff);
00205   }
00206 
00208   static int CDECL StationRatingMaxSorter(const Station * const *a, const Station * const *b)
00209   {
00210     byte maxr1 = 0;
00211     byte maxr2 = 0;
00212 
00213     for (CargoID j = 0; j < NUM_CARGO; j++) {
00214       if (HasBit((*a)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr1 = max(maxr1, (*a)->goods[j].rating);
00215       if (HasBit((*b)->goods[j].acceptance_pickup, GoodsEntry::PICKUP)) maxr2 = max(maxr2, (*b)->goods[j].rating);
00216     }
00217 
00218     return maxr1 - maxr2;
00219   }
00220 
00222   void SortStationsList()
00223   {
00224     if (!this->stations.Sort()) return;
00225 
00226     /* Reset name sorter sort cache */
00227     this->last_station = NULL;
00228 
00229     /* Set the modified widget dirty */
00230     this->InvalidateWidget(SLW_LIST);
00231   }
00232 
00233 public:
00234   CompanyStationsWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
00235   {
00236     this->owner = (Owner)this->window_number;
00237     this->vscroll.cap = 12;
00238     this->resize.step_height = 10;
00239     this->resize.height = this->height - 10 * 7; // minimum if 5 in the list
00240 
00241     /* Add cargo filter buttons */
00242     uint num_active = 0;
00243     for (CargoID c = 0; c < NUM_CARGO; c++) {
00244       if (GetCargo(c)->IsValid()) num_active++;
00245     }
00246 
00247     this->widget_count += num_active;
00248     this->widget = ReallocT(this->widget, this->widget_count + 1);
00249     this->widget[this->widget_count].type = WWT_LAST;
00250 
00251     uint i = 0;
00252     for (CargoID c = 0; c < NUM_CARGO; c++) {
00253       if (!GetCargo(c)->IsValid()) continue;
00254 
00255       Widget *wi = &this->widget[SLW_CARGOSTART + i];
00256       wi->type     = WWT_PANEL;
00257       wi->display_flags = RESIZE_NONE;
00258       wi->colour   = COLOUR_GREY;
00259       wi->left     = 89 + i * 14;
00260       wi->right    = wi->left + 13;
00261       wi->top      = 14;
00262       wi->bottom   = 24;
00263       wi->data     = 0;
00264       wi->tooltips = STR_USE_CTRL_TO_SELECT_MORE;
00265 
00266       if (HasBit(this->cargo_filter, c)) this->LowerWidget(SLW_CARGOSTART + i);
00267       i++;
00268     }
00269 
00270     this->widget[SLW_NOCARGOWAITING].left += num_active * 14;
00271     this->widget[SLW_NOCARGOWAITING].right += num_active * 14;
00272     this->widget[SLW_CARGOALL].left += num_active * 14;
00273     this->widget[SLW_CARGOALL].right += num_active * 14;
00274     this->widget[SLW_PAN_RIGHT].left += num_active * 14;
00275 
00276     if (num_active > 15) {
00277       /* Resize and fix the minimum width, if necessary */
00278       ResizeWindow(this, (num_active - 15) * 14, 0);
00279       this->resize.width = this->width;
00280     }
00281 
00282     if (this->cargo_filter == this->cargo_filter_max) this->cargo_filter = _cargo_mask;
00283 
00284     for (uint i = 0; i < 5; i++) {
00285       if (HasBit(this->facilities, i)) this->LowerWidget(i + SLW_TRAIN);
00286     }
00287     this->SetWidgetLoweredState(SLW_FACILALL, this->facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00288     this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00289     this->SetWidgetLoweredState(SLW_NOCARGOWAITING, this->include_empty);
00290 
00291     this->stations.SetListing(this->last_sorting);
00292     this->stations.SetSortFuncs(this->sorter_funcs);
00293     this->stations.ForceRebuild();
00294     this->stations.NeedResort();
00295     this->SortStationsList();
00296 
00297     this->widget[SLW_SORTDROPBTN].data = this->sorter_names[this->stations.SortType()];
00298 
00299     this->FindWindowPlacementAndResize(desc);
00300   }
00301 
00302   ~CompanyStationsWindow()
00303   {
00304     this->last_sorting = this->stations.GetListing();
00305   }
00306 
00307   virtual void OnPaint()
00308   {
00309     const Owner owner = (Owner)this->window_number;
00310 
00311     this->BuildStationsList(owner);
00312     this->SortStationsList();
00313 
00314     SetVScrollCount(this, this->stations.Length());
00315 
00316     /* draw widgets, with company's name in the caption */
00317     SetDParam(0, owner);
00318     SetDParam(1, this->vscroll.count);
00319 
00320     this->DrawWidgets();
00321 
00322     /* draw arrow pointing up/down for ascending/descending sorting */
00323     this->DrawSortButtonState(SLW_SORTBY, this->stations.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
00324 
00325     int cg_ofst;
00326     int x = 89;
00327     int y = 14;
00328     int xb = 2; 
00329 
00330     uint i = 0;
00331     for (CargoID c = 0; c < NUM_CARGO; c++) {
00332       const CargoSpec *cs = GetCargo(c);
00333       if (!cs->IsValid()) continue;
00334 
00335       cg_ofst = HasBit(this->cargo_filter, c) ? 2 : 1;
00336       GfxFillRect(x + cg_ofst, y + cg_ofst, x + cg_ofst + 10 , y + cg_ofst + 7, cs->rating_colour);
00337       DrawStringCentered(x + 6 + cg_ofst, y + cg_ofst, cs->abbrev, TC_BLACK);
00338       x += 14;
00339       i++;
00340     }
00341 
00342     x += 6;
00343     cg_ofst = this->IsWidgetLowered(SLW_NOCARGOWAITING) ? 2 : 1;
00344     DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_NONE, TC_BLACK);
00345     x += 14;
00346     cg_ofst = this->IsWidgetLowered(SLW_CARGOALL) ? 2 : 1;
00347     DrawStringCentered(x + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00348 
00349     cg_ofst = this->IsWidgetLowered(SLW_FACILALL) ? 2 : 1;
00350     DrawString(71 + cg_ofst, y + cg_ofst, STR_ABBREV_ALL, TC_BLACK);
00351 
00352     if (this->vscroll.count == 0) { // company has no stations
00353       DrawString(xb, 40, STR_304A_NONE, TC_FROMSTRING);
00354       return;
00355     }
00356 
00357     int max = min(this->vscroll.pos + this->vscroll.cap, this->stations.Length());
00358     y = 40; // start of the list-widget
00359 
00360     for (int i = this->vscroll.pos; i < max; ++i) { // do until max number of stations of owner
00361       const Station *st = this->stations[i];
00362       int x;
00363 
00364       assert(st->xy != INVALID_TILE);
00365 
00366       /* Do not do the complex check HasStationInUse here, it may be even false
00367        * when the order had been removed and the station list hasn't been removed yet */
00368       assert(st->owner == owner || (st->owner == OWNER_NONE && !st->IsBuoy()));
00369 
00370       SetDParam(0, st->index);
00371       SetDParam(1, st->facilities);
00372       x = DrawString(xb, y, STR_3049_0, TC_FROMSTRING) + 5;
00373 
00374       /* show cargo waiting and station ratings */
00375       for (CargoID j = 0; j < NUM_CARGO; j++) {
00376         if (!st->goods[j].cargo.Empty()) {
00377           StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
00378           x += 20;
00379         }
00380       }
00381       y += 10;
00382     }
00383   }
00384 
00385   virtual void OnClick(Point pt, int widget)
00386   {
00387     switch (widget) {
00388       case SLW_LIST: {
00389         uint32 id_v = (pt.y - 41) / 10;
00390 
00391         if (id_v >= this->vscroll.cap) return; // click out of bounds
00392 
00393         id_v += this->vscroll.pos;
00394 
00395         if (id_v >= this->stations.Length()) return; // click out of list bound
00396 
00397         const Station *st = this->stations[id_v];
00398         /* do not check HasStationInUse - it is slow and may be invalid */
00399         assert(st->owner == (Owner)this->window_number || (st->owner == OWNER_NONE && !st->IsBuoy()));
00400 
00401         if (_ctrl_pressed) {
00402           ShowExtraViewPortWindow(st->xy);
00403         } else {
00404           ScrollMainWindowToTile(st->xy);
00405         }
00406         break;
00407       }
00408 
00409       case SLW_TRAIN:
00410       case SLW_TRUCK:
00411       case SLW_BUS:
00412       case SLW_AIRPLANE:
00413       case SLW_SHIP:
00414         if (_ctrl_pressed) {
00415           ToggleBit(this->facilities, widget - SLW_TRAIN);
00416           this->ToggleWidgetLoweredState(widget);
00417         } else {
00418           uint i;
00419           FOR_EACH_SET_BIT(i, this->facilities) {
00420             this->RaiseWidget(i + SLW_TRAIN);
00421           }
00422           SetBit(this->facilities, widget - SLW_TRAIN);
00423           this->LowerWidget(widget);
00424         }
00425         this->SetWidgetLoweredState(SLW_FACILALL, this->facilities == (FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK));
00426         this->stations.ForceRebuild();
00427         this->SetDirty();
00428         break;
00429 
00430       case SLW_FACILALL:
00431         for (uint i = 0; i < 5; i++) {
00432           this->LowerWidget(i + SLW_TRAIN);
00433         }
00434         this->LowerWidget(SLW_FACILALL);
00435 
00436         this->facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00437         this->stations.ForceRebuild();
00438         this->SetDirty();
00439         break;
00440 
00441       case SLW_CARGOALL: {
00442         uint i = 0;
00443         for (CargoID c = 0; c < NUM_CARGO; c++) {
00444           if (!GetCargo(c)->IsValid()) continue;
00445           this->LowerWidget(i + SLW_CARGOSTART);
00446           i++;
00447         }
00448         this->LowerWidget(SLW_NOCARGOWAITING);
00449         this->LowerWidget(SLW_CARGOALL);
00450 
00451         this->cargo_filter = _cargo_mask;
00452         this->include_empty = true;
00453         this->stations.ForceRebuild();
00454         this->SetDirty();
00455         break;
00456       }
00457 
00458       case SLW_SORTBY: // flip sorting method asc/desc
00459         this->stations.ToggleSortOrder();
00460         this->flags4 |= WF_TIMEOUT_BEGIN;
00461         this->LowerWidget(SLW_SORTBY);
00462         this->SetDirty();
00463         break;
00464 
00465       case SLW_SORTDROPBTN: // select sorting criteria dropdown menu
00466         ShowDropDownMenu(this, this->sorter_names, this->stations.SortType(), SLW_SORTDROPBTN, 0, 0);
00467         break;
00468 
00469       case SLW_NOCARGOWAITING:
00470         if (_ctrl_pressed) {
00471           this->include_empty = !this->include_empty;
00472           this->ToggleWidgetLoweredState(SLW_NOCARGOWAITING);
00473         } else {
00474           for (uint i = SLW_CARGOSTART; i < this->widget_count; i++) {
00475             this->RaiseWidget(i);
00476           }
00477 
00478           this->cargo_filter = 0;
00479           this->include_empty = true;
00480 
00481           this->LowerWidget(SLW_NOCARGOWAITING);
00482         }
00483         this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00484         this->stations.ForceRebuild();
00485         this->SetDirty();
00486         break;
00487 
00488       default:
00489         if (widget >= SLW_CARGOSTART) { // change cargo_filter
00490           /* Determine the selected cargo type */
00491           CargoID c;
00492           int i = 0;
00493           for (c = 0; c < NUM_CARGO; c++) {
00494             if (!GetCargo(c)->IsValid()) continue;
00495             if (widget - SLW_CARGOSTART == i) break;
00496             i++;
00497           }
00498 
00499           if (_ctrl_pressed) {
00500             ToggleBit(this->cargo_filter, c);
00501             this->ToggleWidgetLoweredState(widget);
00502           } else {
00503             for (uint i = SLW_CARGOSTART; i < this->widget_count; i++) {
00504               this->RaiseWidget(i);
00505             }
00506             this->RaiseWidget(SLW_NOCARGOWAITING);
00507 
00508             this->cargo_filter = 0;
00509             this->include_empty = false;
00510 
00511             SetBit(this->cargo_filter, c);
00512             this->LowerWidget(widget);
00513           }
00514           this->SetWidgetLoweredState(SLW_CARGOALL, this->cargo_filter == _cargo_mask && this->include_empty);
00515           this->stations.ForceRebuild();
00516           this->SetDirty();
00517         }
00518         break;
00519     }
00520   }
00521 
00522   virtual void OnDropdownSelect(int widget, int index)
00523   {
00524     if (this->stations.SortType() != index) {
00525       this->stations.SetSortType(index);
00526 
00527       /* Display the current sort variant */
00528       this->widget[SLW_SORTDROPBTN].data = this->sorter_names[this->stations.SortType()];
00529 
00530       this->SetDirty();
00531     }
00532   }
00533 
00534   virtual void OnTick()
00535   {
00536     if (_pause_game != 0) return;
00537     if (this->stations.NeedResort()) {
00538       DEBUG(misc, 3, "Periodic rebuild station list company %d", this->window_number);
00539       this->SetDirty();
00540     }
00541   }
00542 
00543   virtual void OnTimeout()
00544   {
00545     this->RaiseWidget(SLW_SORTBY);
00546     this->SetDirty();
00547   }
00548 
00549   virtual void OnResize(Point new_size, Point delta)
00550   {
00551     this->vscroll.cap += delta.y / 10;
00552   }
00553 
00554   virtual void OnInvalidateData(int data)
00555   {
00556     if (data == 0) {
00557       this->stations.ForceRebuild();
00558     } else {
00559       this->stations.ForceResort();
00560     }
00561   }
00562 };
00563 
00564 Listing CompanyStationsWindow::last_sorting = {false, 0};
00565 byte CompanyStationsWindow::facilities = FACIL_TRAIN | FACIL_TRUCK_STOP | FACIL_BUS_STOP | FACIL_AIRPORT | FACIL_DOCK;
00566 bool CompanyStationsWindow::include_empty = true;
00567 const uint32 CompanyStationsWindow::cargo_filter_max = UINT32_MAX;
00568 uint32 CompanyStationsWindow::cargo_filter = UINT32_MAX;
00569 const Station *CompanyStationsWindow::last_station = NULL;
00570 
00571 /* Availible station sorting functions */
00572 GUIStationList::SortFunction * const CompanyStationsWindow::sorter_funcs[] = {
00573   &StationNameSorter,
00574   &StationTypeSorter,
00575   &StationWaitingSorter,
00576   &StationRatingMaxSorter
00577 };
00578 
00579 /* Names of the sorting functions */
00580 const StringID CompanyStationsWindow::sorter_names[] = {
00581   STR_SORT_BY_DROPDOWN_NAME,
00582   STR_SORT_BY_FACILITY,
00583   STR_SORT_BY_WAITING,
00584   STR_SORT_BY_RATING_MAX,
00585   INVALID_STRING_ID
00586 };
00587 
00588 
00589 static const Widget _company_stations_widgets[] = {
00590 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},            // SLW_CLOSEBOX
00591 {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   345,     0,    13, STR_3048_STATIONS, STR_018C_WINDOW_TITLE_DRAG_THIS},  // SLW_CAPTION
00592 {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_GREY,   346,   357,     0,    13, 0x0,               STR_STICKY_BUTTON},                // SLW_STICKY
00593 {      WWT_PANEL,     RESIZE_RB,  COLOUR_GREY,     0,   345,    37,   161, 0x0,               STR_3057_STATION_NAMES_CLICK_ON},  // SLW_LIST
00594 {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY,   346,   357,    37,   149, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST}, // SLW_SCROLLBAR
00595 {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   346,   357,   150,   161, 0x0,               STR_RESIZE_BUTTON},                // SLW_RESIZE
00596 
00597 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,     0,    13,    14,    24, STR_TRAIN,         STR_USE_CTRL_TO_SELECT_MORE},      // SLW_TRAIN
00598 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,    14,    27,    14,    24, STR_LORRY,         STR_USE_CTRL_TO_SELECT_MORE},      // SLW_TRUCK
00599 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,    28,    41,    14,    24, STR_BUS,           STR_USE_CTRL_TO_SELECT_MORE},      // SLW_BUS
00600 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,    42,    55,    14,    24, STR_PLANE,         STR_USE_CTRL_TO_SELECT_MORE},      // SLW_AIRPLANE
00601 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,    56,    69,    14,    24, STR_SHIP,          STR_USE_CTRL_TO_SELECT_MORE},      // SLW_SHIP
00602 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,    70,    83,    14,    24, 0x0,               STR_SELECT_ALL_FACILITIES},        // SLW_FACILALL
00603 
00604 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,    83,    88,    14,    24, 0x0,               STR_NULL},                         // SLW_PAN_BETWEEN
00605 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,    89,   102,    14,    24, 0x0,               STR_NO_WAITING_CARGO},             // SLW_NOCARGOWAITING
00606 {      WWT_PANEL,   RESIZE_NONE,  COLOUR_GREY,   103,   116,    14,    24, 0x0,               STR_SELECT_ALL_TYPES},             // SLW_CARGOALL
00607 {      WWT_PANEL,  RESIZE_RIGHT,  COLOUR_GREY,   117,   357,    14,    24, 0x0,               STR_NULL},                         // SLW_PAN_RIGHT
00608 
00609 {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,     0,    80,    25,    36, STR_SORT_BY,       STR_SORT_ORDER_TIP},               // SLW_SORTBY
00610 {   WWT_DROPDOWN,   RESIZE_NONE,  COLOUR_GREY,    81,   243,    25,    36, 0x0,               STR_SORT_CRITERIA_TIP},            // SLW_SORTDROPBTN
00611 {      WWT_PANEL,  RESIZE_RIGHT,  COLOUR_GREY,   244,   357,    25,    36, 0x0,               STR_NULL},                         // SLW_PAN_SORT_RIGHT
00612 {   WIDGETS_END},
00613 };
00614 
00615 static const WindowDesc _company_stations_desc(
00616   WDP_AUTO, WDP_AUTO, 358, 162, 358, 162,
00617   WC_STATION_LIST, WC_NONE,
00618   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00619   _company_stations_widgets
00620 );
00621 
00627 void ShowCompanyStations(CompanyID company)
00628 {
00629   if (!IsValidCompanyID(company)) return;
00630 
00631   AllocateWindowDescFront<CompanyStationsWindow>(&_company_stations_desc, company);
00632 }
00633 
00634 static const Widget _station_view_widgets[] = {
00635 {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,          STR_018B_CLOSE_WINDOW},                // SVW_CLOSEBOX
00636 {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   236,     0,    13, STR_300A_0,        STR_018C_WINDOW_TITLE_DRAG_THIS},
00637 {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_GREY,   237,   248,     0,    13, 0x0,               STR_STICKY_BUTTON},
00638 {      WWT_PANEL,     RESIZE_RB,  COLOUR_GREY,     0,   236,    14,    65, 0x0,               STR_NULL},                             // SVW_WAITING
00639 {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY,   237,   248,    14,    65, 0x0,               STR_0190_SCROLL_BAR_SCROLLS_LIST},
00640 {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,     0,   248,    66,    97, 0x0,               STR_NULL},                             // SVW_ACCEPTLIST / SVW_RATINGLIST
00641 { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,     0,    59,    98,   109, STR_00E4_LOCATION, STR_3053_CENTER_MAIN_VIEW_ON_STATION}, // SVW_LOCATION
00642 { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,    60,   120,    98,   109, STR_3032_RATINGS,  STR_3054_SHOW_STATION_RATINGS},        // SVW_RATINGS / SVW_ACCEPTS
00643 { WWT_PUSHTXTBTN,    RESIZE_RTB,  COLOUR_GREY,   121,   180,    98,   109, STR_0130_RENAME,   STR_3055_CHANGE_NAME_OF_STATION},      // SVW_RENAME
00644 { WWT_PUSHTXTBTN,   RESIZE_LRTB,  COLOUR_GREY,   181,   194,    98,   109, STR_TRAIN,         STR_SCHEDULED_TRAINS_TIP },            // SVW_TRAINS
00645 { WWT_PUSHTXTBTN,   RESIZE_LRTB,  COLOUR_GREY,   195,   208,    98,   109, STR_LORRY,         STR_SCHEDULED_ROAD_VEHICLES_TIP },     // SVW_ROADVEHS
00646 { WWT_PUSHTXTBTN,   RESIZE_LRTB,  COLOUR_GREY,   209,   222,    98,   109, STR_PLANE,         STR_SCHEDULED_AIRCRAFT_TIP },          // SVW_PLANES
00647 { WWT_PUSHTXTBTN,   RESIZE_LRTB,  COLOUR_GREY,   223,   236,    98,   109, STR_SHIP,          STR_SCHEDULED_SHIPS_TIP },             // SVW_SHIPS
00648 {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   237,   248,    98,   109, 0x0,               STR_RESIZE_BUTTON},
00649 {   WIDGETS_END},
00650 };
00651 
00652 SpriteID GetCargoSprite(CargoID i)
00653 {
00654   const CargoSpec *cs = GetCargo(i);
00655   SpriteID sprite;
00656 
00657   if (cs->sprite == 0xFFFF) {
00658     /* A value of 0xFFFF indicates we should draw a custom icon */
00659     sprite = GetCustomCargoSprite(cs);
00660   } else {
00661     sprite = cs->sprite;
00662   }
00663 
00664   if (sprite == 0) sprite = SPR_CARGO_GOODS;
00665 
00666   return sprite;
00667 }
00668 
00677 static void DrawCargoIcons(CargoID i, uint waiting, int x, int y, uint width)
00678 {
00679   uint num = min((waiting + 5) / 10, width / 10); // maximum is width / 10 icons so it won't overflow
00680   if (num == 0) return;
00681 
00682   SpriteID sprite = GetCargoSprite(i);
00683 
00684   do {
00685     DrawSprite(sprite, PAL_NONE, x, y);
00686     x += 10;
00687   } while (--num);
00688 }
00689 
00690 struct CargoData {
00691   CargoID cargo;
00692   StationID source;
00693   uint count;
00694 
00695   CargoData(CargoID cargo, StationID source, uint count) :
00696     cargo(cargo),
00697     source(source),
00698     count(count)
00699   { }
00700 };
00701 
00702 typedef std::list<CargoData> CargoDataList;
00703 
00707 struct StationViewWindow : public Window {
00708   uint32 cargo;                 
00709   uint16 cargo_rows[NUM_CARGO]; 
00710 
00711   StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
00712   {
00713     Owner owner = GetStation(window_number)->owner;
00714     if (owner != OWNER_NONE) this->owner = owner;
00715     this->vscroll.cap = 5;
00716     this->resize.step_height = 10;
00717 
00718     this->FindWindowPlacementAndResize(desc);
00719   }
00720 
00721   ~StationViewWindow()
00722   {
00723     WindowNumber wno =
00724       (this->window_number << 16) | VLW_STATION_LIST | GetStation(this->window_number)->owner;
00725 
00726     DeleteWindowById(WC_TRAINS_LIST, wno | (VEH_TRAIN << 11), false);
00727     DeleteWindowById(WC_ROADVEH_LIST, wno | (VEH_ROAD << 11), false);
00728     DeleteWindowById(WC_SHIPS_LIST, wno | (VEH_SHIP << 11), false);
00729     DeleteWindowById(WC_AIRCRAFT_LIST, wno | (VEH_AIRCRAFT << 11), false);
00730   }
00731 
00732   virtual void OnPaint()
00733   {
00734     StationID station_id = this->window_number;
00735     const Station *st = GetStation(station_id);
00736     CargoDataList cargolist;
00737     uint32 transfers = 0;
00738 
00739     /* count types of cargos waiting in station */
00740     for (CargoID i = 0; i < NUM_CARGO; i++) {
00741       if (st->goods[i].cargo.Empty()) {
00742         this->cargo_rows[i] = 0;
00743       } else {
00744         /* Add an entry for total amount of cargo of this type waiting. */
00745         cargolist.push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
00746 
00747         /* Set the row for this cargo entry for the expand/hide button */
00748         this->cargo_rows[i] = (uint16)cargolist.size();
00749 
00750         /* Add an entry for each distinct cargo source. */
00751         const CargoList::List *packets = st->goods[i].cargo.Packets();
00752         for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
00753           const CargoPacket *cp = *it;
00754           if (cp->source != station_id) {
00755             bool added = false;
00756 
00757             /* Enable the expand/hide button for this cargo type */
00758             SetBit(transfers, i);
00759 
00760             /* Don't add cargo lines if not expanded */
00761             if (!HasBit(this->cargo, i)) break;
00762 
00763             /* Check if we already have this source in the list */
00764             for (CargoDataList::iterator jt = cargolist.begin(); jt != cargolist.end(); jt++) {
00765               CargoData *cd = &(*jt);
00766               if (cd->cargo == i && cd->source == cp->source) {
00767                 cd->count += cp->count;
00768                 added = true;
00769                 break;
00770               }
00771             }
00772 
00773             if (!added) cargolist.push_back(CargoData(i, cp->source, cp->count));
00774           }
00775         }
00776       }
00777     }
00778     SetVScrollCount(this, (int)cargolist.size() + 1); // update scrollbar
00779 
00780     /* disable some buttons */
00781     this->SetWidgetDisabledState(SVW_RENAME,   st->owner != _local_company);
00782     this->SetWidgetDisabledState(SVW_TRAINS,   !(st->facilities & FACIL_TRAIN));
00783     this->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
00784     this->SetWidgetDisabledState(SVW_PLANES,   !(st->facilities & FACIL_AIRPORT));
00785     this->SetWidgetDisabledState(SVW_SHIPS,    !(st->facilities & FACIL_DOCK));
00786 
00787     SetDParam(0, st->index);
00788     SetDParam(1, st->facilities);
00789     this->DrawWidgets();
00790 
00791     int x = 2;  
00792     int y = 15;
00793     int pos = this->vscroll.pos; 
00794 
00795     uint width = this->widget[SVW_WAITING].right - this->widget[SVW_WAITING].left - 4;
00796     int maxrows = this->vscroll.cap;
00797 
00798     StringID str;
00799 
00800     if (--pos < 0) {
00801       str = STR_00D0_NOTHING;
00802       for (CargoID i = 0; i < NUM_CARGO; i++) {
00803         if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
00804       }
00805       SetDParam(0, str);
00806       DrawString(x, y, STR_0008_WAITING, TC_FROMSTRING);
00807       y += 10;
00808     }
00809 
00810     for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
00811       if (--pos < 0) {
00812         const CargoData *cd = &(*it);
00813         if (cd->source == INVALID_STATION) {
00814           /* Heading */
00815           DrawCargoIcons(cd->cargo, cd->count, x, y, width);
00816           SetDParam(0, cd->cargo);
00817           SetDParam(1, cd->count);
00818           if (HasBit(transfers, cd->cargo)) {
00819             /* This cargo has transfers waiting so show the expand or shrink 'button' */
00820             const char *sym = HasBit(this->cargo, cd->cargo) ? "-" : "+";
00821             DrawStringRightAligned(x + width - 8, y, STR_0009, TC_FROMSTRING);
00822             DoDrawString(sym, x + width - 6, y, TC_YELLOW);
00823           } else {
00824             DrawStringRightAligned(x + width, y, STR_0009, TC_FROMSTRING);
00825           }
00826         } else {
00827           SetDParam(0, cd->cargo);
00828           SetDParam(1, cd->count);
00829           SetDParam(2, cd->source);
00830           DrawStringRightAlignedTruncated(x + width, y, STR_EN_ROUTE_FROM, TC_FROMSTRING, width);
00831         }
00832 
00833         y += 10;
00834       }
00835     }
00836 
00837     if (this->widget[SVW_ACCEPTS].data == STR_3032_RATINGS) { // small window with list of accepted cargo
00838       char string[512];
00839       char *b = string;
00840       bool first = true;
00841 
00842       b = InlineString(b, STR_000C_ACCEPTS);
00843 
00844       for (CargoID i = 0; i < NUM_CARGO; i++) {
00845         if (b >= lastof(string) - (1 + 2 * 4)) break; // ',' or ' ' and two calls to Utf8Encode()
00846         if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
00847           if (first) {
00848             first = false;
00849           } else {
00850             /* Add a comma if this is not the first item */
00851             *b++ = ',';
00852             *b++ = ' ';
00853           }
00854           b = InlineString(b, GetCargo(i)->name);
00855         }
00856       }
00857 
00858       /* If first is still true then no cargo is accepted */
00859       if (first) b = InlineString(b, STR_00D0_NOTHING);
00860 
00861       *b = '\0';
00862 
00863       /* Make sure we detect any buffer overflow */
00864       assert(b < endof(string));
00865 
00866       SetDParamStr(0, string);
00867       DrawStringMultiLine(2, this->widget[SVW_ACCEPTLIST].top + 1, STR_JUST_RAW_STRING, this->widget[SVW_ACCEPTLIST].right - this->widget[SVW_ACCEPTLIST].left);
00868     } else { // extended window with list of cargo ratings
00869       y = this->widget[SVW_RATINGLIST].top + 1;
00870 
00871       DrawString(2, y, STR_3034_LOCAL_RATING_OF_TRANSPORT, TC_FROMSTRING);
00872       y += 10;
00873 
00874       for (CargoID i = 0; i < NUM_CARGO; i++) {
00875         const CargoSpec *cs = GetCargo(i);
00876         if (!cs->IsValid()) continue;
00877 
00878         const GoodsEntry *ge = &st->goods[i];
00879         if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
00880 
00881         SetDParam(0, cs->name);
00882         SetDParam(2, ge->rating * 101 >> 8);
00883         SetDParam(1, STR_3035_APPALLING + (ge->rating >> 5));
00884         DrawString(8, y, STR_303D, TC_FROMSTRING);
00885         y += 10;
00886       }
00887     }
00888   }
00889 
00890   void HandleCargoWaitingClick(int row)
00891   {
00892     if (row == 0) return;
00893 
00894     for (CargoID c = 0; c < NUM_CARGO; c++) {
00895       if (this->cargo_rows[c] == row) {
00896         ToggleBit(this->cargo, c);
00897         this->InvalidateWidget(SVW_WAITING);
00898         break;
00899       }
00900     }
00901   }
00902 
00903   virtual void OnClick(Point pt, int widget)
00904   {
00905     switch (widget) {
00906       case SVW_WAITING:
00907         this->HandleCargoWaitingClick((pt.y - this->widget[SVW_WAITING].top) / 10 + this->vscroll.pos);
00908         break;
00909 
00910       case SVW_LOCATION:
00911         if (_ctrl_pressed) {
00912           ShowExtraViewPortWindow(GetStation(this->window_number)->xy);
00913         } else {
00914           ScrollMainWindowToTile(GetStation(this->window_number)->xy);
00915         }
00916         break;
00917 
00918       case SVW_RATINGS:
00919         this->SetDirty();
00920 
00921         if (this->widget[SVW_RATINGS].data == STR_3032_RATINGS) {
00922           /* Switch to ratings view */
00923           this->widget[SVW_RATINGS].data = STR_3033_ACCEPTS;
00924           this->widget[SVW_RATINGS].tooltips = STR_3056_SHOW_LIST_OF_ACCEPTED_CARGO;
00925           ResizeWindowForWidget(this, SVW_ACCEPTLIST, 0, 100);
00926         } else {
00927           /* Switch to accepts view */
00928           this->widget[SVW_RATINGS].data = STR_3032_RATINGS;
00929           this->widget[SVW_RATINGS].tooltips = STR_3054_SHOW_STATION_RATINGS;
00930           ResizeWindowForWidget(this, SVW_ACCEPTLIST, 0, -100);
00931         }
00932 
00933         this->SetDirty();
00934         break;
00935 
00936       case SVW_RENAME:
00937         SetDParam(0, this->window_number);
00938         ShowQueryString(STR_STATION, STR_3030_RENAME_STATION_LOADING, MAX_LENGTH_STATION_NAME_BYTES, MAX_LENGTH_STATION_NAME_PIXELS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT);
00939         break;
00940 
00941       case SVW_TRAINS: { // Show a list of scheduled trains to this station
00942         const Station *st = GetStation(this->window_number);
00943         ShowVehicleListWindow(st->owner, VEH_TRAIN, (StationID)this->window_number);
00944         break;
00945       }
00946 
00947       case SVW_ROADVEHS: { // Show a list of scheduled road-vehicles to this station
00948         const Station *st = GetStation(this->window_number);
00949         ShowVehicleListWindow(st->owner, VEH_ROAD, (StationID)this->window_number);
00950         break;
00951       }
00952 
00953       case SVW_PLANES: { // Show a list of scheduled aircraft to this station
00954         const Station *st = GetStation(this->window_number);
00955         /* Since oilrigs have no owners, show the scheduled aircraft of local company */
00956         Owner owner = (st->owner == OWNER_NONE) ? _local_company : st->owner;
00957         ShowVehicleListWindow(owner, VEH_AIRCRAFT, (StationID)this->window_number);
00958         break;
00959       }
00960 
00961       case SVW_SHIPS: { // Show a list of scheduled ships to this station
00962         const Station *st = GetStation(this->window_number);
00963         /* Since oilrigs/bouys have no owners, show the scheduled ships of local company */
00964         Owner owner = (st->owner == OWNER_NONE) ? _local_company : st->owner;
00965         ShowVehicleListWindow(owner, VEH_SHIP, (StationID)this->window_number);
00966         break;
00967       }
00968     }
00969   }
00970 
00971   virtual void OnQueryTextFinished(char *str)
00972   {
00973     if (str == NULL) return;
00974 
00975     DoCommandP(0, this->window_number, 0, CMD_RENAME_STATION | CMD_MSG(STR_3031_CAN_T_RENAME_STATION), NULL, str);
00976   }
00977 
00978   virtual void OnResize(Point new_size, Point delta)
00979   {
00980     if (delta.x != 0) ResizeButtons(this, SVW_LOCATION, SVW_RENAME);
00981     this->vscroll.cap += delta.y / (int)this->resize.step_height;
00982   }
00983 };
00984 
00985 
00986 static const WindowDesc _station_view_desc(
00987   WDP_AUTO, WDP_AUTO, 249, 110, 249, 110,
00988   WC_STATION_VIEW, WC_NONE,
00989   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
00990   _station_view_widgets
00991 );
00992 
00998 void ShowStationViewWindow(StationID station)
00999 {
01000   AllocateWindowDescFront<StationViewWindow>(&_station_view_desc, station);
01001 }
01002 
01004 struct TileAndStation {
01005   TileIndex tile;    
01006   StationID station; 
01007 };
01008 
01009 static SmallVector<TileAndStation, 8> _deleted_stations_nearby;
01010 static SmallVector<StationID, 8> _stations_nearby_list;
01011 
01013 struct FindNearbyStationContext {
01014   TileIndex tile; 
01015   uint      w;    
01016   uint      h;    
01017 };
01018 
01025 static bool AddNearbyStation(TileIndex tile, void *user_data)
01026 {
01027   FindNearbyStationContext *ctx = (FindNearbyStationContext *)user_data;
01028 
01029   /* First check if there were deleted stations here */
01030   for (uint i = 0; i < _deleted_stations_nearby.Length(); i++) {
01031     TileAndStation *ts = _deleted_stations_nearby.Get(i);
01032     if (ts->tile == tile) {
01033       *_stations_nearby_list.Append() = _deleted_stations_nearby[i].station;
01034       _deleted_stations_nearby.Erase(ts);
01035       i--;
01036     }
01037   }
01038 
01039   /* Check if own station and if we stay within station spread */
01040   if (!IsTileType(tile, MP_STATION)) return false;
01041 
01042   StationID sid = GetStationIndex(tile);
01043   Station *st = GetStation(sid);
01044   if (st->owner != _local_company || _stations_nearby_list.Contains(sid)) return false;
01045 
01046   if (st->rect.BeforeAddRect(ctx->tile, ctx->w, ctx->h, StationRect::ADD_TEST)) {
01047     *_stations_nearby_list.Append() = sid;
01048   }
01049 
01050   return false; // We want to include *all* nearby stations
01051 }
01052 
01063 static const Station *FindStationsNearby(TileIndex tile, int w, int h, bool distant_join)
01064 {
01065   FindNearbyStationContext ctx;
01066   ctx.tile = tile;
01067   ctx.w = w;
01068   ctx.h = h;
01069 
01070   _stations_nearby_list.Clear();
01071   _deleted_stations_nearby.Clear();
01072 
01073   /* Check the inside, to return, if we sit on another station */
01074   BEGIN_TILE_LOOP(t, w, h, tile)
01075     if (t < MapSize() && IsTileType(t, MP_STATION)) return GetStationByTile(t);
01076   END_TILE_LOOP(t, w, h, tile)
01077 
01078   /* Look for deleted stations */
01079   const Station *st;
01080   FOR_ALL_STATIONS(st) {
01081     if (st->facilities == 0 && st->owner == _local_company) {
01082       /* Include only within station spread (yes, it is strictly less than) */
01083       if (max(DistanceMax(tile, st->xy), DistanceMax(TILE_ADDXY(tile, w - 1, h - 1), st->xy)) < _settings_game.station.station_spread) {
01084         TileAndStation *ts = _deleted_stations_nearby.Append();
01085         ts->tile = st->xy;
01086         ts->station = st->index;
01087 
01088         /* Add the station when it's within where we're going to build */
01089         if (IsInsideBS(TileX(st->xy), TileX(ctx.tile), ctx.w) &&
01090             IsInsideBS(TileY(st->xy), TileY(ctx.tile), ctx.h)) {
01091           AddNearbyStation(st->xy, &ctx);
01092         }
01093       }
01094     }
01095   }
01096 
01097   /* Only search tiles where we have a chance to stay within the station spread.
01098    * The complete check needs to be done in the callback as we don't know the
01099    * extent of the found station, yet. */
01100   if (distant_join && min(w, h) >= _settings_game.station.station_spread) return NULL;
01101   uint max_dist = distant_join ? _settings_game.station.station_spread - min(w, h) : 1;
01102 
01103   tile = TILE_ADD(ctx.tile, TileOffsByDir(DIR_N));
01104   CircularTileSearch(&tile, max_dist, w, h, AddNearbyStation, &ctx);
01105 
01106   return NULL;
01107 }
01108 
01109 enum JoinStationWidgets {
01110   JSW_WIDGET_CLOSEBOX = 0,
01111   JSW_WIDGET_CAPTION,
01112   JSW_PANEL,
01113   JSW_SCROLLBAR,
01114   JSW_EMPTY,
01115   JSW_RESIZEBOX,
01116 };
01117 
01118 static const Widget _select_station_widgets[] = {
01119 {   WWT_CLOSEBOX,   RESIZE_NONE, COLOUR_DARK_GREEN,     0,    10,     0,    13, STR_00C5,                        STR_018B_CLOSE_WINDOW},
01120 {    WWT_CAPTION,  RESIZE_RIGHT, COLOUR_DARK_GREEN,    11,   199,     0,    13, STR_SELECT_STATION_TO_JOIN,      STR_018C_WINDOW_TITLE_DRAG_THIS},
01121 {      WWT_PANEL,     RESIZE_RB, COLOUR_DARK_GREEN,     0,   187,    14,    79, 0x0,                             STR_NULL},
01122 {  WWT_SCROLLBAR,    RESIZE_LRB, COLOUR_DARK_GREEN,   188,   199,    14,    79, 0x0,                             STR_0190_SCROLL_BAR_SCROLLS_LIST},
01123 {      WWT_PANEL,    RESIZE_RTB, COLOUR_DARK_GREEN,     0,   187,    80,    91, 0x0,                             STR_NULL},
01124 {  WWT_RESIZEBOX,   RESIZE_LRTB, COLOUR_DARK_GREEN,   188,   199,    80,    91, 0x0,                             STR_RESIZE_BUTTON},
01125 {   WIDGETS_END},
01126 };
01127 
01128 struct SelectStationWindow : Window {
01129   CommandContainer select_station_cmd; 
01130   TileIndex tile; 
01131   int size_x;     
01132   int size_y;     
01133 
01134   SelectStationWindow(const WindowDesc *desc, CommandContainer cmd, int w, int h) :
01135     Window(desc, 0),
01136     select_station_cmd(cmd),
01137     tile(cmd.tile),
01138     size_x(w),
01139     size_y(h)
01140   {
01141     this->vscroll.cap = 6;
01142     this->resize.step_height = 10;
01143 
01144     FindStationsNearby(this->tile, this->size_x, this->size_y, true);
01145 
01146     this->FindWindowPlacementAndResize(desc);
01147   }
01148 
01149   virtual void OnPaint()
01150   {
01151     SetVScrollCount(this, _stations_nearby_list.Length() + 1);
01152 
01153     this->DrawWidgets();
01154 
01155     uint y = 17;
01156     if (this->vscroll.pos == 0) {
01157       DrawStringTruncated(3, y, STR_CREATE_SPLITTED_STATION, TC_FROMSTRING, this->widget[JSW_PANEL].right - 5);
01158       y += 10;
01159     }
01160 
01161     for (uint i = max<uint>(1, this->vscroll.pos); i <= _stations_nearby_list.Length(); ++i, y += 10) {
01162       /* Don't draw anything if it extends past the end of the window. */
01163       if (i - this->vscroll.pos >= this->vscroll.cap) break;
01164 
01165       const Station *st = GetStation(_stations_nearby_list[i - 1]);
01166       SetDParam(0, st->index);
01167       SetDParam(1, st->facilities);
01168       DrawStringTruncated(3, y, STR_3049_0, TC_FROMSTRING, this->widget[JSW_PANEL].right - 5);
01169     }
01170   }
01171 
01172   virtual void OnClick(Point pt, int widget)
01173   {
01174     if (widget != JSW_PANEL) return;
01175 
01176     uint32 st_index = (pt.y - 16) / 10 + this->vscroll.pos;
01177     bool distant_join = (st_index > 0);
01178     if (distant_join) st_index--;
01179 
01180     if (distant_join && st_index >= _stations_nearby_list.Length()) return;
01181 
01182     /* Insert station to be joined into stored command */
01183     SB(this->select_station_cmd.p2, 16, 16,
01184        (distant_join ? _stations_nearby_list[st_index] : NEW_STATION));
01185 
01186     /* Execute stored Command */
01187     DoCommandP(&this->select_station_cmd);
01188 
01189     /* Close Window; this might cause double frees! */
01190     DeleteWindowById(WC_SELECT_STATION, 0);
01191   }
01192 
01193   virtual void OnTick()
01194   {
01195     if (_thd.dirty & 2) {
01196       _thd.dirty &= ~2;
01197       this->SetDirty();
01198     }
01199   }
01200 
01201   virtual void OnResize(Point new_size, Point delta)
01202   {
01203     this->vscroll.cap = (this->widget[JSW_PANEL].bottom - this->widget[JSW_PANEL].top) / 10;
01204   }
01205 
01206   virtual void OnInvalidateData(int data)
01207   {
01208     FindStationsNearby(this->tile, this->size_x, this->size_y, true);
01209     this->SetDirty();
01210   }
01211 };
01212 
01213 static const WindowDesc _select_station_desc(
01214   WDP_AUTO, WDP_AUTO, 200, 92, 200, 182,
01215   WC_SELECT_STATION, WC_NONE,
01216   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_RESIZABLE | WDF_CONSTRUCTION,
01217   _select_station_widgets
01218 );
01219 
01220 
01228 static bool StationJoinerNeeded(CommandContainer cmd, int w, int h)
01229 {
01230   /* Only show selection if distant join is enabled in the settings */
01231   if (!_settings_game.station.distant_join_stations) return false;
01232 
01233   /* If a window is already opened and we didn't ctrl-click,
01234    * return true (i.e. just flash the old window) */
01235   Window *selection_window = FindWindowById(WC_SELECT_STATION, 0);
01236   if (selection_window != NULL) {
01237     if (!_ctrl_pressed) return true;
01238 
01239     /* Abort current distant-join and start new one */
01240     delete selection_window;
01241     UpdateTileSelection();
01242   }
01243 
01244   /* only show the popup, if we press ctrl */
01245   if (!_ctrl_pressed) return false;
01246 
01247   /* Now check if we could build there */
01248   if (CmdFailed(DoCommand(&cmd, CommandFlagsToDCFlags(GetCommandFlags(cmd.cmd))))) return false;
01249 
01250   /* Test for adjacent station or station below selection.
01251    * If adjacent-stations is disabled and we are building next to a station, do not show the selection window.
01252    * but join the other station immediatelly. */
01253   const Station *st = FindStationsNearby(cmd.tile, w, h, false);
01254   return st == NULL && (_settings_game.station.adjacent_stations || _stations_nearby_list.Length() == 0);
01255 }
01256 
01263 void ShowSelectStationIfNeeded(CommandContainer cmd, int w, int h)
01264 {
01265   if (StationJoinerNeeded(cmd, w, h)) {
01266     if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace();
01267     if (BringWindowToFrontById(WC_SELECT_STATION, 0)) return;
01268     new SelectStationWindow(&_select_station_desc, cmd, w, h);
01269   } else {
01270     DoCommandP(&cmd);
01271   }
01272 }

Generated on Mon Dec 14 21:00:03 2009 for OpenTTD by  doxygen 1.5.6