vehicle_gui.cpp

Go to the documentation of this file.
00001 /* $Id$ */
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 "company_func.h"
00015 #include "gui.h"
00016 #include "textbuf_gui.h"
00017 #include "command_func.h"
00018 #include "vehicle_gui.h"
00019 #include "vehicle_gui_base.h"
00020 #include "viewport_func.h"
00021 #include "newgrf_text.h"
00022 #include "newgrf_debug.h"
00023 #include "roadveh.h"
00024 #include "train.h"
00025 #include "aircraft.h"
00026 #include "depot_map.h"
00027 #include "group_gui.h"
00028 #include "strings_func.h"
00029 #include "window_func.h"
00030 #include "vehicle_func.h"
00031 #include "autoreplace_gui.h"
00032 #include "string_func.h"
00033 #include "widgets/dropdown_func.h"
00034 #include "timetable.h"
00035 #include "articulated_vehicles.h"
00036 #include "spritecache.h"
00037 #include "core/geometry_func.hpp"
00038 #include "company_base.h"
00039 #include "engine_func.h"
00040 #include "station_base.h"
00041 #include "tilehighlight_func.h"
00042 #include "zoom_func.h"
00043 
00044 #include "table/strings.h"
00045 
00046 Sorting _sorting;
00047 
00048 static GUIVehicleList::SortFunction VehicleNumberSorter;
00049 static GUIVehicleList::SortFunction VehicleNameSorter;
00050 static GUIVehicleList::SortFunction VehicleAgeSorter;
00051 static GUIVehicleList::SortFunction VehicleProfitThisYearSorter;
00052 static GUIVehicleList::SortFunction VehicleProfitLastYearSorter;
00053 static GUIVehicleList::SortFunction VehicleCargoSorter;
00054 static GUIVehicleList::SortFunction VehicleReliabilitySorter;
00055 static GUIVehicleList::SortFunction VehicleMaxSpeedSorter;
00056 static GUIVehicleList::SortFunction VehicleModelSorter;
00057 static GUIVehicleList::SortFunction VehicleValueSorter;
00058 static GUIVehicleList::SortFunction VehicleLengthSorter;
00059 static GUIVehicleList::SortFunction VehicleTimeToLiveSorter;
00060 static GUIVehicleList::SortFunction VehicleTimetableDelaySorter;
00061 
00062 GUIVehicleList::SortFunction * const BaseVehicleListWindow::vehicle_sorter_funcs[] = {
00063   &VehicleNumberSorter,
00064   &VehicleNameSorter,
00065   &VehicleAgeSorter,
00066   &VehicleProfitThisYearSorter,
00067   &VehicleProfitLastYearSorter,
00068   &VehicleCargoSorter,
00069   &VehicleReliabilitySorter,
00070   &VehicleMaxSpeedSorter,
00071   &VehicleModelSorter,
00072   &VehicleValueSorter,
00073   &VehicleLengthSorter,
00074   &VehicleTimeToLiveSorter,
00075   &VehicleTimetableDelaySorter,
00076 };
00077 
00078 const StringID BaseVehicleListWindow::vehicle_sorter_names[] = {
00079   STR_SORT_BY_NUMBER,
00080   STR_SORT_BY_NAME,
00081   STR_SORT_BY_AGE,
00082   STR_SORT_BY_PROFIT_THIS_YEAR,
00083   STR_SORT_BY_PROFIT_LAST_YEAR,
00084   STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
00085   STR_SORT_BY_RELIABILITY,
00086   STR_SORT_BY_MAX_SPEED,
00087   STR_SORT_BY_MODEL,
00088   STR_SORT_BY_VALUE,
00089   STR_SORT_BY_LENGTH,
00090   STR_SORT_BY_LIFE_TIME,
00091   STR_SORT_BY_TIMETABLE_DELAY,
00092   INVALID_STRING_ID
00093 };
00094 
00095 const StringID BaseVehicleListWindow::vehicle_depot_name[] = {
00096   STR_VEHICLE_LIST_SEND_TRAIN_TO_DEPOT,
00097   STR_VEHICLE_LIST_SEND_ROAD_VEHICLE_TO_DEPOT,
00098   STR_VEHICLE_LIST_SEND_SHIP_TO_DEPOT,
00099   STR_VEHICLE_LIST_SEND_AIRCRAFT_TO_HANGAR
00100 };
00101 
00102 void BaseVehicleListWindow::BuildVehicleList()
00103 {
00104   if (!this->vehicles.NeedRebuild()) return;
00105 
00106   DEBUG(misc, 3, "Building vehicle list type %d for company %d given index %d", this->vli.type, this->vli.company, this->vli.index);
00107 
00108   GenerateVehicleSortList(&this->vehicles, this->vli);
00109 
00110   uint unitnumber = 0;
00111   for (const Vehicle **v = this->vehicles.Begin(); v != this->vehicles.End(); v++) {
00112     unitnumber = max<uint>(unitnumber, (*v)->unitnumber);
00113   }
00114 
00115   /* Because 111 is much less wide than e.g. 999 we use the
00116    * wider numbers to determine the width instead of just
00117    * the random number that it seems to be. */
00118   if (unitnumber >= 1000) {
00119     this->unitnumber_digits = 4;
00120   } else if (unitnumber >= 100) {
00121     this->unitnumber_digits = 3;
00122   } else {
00123     this->unitnumber_digits = 2;
00124   }
00125 
00126   this->vehicles.RebuildDone();
00127   this->vscroll->SetCount(this->vehicles.Length());
00128 }
00129 
00136 Dimension BaseVehicleListWindow::GetActionDropdownSize(bool show_autoreplace, bool show_group)
00137 {
00138   Dimension d = {0, 0};
00139 
00140   if (show_autoreplace) d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_REPLACE_VEHICLES));
00141   d = maxdim(d, GetStringBoundingBox(STR_VEHICLE_LIST_SEND_FOR_SERVICING));
00142   d = maxdim(d, GetStringBoundingBox(this->vehicle_depot_name[this->vli.vtype]));
00143 
00144   if (show_group) {
00145     d = maxdim(d, GetStringBoundingBox(STR_GROUP_ADD_SHARED_VEHICLE));
00146     d = maxdim(d, GetStringBoundingBox(STR_GROUP_REMOVE_ALL_VEHICLES));
00147   }
00148 
00149   return d;
00150 }
00151 
00158 DropDownList *BaseVehicleListWindow::BuildActionDropdownList(bool show_autoreplace, bool show_group)
00159 {
00160   DropDownList *list = new DropDownList();
00161 
00162   if (show_autoreplace) list->push_back(new DropDownListStringItem(STR_VEHICLE_LIST_REPLACE_VEHICLES, ADI_REPLACE, false));
00163   list->push_back(new DropDownListStringItem(STR_VEHICLE_LIST_SEND_FOR_SERVICING, ADI_SERVICE, false));
00164   list->push_back(new DropDownListStringItem(this->vehicle_depot_name[this->vli.vtype], ADI_DEPOT, false));
00165 
00166   if (show_group) {
00167     list->push_back(new DropDownListStringItem(STR_GROUP_ADD_SHARED_VEHICLE, ADI_ADD_SHARED, false));
00168     list->push_back(new DropDownListStringItem(STR_GROUP_REMOVE_ALL_VEHICLES, ADI_REMOVE_ALL, false));
00169   }
00170 
00171   return list;
00172 }
00173 
00174 /* cached values for VehicleNameSorter to spare many GetString() calls */
00175 static const Vehicle *_last_vehicle[2] = { NULL, NULL };
00176 
00177 void BaseVehicleListWindow::SortVehicleList()
00178 {
00179   if (this->vehicles.Sort()) return;
00180 
00181   /* invalidate cached values for name sorter - vehicle names could change */
00182   _last_vehicle[0] = _last_vehicle[1] = NULL;
00183 }
00184 
00185 void DepotSortList(VehicleList *list)
00186 {
00187   if (list->Length() < 2) return;
00188   QSortT(list->Begin(), list->Length(), &VehicleNumberSorter);
00189 }
00190 
00192 static void DrawVehicleProfitButton(const Vehicle *v, int x, int y)
00193 {
00194   SpriteID spr;
00195 
00196   /* draw profit-based coloured icons */
00197   if (v->age <= VEHICLE_PROFIT_MIN_AGE) {
00198     spr = SPR_PROFIT_NA;
00199   } else if (v->GetDisplayProfitLastYear() < 0) {
00200     spr = SPR_PROFIT_NEGATIVE;
00201   } else if (v->GetDisplayProfitLastYear() < VEHICLE_PROFIT_THRESHOLD) {
00202     spr = SPR_PROFIT_SOME;
00203   } else {
00204     spr = SPR_PROFIT_LOT;
00205   }
00206   DrawSprite(spr, PAL_NONE, x, y);
00207 }
00208 
00210 static const uint MAX_REFIT_CYCLE = 256;
00211 
00220 byte GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for, CargoID dest_cargo_type)
00221 {
00222   const Engine *e_from = v_from->GetEngine();
00223   const Engine *e_for  = v_for->GetEngine();
00224 
00225   /* If one them doesn't carry cargo, there's no need to find a sub type */
00226   if (!e_from->CanCarryCargo() || !e_for->CanCarryCargo()) return 0;
00227 
00228   if (!HasBit(e_from->info.callback_mask, CBM_VEHICLE_CARGO_SUFFIX) ||
00229       !HasBit(e_for->info.callback_mask,  CBM_VEHICLE_CARGO_SUFFIX)) {
00230     /* One of the engines doesn't have cargo suffixes, i.e. sub types. */
00231     return 0;
00232   }
00233 
00234   if (dest_cargo_type == INVALID_CARGO) dest_cargo_type = v_from->cargo_type;
00235 
00236   /* It has to be possible for v_for to carry the cargo of v_from. */
00237   if (!HasBit(e_for->info.refit_mask, dest_cargo_type)) return 0;
00238 
00239   StringID expected_string = GetCargoSubtypeText(v_from);
00240 
00241   CargoID old_cargo_type = v_for->cargo_type;
00242   byte old_cargo_subtype = v_for->cargo_subtype;
00243   byte ret_refit_cyc = 0;
00244 
00245   /* Set the 'destination' cargo */
00246   v_for->cargo_type = dest_cargo_type;
00247 
00248   /* Cycle through the refits */
00249   for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
00250     v_for->cargo_subtype = refit_cyc;
00251 
00252     /* Make sure we don't pick up anything cached. */
00253     v_for->First()->InvalidateNewGRFCache();
00254     v_for->InvalidateNewGRFCache();
00255     uint16 callback = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v_for->engine_type, v_for);
00256 
00257     if (callback != CALLBACK_FAILED) {
00258       if (callback > 0x400) ErrorUnknownCallbackResult(v_for->GetGRFID(), CBID_VEHICLE_CARGO_SUFFIX, callback);
00259       if (callback >= 0x400 || (v_for->GetGRF()->grf_version < 8 && callback == 0xFF)) callback = CALLBACK_FAILED;
00260     }
00261     if (callback == CALLBACK_FAILED) break;
00262 
00263     if (GetCargoSubtypeText(v_for) != expected_string) continue;
00264 
00265     /* We found something matching. */
00266     ret_refit_cyc = refit_cyc;
00267     break;
00268   }
00269 
00270   /* Reset the vehicle's cargo type */
00271   v_for->cargo_type    = old_cargo_type;
00272   v_for->cargo_subtype = old_cargo_subtype;
00273 
00274   /* Make sure we don't taint the vehicle. */
00275   v_for->First()->InvalidateNewGRFCache();
00276   v_for->InvalidateNewGRFCache();
00277 
00278   return ret_refit_cyc;
00279 }
00280 
00282 struct RefitOption {
00283   CargoID cargo;    
00284   byte subtype;     
00285   uint16 value;     
00286   const Engine *engine;  
00287 
00293   FORCEINLINE bool operator != (const RefitOption &other) const
00294   {
00295     return other.cargo != this->cargo || other.value != this->value;
00296   }
00297 
00303   FORCEINLINE bool operator == (const RefitOption &other) const
00304   {
00305     return other.cargo == this->cargo && other.value == this->value;
00306   }
00307 };
00308 
00309 typedef SmallVector<RefitOption, 32> SubtypeList; 
00310 
00320 static void DrawVehicleRefitWindow(const SubtypeList list[NUM_CARGO], int sel, uint pos, uint rows, uint delta, const Rect &r)
00321 {
00322   uint y = r.top + WD_MATRIX_TOP;
00323   uint current = 0;
00324 
00325   /* Draw the list of subtypes for each cargo, and find the selected refit option (by its position). */
00326   for (uint i = 0; current < pos + rows && i < NUM_CARGO; i++) {
00327     for (uint j = 0; current < pos + rows && j < list[i].Length(); j++) {
00328       /* Refit options with a position smaller than pos don't have to be drawn. */
00329       if (current < pos) {
00330         current++;
00331         continue;
00332       }
00333 
00334       TextColour colour = (sel == (int)current) ? TC_WHITE : TC_BLACK;
00335       const RefitOption refit = list[i][j];
00336       /* Get the cargo name. */
00337       SetDParam(0, CargoSpec::Get(refit.cargo)->name);
00338       /* If the callback succeeded, draw the cargo suffix. */
00339       if (refit.value != CALLBACK_FAILED && refit.value < 0x400) {
00340         SetDParam(1, GetGRFStringID(refit.engine->GetGRFID(), 0xD000 + refit.value));
00341         DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y, STR_JUST_STRING_SPACE_STRING, colour);
00342       } else {
00343         DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y, STR_JUST_STRING, colour);
00344       }
00345 
00346       y += delta;
00347       current++;
00348     }
00349   }
00350 }
00351 
00353 enum VehicleRefitWidgets {
00354   VRW_CAPTION,
00355   VRW_VEHICLE_PANEL_DISPLAY,
00356   VRW_SHOW_HSCROLLBAR,
00357   VRW_HSCROLLBAR,
00358   VRW_SELECTHEADER,
00359   VRW_MATRIX,
00360   VRW_SCROLLBAR,
00361   VRW_INFOPANEL,
00362   VRW_REFITBUTTON,
00363 };
00364 
00366 struct RefitWindow : public Window {
00367   int sel;                     
00368   RefitOption *cargo;          
00369   SubtypeList list[NUM_CARGO]; 
00370   VehicleOrderID order;        
00371   uint information_width;      
00372   Scrollbar *vscroll;          
00373   Scrollbar *hscroll;          
00374   int vehicle_width;           
00375   int sprite_left;             
00376   int sprite_right;            
00377   uint vehicle_margin;         
00378   int click_x;                 
00379   VehicleID selected_vehicle;  
00380   uint8 num_vehicles;          
00381   bool auto_refit;             
00382 
00386   void BuildRefitList()
00387   {
00388     for (uint i = 0; i < NUM_CARGO; i++) this->list[i].Clear();
00389     Vehicle *v = Vehicle::Get(this->window_number);
00390 
00391     /* Check only the selected vehicles. */
00392     VehicleSet vehicles_to_refit;
00393     GetVehicleSet(vehicles_to_refit, Vehicle::Get(this->selected_vehicle), this->num_vehicles);
00394 
00395     do {
00396       if (v->type == VEH_TRAIN && !vehicles_to_refit.Contains(v->index)) continue;
00397       const Engine *e = v->GetEngine();
00398       uint32 cmask = e->info.refit_mask;
00399       byte callback_mask = e->info.callback_mask;
00400 
00401       /* Skip this engine if it does not carry anything */
00402       if (!e->CanCarryCargo()) continue;
00403       /* Skip this engine if we build the list for auto-refitting and engine doesn't allow it. */
00404       if (this->auto_refit && !HasBit(e->info.misc_flags, EF_AUTO_REFIT)) continue;
00405 
00406       /* Loop through all cargos in the refit mask */
00407       int current_index = 0;
00408       const CargoSpec *cs;
00409       FOR_ALL_SORTED_CARGOSPECS(cs) {
00410         CargoID cid = cs->Index();
00411         /* Skip cargo type if it's not listed */
00412         if (!HasBit(cmask, cid)) {
00413           current_index++;
00414           continue;
00415         }
00416 
00417         /* Check the vehicle's callback mask for cargo suffixes */
00418         if (HasBit(callback_mask, CBM_VEHICLE_CARGO_SUFFIX)) {
00419           /* Make a note of the original cargo type. It has to be
00420            * changed to test the cargo & subtype... */
00421           CargoID temp_cargo = v->cargo_type;
00422           byte temp_subtype  = v->cargo_subtype;
00423 
00424           v->cargo_type = cid;
00425 
00426           for (uint refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
00427             v->cargo_subtype = refit_cyc;
00428 
00429             /* Make sure we don't pick up anything cached. */
00430             v->First()->InvalidateNewGRFCache();
00431             v->InvalidateNewGRFCache();
00432             uint16 callback = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v->engine_type, v);
00433 
00434             if (callback != CALLBACK_FAILED) {
00435               if (callback > 0x400) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_CARGO_SUFFIX, callback);
00436               if (callback >= 0x400 || (v->GetGRF()->grf_version < 8 && callback == 0xFF)) callback = CALLBACK_FAILED;
00437             }
00438             if (refit_cyc != 0 && callback == CALLBACK_FAILED) break;
00439 
00440             RefitOption option;
00441             option.cargo   = cid;
00442             option.subtype = refit_cyc;
00443             option.value   = callback;
00444             option.engine  = v->GetEngine();
00445             this->list[current_index].Include(option);
00446           }
00447 
00448           /* Reset the vehicle's cargo type */
00449           v->cargo_type    = temp_cargo;
00450           v->cargo_subtype = temp_subtype;
00451 
00452           /* And make sure we haven't tainted the cache */
00453           v->First()->InvalidateNewGRFCache();
00454           v->InvalidateNewGRFCache();
00455         } else {
00456           /* No cargo suffix callback -- use no subtype */
00457           RefitOption option;
00458           option.cargo   = cid;
00459           option.subtype = 0;
00460           option.value   = CALLBACK_FAILED;
00461           option.engine  = NULL;
00462           this->list[current_index].Include(option);
00463         }
00464         current_index++;
00465       }
00466     } while (v->IsGroundVehicle() && (v = v->Next()) != NULL);
00467 
00468     int scroll_size = 0;
00469     for (uint i = 0; i < NUM_CARGO; i++) {
00470       scroll_size += (this->list[i].Length());
00471     }
00472     this->vscroll->SetCount(scroll_size);
00473   }
00474 
00479   RefitOption *GetRefitOption()
00480   {
00481     if (this->sel < 0) return NULL;
00482     int subtype = 0;
00483     for (uint i = 0; subtype <= this->sel && i < NUM_CARGO; i++) {
00484       for (uint j = 0; subtype <= this->sel && j < this->list[i].Length(); j++) {
00485         if (subtype == this->sel) {
00486           return &this->list[i][j];
00487         }
00488         subtype++;
00489       }
00490     }
00491 
00492     return NULL;
00493   }
00494 
00495   RefitWindow(const WindowDesc *desc, const Vehicle *v, VehicleOrderID order, bool auto_refit) : Window()
00496   {
00497     this->sel = -1;
00498     this->auto_refit = auto_refit;
00499     this->CreateNestedTree(desc);
00500 
00501     this->vscroll = this->GetScrollbar(VRW_SCROLLBAR);
00502     this->hscroll = (v->IsGroundVehicle() ? this->GetScrollbar(VRW_HSCROLLBAR) : NULL);
00503     this->GetWidget<NWidgetCore>(VRW_SELECTHEADER)->tool_tip = STR_REFIT_TRAIN_LIST_TOOLTIP + v->type;
00504     this->GetWidget<NWidgetCore>(VRW_MATRIX)->tool_tip       = STR_REFIT_TRAIN_LIST_TOOLTIP + v->type;
00505     NWidgetCore *nwi = this->GetWidget<NWidgetCore>(VRW_REFITBUTTON);
00506     nwi->widget_data = STR_REFIT_TRAIN_REFIT_BUTTON + v->type;
00507     nwi->tool_tip    = STR_REFIT_TRAIN_REFIT_TOOLTIP + v->type;
00508     this->GetWidget<NWidgetStacked>(VRW_SHOW_HSCROLLBAR)->SetDisplayedPlane(v->IsGroundVehicle() ? 0 : SZSP_HORIZONTAL);
00509     this->GetWidget<NWidgetCore>(VRW_VEHICLE_PANEL_DISPLAY)->tool_tip = (v->type == VEH_TRAIN) ? STR_REFIT_SELECT_VEHICLES_TOOLTIP : STR_NULL;
00510 
00511     this->FinishInitNested(desc, v->index);
00512     this->owner = v->owner;
00513 
00514     this->order = order;
00515     this->SetWidgetDisabledState(VRW_REFITBUTTON, this->sel == -1);
00516   }
00517 
00518   virtual void OnInit()
00519   {
00520     if (this->cargo != NULL) {
00521       /* Store the RefitOption currently in use. */
00522       RefitOption current_refit_option = *(this->cargo);
00523 
00524       /* Rebuild the refit list */
00525       this->BuildRefitList();
00526       this->sel = -1;
00527       this->cargo = NULL;
00528       int current = 0;
00529       for (uint i = 0; this->cargo == NULL && i < NUM_CARGO; i++) {
00530         for (uint j = 0; j < list[i].Length(); j++) {
00531           if (list[i][j] == current_refit_option) {
00532             this->sel = current;
00533             this->cargo = &list[i][j];
00534             this->vscroll->ScrollTowards(current);
00535             break;
00536           }
00537           current++;
00538         }
00539       }
00540 
00541       this->SetWidgetDisabledState(VRW_REFITBUTTON, this->sel == -1);
00542       /* If the selected refit option was not found, scroll the window to the initial position. */
00543       if (this->sel == -1) this->vscroll->ScrollTowards(0);
00544     } else {
00545       /* Rebuild the refit list */
00546       this->OnInvalidateData(0);
00547     }
00548   }
00549 
00550   virtual void OnPaint()
00551   {
00552     /* Determine amount of items for scroller. */
00553     if (this->hscroll != NULL) this->hscroll->SetCount(this->vehicle_width);
00554 
00555     /* Calculate sprite position. */
00556     NWidgetCore *vehicle_panel_display = this->GetWidget<NWidgetCore>(VRW_VEHICLE_PANEL_DISPLAY);
00557     int sprite_width = max(0, ((int)vehicle_panel_display->current_x - this->vehicle_width) / 2);
00558     this->sprite_left = vehicle_panel_display->pos_x;
00559     this->sprite_right = vehicle_panel_display->pos_x + vehicle_panel_display->current_x - 1;
00560     if (_current_text_dir == TD_RTL) {
00561       this->sprite_right -= sprite_width;
00562       this->vehicle_margin = vehicle_panel_display->current_x - sprite_right;
00563     } else {
00564       this->sprite_left += sprite_width;
00565       this->vehicle_margin = sprite_left;
00566     }
00567 
00568     this->DrawWidgets();
00569   }
00570 
00571   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00572   {
00573     switch (widget) {
00574       case VRW_MATRIX:
00575         resize->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
00576         size->height = resize->height * 8;
00577         break;
00578 
00579       case VRW_VEHICLE_PANEL_DISPLAY:
00580         size->height = GetVehicleHeight(Vehicle::Get(this->window_number)->type);
00581         break;
00582 
00583       case VRW_INFOPANEL:
00584         size->width = WD_FRAMERECT_LEFT + this->information_width + WD_FRAMERECT_RIGHT;
00585         break;
00586     }
00587   }
00588 
00589   virtual void SetStringParameters(int widget) const
00590   {
00591     if (widget == VRW_CAPTION) SetDParam(0, Vehicle::Get(this->window_number)->index);
00592   }
00593 
00600   StringID GetCapacityString(RefitOption *option) const
00601   {
00602     assert(_current_company == _local_company);
00603     Vehicle *v = Vehicle::Get(this->window_number);
00604     CommandCost cost = DoCommand(v->tile, this->selected_vehicle, option->cargo | (int)this->auto_refit << 6 | option->subtype << 8 |
00605         this->num_vehicles << 16, DC_QUERY_COST, GetCmdRefitVeh(v->type));
00606 
00607     if (cost.Failed()) return INVALID_STRING_ID;
00608 
00609     SetDParam(0, option->cargo);
00610     SetDParam(1, _returned_refit_capacity);
00611 
00612     if (_returned_mail_refit_capacity > 0) {
00613       SetDParam(2, CT_MAIL);
00614       SetDParam(3, _returned_mail_refit_capacity);
00615       SetDParam(4, cost.GetCost());
00616       return STR_REFIT_NEW_CAPACITY_COST_OF_AIRCRAFT_REFIT;
00617     } else {
00618       SetDParam(2, cost.GetCost());
00619       return STR_REFIT_NEW_CAPACITY_COST_OF_REFIT;
00620     }
00621   }
00622 
00623   virtual void DrawWidget(const Rect &r, int widget) const
00624   {
00625     switch (widget) {
00626       case VRW_VEHICLE_PANEL_DISPLAY: {
00627         Vehicle *v = Vehicle::Get(this->window_number);
00628         DrawVehicleImage(v, this->sprite_left + WD_FRAMERECT_LEFT, this->sprite_right - WD_FRAMERECT_RIGHT,
00629           r.top + WD_FRAMERECT_TOP, INVALID_VEHICLE, EIT_IN_DETAILS, this->hscroll != NULL ? this->hscroll->GetPosition() : 0);
00630 
00631         /* Highlight selected vehicles. */
00632         if (this->order != INVALID_VEH_ORDER_ID) break;
00633         int x = 0;
00634         switch (v->type) {
00635           case VEH_TRAIN: {
00636             VehicleSet vehicles_to_refit;
00637             GetVehicleSet(vehicles_to_refit, Vehicle::Get(this->selected_vehicle), this->num_vehicles);
00638 
00639             int left = INT32_MIN;
00640             int width = 0;
00641 
00642             for (Train *u = Train::From(v); u != NULL; u = u->Next()) {
00643               /* Start checking. */
00644               if (vehicles_to_refit.Contains(u->index) && left == INT32_MIN) {
00645                 left = x - this->hscroll->GetPosition() + r.left + this->vehicle_margin;
00646                 width = 0;
00647               }
00648 
00649               /* Draw a selection. */
00650               if ((!vehicles_to_refit.Contains(u->index) || u->Next() == NULL) && left != INT32_MIN) {
00651                 if (u->Next() == NULL && vehicles_to_refit.Contains(u->index)) {
00652                   int current_width = u->GetDisplayImageWidth();
00653                   width += current_width;
00654                   x += current_width;
00655                 }
00656 
00657                 int right = Clamp(left + width, 0, r.right);
00658                 left = max(0, left);
00659 
00660                 if (_current_text_dir == TD_RTL) {
00661                   right = this->GetWidget<NWidgetCore>(VRW_VEHICLE_PANEL_DISPLAY)->current_x - left;
00662                   left = right - width;
00663                 }
00664 
00665                 if (left != right) {
00666                   DrawFrameRect(left, r.top + WD_FRAMERECT_TOP, right, r.top + WD_FRAMERECT_TOP + 13, COLOUR_WHITE, FR_BORDERONLY);
00667                 }
00668 
00669                 left = INT32_MIN;
00670               }
00671 
00672               int current_width = u->GetDisplayImageWidth();
00673               width += current_width;
00674               x += current_width;
00675             }
00676             break;
00677           }
00678 
00679           default: break;
00680         }
00681         break;
00682       }
00683 
00684       case VRW_MATRIX:
00685         DrawVehicleRefitWindow(this->list, this->sel, this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->resize.step_height, r);
00686         break;
00687 
00688       case VRW_INFOPANEL:
00689         if (this->cargo != NULL) {
00690           StringID string = this->GetCapacityString(this->cargo);
00691           if (string != INVALID_STRING_ID) {
00692             DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT,
00693                 r.top + WD_FRAMERECT_TOP, r.bottom - WD_FRAMERECT_BOTTOM, string);
00694           }
00695         }
00696         break;
00697     }
00698   }
00699 
00705   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
00706   {
00707     switch (data) {
00708       case -666: // Autoreplace replaced the vehicle; selected_vehicle became invalid.
00709       case 0: { // The consist has changed; rebuild the entire list.
00710         /* Clear the selection. */
00711         Vehicle *v = Vehicle::Get(this->window_number);
00712         this->selected_vehicle = v->index;
00713         this->num_vehicles = UINT8_MAX;
00714         /* FALL THROUGH */
00715       }
00716 
00717       case 2: { // The vehicle selection has changed; rebuild the entire list.
00718         if (!gui_scope) break;
00719         this->BuildRefitList();
00720 
00721         /* The vehicle width has changed too. */
00722         this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
00723         uint max_width = 0;
00724 
00725         /* Check the width of all cargo information strings. */
00726         for (uint i = 0; i < NUM_CARGO; i++) {
00727           for (uint j = 0; j < this->list[i].Length(); j++) {
00728             StringID string = this->GetCapacityString(&list[i][j]);
00729             if (string != INVALID_STRING_ID) {
00730               Dimension dim = GetStringBoundingBox(string);
00731               max_width = max(dim.width, max_width);
00732             }
00733           }
00734         }
00735 
00736         if (this->information_width < max_width) {
00737           this->information_width = max_width;
00738           this->ReInit();
00739         }
00740         /* FALL THROUGH */
00741       }
00742 
00743       case 1: // A new cargo has been selected.
00744         if (!gui_scope) break;
00745         this->cargo = GetRefitOption();
00746         break;
00747     }
00748   }
00749 
00750   int GetClickPosition(int click_x)
00751   {
00752     const NWidgetCore *matrix_widget = this->GetWidget<NWidgetCore>(VRW_VEHICLE_PANEL_DISPLAY);
00753     if (_current_text_dir == TD_RTL) click_x = matrix_widget->current_x - click_x;
00754     click_x -= this->vehicle_margin;
00755     if (this->hscroll != NULL) click_x += this->hscroll->GetPosition();
00756 
00757     return click_x;
00758   }
00759 
00760   void SetSelectedVehicles(int drag_x)
00761   {
00762     drag_x = GetClickPosition(drag_x);
00763 
00764     int left_x  = min(this->click_x, drag_x);
00765     int right_x = max(this->click_x, drag_x);
00766     this->num_vehicles = 0;
00767 
00768     Vehicle *v = Vehicle::Get(this->window_number);
00769     /* Find the vehicle part that was clicked. */
00770     switch (v->type) {
00771       case VEH_TRAIN: {
00772         /* Don't select anything if we are not clicking in the vehicle. */
00773         if (left_x >= 0) {
00774           const Train *u = Train::From(v);
00775           bool start_counting = false;
00776           for (; u != NULL; u = u->Next()) {
00777             int current_width = u->GetDisplayImageWidth();
00778             left_x  -= current_width;
00779             right_x -= current_width;
00780 
00781             if (left_x < 0 && !start_counting) {
00782               this->selected_vehicle = u->index;
00783               start_counting = true;
00784 
00785               /* Count the first vehicle, even if articulated part */
00786               this->num_vehicles++;
00787             } else if (start_counting && !u->IsArticulatedPart()) {
00788               /* Do not count articulated parts */
00789               this->num_vehicles++;
00790             }
00791 
00792             if (right_x < 0) break;
00793           }
00794         }
00795 
00796         /* If the selection is not correct, clear it. */
00797         if (this->num_vehicles != 0) {
00798           if (_ctrl_pressed) this->num_vehicles = UINT8_MAX;
00799           break;
00800         }
00801         /* FALL THROUGH */
00802       }
00803 
00804       default:
00805         /* Clear the selection. */
00806         this->selected_vehicle = v->index;
00807         this->num_vehicles = UINT8_MAX;
00808         break;
00809     }
00810   }
00811 
00812   virtual void OnClick(Point pt, int widget, int click_count)
00813   {
00814     switch (widget) {
00815       case VRW_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
00816         if (this->order != INVALID_VEH_ORDER_ID) break;
00817         NWidgetBase *nwi = this->GetWidget<NWidgetBase>(VRW_VEHICLE_PANEL_DISPLAY);
00818         this->click_x = GetClickPosition(pt.x - nwi->pos_x);
00819         this->SetSelectedVehicles(pt.x - nwi->pos_x);
00820         this->SetWidgetDirty(VRW_VEHICLE_PANEL_DISPLAY);
00821         if (!_ctrl_pressed) {
00822           SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
00823         } else {
00824           /* The vehicle selection has changed. */
00825           this->InvalidateData(2);
00826         }
00827         break;
00828       }
00829 
00830       case VRW_MATRIX: { // listbox
00831         this->sel = this->vscroll->GetScrolledRowFromWidget(pt.y, this, VRW_MATRIX);
00832         if (this->sel == INT_MAX) this->sel = -1;
00833         this->SetWidgetDisabledState(VRW_REFITBUTTON, this->sel == -1);
00834         this->InvalidateData(1);
00835 
00836         if (click_count == 1) break;
00837         /* FALL THROUGH */
00838       }
00839 
00840       case VRW_REFITBUTTON: // refit button
00841         if (this->cargo != NULL) {
00842           const Vehicle *v = Vehicle::Get(this->window_number);
00843 
00844           if (this->order == INVALID_VEH_ORDER_ID) {
00845             bool delete_window = this->selected_vehicle == v->index && this->num_vehicles == UINT8_MAX;
00846             if (DoCommandP(v->tile, this->selected_vehicle, this->cargo->cargo | this->cargo->subtype << 8 | this->num_vehicles << 16, GetCmdRefitVeh(v)) && delete_window) delete this;
00847           } else {
00848             if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8 | this->order << 16, CMD_ORDER_REFIT)) delete this;
00849           }
00850         }
00851         break;
00852     }
00853   }
00854 
00855   virtual void OnMouseDrag(Point pt, int widget)
00856   {
00857     switch (widget) {
00858       case VRW_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
00859         if (this->order != INVALID_VEH_ORDER_ID) break;
00860         NWidgetBase *nwi = this->GetWidget<NWidgetBase>(VRW_VEHICLE_PANEL_DISPLAY);
00861         this->SetSelectedVehicles(pt.x - nwi->pos_x);
00862         this->SetWidgetDirty(VRW_VEHICLE_PANEL_DISPLAY);
00863         break;
00864       }
00865     }
00866   }
00867 
00868   virtual void OnDragDrop(Point pt, int widget)
00869   {
00870     switch (widget) {
00871       case VRW_VEHICLE_PANEL_DISPLAY: { // Vehicle image.
00872         if (this->order != INVALID_VEH_ORDER_ID) break;
00873         NWidgetBase *nwi = this->GetWidget<NWidgetBase>(VRW_VEHICLE_PANEL_DISPLAY);
00874         this->SetSelectedVehicles(pt.x - nwi->pos_x);
00875         this->InvalidateData(2);
00876         break;
00877       }
00878     }
00879   }
00880 
00881   virtual void OnResize()
00882   {
00883     this->vehicle_width = GetVehicleWidth(Vehicle::Get(this->window_number), EIT_IN_DETAILS);
00884     this->vscroll->SetCapacityFromWidget(this, VRW_MATRIX);
00885     if (this->hscroll != NULL) this->hscroll->SetCapacityFromWidget(this, VRW_VEHICLE_PANEL_DISPLAY);
00886     this->GetWidget<NWidgetCore>(VRW_MATRIX)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
00887   }
00888 };
00889 
00890 static const NWidgetPart _nested_vehicle_refit_widgets[] = {
00891   NWidget(NWID_HORIZONTAL),
00892     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00893     NWidget(WWT_CAPTION, COLOUR_GREY, VRW_CAPTION), SetDataTip(STR_REFIT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00894   EndContainer(),
00895   /* Vehicle display + scrollbar. */
00896   NWidget(NWID_VERTICAL),
00897     NWidget(WWT_PANEL, COLOUR_GREY, VRW_VEHICLE_PANEL_DISPLAY), SetMinimalSize(228, 14), SetResize(1, 0), SetScrollbar(VRW_HSCROLLBAR), EndContainer(),
00898     NWidget(NWID_SELECTION, INVALID_COLOUR, VRW_SHOW_HSCROLLBAR),
00899       NWidget(NWID_HSCROLLBAR, COLOUR_GREY, VRW_HSCROLLBAR),
00900     EndContainer(),
00901   EndContainer(),
00902   NWidget(WWT_TEXTBTN, COLOUR_GREY, VRW_SELECTHEADER), SetDataTip(STR_REFIT_TITLE, STR_NULL), SetResize(1, 0),
00903   /* Matrix + scrollbar. */
00904   NWidget(NWID_HORIZONTAL),
00905     NWidget(WWT_MATRIX, COLOUR_GREY, VRW_MATRIX), SetMinimalSize(228, 112), SetResize(1, 14), SetFill(1, 1), SetDataTip(0x801, STR_NULL), SetScrollbar(VRW_SCROLLBAR),
00906     NWidget(NWID_VSCROLLBAR, COLOUR_GREY, VRW_SCROLLBAR),
00907   EndContainer(),
00908   NWidget(WWT_PANEL, COLOUR_GREY, VRW_INFOPANEL), SetMinimalTextLines(2, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM), SetResize(1, 0), EndContainer(),
00909   NWidget(NWID_HORIZONTAL),
00910     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VRW_REFITBUTTON), SetFill(1, 0), SetResize(1, 0),
00911     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00912   EndContainer(),
00913 };
00914 
00915 static const WindowDesc _vehicle_refit_desc(
00916   WDP_AUTO, 240, 174,
00917   WC_VEHICLE_REFIT, WC_VEHICLE_VIEW,
00918   WDF_UNCLICK_BUTTONS | WDF_CONSTRUCTION,
00919   _nested_vehicle_refit_widgets, lengthof(_nested_vehicle_refit_widgets)
00920 );
00921 
00929 void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent, bool auto_refit)
00930 {
00931   DeleteWindowById(WC_VEHICLE_REFIT, v->index);
00932   RefitWindow *w = new RefitWindow(&_vehicle_refit_desc, v, order, auto_refit);
00933   w->parent = parent;
00934 }
00935 
00937 uint ShowRefitOptionsList(int left, int right, int y, EngineID engine)
00938 {
00939   /* List of cargo types of this engine */
00940   uint32 cmask = GetUnionOfArticulatedRefitMasks(engine, false);
00941   /* List of cargo types available in this climate */
00942   uint32 lmask = _cargo_mask;
00943   char string[512];
00944   char *b = string;
00945 
00946   /* Draw nothing if the engine is not refittable */
00947   if (HasAtMostOneBit(cmask)) return y;
00948 
00949   b = InlineString(b, STR_PURCHASE_INFO_REFITTABLE_TO);
00950 
00951   if (cmask == lmask) {
00952     /* Engine can be refitted to all types in this climate */
00953     b = InlineString(b, STR_PURCHASE_INFO_ALL_TYPES);
00954   } else {
00955     /* Check if we are able to refit to more cargo types and unable to. If
00956      * so, invert the cargo types to list those that we can't refit to. */
00957     if (CountBits(cmask ^ lmask) < CountBits(cmask) && CountBits(cmask ^ lmask) <= 7) {
00958       cmask ^= lmask;
00959       b = InlineString(b, STR_PURCHASE_INFO_ALL_BUT);
00960     }
00961 
00962     bool first = true;
00963 
00964     /* Add each cargo type to the list */
00965     const CargoSpec *cs;
00966     FOR_ALL_SORTED_CARGOSPECS(cs) {
00967       if (!HasBit(cmask, cs->Index())) continue;
00968 
00969       if (b >= lastof(string) - (2 + 2 * 4)) break; // ", " and two calls to Utf8Encode()
00970 
00971       if (!first) b = strecpy(b, ", ", lastof(string));
00972       first = false;
00973 
00974       b = InlineString(b, cs->name);
00975     }
00976   }
00977 
00978   /* Terminate and display the completed string */
00979   *b = '\0';
00980 
00981   /* Make sure we detect any buffer overflow */
00982   assert(b < endof(string));
00983 
00984   SetDParamStr(0, string);
00985   return DrawStringMultiLine(left, right, y, INT32_MAX, STR_JUST_RAW_STRING);
00986 }
00987 
00989 StringID GetCargoSubtypeText(const Vehicle *v)
00990 {
00991   if (HasBit(EngInfo(v->engine_type)->callback_mask, CBM_VEHICLE_CARGO_SUFFIX)) {
00992     uint16 cb = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v->engine_type, v);
00993     if (cb != CALLBACK_FAILED) {
00994       if (cb > 0x400) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_CARGO_SUFFIX, cb);
00995       if (cb >= 0x400 || (v->GetGRF()->grf_version < 8 && cb == 0xFF)) cb = CALLBACK_FAILED;
00996     }
00997     if (cb != CALLBACK_FAILED) {
00998       return GetGRFStringID(v->GetGRFID(), 0xD000 + cb);
00999     }
01000   }
01001   return STR_EMPTY;
01002 }
01003 
01005 static int CDECL VehicleNumberSorter(const Vehicle * const *a, const Vehicle * const *b)
01006 {
01007   return (*a)->unitnumber - (*b)->unitnumber;
01008 }
01009 
01011 static int CDECL VehicleNameSorter(const Vehicle * const *a, const Vehicle * const *b)
01012 {
01013   static char last_name[2][64];
01014 
01015   if (*a != _last_vehicle[0]) {
01016     _last_vehicle[0] = *a;
01017     SetDParam(0, (*a)->index);
01018     GetString(last_name[0], STR_VEHICLE_NAME, lastof(last_name[0]));
01019   }
01020 
01021   if (*b != _last_vehicle[1]) {
01022     _last_vehicle[1] = *b;
01023     SetDParam(0, (*b)->index);
01024     GetString(last_name[1], STR_VEHICLE_NAME, lastof(last_name[1]));
01025   }
01026 
01027   int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
01028   return (r != 0) ? r : VehicleNumberSorter(a, b);
01029 }
01030 
01032 static int CDECL VehicleAgeSorter(const Vehicle * const *a, const Vehicle * const *b)
01033 {
01034   int r = (*a)->age - (*b)->age;
01035   return (r != 0) ? r : VehicleNumberSorter(a, b);
01036 }
01037 
01039 static int CDECL VehicleProfitThisYearSorter(const Vehicle * const *a, const Vehicle * const *b)
01040 {
01041   int r = ClampToI32((*a)->GetDisplayProfitThisYear() - (*b)->GetDisplayProfitThisYear());
01042   return (r != 0) ? r : VehicleNumberSorter(a, b);
01043 }
01044 
01046 static int CDECL VehicleProfitLastYearSorter(const Vehicle * const *a, const Vehicle * const *b)
01047 {
01048   int r = ClampToI32((*a)->GetDisplayProfitLastYear() - (*b)->GetDisplayProfitLastYear());
01049   return (r != 0) ? r : VehicleNumberSorter(a, b);
01050 }
01051 
01053 static int CDECL VehicleCargoSorter(const Vehicle * const *a, const Vehicle * const *b)
01054 {
01055   const Vehicle *v;
01056   CargoArray diff;
01057 
01058   /* Append the cargo of the connected weagons */
01059   for (v = *a; v != NULL; v = v->Next()) diff[v->cargo_type] += v->cargo_cap;
01060   for (v = *b; v != NULL; v = v->Next()) diff[v->cargo_type] -= v->cargo_cap;
01061 
01062   int r = 0;
01063   for (CargoID i = 0; i < NUM_CARGO; i++) {
01064     r = diff[i];
01065     if (r != 0) break;
01066   }
01067 
01068   return (r != 0) ? r : VehicleNumberSorter(a, b);
01069 }
01070 
01072 static int CDECL VehicleReliabilitySorter(const Vehicle * const *a, const Vehicle * const *b)
01073 {
01074   int r = (*a)->reliability - (*b)->reliability;
01075   return (r != 0) ? r : VehicleNumberSorter(a, b);
01076 }
01077 
01079 static int CDECL VehicleMaxSpeedSorter(const Vehicle * const *a, const Vehicle * const *b)
01080 {
01081   int r = (*a)->vcache.cached_max_speed - (*b)->vcache.cached_max_speed;
01082   return (r != 0) ? r : VehicleNumberSorter(a, b);
01083 }
01084 
01086 static int CDECL VehicleModelSorter(const Vehicle * const *a, const Vehicle * const *b)
01087 {
01088   int r = (*a)->engine_type - (*b)->engine_type;
01089   return (r != 0) ? r : VehicleNumberSorter(a, b);
01090 }
01091 
01093 static int CDECL VehicleValueSorter(const Vehicle * const *a, const Vehicle * const *b)
01094 {
01095   const Vehicle *u;
01096   Money diff = 0;
01097 
01098   for (u = *a; u != NULL; u = u->Next()) diff += u->value;
01099   for (u = *b; u != NULL; u = u->Next()) diff -= u->value;
01100 
01101   int r = ClampToI32(diff);
01102   return (r != 0) ? r : VehicleNumberSorter(a, b);
01103 }
01104 
01106 static int CDECL VehicleLengthSorter(const Vehicle * const *a, const Vehicle * const *b)
01107 {
01108   int r = (*a)->GetGroundVehicleCache()->cached_total_length - (*b)->GetGroundVehicleCache()->cached_total_length;
01109   return (r != 0) ? r : VehicleNumberSorter(a, b);
01110 }
01111 
01113 static int CDECL VehicleTimeToLiveSorter(const Vehicle * const *a, const Vehicle * const *b)
01114 {
01115   int r = ClampToI32(((*a)->max_age - (*a)->age) - ((*b)->max_age - (*b)->age));
01116   return (r != 0) ? r : VehicleNumberSorter(a, b);
01117 }
01118 
01120 static int CDECL VehicleTimetableDelaySorter(const Vehicle * const *a, const Vehicle * const *b)
01121 {
01122   int r = (*a)->lateness_counter - (*b)->lateness_counter;
01123   return (r != 0) ? r : VehicleNumberSorter(a, b);
01124 }
01125 
01126 void InitializeGUI()
01127 {
01128   MemSetT(&_sorting, 0);
01129 }
01130 
01137 static inline void ChangeVehicleWindow(WindowClass window_class, VehicleID from_index, VehicleID to_index)
01138 {
01139   Window *w = FindWindowById(window_class, from_index);
01140   if (w != NULL) {
01141     /* Update window_number */
01142     w->window_number = to_index;
01143     if (w->viewport != NULL) w->viewport->follow_vehicle = to_index;
01144 
01145     /* Update vehicle drag data */
01146     if (_thd.window_class == window_class && _thd.window_number == (WindowNumber)from_index) {
01147       _thd.window_number = to_index;
01148     }
01149 
01150     /* Notify the window. */
01151     w->InvalidateData(-666, false);
01152   }
01153 }
01154 
01160 void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index)
01161 {
01162   ChangeVehicleWindow(WC_VEHICLE_VIEW,      from_index, to_index);
01163   ChangeVehicleWindow(WC_VEHICLE_ORDERS,    from_index, to_index);
01164   ChangeVehicleWindow(WC_VEHICLE_REFIT,     from_index, to_index);
01165   ChangeVehicleWindow(WC_VEHICLE_DETAILS,   from_index, to_index);
01166   ChangeVehicleWindow(WC_VEHICLE_TIMETABLE, from_index, to_index);
01167 }
01168 
01169 enum VehicleListWindowWidgets {
01170   VLW_WIDGET_CAPTION,
01171   VLW_WIDGET_SORT_ORDER,
01172   VLW_WIDGET_SORT_BY_PULLDOWN,
01173   VLW_WIDGET_LIST,
01174   VLW_WIDGET_SCROLLBAR,
01175   VLW_WIDGET_HIDE_BUTTONS,
01176   VLW_WIDGET_AVAILABLE_VEHICLES,
01177   VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
01178   VLW_WIDGET_STOP_ALL,
01179   VLW_WIDGET_START_ALL,
01180 };
01181 
01182 static const NWidgetPart _nested_vehicle_list[] = {
01183   NWidget(NWID_HORIZONTAL),
01184     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
01185     NWidget(WWT_CAPTION, COLOUR_GREY, VLW_WIDGET_CAPTION),
01186     NWidget(WWT_SHADEBOX, COLOUR_GREY),
01187     NWidget(WWT_STICKYBOX, COLOUR_GREY),
01188   EndContainer(),
01189 
01190   NWidget(NWID_HORIZONTAL),
01191     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLW_WIDGET_SORT_ORDER), SetMinimalSize(81, 12), SetFill(0, 1), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
01192     NWidget(WWT_DROPDOWN, COLOUR_GREY, VLW_WIDGET_SORT_BY_PULLDOWN), SetMinimalSize(167, 12), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
01193     NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetFill(1, 1), SetResize(1, 0),
01194     EndContainer(),
01195   EndContainer(),
01196 
01197   NWidget(NWID_HORIZONTAL),
01198     NWidget(WWT_MATRIX, COLOUR_GREY, VLW_WIDGET_LIST), SetMinimalSize(248, 0), SetFill(1, 0), SetResize(1, 1), SetScrollbar(VLW_WIDGET_SCROLLBAR),
01199     NWidget(NWID_VSCROLLBAR, COLOUR_GREY, VLW_WIDGET_SCROLLBAR),
01200   EndContainer(),
01201 
01202   NWidget(NWID_HORIZONTAL),
01203     NWidget(NWID_SELECTION, INVALID_COLOUR, VLW_WIDGET_HIDE_BUTTONS),
01204       NWidget(NWID_HORIZONTAL),
01205         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLW_WIDGET_AVAILABLE_VEHICLES), SetMinimalSize(106, 12), SetFill(0, 1),
01206                 SetDataTip(STR_BLACK_STRING, STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP),
01207         NWidget(WWT_DROPDOWN, COLOUR_GREY, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN), SetMinimalSize(118, 12), SetFill(0, 1),
01208                 SetDataTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP),
01209         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VLW_WIDGET_STOP_ALL), SetMinimalSize(12, 12), SetFill(0, 1),
01210                 SetDataTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP),
01211         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VLW_WIDGET_START_ALL), SetMinimalSize(12, 12), SetFill(0, 1),
01212                 SetDataTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP),
01213         NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetResize(1, 0), SetFill(1, 1), EndContainer(),
01214       EndContainer(),
01215       /* Widget to be shown for other companies hiding the previous 5 widgets. */
01216       NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(),
01217     EndContainer(),
01218     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
01219   EndContainer(),
01220 };
01221 
01222 static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, VehicleOrderID start = 0)
01223 {
01224   const Order *order = v->GetOrder(start);
01225   if (order == NULL) return;
01226 
01227   int i = 0;
01228   VehicleOrderID oid = start;
01229 
01230   do {
01231     if (oid == v->cur_real_order_index) DrawString(left, right, y, STR_TINY_RIGHT_ARROW, TC_BLACK);
01232 
01233     if (order->IsType(OT_GOTO_STATION)) {
01234       SetDParam(0, order->GetDestination());
01235       DrawString(left + 6, right - 6, y, STR_TINY_BLACK_STATION);
01236 
01237       y += FONT_HEIGHT_SMALL;
01238       if (++i == 4) break;
01239     }
01240 
01241     oid++;
01242     order = order->next;
01243     if (order == NULL) {
01244       order = v->orders.list->GetFirstOrder();
01245       oid = 0;
01246     }
01247   } while (oid != start);
01248 }
01249 
01259 void DrawVehicleImage(const Vehicle *v, int left, int right, int y, VehicleID selection, EngineImageType image_type, int skip)
01260 {
01261   switch (v->type) {
01262     case VEH_TRAIN:    DrawTrainImage(Train::From(v), left, right, y, selection, image_type, skip); break;
01263     case VEH_ROAD:     DrawRoadVehImage(v, left, right, y, selection, image_type, skip);  break;
01264     case VEH_SHIP:     DrawShipImage(v, left, right, y, selection, image_type);     break;
01265     case VEH_AIRCRAFT: DrawAircraftImage(v, left, right, y, selection, image_type); break;
01266     default: NOT_REACHED();
01267   }
01268 }
01269 
01276 uint GetVehicleListHeight(VehicleType type, uint divisor)
01277 {
01278   /* Name + vehicle + profit */
01279   uint base = GetVehicleHeight(type) + 2 * FONT_HEIGHT_SMALL;
01280   /* Drawing of the 4 small orders + profit*/
01281   if (type >= VEH_SHIP) base = max(base, 5U * FONT_HEIGHT_SMALL);
01282 
01283   if (divisor == 1) return base;
01284 
01285   /* Make sure the height is dividable by divisor */
01286   uint rem = base % divisor;
01287   return base + (rem == 0 ? 0 : divisor - rem);
01288 }
01289 
01296 void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const
01297 {
01298   int left = r.left + WD_MATRIX_LEFT;
01299   int right = r.right - WD_MATRIX_RIGHT;
01300   int width = right - left;
01301   bool rtl = _current_text_dir == TD_RTL;
01302 
01303   int text_offset = GetDigitWidth() * this->unitnumber_digits + WD_FRAMERECT_RIGHT;
01304   int text_left  = left  + (rtl ?           0 : text_offset);
01305   int text_right = right - (rtl ? text_offset :           0);
01306 
01307   bool show_orderlist = this->vli.vtype >= VEH_SHIP;
01308   int orderlist_left  = left  + (rtl ? 0 : max(100 + text_offset, width / 2));
01309   int orderlist_right = right - (rtl ? max(100 + text_offset, width / 2) : 0);
01310 
01311   int image_left  = (rtl && show_orderlist) ? orderlist_right : text_left;
01312   int image_right = (!rtl && show_orderlist) ? orderlist_left : text_right;
01313 
01314   int vehicle_button_x = rtl ? right - GetSpriteSize(SPR_PROFIT_LOT).width : left;
01315 
01316   int y = r.top;
01317   uint max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->vehicles.Length());
01318   for (uint i = this->vscroll->GetPosition(); i < max; ++i) {
01319     const Vehicle *v = this->vehicles[i];
01320     StringID str;
01321 
01322     SetDParam(0, v->GetDisplayProfitThisYear());
01323     SetDParam(1, v->GetDisplayProfitLastYear());
01324 
01325     DrawVehicleImage(v, image_left, image_right, y + FONT_HEIGHT_SMALL - 1, selected_vehicle, EIT_IN_LIST, 0);
01326     DrawString(text_left, text_right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 1, STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR);
01327 
01328     if (v->name != NULL) {
01329       /* The vehicle got a name so we will print it */
01330       SetDParam(0, v->index);
01331       DrawString(text_left, text_right, y, STR_TINY_BLACK_VEHICLE);
01332     } else if (v->group_id != DEFAULT_GROUP) {
01333       /* The vehicle has no name, but is member of a group, so print group name */
01334       SetDParam(0, v->group_id);
01335       DrawString(text_left, text_right, y, STR_TINY_GROUP, TC_BLACK);
01336     }
01337 
01338     if (show_orderlist) DrawSmallOrderList(v, orderlist_left, orderlist_right, y, v->cur_real_order_index);
01339 
01340     if (v->IsInDepot()) {
01341       str = STR_BLUE_COMMA;
01342     } else {
01343       str = (v->age > v->max_age - DAYS_IN_LEAP_YEAR) ? STR_RED_COMMA : STR_BLACK_COMMA;
01344     }
01345 
01346     SetDParam(0, v->unitnumber);
01347     DrawString(left, right, y + 2, str);
01348 
01349     DrawVehicleProfitButton(v, vehicle_button_x, y + FONT_HEIGHT_NORMAL + 3);
01350 
01351     y += line_height;
01352   }
01353 }
01354 
01364 struct VehicleListWindow : public BaseVehicleListWindow {
01365 private:
01367   enum ButtonPlanes {
01368     BP_SHOW_BUTTONS, 
01369     BP_HIDE_BUTTONS, 
01370   };
01371 
01372 public:
01373   VehicleListWindow(const WindowDesc *desc, WindowNumber window_number) : BaseVehicleListWindow(window_number)
01374   {
01375     /* Set up sorting. Make the window-specific _sorting variable
01376      * point to the correct global _sorting struct so we are freed
01377      * from having conditionals during window operation */
01378     switch (this->vli.vtype) {
01379       case VEH_TRAIN:    this->sorting = &_sorting.train; break;
01380       case VEH_ROAD:     this->sorting = &_sorting.roadveh; break;
01381       case VEH_SHIP:     this->sorting = &_sorting.ship; break;
01382       case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break;
01383       default: NOT_REACHED();
01384     }
01385 
01386     this->CreateNestedTree(desc);
01387 
01388     this->vscroll = this->GetScrollbar(VLW_WIDGET_SCROLLBAR);
01389 
01390     this->vehicles.SetListing(*this->sorting);
01391     this->vehicles.ForceRebuild();
01392     this->vehicles.NeedResort();
01393     this->BuildVehicleList();
01394     this->SortVehicleList();
01395 
01396     /* Set up the window widgets */
01397     this->GetWidget<NWidgetCore>(VLW_WIDGET_LIST)->tool_tip = STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP + this->vli.vtype;
01398 
01399     if (this->vli.type == VL_SHARED_ORDERS) {
01400       this->GetWidget<NWidgetCore>(VLW_WIDGET_CAPTION)->widget_data = STR_VEHICLE_LIST_SHARED_ORDERS_LIST_CAPTION;
01401     } else {
01402       this->GetWidget<NWidgetCore>(VLW_WIDGET_CAPTION)->widget_data = STR_VEHICLE_LIST_TRAIN_CAPTION + this->vli.vtype;
01403     }
01404 
01405     this->FinishInitNested(desc, window_number);
01406     this->owner = this->vli.company;
01407 
01408     if (this->vli.vtype == VEH_TRAIN) ResizeWindow(this, 65, 0);
01409   }
01410 
01411   ~VehicleListWindow()
01412   {
01413     *this->sorting = this->vehicles.GetListing();
01414   }
01415 
01416   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01417   {
01418     switch (widget) {
01419       case VLW_WIDGET_LIST:
01420         resize->height = GetVehicleListHeight(this->vli.vtype, 1);
01421 
01422         switch (this->vli.vtype) {
01423           case VEH_TRAIN:
01424           case VEH_ROAD:
01425             size->height = 6 * resize->height;
01426             break;
01427           case VEH_SHIP:
01428           case VEH_AIRCRAFT:
01429             size->height = 4 * resize->height;
01430             break;
01431           default: NOT_REACHED();
01432         }
01433         break;
01434 
01435       case VLW_WIDGET_SORT_ORDER: {
01436         Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
01437         d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better.
01438         d.height += padding.height;
01439         *size = maxdim(*size, d);
01440         break;
01441       }
01442 
01443       case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
01444         Dimension d = this->GetActionDropdownSize(this->vli.type == VL_STANDARD, false);
01445         d.height += padding.height;
01446         d.width  += padding.width;
01447         *size = maxdim(*size, d);
01448         break;
01449       }
01450     }
01451   }
01452 
01453   virtual void SetStringParameters(int widget) const
01454   {
01455     switch (widget) {
01456       case VLW_WIDGET_AVAILABLE_VEHICLES:
01457         SetDParam(0, STR_VEHICLE_LIST_AVAILABLE_TRAINS + this->vli.vtype);
01458         break;
01459 
01460       case VLW_WIDGET_CAPTION: {
01461         switch (this->vli.type) {
01462           case VL_SHARED_ORDERS: // Shared Orders
01463             if (this->vehicles.Length() == 0) {
01464               /* We can't open this window without vehicles using this order
01465                * and we should close the window when deleting the order. */
01466               NOT_REACHED();
01467             }
01468             SetDParam(0, this->vscroll->GetCount());
01469             break;
01470 
01471           case VL_STANDARD: // Company Name
01472             SetDParam(0, STR_COMPANY_NAME);
01473             SetDParam(1, this->vli.index);
01474             SetDParam(3, this->vscroll->GetCount());
01475             break;
01476 
01477           case VL_STATION_LIST: // Station/Waypoint Name
01478             SetDParam(0, Station::IsExpected(BaseStation::Get(this->vli.index)) ? STR_STATION_NAME : STR_WAYPOINT_NAME);
01479             SetDParam(1, this->vli.index);
01480             SetDParam(3, this->vscroll->GetCount());
01481             break;
01482 
01483           case VL_DEPOT_LIST:
01484             SetDParam(0, STR_DEPOT_CAPTION);
01485             SetDParam(1, this->vli.vtype);
01486             SetDParam(2, this->vli.index);
01487             SetDParam(3, this->vscroll->GetCount());
01488             break;
01489           default: NOT_REACHED();
01490         }
01491         break;
01492       }
01493     }
01494   }
01495 
01496   virtual void DrawWidget(const Rect &r, int widget) const
01497   {
01498     switch (widget) {
01499       case VLW_WIDGET_SORT_ORDER:
01500         /* draw arrow pointing up/down for ascending/descending sorting */
01501         this->DrawSortButtonState(widget, this->vehicles.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
01502         break;
01503 
01504       case VLW_WIDGET_LIST:
01505         this->DrawVehicleListItems(INVALID_VEHICLE, this->resize.step_height, r);
01506         break;
01507     }
01508   }
01509 
01510   virtual void OnPaint()
01511   {
01512     this->BuildVehicleList();
01513     this->SortVehicleList();
01514 
01515     if (this->vehicles.Length() == 0 && this->IsWidgetLowered(VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN)) {
01516       HideDropDownMenu(this);
01517     }
01518 
01519     /* Hide the widgets that we will not use in this window
01520      * Some windows contains actions only fit for the owner */
01521     int plane_to_show = (this->owner == _local_company) ? BP_SHOW_BUTTONS : BP_HIDE_BUTTONS;
01522     NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(VLW_WIDGET_HIDE_BUTTONS);
01523     if (plane_to_show != nwi->shown_plane) {
01524       nwi->SetDisplayedPlane(plane_to_show);
01525       nwi->SetDirty(this);
01526     }
01527     if (this->owner == _local_company) {
01528       this->SetWidgetDisabledState(VLW_WIDGET_AVAILABLE_VEHICLES, this->vli.type != VL_STANDARD);
01529       this->SetWidgetsDisabledState(this->vehicles.Length() == 0,
01530         VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
01531         VLW_WIDGET_STOP_ALL,
01532         VLW_WIDGET_START_ALL,
01533         WIDGET_LIST_END);
01534     }
01535 
01536     /* Set text of sort by dropdown widget. */
01537     this->GetWidget<NWidgetCore>(VLW_WIDGET_SORT_BY_PULLDOWN)->widget_data = this->vehicle_sorter_names[this->vehicles.SortType()];
01538 
01539     this->DrawWidgets();
01540   }
01541 
01542   virtual void OnClick(Point pt, int widget, int click_count)
01543   {
01544     switch (widget) {
01545       case VLW_WIDGET_SORT_ORDER: // Flip sorting method ascending/descending
01546         this->vehicles.ToggleSortOrder();
01547         this->SetDirty();
01548         break;
01549 
01550       case VLW_WIDGET_SORT_BY_PULLDOWN:// Select sorting criteria dropdown menu
01551         ShowDropDownMenu(this, this->vehicle_sorter_names, this->vehicles.SortType(), VLW_WIDGET_SORT_BY_PULLDOWN, 0,
01552             (this->vli.vtype == VEH_TRAIN || this->vli.vtype == VEH_ROAD) ? 0 : (1 << 10));
01553         return;
01554 
01555       case VLW_WIDGET_LIST: { // Matrix to show vehicles
01556         uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, VLW_WIDGET_LIST);
01557         if (id_v >= this->vehicles.Length()) return; // click out of list bound
01558 
01559         const Vehicle *v = this->vehicles[id_v];
01560         if (!VehicleClicked(v)) ShowVehicleViewWindow(v);
01561         break;
01562       }
01563 
01564       case VLW_WIDGET_AVAILABLE_VEHICLES:
01565         ShowBuildVehicleWindow(INVALID_TILE, this->vli.vtype);
01566         break;
01567 
01568       case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
01569         DropDownList *list = this->BuildActionDropdownList(VehicleListIdentifier(this->window_number).type == VL_STANDARD, false);
01570         ShowDropDownList(this, list, 0, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN);
01571         break;
01572       }
01573 
01574       case VLW_WIDGET_STOP_ALL:
01575       case VLW_WIDGET_START_ALL:
01576         DoCommandP(0, (1 << 1) | (widget == VLW_WIDGET_START_ALL ? (1 << 0) : 0), this->window_number, CMD_MASS_START_STOP);
01577         break;
01578     }
01579   }
01580 
01581   virtual void OnDropdownSelect(int widget, int index)
01582   {
01583     switch (widget) {
01584       case VLW_WIDGET_SORT_BY_PULLDOWN:
01585         this->vehicles.SetSortType(index);
01586         break;
01587       case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN:
01588         assert(this->vehicles.Length() != 0);
01589 
01590         switch (index) {
01591           case ADI_REPLACE: // Replace window
01592             ShowReplaceGroupVehicleWindow(DEFAULT_GROUP, this->vli.vtype);
01593             break;
01594           case ADI_SERVICE: // Send for servicing
01595           case ADI_DEPOT: // Send to Depots
01596             DoCommandP(0, DEPOT_MASS_SEND | (index == ADI_SERVICE ? DEPOT_SERVICE : (DepotCommand)0), this->window_number, GetCmdSendToDepot(this->vli.vtype));
01597             break;
01598 
01599           default: NOT_REACHED();
01600         }
01601         break;
01602       default: NOT_REACHED();
01603     }
01604     this->SetDirty();
01605   }
01606 
01607   virtual void OnTick()
01608   {
01609     if (_pause_mode != PM_UNPAUSED) return;
01610     if (this->vehicles.NeedResort()) {
01611       StationID station = (this->vli.type == VL_STATION_LIST) ? this->vli.index : INVALID_STATION;
01612 
01613       DEBUG(misc, 3, "Periodic resort %d list company %d at station %d", this->vli.vtype, this->owner, station);
01614       this->SetDirty();
01615     }
01616   }
01617 
01618   virtual void OnResize()
01619   {
01620     this->vscroll->SetCapacityFromWidget(this, VLW_WIDGET_LIST);
01621     this->GetWidget<NWidgetCore>(VLW_WIDGET_LIST)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
01622   }
01623 
01629   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01630   {
01631     if (!gui_scope && HasBit(data, 31) && this->vli.type == VL_SHARED_ORDERS) {
01632       /* Needs to be done in command-scope, so everything stays valid */
01633       this->vli.index = GB(data, 0, 20);
01634       this->window_number = this->vli.Pack();
01635       this->vehicles.ForceRebuild();
01636       return;
01637     }
01638 
01639     if (data == 0) {
01640       /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
01641       this->vehicles.ForceRebuild();
01642     } else {
01643       this->vehicles.ForceResort();
01644     }
01645   }
01646 };
01647 
01648 static WindowDesc _vehicle_list_desc(
01649   WDP_AUTO, 260, 246,
01650   WC_INVALID, WC_NONE,
01651   WDF_UNCLICK_BUTTONS,
01652   _nested_vehicle_list, lengthof(_nested_vehicle_list)
01653 );
01654 
01655 static void ShowVehicleListWindowLocal(CompanyID company, VehicleListType vlt, VehicleType vehicle_type, uint16 unique_number)
01656 {
01657   if (!Company::IsValidID(company)) return;
01658 
01659   _vehicle_list_desc.cls = GetWindowClassForVehicleType(vehicle_type);
01660   AllocateWindowDescFront<VehicleListWindow>(&_vehicle_list_desc, VehicleListIdentifier(vlt, vehicle_type, company, unique_number).Pack());
01661 }
01662 
01663 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type)
01664 {
01665   /* If _settings_client.gui.advanced_vehicle_list > 1, display the Advanced list
01666    * if _settings_client.gui.advanced_vehicle_list == 1, display Advanced list only for local company
01667    * if _ctrl_pressed, do the opposite action (Advanced list x Normal list)
01668    */
01669 
01670   if ((_settings_client.gui.advanced_vehicle_list > (uint)(company != _local_company)) != _ctrl_pressed) {
01671     ShowCompanyGroup(company, vehicle_type);
01672   } else {
01673     ShowVehicleListWindowLocal(company, VL_STANDARD, vehicle_type, company);
01674   }
01675 }
01676 
01677 void ShowVehicleListWindow(const Vehicle *v)
01678 {
01679   ShowVehicleListWindowLocal(v->owner, VL_SHARED_ORDERS, v->type, v->FirstShared()->index);
01680 }
01681 
01682 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station)
01683 {
01684   if (!Company::IsValidID(company)) {
01685     company = _local_company;
01686     /* This can happen when opening the vehicle list as a spectator. */
01687     if (!Company::IsValidID(company)) return;
01688     _vehicle_list_desc.flags |= WDF_CONSTRUCTION;
01689   } else {
01690     _vehicle_list_desc.flags &= ~WDF_CONSTRUCTION;
01691   }
01692 
01693   ShowVehicleListWindowLocal(company, VL_STATION_LIST, vehicle_type, station);
01694 }
01695 
01696 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile)
01697 {
01698   uint16 depot_airport_index;
01699 
01700   if (vehicle_type == VEH_AIRCRAFT) {
01701     depot_airport_index = GetStationIndex(depot_tile);
01702   } else {
01703     depot_airport_index = GetDepotIndex(depot_tile);
01704   }
01705   ShowVehicleListWindowLocal(company, VL_DEPOT_LIST, vehicle_type, depot_airport_index);
01706 }
01707 
01708 
01709 /* Unified vehicle GUI - Vehicle Details Window */
01710 
01712 enum VehicleDetailsWindowWidgets {
01713   VLD_WIDGET_CAPTION,
01714   VLD_WIDGET_RENAME_VEHICLE,
01715   VLD_WIDGET_TOP_DETAILS,
01716   VLD_WIDGET_INCREASE_SERVICING_INTERVAL,
01717   VLD_WIDGET_DECREASE_SERVICING_INTERVAL,
01718   VLD_WIDGET_SERVICING_INTERVAL,
01719   VLD_WIDGET_MIDDLE_DETAILS,
01720   VLD_WIDGET_MATRIX,
01721   VLD_WIDGET_SCROLLBAR,
01722   VLD_WIDGET_DETAILS_CARGO_CARRIED,
01723   VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
01724   VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
01725   VLD_WIDGET_DETAILS_TOTAL_CARGO,
01726 };
01727 
01728 assert_compile(VLD_WIDGET_DETAILS_CARGO_CARRIED    == VLD_WIDGET_DETAILS_CARGO_CARRIED + TDW_TAB_CARGO   );
01729 assert_compile(VLD_WIDGET_DETAILS_TRAIN_VEHICLES   == VLD_WIDGET_DETAILS_CARGO_CARRIED + TDW_TAB_INFO    );
01730 assert_compile(VLD_WIDGET_DETAILS_CAPACITY_OF_EACH == VLD_WIDGET_DETAILS_CARGO_CARRIED + TDW_TAB_CAPACITY);
01731 assert_compile(VLD_WIDGET_DETAILS_TOTAL_CARGO      == VLD_WIDGET_DETAILS_CARGO_CARRIED + TDW_TAB_TOTALS  );
01732 
01734 static const NWidgetPart _nested_nontrain_vehicle_details_widgets[] = {
01735   NWidget(NWID_HORIZONTAL),
01736     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
01737     NWidget(WWT_CAPTION, COLOUR_GREY, VLD_WIDGET_CAPTION), SetDataTip(STR_VEHICLE_DETAILS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01738     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_RENAME_VEHICLE), SetMinimalSize(40, 0), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 2), SetDataTip(STR_VEHICLE_NAME_BUTTON, STR_NULL /* filled in later */),
01739     NWidget(WWT_SHADEBOX, COLOUR_GREY),
01740     NWidget(WWT_STICKYBOX, COLOUR_GREY),
01741   EndContainer(),
01742   NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_TOP_DETAILS), SetMinimalSize(405, 42), SetResize(1, 0), EndContainer(),
01743   NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_MIDDLE_DETAILS), SetMinimalSize(405, 45), SetResize(1, 0), EndContainer(),
01744   NWidget(NWID_HORIZONTAL),
01745     NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, VLD_WIDGET_DECREASE_SERVICING_INTERVAL), SetFill(0, 1),
01746         SetDataTip(AWV_DECREASE, STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP),
01747     NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, VLD_WIDGET_INCREASE_SERVICING_INTERVAL), SetFill(0, 1),
01748         SetDataTip(AWV_INCREASE, STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP),
01749     NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_SERVICING_INTERVAL), SetFill(1, 1), SetResize(1, 0), EndContainer(),
01750     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
01751   EndContainer(),
01752 };
01753 
01755 static const NWidgetPart _nested_train_vehicle_details_widgets[] = {
01756   NWidget(NWID_HORIZONTAL),
01757     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
01758     NWidget(WWT_CAPTION, COLOUR_GREY, VLD_WIDGET_CAPTION), SetDataTip(STR_VEHICLE_DETAILS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01759     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_RENAME_VEHICLE), SetMinimalSize(40, 0), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 2), SetDataTip(STR_VEHICLE_NAME_BUTTON, STR_NULL /* filled in later */),
01760     NWidget(WWT_SHADEBOX, COLOUR_GREY),
01761     NWidget(WWT_STICKYBOX, COLOUR_GREY),
01762   EndContainer(),
01763   NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_TOP_DETAILS), SetResize(1, 0), SetMinimalSize(405, 42), EndContainer(),
01764   NWidget(NWID_HORIZONTAL),
01765     NWidget(WWT_MATRIX, COLOUR_GREY, VLD_WIDGET_MATRIX), SetResize(1, 1), SetMinimalSize(393, 45), SetDataTip(0x701, STR_NULL), SetFill(1, 0), SetScrollbar(VLD_WIDGET_SCROLLBAR),
01766     NWidget(NWID_VSCROLLBAR, COLOUR_GREY, VLD_WIDGET_SCROLLBAR),
01767   EndContainer(),
01768   NWidget(NWID_HORIZONTAL),
01769     NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, VLD_WIDGET_DECREASE_SERVICING_INTERVAL), SetFill(0, 1),
01770         SetDataTip(AWV_DECREASE, STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP),
01771     NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, VLD_WIDGET_INCREASE_SERVICING_INTERVAL), SetFill(0, 1),
01772         SetDataTip(AWV_INCREASE, STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP),
01773     NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_SERVICING_INTERVAL), SetFill(1, 1), SetResize(1, 0), EndContainer(),
01774   EndContainer(),
01775   NWidget(NWID_HORIZONTAL),
01776     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_DETAILS_CARGO_CARRIED), SetMinimalSize(96, 12),
01777         SetDataTip(STR_VEHICLE_DETAIL_TAB_CARGO, STR_VEHICLE_DETAILS_TRAIN_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01778     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_DETAILS_TRAIN_VEHICLES), SetMinimalSize(99, 12),
01779         SetDataTip(STR_VEHICLE_DETAIL_TAB_INFORMATION, STR_VEHICLE_DETAILS_TRAIN_INFORMATION_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01780     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_DETAILS_CAPACITY_OF_EACH), SetMinimalSize(99, 12),
01781         SetDataTip(STR_VEHICLE_DETAIL_TAB_CAPACITIES, STR_VEHICLE_DETAILS_TRAIN_CAPACITIES_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01782     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_DETAILS_TOTAL_CARGO), SetMinimalSize(99, 12),
01783         SetDataTip(STR_VEHICLE_DETAIL_TAB_TOTAL_CARGO, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01784     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
01785   EndContainer(),
01786 };
01787 
01788 
01789 extern int GetTrainDetailsWndVScroll(VehicleID veh_id, TrainDetailsWindowTabs det_tab);
01790 extern void DrawTrainDetails(const Train *v, int left, int right, int y, int vscroll_pos, uint16 vscroll_cap, TrainDetailsWindowTabs det_tab);
01791 extern void DrawRoadVehDetails(const Vehicle *v, int left, int right, int y);
01792 extern void DrawShipDetails(const Vehicle *v, int left, int right, int y);
01793 extern void DrawAircraftDetails(const Aircraft *v, int left, int right, int y);
01794 
01796 struct VehicleDetailsWindow : Window {
01797   TrainDetailsWindowTabs tab; 
01798   Scrollbar *vscroll;
01799 
01801   VehicleDetailsWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
01802   {
01803     const Vehicle *v = Vehicle::Get(window_number);
01804 
01805     this->CreateNestedTree(desc);
01806     this->vscroll = (v->type == VEH_TRAIN ? this->GetScrollbar(VLD_WIDGET_SCROLLBAR) : NULL);
01807     this->FinishInitNested(desc, window_number);
01808 
01809     this->GetWidget<NWidgetCore>(VLD_WIDGET_RENAME_VEHICLE)->tool_tip = STR_VEHICLE_DETAILS_TRAIN_RENAME + v->type;
01810 
01811     this->owner = v->owner;
01812     this->tab = TDW_TAB_CARGO;
01813   }
01814 
01820   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01821   {
01822     if (data == -666) {
01823       /* Autoreplace replaced the vehicle.
01824        * Nothing to do for this window. */
01825       return;
01826     }
01827     if (!gui_scope) return;
01828     const Vehicle *v = Vehicle::Get(this->window_number);
01829     if (v->type == VEH_ROAD) {
01830       const NWidgetBase *nwid_info = this->GetWidget<NWidgetBase>(VLD_WIDGET_MIDDLE_DETAILS);
01831       uint aimed_height = this->GetRoadVehDetailsHeight(v);
01832       /* If the number of articulated parts changes, the size of the window must change too. */
01833       if (aimed_height != nwid_info->current_y) {
01834         this->ReInit();
01835       }
01836     }
01837   }
01838 
01844   uint GetRoadVehDetailsHeight(const Vehicle *v)
01845   {
01846     uint desired_height;
01847     if (v->HasArticulatedPart()) {
01848       /* An articulated RV has its text drawn under the sprite instead of after it, hence 15 pixels extra. */
01849       desired_height = WD_FRAMERECT_TOP + 15 + 3 * FONT_HEIGHT_NORMAL + 2 + WD_FRAMERECT_BOTTOM;
01850       /* Add space for the cargo amount for each part. */
01851       for (const Vehicle *u = v; u != NULL; u = u->Next()) {
01852         if (u->cargo_cap != 0) desired_height += FONT_HEIGHT_NORMAL + 1;
01853       }
01854     } else {
01855       desired_height = WD_FRAMERECT_TOP + 4 * FONT_HEIGHT_NORMAL + 3 + WD_FRAMERECT_BOTTOM;
01856     }
01857     return desired_height;
01858   }
01859 
01860   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01861   {
01862     switch (widget) {
01863       case VLD_WIDGET_TOP_DETAILS: {
01864         Dimension dim = { 0, 0 };
01865         size->height = WD_FRAMERECT_TOP + 4 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
01866 
01867         for (uint i = 0; i < 4; i++) SetDParam(i, INT16_MAX);
01868         static const StringID info_strings[] = {
01869           STR_VEHICLE_INFO_MAX_SPEED,
01870           STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED,
01871           STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE,
01872           STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR,
01873           STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS
01874         };
01875         for (uint i = 0; i < lengthof(info_strings); i++) {
01876           dim = maxdim(dim, GetStringBoundingBox(info_strings[i]));
01877         }
01878         SetDParam(0, STR_VEHICLE_INFO_AGE);
01879         dim = maxdim(dim, GetStringBoundingBox(STR_VEHICLE_INFO_AGE_RUNNING_COST_YR));
01880         size->width = dim.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01881         break;
01882       }
01883 
01884       case VLD_WIDGET_MIDDLE_DETAILS: {
01885         const Vehicle *v = Vehicle::Get(this->window_number);
01886         switch (v->type) {
01887           case VEH_ROAD:
01888             size->height = this->GetRoadVehDetailsHeight(v);
01889             break;
01890 
01891           case VEH_SHIP:
01892             size->height = WD_FRAMERECT_TOP + 4 * FONT_HEIGHT_NORMAL + 3 + WD_FRAMERECT_BOTTOM;
01893             break;
01894 
01895           case VEH_AIRCRAFT:
01896             size->height = WD_FRAMERECT_TOP + 5 * FONT_HEIGHT_NORMAL + 4 + WD_FRAMERECT_BOTTOM;
01897             break;
01898 
01899           default:
01900             NOT_REACHED(); // Train uses VLD_WIDGET_MATRIX instead.
01901         }
01902         break;
01903       }
01904 
01905       case VLD_WIDGET_MATRIX:
01906         resize->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
01907         size->height = 4 * resize->height;
01908         break;
01909 
01910       case VLD_WIDGET_SERVICING_INTERVAL:
01911         SetDParam(0, 9999); // Roughly the maximum interval
01912         SetDParam(1, MAX_YEAR * DAYS_IN_YEAR); // Roughly the maximum year
01913         size->width = max(GetStringBoundingBox(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT).width, GetStringBoundingBox(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS).width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01914         size->height = WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
01915         break;
01916     }
01917   }
01918 
01920   static bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type, CompanyID company_id)
01921   {
01922     const VehicleDefaultSettings *vds = &Company::Get(company_id)->settings.vehicle;
01923     switch (vehicle_type) {
01924       default: NOT_REACHED();
01925       case VEH_TRAIN:    return vds->servint_trains   != 0;
01926       case VEH_ROAD:     return vds->servint_roadveh  != 0;
01927       case VEH_SHIP:     return vds->servint_ships    != 0;
01928       case VEH_AIRCRAFT: return vds->servint_aircraft != 0;
01929     }
01930   }
01931 
01943   static void DrawVehicleDetails(const Vehicle *v, int left, int right, int y, int vscroll_pos, uint vscroll_cap, TrainDetailsWindowTabs det_tab)
01944   {
01945     switch (v->type) {
01946       case VEH_TRAIN:    DrawTrainDetails(Train::From(v), left, right, y, vscroll_pos, vscroll_cap, det_tab);  break;
01947       case VEH_ROAD:     DrawRoadVehDetails(v, left, right, y);  break;
01948       case VEH_SHIP:     DrawShipDetails(v, left, right, y);     break;
01949       case VEH_AIRCRAFT: DrawAircraftDetails(Aircraft::From(v), left, right, y); break;
01950       default: NOT_REACHED();
01951     }
01952   }
01953 
01954   virtual void SetStringParameters(int widget) const
01955   {
01956     if (widget == VLD_WIDGET_CAPTION) SetDParam(0, Vehicle::Get(this->window_number)->index);
01957   }
01958 
01959   virtual void DrawWidget(const Rect &r, int widget) const
01960   {
01961     const Vehicle *v = Vehicle::Get(this->window_number);
01962 
01963     switch (widget) {
01964       case VLD_WIDGET_TOP_DETAILS: {
01965         int y = r.top + WD_FRAMERECT_TOP;
01966 
01967         /* Draw running cost */
01968         SetDParam(1, v->age / DAYS_IN_LEAP_YEAR);
01969         SetDParam(0, (v->age + DAYS_IN_YEAR < v->max_age) ? STR_VEHICLE_INFO_AGE : STR_VEHICLE_INFO_AGE_RED);
01970         SetDParam(2, v->max_age / DAYS_IN_LEAP_YEAR);
01971         SetDParam(3, v->GetDisplayRunningCost());
01972         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_AGE_RUNNING_COST_YR);
01973         y += FONT_HEIGHT_NORMAL;
01974 
01975         /* Draw max speed */
01976         StringID string;
01977         if (v->type == VEH_TRAIN ||
01978             (v->type == VEH_ROAD && _settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL)) {
01979           const GroundVehicleCache *gcache = v->GetGroundVehicleCache();
01980           SetDParam(2, v->GetDisplayMaxSpeed());
01981           SetDParam(1, gcache->cached_power);
01982           SetDParam(0, gcache->cached_weight);
01983           SetDParam(3, gcache->cached_max_te / 1000);
01984           if (v->type == VEH_TRAIN && (_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL ||
01985               GetRailTypeInfo(Train::From(v)->railtype)->acceleration_type == 2)) {
01986             string = STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED;
01987           } else {
01988             string = STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE;
01989           }
01990         } else {
01991           SetDParam(0, v->GetDisplayMaxSpeed());
01992           string = STR_VEHICLE_INFO_MAX_SPEED;
01993         }
01994         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, string);
01995         y += FONT_HEIGHT_NORMAL;
01996 
01997         /* Draw profit */
01998         SetDParam(0, v->GetDisplayProfitThisYear());
01999         SetDParam(1, v->GetDisplayProfitLastYear());
02000         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR);
02001         y += FONT_HEIGHT_NORMAL;
02002 
02003         /* Draw breakdown & reliability */
02004         SetDParam(0, ToPercent16(v->reliability));
02005         SetDParam(1, v->breakdowns_since_last_service);
02006         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS);
02007         break;
02008       }
02009 
02010       case VLD_WIDGET_MATRIX:
02011         /* For trains only. */
02012         DrawVehicleDetails(v, r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, r.top + WD_MATRIX_TOP, this->vscroll->GetPosition(), this->vscroll->GetCapacity(), this->tab);
02013         break;
02014 
02015       case VLD_WIDGET_MIDDLE_DETAILS: {
02016         /* For other vehicles, at the place of the matrix. */
02017         bool rtl = _current_text_dir == TD_RTL;
02018         uint sprite_width = max<uint>(UnScaleByZoom(GetSprite(v->GetImage(rtl ? DIR_E : DIR_W, EIT_IN_DETAILS), ST_NORMAL)->width, ZOOM_LVL_GUI), 70U) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
02019 
02020         uint text_left  = r.left  + (rtl ? 0 : sprite_width);
02021         uint text_right = r.right - (rtl ? sprite_width : 0);
02022 
02023         /* Articulated road vehicles use a complete line. */
02024         if (v->type == VEH_ROAD && v->HasArticulatedPart()) {
02025           DrawVehicleImage(v, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, INVALID_VEHICLE, EIT_IN_DETAILS, 0);
02026         } else {
02027           uint sprite_left  = rtl ? text_right : r.left;
02028           uint sprite_right = rtl ? r.right : text_left;
02029 
02030           DrawVehicleImage(v, sprite_left + WD_FRAMERECT_LEFT, sprite_right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, INVALID_VEHICLE, EIT_IN_DETAILS, 0);
02031         }
02032         DrawVehicleDetails(v, text_left + WD_FRAMERECT_LEFT, text_right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, 0, 0, this->tab);
02033         break;
02034       }
02035 
02036       case VLD_WIDGET_SERVICING_INTERVAL:
02037         /* Draw service interval text */
02038         SetDParam(0, v->service_interval);
02039         SetDParam(1, v->date_of_last_service);
02040         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + (r.bottom - r.top + 1 - FONT_HEIGHT_NORMAL) / 2,
02041             Company::Get(v->owner)->settings.vehicle.servint_ispercent ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS);
02042         break;
02043     }
02044   }
02045 
02047   virtual void OnPaint()
02048   {
02049     const Vehicle *v = Vehicle::Get(this->window_number);
02050 
02051     this->SetWidgetDisabledState(VLD_WIDGET_RENAME_VEHICLE, v->owner != _local_company);
02052 
02053     if (v->type == VEH_TRAIN) {
02054       this->DisableWidget(this->tab + VLD_WIDGET_DETAILS_CARGO_CARRIED);
02055       this->vscroll->SetCount(GetTrainDetailsWndVScroll(v->index, this->tab));
02056     }
02057 
02058     /* Disable service-scroller when interval is set to disabled */
02059     this->SetWidgetsDisabledState(!IsVehicleServiceIntervalEnabled(v->type, v->owner),
02060       VLD_WIDGET_INCREASE_SERVICING_INTERVAL,
02061       VLD_WIDGET_DECREASE_SERVICING_INTERVAL,
02062       WIDGET_LIST_END);
02063 
02064     this->DrawWidgets();
02065   }
02066 
02067   virtual void OnClick(Point pt, int widget, int click_count)
02068   {
02069     switch (widget) {
02070       case VLD_WIDGET_RENAME_VEHICLE: { // rename
02071         const Vehicle *v = Vehicle::Get(this->window_number);
02072         SetDParam(0, v->index);
02073         ShowQueryString(STR_VEHICLE_NAME, STR_QUERY_RENAME_TRAIN_CAPTION + v->type,
02074             MAX_LENGTH_VEHICLE_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
02075         break;
02076       }
02077 
02078       case VLD_WIDGET_INCREASE_SERVICING_INTERVAL:   // increase int
02079       case VLD_WIDGET_DECREASE_SERVICING_INTERVAL: { // decrease int
02080         int mod = _ctrl_pressed ? 5 : 10;
02081         const Vehicle *v = Vehicle::Get(this->window_number);
02082 
02083         mod = (widget == VLD_WIDGET_DECREASE_SERVICING_INTERVAL) ? -mod : mod;
02084         mod = GetServiceIntervalClamped(mod + v->service_interval, v->owner);
02085         if (mod == v->service_interval) return;
02086 
02087         DoCommandP(v->tile, v->index, mod, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_ERROR_CAN_T_CHANGE_SERVICING));
02088         break;
02089       }
02090 
02091       case VLD_WIDGET_DETAILS_CARGO_CARRIED:
02092       case VLD_WIDGET_DETAILS_TRAIN_VEHICLES:
02093       case VLD_WIDGET_DETAILS_CAPACITY_OF_EACH:
02094       case VLD_WIDGET_DETAILS_TOTAL_CARGO:
02095         this->SetWidgetsDisabledState(false,
02096           VLD_WIDGET_DETAILS_CARGO_CARRIED,
02097           VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
02098           VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
02099           VLD_WIDGET_DETAILS_TOTAL_CARGO,
02100           widget,
02101           WIDGET_LIST_END);
02102 
02103         this->tab = (TrainDetailsWindowTabs)(widget - VLD_WIDGET_DETAILS_CARGO_CARRIED);
02104         this->SetDirty();
02105         break;
02106     }
02107   }
02108 
02109   virtual void OnQueryTextFinished(char *str)
02110   {
02111     if (str == NULL) return;
02112 
02113     DoCommandP(0, this->window_number, 0, CMD_RENAME_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN + Vehicle::Get(this->window_number)->type), NULL, str);
02114   }
02115 
02116   virtual void OnResize()
02117   {
02118     NWidgetCore *nwi = this->GetWidget<NWidgetCore>(VLD_WIDGET_MATRIX);
02119     if (nwi != NULL) {
02120       this->vscroll->SetCapacityFromWidget(this, VLD_WIDGET_MATRIX);
02121       nwi->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
02122     }
02123   }
02124 };
02125 
02127 static const WindowDesc _train_vehicle_details_desc(
02128   WDP_AUTO, 405, 178,
02129   WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW,
02130   WDF_UNCLICK_BUTTONS,
02131   _nested_train_vehicle_details_widgets, lengthof(_nested_train_vehicle_details_widgets)
02132 );
02133 
02135 static const WindowDesc _nontrain_vehicle_details_desc(
02136   WDP_AUTO, 405, 113,
02137   WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW,
02138   WDF_UNCLICK_BUTTONS,
02139   _nested_nontrain_vehicle_details_widgets, lengthof(_nested_nontrain_vehicle_details_widgets)
02140 );
02141 
02143 static void ShowVehicleDetailsWindow(const Vehicle *v)
02144 {
02145   DeleteWindowById(WC_VEHICLE_ORDERS, v->index, false);
02146   DeleteWindowById(WC_VEHICLE_TIMETABLE, v->index, false);
02147   AllocateWindowDescFront<VehicleDetailsWindow>((v->type == VEH_TRAIN) ? &_train_vehicle_details_desc : &_nontrain_vehicle_details_desc, v->index);
02148 }
02149 
02150 
02151 /* Unified vehicle GUI - Vehicle View Window */
02152 
02154 static const NWidgetPart _nested_vehicle_view_widgets[] = {
02155   NWidget(NWID_HORIZONTAL),
02156     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
02157     NWidget(WWT_CAPTION, COLOUR_GREY, VVW_WIDGET_CAPTION), SetDataTip(STR_VEHICLE_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
02158     NWidget(WWT_DEBUGBOX, COLOUR_GREY),
02159     NWidget(WWT_SHADEBOX, COLOUR_GREY),
02160     NWidget(WWT_STICKYBOX, COLOUR_GREY),
02161   EndContainer(),
02162   NWidget(NWID_HORIZONTAL),
02163     NWidget(WWT_PANEL, COLOUR_GREY),
02164       NWidget(WWT_INSET, COLOUR_GREY), SetPadding(2, 2, 2, 2),
02165         NWidget(NWID_VIEWPORT, INVALID_COLOUR, VVW_WIDGET_VIEWPORT), SetMinimalSize(226, 84), SetResize(1, 1), SetPadding(1, 1, 1, 1),
02166       EndContainer(),
02167     EndContainer(),
02168     NWidget(NWID_VERTICAL),
02169       NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_CENTER_MAIN_VIEH), SetMinimalSize(18, 18), SetFill(1, 1), SetDataTip(SPR_CENTRE_VIEW_VEHICLE, 0x0 /* filled later */),
02170       NWidget(NWID_SELECTION, INVALID_COLOUR, VVW_WIDGET_SELECT_DEPOT_CLONE),
02171         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_GOTO_DEPOT), SetMinimalSize(18, 18), SetFill(1, 1), SetDataTip(0x0 /* filled later */, 0x0 /* filled later */),
02172         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_CLONE_VEH), SetMinimalSize(18, 18), SetFill(1, 1), SetDataTip(0x0 /* filled later */, 0x0 /* filled later */),
02173       EndContainer(),
02174       /* For trains only, 'ignore signal' button. */
02175       NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_FORCE_PROCEED), SetMinimalSize(18, 18), SetFill(1, 1),
02176                       SetDataTip(SPR_IGNORE_SIGNALS, STR_VEHICLE_VIEW_TRAIN_IGNORE_SIGNAL_TOOLTIP),
02177       NWidget(NWID_SELECTION, INVALID_COLOUR, VVW_WIDGET_SELECT_REFIT_TURN),
02178         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_REFIT_VEH), SetMinimalSize(18, 18), SetFill(1, 1), SetDataTip(SPR_REFIT_VEHICLE, 0x0 /* filled later */),
02179         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_TURN_AROUND), SetMinimalSize(18, 18), SetFill(1, 1),
02180                         SetDataTip(SPR_FORCE_VEHICLE_TURN, STR_VEHICLE_VIEW_ROAD_VEHICLE_REVERSE_TOOLTIP),
02181       EndContainer(),
02182       NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_SHOW_ORDERS), SetFill(1, 1), SetMinimalSize(18, 18), SetDataTip(SPR_SHOW_ORDERS, 0x0 /* filled later */),
02183       NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_SHOW_DETAILS), SetFill(1, 1), SetMinimalSize(18, 18), SetDataTip(SPR_SHOW_VEHICLE_DETAILS, 0x0 /* filled later */),
02184       NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetMinimalSize(18, 0), SetResize(0, 1), EndContainer(),
02185     EndContainer(),
02186   EndContainer(),
02187   NWidget(NWID_HORIZONTAL),
02188     NWidget(WWT_PUSHBTN, COLOUR_GREY, VVW_WIDGET_START_STOP_VEH), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + 2), SetResize(1, 0), SetFill(1, 0),
02189     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
02190   EndContainer(),
02191 };
02192 
02194 static const WindowDesc _vehicle_view_desc(
02195   WDP_AUTO, 250, 116,
02196   WC_VEHICLE_VIEW, WC_NONE,
02197   WDF_UNCLICK_BUTTONS,
02198   _nested_vehicle_view_widgets, lengthof(_nested_vehicle_view_widgets)
02199 );
02200 
02205 static const WindowDesc _train_view_desc(
02206   WDP_AUTO, 250, 134,
02207   WC_VEHICLE_VIEW, WC_NONE,
02208   WDF_UNCLICK_BUTTONS,
02209   _nested_vehicle_view_widgets, lengthof(_nested_vehicle_view_widgets)
02210 );
02211 
02212 
02213 /* Just to make sure, nobody has changed the vehicle type constants, as we are
02214    using them for array indexing in a number of places here. */
02215 assert_compile(VEH_TRAIN == 0);
02216 assert_compile(VEH_ROAD == 1);
02217 assert_compile(VEH_SHIP == 2);
02218 assert_compile(VEH_AIRCRAFT == 3);
02219 
02221 static const ZoomLevel _vehicle_view_zoom_levels[] = {
02222   ZOOM_LVL_TRAIN,
02223   ZOOM_LVL_ROADVEH,
02224   ZOOM_LVL_SHIP,
02225   ZOOM_LVL_AIRCRAFT,
02226 };
02227 
02228 /* Constants for geometry of vehicle view viewport */
02229 static const int VV_INITIAL_VIEWPORT_WIDTH = 226;
02230 static const int VV_INITIAL_VIEWPORT_HEIGHT = 84;
02231 static const int VV_INITIAL_VIEWPORT_HEIGHT_TRAIN = 102;
02232 
02234 enum VehicleCommandTranslation {
02235   VCT_CMD_START_STOP = 0,
02236   VCT_CMD_CLONE_VEH,
02237   VCT_CMD_TURN_AROUND,
02238 };
02239 
02241 static const uint32 _vehicle_command_translation_table[][4] = {
02242   { // VCT_CMD_START_STOP
02243     CMD_START_STOP_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_STOP_START_TRAIN),
02244     CMD_START_STOP_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_STOP_START_ROAD_VEHICLE),
02245     CMD_START_STOP_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_STOP_START_SHIP),
02246     CMD_START_STOP_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_STOP_START_AIRCRAFT)
02247   },
02248   { // VCT_CMD_CLONE_VEH
02249     CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_TRAIN),
02250     CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_ROAD_VEHICLE),
02251     CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_SHIP),
02252     CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_AIRCRAFT)
02253   },
02254   { // VCT_CMD_TURN_AROUND
02255     CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN),
02256     CMD_TURN_ROADVEH            | CMD_MSG(STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN),
02257     0xffffffff, // invalid for ships
02258     0xffffffff  // invalid for aircrafts
02259   },
02260 };
02261 
02269 void CcStartStopVehicle(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
02270 {
02271   if (result.Failed()) return;
02272 
02273   const Vehicle *v = Vehicle::GetIfValid(p1);
02274   if (v == NULL || !v->IsPrimaryVehicle() || v->owner != _local_company) return;
02275 
02276   StringID msg = (v->vehstatus & VS_STOPPED) ? STR_VEHICLE_COMMAND_STOPPED : STR_VEHICLE_COMMAND_STARTED;
02277   Point pt = RemapCoords(v->x_pos, v->y_pos, v->z_pos);
02278   AddTextEffect(msg, pt.x, pt.y, DAY_TICKS, TE_RISING);
02279 }
02280 
02286 void StartStopVehicle(const Vehicle *v, bool texteffect)
02287 {
02288   assert(v->IsPrimaryVehicle());
02289   DoCommandP(v->tile, v->index, 0, _vehicle_command_translation_table[VCT_CMD_START_STOP][v->type], texteffect ? CcStartStopVehicle : NULL);
02290 }
02291 
02293 static bool IsVehicleRefitable(const Vehicle *v)
02294 {
02295   if (!v->IsStoppedInDepot()) return false;
02296 
02297   do {
02298     if (IsEngineRefittable(v->engine_type)) return true;
02299   } while (v->IsGroundVehicle() && (v = v->Next()) != NULL);
02300 
02301   return false;
02302 }
02303 
02305 struct VehicleViewWindow : Window {
02306 private:
02308   enum PlaneSelections {
02309     SEL_DC_GOTO_DEPOT,  
02310     SEL_DC_CLONE,       
02311 
02312     SEL_RT_REFIT,       
02313     SEL_RT_TURN_AROUND, 
02314 
02315     SEL_DC_BASEPLANE = SEL_DC_GOTO_DEPOT, 
02316     SEL_RT_BASEPLANE = SEL_RT_REFIT,      
02317   };
02318 
02323   void SelectPlane(PlaneSelections plane)
02324   {
02325     switch (plane) {
02326       case SEL_DC_GOTO_DEPOT:
02327       case SEL_DC_CLONE:
02328         this->GetWidget<NWidgetStacked>(VVW_WIDGET_SELECT_DEPOT_CLONE)->SetDisplayedPlane(plane - SEL_DC_BASEPLANE);
02329         break;
02330 
02331       case SEL_RT_REFIT:
02332       case SEL_RT_TURN_AROUND:
02333         this->GetWidget<NWidgetStacked>(VVW_WIDGET_SELECT_REFIT_TURN)->SetDisplayedPlane(plane - SEL_RT_BASEPLANE);
02334         break;
02335 
02336       default:
02337         NOT_REACHED();
02338     }
02339   }
02340 
02341 public:
02342   VehicleViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
02343   {
02344     this->CreateNestedTree(desc);
02345 
02346     /* Sprites for the 'send to depot' button indexed by vehicle type. */
02347     static const SpriteID vehicle_view_goto_depot_sprites[] = {
02348       SPR_SEND_TRAIN_TODEPOT,
02349       SPR_SEND_ROADVEH_TODEPOT,
02350       SPR_SEND_SHIP_TODEPOT,
02351       SPR_SEND_AIRCRAFT_TODEPOT,
02352     };
02353     const Vehicle *v = Vehicle::Get(window_number);
02354     this->GetWidget<NWidgetCore>(VVW_WIDGET_GOTO_DEPOT)->widget_data = vehicle_view_goto_depot_sprites[v->type];
02355 
02356     /* Sprites for the 'clone vehicle' button indexed by vehicle type. */
02357     static const SpriteID vehicle_view_clone_sprites[] = {
02358       SPR_CLONE_TRAIN,
02359       SPR_CLONE_ROADVEH,
02360       SPR_CLONE_SHIP,
02361       SPR_CLONE_AIRCRAFT,
02362     };
02363     this->GetWidget<NWidgetCore>(VVW_WIDGET_CLONE_VEH)->widget_data = vehicle_view_clone_sprites[v->type];
02364 
02365     switch (v->type) {
02366       case VEH_TRAIN:
02367         this->GetWidget<NWidgetCore>(VVW_WIDGET_TURN_AROUND)->tool_tip = STR_VEHICLE_VIEW_TRAIN_REVERSE_TOOLTIP;
02368         break;
02369 
02370       case VEH_ROAD:
02371         break;
02372 
02373       case VEH_SHIP:
02374       case VEH_AIRCRAFT:
02375         this->SelectPlane(SEL_RT_REFIT);
02376         break;
02377 
02378       default: NOT_REACHED();
02379     }
02380     this->FinishInitNested(desc, window_number);
02381     this->owner = v->owner;
02382     this->GetWidget<NWidgetViewport>(VVW_WIDGET_VIEWPORT)->InitializeViewport(this, this->window_number | (1 << 31), _vehicle_view_zoom_levels[v->type]);
02383 
02384     this->GetWidget<NWidgetCore>(VVW_WIDGET_START_STOP_VEH)->tool_tip   = STR_VEHICLE_VIEW_TRAIN_STATE_START_STOP_TOOLTIP + v->type;
02385     this->GetWidget<NWidgetCore>(VVW_WIDGET_CENTER_MAIN_VIEH)->tool_tip = STR_VEHICLE_VIEW_TRAIN_LOCATION_TOOLTIP + v->type;
02386     this->GetWidget<NWidgetCore>(VVW_WIDGET_REFIT_VEH)->tool_tip        = STR_VEHICLE_VIEW_TRAIN_REFIT_TOOLTIP + v->type;
02387     this->GetWidget<NWidgetCore>(VVW_WIDGET_GOTO_DEPOT)->tool_tip       = STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP + v->type;
02388     this->GetWidget<NWidgetCore>(VVW_WIDGET_SHOW_ORDERS)->tool_tip      = STR_VEHICLE_VIEW_TRAIN_ORDERS_TOOLTIP + v->type;
02389     this->GetWidget<NWidgetCore>(VVW_WIDGET_SHOW_DETAILS)->tool_tip     = STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP + v->type;
02390     this->GetWidget<NWidgetCore>(VVW_WIDGET_CLONE_VEH)->tool_tip        = STR_VEHICLE_VIEW_CLONE_TRAIN_INFO + v->type;
02391   }
02392 
02393   ~VehicleViewWindow()
02394   {
02395     DeleteWindowById(WC_VEHICLE_ORDERS, this->window_number, false);
02396     DeleteWindowById(WC_VEHICLE_REFIT, this->window_number, false);
02397     DeleteWindowById(WC_VEHICLE_DETAILS, this->window_number, false);
02398     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false);
02399   }
02400 
02401   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
02402   {
02403     const Vehicle *v = Vehicle::Get(this->window_number);
02404     switch (widget) {
02405       case VVW_WIDGET_FORCE_PROCEED:
02406         if (v->type != VEH_TRAIN) {
02407           size->height = 0;
02408           size->width = 0;
02409         }
02410         break;
02411 
02412       case VVW_WIDGET_VIEWPORT:
02413         size->width = VV_INITIAL_VIEWPORT_WIDTH;
02414         size->height = (v->type == VEH_TRAIN) ? VV_INITIAL_VIEWPORT_HEIGHT_TRAIN : VV_INITIAL_VIEWPORT_HEIGHT;
02415         break;
02416     }
02417   }
02418 
02419   virtual void OnPaint()
02420   {
02421     const Vehicle *v = Vehicle::Get(this->window_number);
02422     bool is_localcompany = v->owner == _local_company;
02423     bool refitable_and_stopped_in_depot = IsVehicleRefitable(v);
02424 
02425     this->SetWidgetDisabledState(VVW_WIDGET_GOTO_DEPOT, !is_localcompany);
02426     this->SetWidgetDisabledState(VVW_WIDGET_REFIT_VEH, !refitable_and_stopped_in_depot || !is_localcompany);
02427     this->SetWidgetDisabledState(VVW_WIDGET_CLONE_VEH, !is_localcompany);
02428 
02429     if (v->type == VEH_TRAIN) {
02430       this->SetWidgetLoweredState(VVW_WIDGET_FORCE_PROCEED, Train::From(v)->force_proceed == TFP_SIGNAL);
02431       this->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !is_localcompany);
02432       this->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !is_localcompany);
02433     }
02434 
02435     this->DrawWidgets();
02436   }
02437 
02438   virtual void SetStringParameters(int widget) const
02439   {
02440     if (widget != VVW_WIDGET_CAPTION) return;
02441 
02442     const Vehicle *v = Vehicle::Get(this->window_number);
02443     SetDParam(0, v->index);
02444   }
02445 
02446   virtual void DrawWidget(const Rect &r, int widget) const
02447   {
02448     if (widget != VVW_WIDGET_START_STOP_VEH) return;
02449 
02450     const Vehicle *v = Vehicle::Get(this->window_number);
02451     StringID str;
02452     if (v->vehstatus & VS_CRASHED) {
02453       str = STR_VEHICLE_STATUS_CRASHED;
02454     } else if (v->type != VEH_AIRCRAFT && v->breakdown_ctr == 1) { // check for aircraft necessary?
02455       str = STR_VEHICLE_STATUS_BROKEN_DOWN;
02456     } else if (v->vehstatus & VS_STOPPED) {
02457       if (v->type == VEH_TRAIN) {
02458         if (v->cur_speed == 0) {
02459           if (Train::From(v)->gcache.cached_power == 0) {
02460             str = STR_VEHICLE_STATUS_TRAIN_NO_POWER;
02461           } else {
02462             str = STR_VEHICLE_STATUS_STOPPED;
02463           }
02464         } else {
02465           SetDParam(0, v->GetDisplaySpeed());
02466           str = STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL;
02467         }
02468       } else { // no train
02469         str = STR_VEHICLE_STATUS_STOPPED;
02470       }
02471     } else if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_TRAIN_STUCK) && !v->current_order.IsType(OT_LOADING)) {
02472       str = STR_VEHICLE_STATUS_TRAIN_STUCK;
02473     } else { // vehicle is in a "normal" state, show current order
02474       switch (v->current_order.GetType()) {
02475         case OT_GOTO_STATION: {
02476           SetDParam(0, v->current_order.GetDestination());
02477           SetDParam(1, v->GetDisplaySpeed());
02478           str = STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL;
02479           break;
02480         }
02481 
02482         case OT_GOTO_DEPOT: {
02483           SetDParam(0, v->type);
02484           SetDParam(1, v->current_order.GetDestination());
02485           SetDParam(2, v->GetDisplaySpeed());
02486           if (v->current_order.GetDepotActionType() & ODATFB_NEAREST_DEPOT) {
02487             /* This case *only* happens when multiple nearest depot orders
02488              * follow eachother (including an order list only one order: a
02489              * nearest depot order) and there are no reachable depots.
02490              * It is primarily to guard for the case that there is no
02491              * depot with index 0, which would be used as fallback for
02492              * evaluating the string in the status bar. */
02493             str = STR_EMPTY;
02494           } else if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
02495             str = STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_VEL;
02496           } else {
02497             str = STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_SERVICE_VEL;
02498           }
02499           break;
02500         }
02501 
02502         case OT_LOADING:
02503           str = STR_VEHICLE_STATUS_LOADING_UNLOADING;
02504           break;
02505 
02506         case OT_GOTO_WAYPOINT: {
02507           assert(v->type == VEH_TRAIN || v->type == VEH_SHIP);
02508           SetDParam(0, v->current_order.GetDestination());
02509           str = STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL;
02510           SetDParam(1, v->GetDisplaySpeed());
02511           break;
02512         }
02513 
02514         case OT_LEAVESTATION:
02515           if (v->type != VEH_AIRCRAFT) {
02516             str = STR_VEHICLE_STATUS_LEAVING;
02517             break;
02518           }
02519           /* FALL THROUGH, if aircraft. Does this even happen? */
02520 
02521         default:
02522           if (v->GetNumManualOrders() == 0) {
02523             str = STR_VEHICLE_STATUS_NO_ORDERS_VEL;
02524             SetDParam(0, v->GetDisplaySpeed());
02525           } else {
02526             str = STR_EMPTY;
02527           }
02528           break;
02529       }
02530     }
02531 
02532     /* draw the flag plus orders */
02533     DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP);
02534     DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_FROMSTRING, SA_HOR_CENTER);
02535   }
02536 
02537   virtual void OnClick(Point pt, int widget, int click_count)
02538   {
02539     const Vehicle *v = Vehicle::Get(this->window_number);
02540 
02541     switch (widget) {
02542       case VVW_WIDGET_START_STOP_VEH: // start stop
02543         if (_ctrl_pressed) {
02544           /* Scroll to current order destination */
02545           TileIndex tile = v->current_order.GetLocation(v);
02546           if (tile != INVALID_TILE) ScrollMainWindowToTile(tile);
02547         } else {
02548           /* Start/Stop */
02549           StartStopVehicle(v, false);
02550         }
02551         break;
02552       case VVW_WIDGET_CENTER_MAIN_VIEH: {// center main view
02553         const Window *mainwindow = FindWindowById(WC_MAIN_WINDOW, 0);
02554         /* code to allow the main window to 'follow' the vehicle if the ctrl key is pressed */
02555         if (_ctrl_pressed && mainwindow->viewport->zoom <= ZOOM_LVL_OUT_4X) {
02556           mainwindow->viewport->follow_vehicle = v->index;
02557         } else {
02558           ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos);
02559         }
02560         break;
02561       }
02562 
02563       case VVW_WIDGET_GOTO_DEPOT: // goto hangar
02564         DoCommandP(v->tile, v->index | (_ctrl_pressed ? DEPOT_SERVICE : 0U), 0, GetCmdSendToDepot(v));
02565         break;
02566       case VVW_WIDGET_REFIT_VEH: // refit
02567         ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID, this);
02568         break;
02569       case VVW_WIDGET_SHOW_ORDERS: // show orders
02570         if (_ctrl_pressed) {
02571           ShowTimetableWindow(v);
02572         } else {
02573           ShowOrdersWindow(v);
02574         }
02575         break;
02576       case VVW_WIDGET_SHOW_DETAILS: // show details
02577         ShowVehicleDetailsWindow(v);
02578         break;
02579       case VVW_WIDGET_CLONE_VEH: // clone vehicle
02580         DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0,
02581                     _vehicle_command_translation_table[VCT_CMD_CLONE_VEH][v->type],
02582                     CcCloneVehicle);
02583         break;
02584       case VVW_WIDGET_TURN_AROUND: // turn around
02585         assert(v->IsGroundVehicle());
02586         DoCommandP(v->tile, v->index, 0,
02587                     _vehicle_command_translation_table[VCT_CMD_TURN_AROUND][v->type]);
02588         break;
02589       case VVW_WIDGET_FORCE_PROCEED: // force proceed
02590         assert(v->type == VEH_TRAIN);
02591         DoCommandP(v->tile, v->index, 0, CMD_FORCE_TRAIN_PROCEED | CMD_MSG(STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL));
02592         break;
02593     }
02594   }
02595 
02596   virtual void OnResize()
02597   {
02598     if (this->viewport != NULL) {
02599       NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(VVW_WIDGET_VIEWPORT);
02600       nvp->UpdateViewportCoordinates(this);
02601     }
02602   }
02603 
02604   virtual void OnTick()
02605   {
02606     const Vehicle *v = Vehicle::Get(this->window_number);
02607     bool veh_stopped = v->IsStoppedInDepot();
02608 
02609     /* Widget VVW_WIDGET_GOTO_DEPOT must be hidden if the vehicle is already stopped in depot.
02610      * Widget VVW_WIDGET_CLONE_VEH should then be shown, since cloning is allowed only while in depot and stopped.
02611      */
02612     PlaneSelections plane = veh_stopped ? SEL_DC_CLONE : SEL_DC_GOTO_DEPOT;
02613     NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(VVW_WIDGET_SELECT_DEPOT_CLONE); // Selection widget 'send to depot' / 'clone'.
02614     if (nwi->shown_plane + SEL_DC_BASEPLANE != plane) {
02615       this->SelectPlane(plane);
02616       this->SetWidgetDirty(VVW_WIDGET_SELECT_DEPOT_CLONE);
02617     }
02618     /* The same system applies to widget VVW_WIDGET_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/
02619     if (v->IsGroundVehicle()) {
02620       PlaneSelections plane = veh_stopped ? SEL_RT_REFIT : SEL_RT_TURN_AROUND;
02621       NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(VVW_WIDGET_SELECT_REFIT_TURN);
02622       if (nwi->shown_plane + SEL_RT_BASEPLANE != plane) {
02623         this->SelectPlane(plane);
02624         this->SetWidgetDirty(VVW_WIDGET_SELECT_REFIT_TURN);
02625       }
02626     }
02627   }
02628 
02634   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
02635   {
02636     if (data == -666) {
02637       /* Autoreplace replaced the vehicle.
02638        * Nothing to do for this window. */
02639       return;
02640     }
02641   }
02642 
02643   virtual bool IsNewGRFInspectable() const
02644   {
02645     return ::IsNewGRFInspectable(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
02646   }
02647 
02648   virtual void ShowNewGRFInspectWindow() const
02649   {
02650 		::ShowNewGRFInspectWindow(GetGrfSpecFeature(Vehicle::Get(this->window_number)->type), this->window_number);
02651   }
02652 };
02653 
02654 
02656 void ShowVehicleViewWindow(const Vehicle *v)
02657 {
02658   AllocateWindowDescFront<VehicleViewWindow>((v->type == VEH_TRAIN) ? &_train_view_desc : &_vehicle_view_desc, v->index);
02659 }
02660 
02666 bool VehicleClicked(const Vehicle *v)
02667 {
02668   assert(v != NULL);
02669   if (!(_thd.place_mode & HT_VEHICLE)) return false;
02670 
02671   v = v->First();
02672   if (!v->IsPrimaryVehicle()) return false;
02673 
02674   _thd.GetCallbackWnd()->OnVehicleSelect(v);
02675   return true;
02676 }
02677 
02678 void StopGlobalFollowVehicle(const Vehicle *v)
02679 {
02680   Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02681   if (w != NULL && w->viewport->follow_vehicle == v->index) {
02682     ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos, true); // lock the main view on the vehicle's last position
02683     w->viewport->follow_vehicle = INVALID_VEHICLE;
02684   }
02685 }
02686 
02687 
02695 void CcBuildPrimaryVehicle(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2)
02696 {
02697   if (result.Failed()) return;
02698 
02699   const Vehicle *v = Vehicle::Get(_new_vehicle_id);
02700   ShowVehicleViewWindow(v);
02701 }
02702 
02708 int GetVehicleWidth(Vehicle *v, EngineImageType image_type)
02709 {
02710   int vehicle_width = 0;
02711 
02712   switch (v->type) {
02713     case VEH_TRAIN:
02714       for (const Train *u = Train::From(v); u != NULL; u = u->Next()) {
02715         vehicle_width += u->GetDisplayImageWidth();
02716       }
02717       break;
02718 
02719     case VEH_ROAD:
02720       for (const RoadVehicle *u = RoadVehicle::From(v); u != NULL; u = u->Next()) {
02721         vehicle_width += u->GetDisplayImageWidth();
02722       }
02723       break;
02724 
02725     default:
02726       bool rtl = _current_text_dir == TD_RTL;
02727       SpriteID sprite = v->GetImage(rtl ? DIR_E : DIR_W, image_type);
02728       const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
02729       vehicle_width = UnScaleByZoom(real_sprite->width, ZOOM_LVL_GUI);
02730 
02731       break;
02732   }
02733 
02734   return vehicle_width;
02735 }