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 "openttd.h"
00014 #include "debug.h"
00015 #include "company_func.h"
00016 #include "gui.h"
00017 #include "window_gui.h"
00018 #include "textbuf_gui.h"
00019 #include "command_func.h"
00020 #include "vehicle_gui.h"
00021 #include "vehicle_gui_base.h"
00022 #include "viewport_func.h"
00023 #include "gfx_func.h"
00024 #include "newgrf_engine.h"
00025 #include "newgrf_text.h"
00026 #include "waypoint_base.h"
00027 #include "roadveh.h"
00028 #include "train.h"
00029 #include "aircraft.h"
00030 #include "depot_base.h"
00031 #include "group_gui.h"
00032 #include "strings_func.h"
00033 #include "window_func.h"
00034 #include "vehicle_func.h"
00035 #include "autoreplace_gui.h"
00036 #include "string_func.h"
00037 #include "widgets/dropdown_func.h"
00038 #include "timetable.h"
00039 #include "vehiclelist.h"
00040 #include "articulated_vehicles.h"
00041 #include "cargotype.h"
00042 #include "spritecache.h"
00043 #include "infrastructure_func.h"
00044 
00045 #include "table/sprites.h"
00046 #include "table/strings.h"
00047 
00048 Sorting _sorting;
00049 
00050 static GUIVehicleList::SortFunction VehicleNumberSorter;
00051 static GUIVehicleList::SortFunction VehicleNameSorter;
00052 static GUIVehicleList::SortFunction VehicleAgeSorter;
00053 static GUIVehicleList::SortFunction VehicleProfitThisYearSorter;
00054 static GUIVehicleList::SortFunction VehicleProfitLastYearSorter;
00055 static GUIVehicleList::SortFunction VehicleCargoSorter;
00056 static GUIVehicleList::SortFunction VehicleReliabilitySorter;
00057 static GUIVehicleList::SortFunction VehicleMaxSpeedSorter;
00058 static GUIVehicleList::SortFunction VehicleModelSorter;
00059 static GUIVehicleList::SortFunction VehicleValueSorter;
00060 static GUIVehicleList::SortFunction VehicleLengthSorter;
00061 static GUIVehicleList::SortFunction VehicleTimeToLiveSorter;
00062 static GUIVehicleList::SortFunction VehicleTimetableDelaySorter;
00063 
00064 GUIVehicleList::SortFunction * const BaseVehicleListWindow::vehicle_sorter_funcs[] = {
00065   &VehicleNumberSorter,
00066   &VehicleNameSorter,
00067   &VehicleAgeSorter,
00068   &VehicleProfitThisYearSorter,
00069   &VehicleProfitLastYearSorter,
00070   &VehicleCargoSorter,
00071   &VehicleReliabilitySorter,
00072   &VehicleMaxSpeedSorter,
00073   &VehicleModelSorter,
00074   &VehicleValueSorter,
00075   &VehicleLengthSorter,
00076   &VehicleTimeToLiveSorter,
00077   &VehicleTimetableDelaySorter,
00078 };
00079 
00080 const StringID BaseVehicleListWindow::vehicle_sorter_names[] = {
00081   STR_SORT_BY_NUMBER,
00082   STR_SORT_BY_NAME,
00083   STR_SORT_BY_AGE,
00084   STR_SORT_BY_PROFIT_THIS_YEAR,
00085   STR_SORT_BY_PROFIT_LAST_YEAR,
00086   STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
00087   STR_SORT_BY_RELIABILITY,
00088   STR_SORT_BY_MAX_SPEED,
00089   STR_SORT_BY_MODEL,
00090   STR_SORT_BY_VALUE,
00091   STR_SORT_BY_LENGTH,
00092   STR_SORT_BY_LIFE_TIME,
00093   STR_SORT_BY_TIMETABLE_DELAY,
00094   INVALID_STRING_ID
00095 };
00096 
00097 void BaseVehicleListWindow::BuildVehicleList(Owner owner, uint16 index, uint16 window_type)
00098 {
00099   if (!this->vehicles.NeedRebuild()) return;
00100 
00101   DEBUG(misc, 3, "Building vehicle list for company %d at station %d", owner, index);
00102 
00103   GenerateVehicleSortList(&this->vehicles, this->vehicle_type, owner, index, window_type);
00104 
00105   uint unitnumber = 0;
00106   for (const Vehicle **v = this->vehicles.Begin(); v != this->vehicles.End(); v++) {
00107     unitnumber = max<uint>(unitnumber, (*v)->unitnumber);
00108   }
00109 
00110   /* Because 111 is much less wide than e.g. 999 we use the
00111    * wider numbers to determine the width instead of just
00112    * the random number that it seems to be. */
00113   if (unitnumber >= 1000) {
00114     this->unitnumber_digits = 4;
00115   } else if (unitnumber >= 100) {
00116     this->unitnumber_digits = 3;
00117   } else {
00118     this->unitnumber_digits = 2;
00119   }
00120 
00121   this->vehicles.RebuildDone();
00122   this->vscroll.SetCount(this->vehicles.Length());
00123 }
00124 
00125 /* cached values for VehicleNameSorter to spare many GetString() calls */
00126 static const Vehicle *_last_vehicle[2] = { NULL, NULL };
00127 
00128 void BaseVehicleListWindow::SortVehicleList()
00129 {
00130   if (this->vehicles.Sort()) return;
00131 
00132   /* invalidate cached values for name sorter - vehicle names could change */
00133   _last_vehicle[0] = _last_vehicle[1] = NULL;
00134 }
00135 
00136 void DepotSortList(VehicleList *list)
00137 {
00138   if (list->Length() < 2) return;
00139   QSortT(list->Begin(), list->Length(), &VehicleNumberSorter);
00140 }
00141 
00143 static void DrawVehicleProfitButton(const Vehicle *v, int x, int y)
00144 {
00145   SpriteID pal;
00146 
00147   /* draw profit-based coloured icons */
00148   if (v->age <= DAYS_IN_YEAR * 2) {
00149     pal = PALETTE_TO_GREY;
00150   } else if (v->GetDisplayProfitLastYear() < 0) {
00151     pal = PALETTE_TO_RED;
00152   } else if (v->GetDisplayProfitLastYear() < 10000) {
00153     pal = PALETTE_TO_YELLOW;
00154   } else {
00155     pal = PALETTE_TO_GREEN;
00156   }
00157   DrawSprite(SPR_BLOT, pal, x, y);
00158 }
00159 
00161 static const int MAX_REFIT_CYCLE = 16;
00162 
00170 byte GetBestFittingSubType(Vehicle *v_from, Vehicle *v_for)
00171 {
00172   const Engine *e_from = Engine::Get(v_from->engine_type);
00173   const Engine *e_for  = Engine::Get(v_for->engine_type);
00174 
00175   /* If one them doesn't carry cargo, there's no need to find a sub type */
00176   if (!e_from->CanCarryCargo() || !e_for->CanCarryCargo()) return 0;
00177 
00178   if (!HasBit(e_from->info.callback_mask, CBM_VEHICLE_CARGO_SUFFIX) ||
00179       !HasBit(e_for->info.callback_mask,  CBM_VEHICLE_CARGO_SUFFIX)) {
00180     /* One of the engines doesn't have cargo suffixes, i.e. sub types. */
00181     return 0;
00182   }
00183 
00184   /* It has to be possible for v_for to carry the cargo of v_from. */
00185   if (!HasBit(e_for->info.refit_mask, v_from->cargo_type)) return 0;
00186 
00187   StringID expected_string = GetCargoSubtypeText(v_from);
00188 
00189   CargoID old_cargo_type = v_for->cargo_type;
00190   byte old_cargo_subtype = v_for->cargo_subtype;
00191   byte ret_refit_cyc = 0;
00192 
00193   /* Set the 'destination' cargo */
00194   v_for->cargo_type = v_from->cargo_type;
00195 
00196   /* Cycle through the refits */
00197   for (byte refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE; refit_cyc++) {
00198     v_for->cargo_subtype = refit_cyc;
00199 
00200     /* Make sure we don't pick up anything cached. */
00201     v_for->First()->InvalidateNewGRFCache();
00202     v_for->InvalidateNewGRFCache();
00203     uint16 callback = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v_for->engine_type, v_for);
00204 
00205     if (callback == 0xFF) callback = CALLBACK_FAILED;
00206     if (callback == CALLBACK_FAILED) break;
00207 
00208     if (GetCargoSubtypeText(v_for) != expected_string) continue;
00209 
00210     /* We found something matching. */
00211     ret_refit_cyc = refit_cyc;
00212     break;
00213   }
00214 
00215   /* Reset the vehicle's cargo type */
00216   v_for->cargo_type    = old_cargo_type;
00217   v_for->cargo_subtype = old_cargo_subtype;
00218 
00219   /* Make sure we don't taint the vehicle. */
00220   v_for->First()->InvalidateNewGRFCache();
00221   v_for->InvalidateNewGRFCache();
00222 
00223   return ret_refit_cyc;
00224 }
00225 
00226 struct RefitOption {
00227   CargoID cargo;
00228   byte subtype;
00229   uint16 value;
00230   EngineID engine;
00231 };
00232 
00233 struct RefitList {
00234   uint num_lines;     
00235   RefitOption *items;
00236 };
00237 
00238 static RefitList *BuildRefitList(const Vehicle *v)
00239 {
00240   uint max_lines = 256;
00241   RefitOption *refit = CallocT<RefitOption>(max_lines);
00242   RefitList *list = CallocT<RefitList>(1);
00243   Vehicle *u = const_cast<Vehicle *>(v);
00244   uint num_lines = 0;
00245   uint i;
00246 
00247   do {
00248     const Engine *e = Engine::Get(u->engine_type);
00249     uint32 cmask = e->info.refit_mask;
00250     byte callback_mask = e->info.callback_mask;
00251 
00252     /* Skip this engine if it has no capacity */
00253     if (u->cargo_cap == 0) continue;
00254 
00255     /* Loop through all cargos in the refit mask */
00256     for (CargoID cid = 0; cid < NUM_CARGO && num_lines < max_lines; cid++) {
00257       /* Skip cargo type if it's not listed */
00258       if (!HasBit(cmask, cid)) continue;
00259 
00260       /* Check the vehicle's callback mask for cargo suffixes */
00261       if (HasBit(callback_mask, CBM_VEHICLE_CARGO_SUFFIX)) {
00262         /* Make a note of the original cargo type. It has to be
00263          * changed to test the cargo & subtype... */
00264         CargoID temp_cargo = u->cargo_type;
00265         byte temp_subtype  = u->cargo_subtype;
00266         byte refit_cyc;
00267 
00268         u->cargo_type = cid;
00269 
00270         for (refit_cyc = 0; refit_cyc < MAX_REFIT_CYCLE && num_lines < max_lines; refit_cyc++) {
00271           bool duplicate = false;
00272           uint16 callback;
00273 
00274           u->cargo_subtype = refit_cyc;
00275 
00276           /* Make sure we don't pick up anything cached. */
00277           u->First()->InvalidateNewGRFCache();
00278           u->InvalidateNewGRFCache();
00279           callback = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, u->engine_type, u);
00280 
00281           if (callback == 0xFF) callback = CALLBACK_FAILED;
00282           if (refit_cyc != 0 && callback == CALLBACK_FAILED) break;
00283 
00284           /* Check if this cargo and subtype combination are listed */
00285           for (i = 0; i < num_lines && !duplicate; i++) {
00286             if (refit[i].cargo == cid && refit[i].value == callback) duplicate = true;
00287           }
00288 
00289           if (duplicate) continue;
00290 
00291           refit[num_lines].cargo   = cid;
00292           refit[num_lines].subtype = refit_cyc;
00293           refit[num_lines].value   = callback;
00294           refit[num_lines].engine  = u->engine_type;
00295           num_lines++;
00296         }
00297 
00298         /* Reset the vehicle's cargo type */
00299         u->cargo_type    = temp_cargo;
00300         u->cargo_subtype = temp_subtype;
00301 
00302         /* And make sure we haven't tainted the cache */
00303         u->First()->InvalidateNewGRFCache();
00304         u->InvalidateNewGRFCache();
00305       } else {
00306         /* No cargo suffix callback -- use no subtype */
00307         bool duplicate = false;
00308 
00309         for (i = 0; i < num_lines && !duplicate; i++) {
00310           if (refit[i].cargo == cid && refit[i].value == CALLBACK_FAILED) duplicate = true;
00311         }
00312 
00313         if (!duplicate) {
00314           refit[num_lines].cargo   = cid;
00315           refit[num_lines].subtype = 0;
00316           refit[num_lines].value   = CALLBACK_FAILED;
00317           refit[num_lines].engine  = INVALID_ENGINE;
00318           num_lines++;
00319         }
00320       }
00321     }
00322   } while ((v->type == VEH_TRAIN || v->type == VEH_ROAD) && (u = u->Next()) != NULL && num_lines < max_lines);
00323 
00324   list->num_lines = num_lines;
00325   list->items = refit;
00326 
00327   return list;
00328 }
00329 
00338 static void DrawVehicleRefitWindow(const RefitList *list, int sel, uint pos, uint rows, uint delta, const Rect &r)
00339 {
00340   uint y = r.top + WD_MATRIX_TOP;
00341   /* Draw the list, and find the selected cargo (by its position in list) */
00342   for (uint i = pos; i < pos + rows && i < list->num_lines; i++) {
00343     TextColour colour = (sel == (int)i) ? TC_WHITE : TC_BLACK;
00344     RefitOption *refit = &list->items[i];
00345 
00346     /* Get the cargo name */
00347     SetDParam(0, CargoSpec::Get(refit->cargo)->name);
00348 
00349     /* If the callback succeeded, draw the cargo suffix */
00350     if (refit->value != CALLBACK_FAILED) {
00351       SetDParam(1, GetGRFStringID(GetEngineGRFID(refit->engine), 0xD000 + refit->value));
00352       DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y, STR_JUST_STRING_SPACE_STRING, colour);
00353     } else {
00354       DrawString(r.left + WD_MATRIX_LEFT, r.right - WD_MATRIX_RIGHT, y, STR_JUST_STRING, colour);
00355     }
00356 
00357     y += delta;
00358   }
00359 }
00360 
00362 enum VehicleRefitWidgets {
00363   VRW_CAPTION,
00364   VRW_SELECTHEADER,
00365   VRW_MATRIX,
00366   VRW_SCROLLBAR,
00367   VRW_INFOPANEL,
00368   VRW_REFITBUTTON,
00369 };
00370 
00372 struct RefitWindow : public Window {
00373   int sel;              
00374   RefitOption *cargo;   
00375   RefitList *list;      
00376   uint length;          
00377   VehicleOrderID order; 
00378 
00379   RefitWindow(const WindowDesc *desc, const Vehicle *v, VehicleOrderID order) : Window()
00380   {
00381     this->CreateNestedTree(desc);
00382 
00383     this->GetWidget<NWidgetCore>(VRW_SELECTHEADER)->tool_tip = STR_REFIT_TRAIN_LIST_TOOLTIP + v->type;
00384     this->GetWidget<NWidgetCore>(VRW_MATRIX)->tool_tip       = STR_REFIT_TRAIN_LIST_TOOLTIP + v->type;
00385     NWidgetCore *nwi = this->GetWidget<NWidgetCore>(VRW_REFITBUTTON);
00386     nwi->widget_data = STR_REFIT_TRAIN_REFIT_BUTTON + v->type;
00387     nwi->tool_tip    = STR_REFIT_TRAIN_REFIT_TOOLTIP + v->type;
00388 
00389     this->FinishInitNested(desc, v->index);
00390     this->owner = v->owner;
00391 
00392     this->order = order;
00393     this->sel  = -1;
00394     this->list = BuildRefitList(v);
00395     if (v->type == VEH_TRAIN) this->length = CountVehiclesInChain(v);
00396     this->vscroll.SetCount(this->list->num_lines);
00397   }
00398 
00399   ~RefitWindow()
00400   {
00401     free(this->list->items);
00402     free(this->list);
00403   }
00404 
00405   virtual void OnPaint()
00406   {
00407     Vehicle *v = Vehicle::Get(this->window_number);
00408 
00409     if (v->type == VEH_TRAIN) {
00410       uint length = CountVehiclesInChain(v);
00411 
00412       if (length != this->length) {
00413         /* Consist length has changed, so rebuild the refit list */
00414         free(this->list->items);
00415         free(this->list);
00416         this->list = BuildRefitList(v);
00417         this->length = length;
00418       }
00419     }
00420 
00421     this->vscroll.SetCount(this->list->num_lines);
00422 
00423     this->cargo = (this->sel >= 0 && this->sel < (int)this->list->num_lines) ? &this->list->items[this->sel] : NULL;
00424     this->DrawWidgets();
00425   }
00426 
00427   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
00428   {
00429     switch (widget) {
00430       case VRW_MATRIX:
00431         resize->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
00432         size->height = resize->height * 8;
00433         break;
00434     }
00435   }
00436 
00437   virtual void SetStringParameters(int widget) const
00438   {
00439     if (widget == VRW_CAPTION) SetDParam(0, Vehicle::Get(this->window_number)->index);
00440   }
00441 
00442   virtual void DrawWidget(const Rect &r, int widget) const
00443   {
00444     switch (widget) {
00445       case VRW_MATRIX:
00446         DrawVehicleRefitWindow(this->list, this->sel, this->vscroll.GetPosition(), this->vscroll.GetCapacity(), this->resize.step_height, r);
00447         break;
00448 
00449       case VRW_INFOPANEL:
00450         if (this->cargo != NULL) {
00451           Vehicle *v = Vehicle::Get(this->window_number);
00452           CommandCost cost = DoCommand(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8, DC_QUERY_COST, GetCmdRefitVeh(v->type));
00453           if (CmdSucceeded(cost)) {
00454             SetDParam(0, this->cargo->cargo);
00455             SetDParam(1, _returned_refit_capacity);
00456             SetDParam(2, cost.GetCost());
00457             DrawStringMultiLine(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT,
00458                 r.top + WD_FRAMERECT_TOP, r.bottom - WD_FRAMERECT_BOTTOM, STR_REFIT_NEW_CAPACITY_COST_OF_REFIT);
00459           }
00460         }
00461         break;
00462     }
00463   }
00464 
00465   virtual void OnDoubleClick(Point pt, int widget)
00466   {
00467     if (widget == VRW_MATRIX) this->OnClick(pt, VRW_REFITBUTTON);
00468   }
00469 
00470   virtual void OnClick(Point pt, int widget)
00471   {
00472     switch (widget) {
00473       case VRW_MATRIX: { // listbox
00474         int y = pt.y - this->GetWidget<NWidgetBase>(VRW_MATRIX)->pos_y;
00475         if (y >= 0) {
00476           this->sel = (y / (int)this->resize.step_height) + this->vscroll.GetPosition();
00477           this->SetDirty();
00478         }
00479         break;
00480       }
00481 
00482       case VRW_REFITBUTTON: // refit button
00483         if (this->cargo != NULL) {
00484           const Vehicle *v = Vehicle::Get(this->window_number);
00485 
00486           if (this->order == INVALID_VEH_ORDER_ID) {
00487             if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8, GetCmdRefitVeh(v))) delete this;
00488           } else {
00489             if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8 | this->order << 16, CMD_ORDER_REFIT)) delete this;
00490           }
00491         }
00492         break;
00493     }
00494   }
00495 
00496   virtual void OnResize()
00497   {
00498     this->vscroll.SetCapacityFromWidget(this, VRW_MATRIX);
00499     this->GetWidget<NWidgetCore>(VRW_MATRIX)->widget_data = (this->vscroll.GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
00500   }
00501 };
00502 
00503 static const NWidgetPart _nested_vehicle_refit_widgets[] = {
00504   NWidget(NWID_HORIZONTAL),
00505     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00506     NWidget(WWT_CAPTION, COLOUR_GREY, VRW_CAPTION), SetDataTip(STR_REFIT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00507   EndContainer(),
00508   NWidget(WWT_TEXTBTN, COLOUR_GREY, VRW_SELECTHEADER), SetDataTip(STR_REFIT_TITLE, STR_NULL), SetResize(1, 0),
00509   /* Matrix + scrollbar. */
00510   NWidget(NWID_HORIZONTAL),
00511     NWidget(WWT_MATRIX, COLOUR_GREY, VRW_MATRIX), SetMinimalSize(228, 112), SetResize(1, 14), SetFill(1, 1), SetDataTip(0x801, STR_NULL),
00512     NWidget(WWT_SCROLLBAR, COLOUR_GREY, VRW_SCROLLBAR),
00513   EndContainer(),
00514   NWidget(WWT_PANEL, COLOUR_GREY, VRW_INFOPANEL), SetMinimalTextLines(2, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM), SetResize(1, 0), EndContainer(),
00515   NWidget(NWID_HORIZONTAL),
00516     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VRW_REFITBUTTON), SetFill(1, 0), SetResize(1, 0),
00517     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00518   EndContainer(),
00519 };
00520 
00521 static const WindowDesc _vehicle_refit_desc(
00522   WDP_AUTO, 240, 174,
00523   WC_VEHICLE_REFIT, WC_VEHICLE_VIEW,
00524   WDF_UNCLICK_BUTTONS | WDF_CONSTRUCTION,
00525   _nested_vehicle_refit_widgets, lengthof(_nested_vehicle_refit_widgets)
00526 );
00527 
00533 void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent)
00534 {
00535   DeleteWindowById(WC_VEHICLE_REFIT, v->index);
00536   RefitWindow *w = new RefitWindow(&_vehicle_refit_desc, v, order);
00537   w->parent = parent;
00538 }
00539 
00541 uint ShowRefitOptionsList(int left, int right, int y, EngineID engine)
00542 {
00543   /* List of cargo types of this engine */
00544   uint32 cmask = GetUnionOfArticulatedRefitMasks(engine, false);
00545   /* List of cargo types available in this climate */
00546   uint32 lmask = _cargo_mask;
00547   char string[512];
00548   char *b = string;
00549 
00550   /* Draw nothing if the engine is not refittable */
00551   if (CountBits(cmask) <= 1) return y;
00552 
00553   b = InlineString(b, STR_PURCHASE_INFO_REFITTABLE_TO);
00554 
00555   if (cmask == lmask) {
00556     /* Engine can be refitted to all types in this climate */
00557     b = InlineString(b, STR_PURCHASE_INFO_ALL_TYPES);
00558   } else {
00559     /* Check if we are able to refit to more cargo types and unable to. If
00560      * so, invert the cargo types to list those that we can't refit to. */
00561     if (CountBits(cmask ^ lmask) < CountBits(cmask)) {
00562       cmask ^= lmask;
00563       b = InlineString(b, STR_PURCHASE_INFO_ALL_BUT);
00564     }
00565 
00566     bool first = true;
00567 
00568     /* Add each cargo type to the list */
00569     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00570       if (!HasBit(cmask, cid)) continue;
00571 
00572       if (b >= lastof(string) - (2 + 2 * 4)) break; // ", " and two calls to Utf8Encode()
00573 
00574       if (!first) b = strecpy(b, ", ", lastof(string));
00575       first = false;
00576 
00577       b = InlineString(b, CargoSpec::Get(cid)->name);
00578     }
00579   }
00580 
00581   /* Terminate and display the completed string */
00582   *b = '\0';
00583 
00584   /* Make sure we detect any buffer overflow */
00585   assert(b < endof(string));
00586 
00587   SetDParamStr(0, string);
00588   return DrawStringMultiLine(left, right, y, INT32_MAX, STR_JUST_RAW_STRING);
00589 }
00590 
00592 StringID GetCargoSubtypeText(const Vehicle *v)
00593 {
00594   if (HasBit(EngInfo(v->engine_type)->callback_mask, CBM_VEHICLE_CARGO_SUFFIX)) {
00595     uint16 cb = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v->engine_type, v);
00596     if (cb != CALLBACK_FAILED) {
00597       return GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + cb);
00598     }
00599   }
00600   return STR_EMPTY;
00601 }
00602 
00604 static int CDECL VehicleNumberSorter(const Vehicle * const *a, const Vehicle * const *b)
00605 {
00606   return (*a)->unitnumber - (*b)->unitnumber;
00607 }
00608 
00610 static int CDECL VehicleNameSorter(const Vehicle * const *a, const Vehicle * const *b)
00611 {
00612   static char last_name[2][64];
00613 
00614   if (*a != _last_vehicle[0]) {
00615     _last_vehicle[0] = *a;
00616     SetDParam(0, (*a)->index);
00617     GetString(last_name[0], STR_VEHICLE_NAME, lastof(last_name[0]));
00618   }
00619 
00620   if (*b != _last_vehicle[1]) {
00621     _last_vehicle[1] = *b;
00622     SetDParam(0, (*b)->index);
00623     GetString(last_name[1], STR_VEHICLE_NAME, lastof(last_name[1]));
00624   }
00625 
00626   int r = strcmp(last_name[0], last_name[1]);
00627   return (r != 0) ? r : VehicleNumberSorter(a, b);
00628 }
00629 
00631 static int CDECL VehicleAgeSorter(const Vehicle * const *a, const Vehicle * const *b)
00632 {
00633   int r = (*a)->age - (*b)->age;
00634   return (r != 0) ? r : VehicleNumberSorter(a, b);
00635 }
00636 
00638 static int CDECL VehicleProfitThisYearSorter(const Vehicle * const *a, const Vehicle * const *b)
00639 {
00640   int r = ClampToI32((*a)->GetDisplayProfitThisYear() - (*b)->GetDisplayProfitThisYear());
00641   return (r != 0) ? r : VehicleNumberSorter(a, b);
00642 }
00643 
00645 static int CDECL VehicleProfitLastYearSorter(const Vehicle * const *a, const Vehicle * const *b)
00646 {
00647   int r = ClampToI32((*a)->GetDisplayProfitLastYear() - (*b)->GetDisplayProfitLastYear());
00648   return (r != 0) ? r : VehicleNumberSorter(a, b);
00649 }
00650 
00652 static int CDECL VehicleCargoSorter(const Vehicle * const *a, const Vehicle * const *b)
00653 {
00654   const Vehicle *v;
00655   CargoArray diff;
00656 
00657   /* Append the cargo of the connected weagons */
00658   for (v = *a; v != NULL; v = v->Next()) diff[v->cargo_type] += v->cargo_cap;
00659   for (v = *b; v != NULL; v = v->Next()) diff[v->cargo_type] -= v->cargo_cap;
00660 
00661   int r = 0;
00662   for (CargoID i = 0; i < NUM_CARGO; i++) {
00663     r = diff[i];
00664     if (r != 0) break;
00665   }
00666 
00667   return (r != 0) ? r : VehicleNumberSorter(a, b);
00668 }
00669 
00671 static int CDECL VehicleReliabilitySorter(const Vehicle * const *a, const Vehicle * const *b)
00672 {
00673   int r = (*a)->reliability - (*b)->reliability;
00674   return (r != 0) ? r : VehicleNumberSorter(a, b);
00675 }
00676 
00678 static int CDECL VehicleMaxSpeedSorter(const Vehicle * const *a, const Vehicle * const *b)
00679 {
00680   int r = 0;
00681   if ((*a)->type == VEH_TRAIN && (*b)->type == VEH_TRAIN) {
00682     r = Train::From(*a)->tcache.cached_max_speed - Train::From(*b)->tcache.cached_max_speed;
00683   } else {
00684     r = (*a)->max_speed - (*b)->max_speed;
00685   }
00686   return (r != 0) ? r : VehicleNumberSorter(a, b);
00687 }
00688 
00690 static int CDECL VehicleModelSorter(const Vehicle * const *a, const Vehicle * const *b)
00691 {
00692   int r = (*a)->engine_type - (*b)->engine_type;
00693   return (r != 0) ? r : VehicleNumberSorter(a, b);
00694 }
00695 
00697 static int CDECL VehicleValueSorter(const Vehicle * const *a, const Vehicle * const *b)
00698 {
00699   const Vehicle *u;
00700   Money diff = 0;
00701 
00702   for (u = *a; u != NULL; u = u->Next()) diff += u->value;
00703   for (u = *b; u != NULL; u = u->Next()) diff -= u->value;
00704 
00705   int r = ClampToI32(diff);
00706   return (r != 0) ? r : VehicleNumberSorter(a, b);
00707 }
00708 
00710 static int CDECL VehicleLengthSorter(const Vehicle * const *a, const Vehicle * const *b)
00711 {
00712   int r = 0;
00713   switch ((*a)->type) {
00714     case VEH_TRAIN:
00715       r = Train::From(*a)->tcache.cached_total_length - Train::From(*b)->tcache.cached_total_length;
00716       break;
00717 
00718     case VEH_ROAD: {
00719       const RoadVehicle *u;
00720       for (u = RoadVehicle::From(*a); u != NULL; u = u->Next()) r += u->rcache.cached_veh_length;
00721       for (u = RoadVehicle::From(*b); u != NULL; u = u->Next()) r -= u->rcache.cached_veh_length;
00722     } break;
00723 
00724     default: NOT_REACHED();
00725   }
00726   return (r != 0) ? r : VehicleNumberSorter(a, b);
00727 }
00728 
00730 static int CDECL VehicleTimeToLiveSorter(const Vehicle * const *a, const Vehicle * const *b)
00731 {
00732   int r = ClampToI32(((*a)->max_age - (*a)->age) - ((*b)->max_age - (*b)->age));
00733   return (r != 0) ? r : VehicleNumberSorter(a, b);
00734 }
00735 
00737 static int CDECL VehicleTimetableDelaySorter(const Vehicle * const *a, const Vehicle * const *b)
00738 {
00739   int r = (*a)->lateness_counter - (*b)->lateness_counter;
00740   return (r != 0) ? r : VehicleNumberSorter(a, b);
00741 }
00742 
00743 void InitializeGUI()
00744 {
00745   MemSetT(&_sorting, 0);
00746 }
00747 
00754 static inline void ChangeVehicleWindow(WindowClass window_class, VehicleID from_index, VehicleID to_index)
00755 {
00756   Window *w = FindWindowById(window_class, from_index);
00757   if (w != NULL) {
00758     w->window_number = to_index;
00759     if (w->viewport != NULL) w->viewport->follow_vehicle = to_index;
00760     if (to_index != INVALID_VEHICLE) w->InvalidateData();
00761   }
00762 }
00763 
00769 void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index)
00770 {
00771   ChangeVehicleWindow(WC_VEHICLE_VIEW,      from_index, to_index);
00772   ChangeVehicleWindow(WC_VEHICLE_ORDERS,    from_index, to_index);
00773   ChangeVehicleWindow(WC_VEHICLE_REFIT,     from_index, to_index);
00774   ChangeVehicleWindow(WC_VEHICLE_DETAILS,   from_index, to_index);
00775   ChangeVehicleWindow(WC_VEHICLE_TIMETABLE, from_index, to_index);
00776 }
00777 
00778 enum VehicleListWindowWidgets {
00779   VLW_WIDGET_CAPTION,
00780   VLW_WIDGET_SORT_ORDER,
00781   VLW_WIDGET_SORT_BY_PULLDOWN,
00782   VLW_WIDGET_LIST,
00783   VLW_WIDGET_SCROLLBAR,
00784   VLW_WIDGET_HIDE_BUTTONS,
00785   VLW_WIDGET_AVAILABLE_VEHICLES,
00786   VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
00787   VLW_WIDGET_STOP_ALL,
00788   VLW_WIDGET_START_ALL,
00789 };
00790 
00791 static const NWidgetPart _nested_vehicle_list[] = {
00792   NWidget(NWID_HORIZONTAL),
00793     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00794     NWidget(WWT_CAPTION, COLOUR_GREY, VLW_WIDGET_CAPTION),
00795     NWidget(WWT_SHADEBOX, COLOUR_GREY),
00796     NWidget(WWT_STICKYBOX, COLOUR_GREY),
00797   EndContainer(),
00798 
00799   NWidget(NWID_HORIZONTAL),
00800     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLW_WIDGET_SORT_ORDER), SetMinimalSize(81, 12), SetFill(0, 1), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
00801     NWidget(WWT_DROPDOWN, COLOUR_GREY, VLW_WIDGET_SORT_BY_PULLDOWN), SetMinimalSize(167, 12), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIAP),
00802     NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetFill(1, 1), SetResize(1, 0),
00803     EndContainer(),
00804   EndContainer(),
00805 
00806   NWidget(NWID_HORIZONTAL),
00807     NWidget(WWT_MATRIX, COLOUR_GREY, VLW_WIDGET_LIST), SetMinimalSize(248, 0), SetFill(1, 0),
00808     NWidget(WWT_SCROLLBAR, COLOUR_GREY, VLW_WIDGET_SCROLLBAR),
00809   EndContainer(),
00810 
00811   NWidget(NWID_HORIZONTAL),
00812     NWidget(NWID_SELECTION, INVALID_COLOUR, VLW_WIDGET_HIDE_BUTTONS),
00813       NWidget(NWID_HORIZONTAL),
00814         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLW_WIDGET_AVAILABLE_VEHICLES), SetMinimalSize(106, 12), SetFill(0, 1),
00815                 SetDataTip(0x0, STR_VEHICLE_LIST_AVAILABLE_ENGINES_TOOLTIP),
00816         NWidget(WWT_DROPDOWN, COLOUR_GREY, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN), SetMinimalSize(118, 12), SetFill(0, 1),
00817                 SetDataTip(STR_VEHICLE_LIST_MANAGE_LIST, STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP),
00818         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VLW_WIDGET_STOP_ALL), SetMinimalSize(12, 12), SetFill(0, 1),
00819                 SetDataTip(SPR_FLAG_VEH_STOPPED, STR_VEHICLE_LIST_MASS_STOP_LIST_TOOLTIP),
00820         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VLW_WIDGET_START_ALL), SetMinimalSize(12, 12), SetFill(0, 1),
00821                 SetDataTip(SPR_FLAG_VEH_RUNNING, STR_VEHICLE_LIST_MASS_START_LIST_TOOLTIP),
00822         NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(0, 12), SetResize(1, 0), SetFill(1, 1), EndContainer(),
00823       EndContainer(),
00824       /* Widget to be shown for other companies hiding the previous 5 widgets. */
00825       NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 0), EndContainer(),
00826     EndContainer(),
00827     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00828   EndContainer(),
00829 };
00830 
00831 static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, VehicleOrderID start = 0)
00832 {
00833   const Order *order = v->GetOrder(start);
00834   if (order == NULL) return;
00835 
00836   int i = 0;
00837   VehicleOrderID oid = start;
00838 
00839   do {
00840     if (oid == v->cur_order_index) DrawString(left, right, y, STR_TINY_RIGHT_ARROW, TC_BLACK);
00841 
00842     if (order->IsType(OT_GOTO_STATION)) {
00843       SetDParam(0, order->GetDestination());
00844       DrawString(left + 6, right - 6, y, STR_TINY_BLACK_STATION);
00845 
00846       y += FONT_HEIGHT_SMALL;
00847       if (++i == 4) break;
00848     }
00849 
00850     oid++;
00851     order = order->next;
00852     if (order == NULL) {
00853       order = v->orders.list->GetFirstOrder();
00854       oid = 0;
00855     }
00856   } while (oid != start);
00857 }
00858 
00868 static void DrawVehicleImage(const Vehicle *v, int left, int right, int y, VehicleID selection, int skip)
00869 {
00870   switch (v->type) {
00871     case VEH_TRAIN:    DrawTrainImage(Train::From(v), left, right, y, selection, skip); break;
00872     case VEH_ROAD:     DrawRoadVehImage(v, left, right, y, selection);  break;
00873     case VEH_SHIP:     DrawShipImage(v, left, right, y, selection);     break;
00874     case VEH_AIRCRAFT: DrawAircraftImage(v, left, right, y, selection); break;
00875     default: NOT_REACHED();
00876   }
00877 }
00878 
00885 uint GetVehicleListHeight(VehicleType type, uint divisor)
00886 {
00887   /* Name + vehicle + profit */
00888   uint base = GetVehicleHeight(type) + 2 * FONT_HEIGHT_SMALL;
00889   /* Drawing of the 4 small orders + profit*/
00890   if (type >= VEH_SHIP) base = max(base, 5U * FONT_HEIGHT_SMALL);
00891 
00892   if (divisor == 1) return base;
00893 
00894   /* Make sure the height is dividable by divisor */
00895   uint rem = base % divisor;
00896   return base + (rem == 0 ? 0 : divisor - rem);
00897 }
00898 
00905 void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int line_height, const Rect &r) const
00906 {
00907   int left = r.left + WD_MATRIX_LEFT;
00908   int right = r.right - WD_MATRIX_RIGHT;
00909   int width = right - left;
00910   bool rtl = _dynlang.text_dir == TD_RTL;
00911 
00912   int text_offset = GetDigitWidth() * this->unitnumber_digits + WD_FRAMERECT_RIGHT;
00913   int text_left  = left  + (rtl ?           0 : text_offset);
00914   int text_right = right - (rtl ? text_offset :           0);
00915 
00916   bool show_orderlist = vehicle_type >= VEH_SHIP;
00917   int orderlist_left  = left  + (rtl ? 0 : max(100 + text_offset, width / 2));
00918   int orderlist_right = right - (rtl ? max(100 + text_offset, width / 2) : 0);
00919 
00920   int image_left  = (rtl && show_orderlist) ? orderlist_right : text_left;
00921   int image_right = (!rtl && show_orderlist) ? orderlist_left : text_right;
00922 
00923   int vehicle_button_x = rtl ? right - 8 : left;
00924 
00925   int y = r.top;
00926   uint max = min(this->vscroll.GetPosition() + this->vscroll.GetCapacity(), this->vehicles.Length());
00927   for (uint i = this->vscroll.GetPosition(); i < max; ++i) {
00928     const Vehicle *v = this->vehicles[i];
00929     StringID str;
00930 
00931     SetDParam(0, v->GetDisplayProfitThisYear());
00932     SetDParam(1, v->GetDisplayProfitLastYear());
00933 
00934     DrawVehicleImage(v, image_left, image_right, y + FONT_HEIGHT_SMALL - 1, selected_vehicle, 0);
00935     DrawString(text_left, text_right, y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 1, STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR);
00936 
00937     if (v->name != NULL) {
00938       /* The vehicle got a name so we will print it */
00939       SetDParam(0, v->index);
00940       DrawString(text_left, text_right, y, STR_TINY_BLACK_VEHICLE);
00941     } else if (v->group_id != DEFAULT_GROUP) {
00942       /* The vehicle has no name, but is member of a group, so print group name */
00943       SetDParam(0, v->group_id);
00944       DrawString(text_left, text_right, y, STR_TINY_GROUP, TC_BLACK);
00945     }
00946 
00947     if (show_orderlist) DrawSmallOrderList(v, orderlist_left, orderlist_right, y, v->cur_order_index);
00948 
00949     if (v->IsInDepot()) {
00950       str = STR_BLUE_COMMA;
00951     } else {
00952       str = (v->age > v->max_age - DAYS_IN_LEAP_YEAR) ? STR_RED_COMMA : STR_BLACK_COMMA;
00953     }
00954 
00955     SetDParam(0, v->unitnumber);
00956     DrawString(left, right, y + 2, str);
00957 
00958     DrawVehicleProfitButton(v, vehicle_button_x, y + FONT_HEIGHT_NORMAL + 3);
00959 
00960     y += line_height;
00961   }
00962 }
00963 
00973 struct VehicleListWindow : public BaseVehicleListWindow {
00974 private:
00976   enum ButtonPlanes {
00977     BP_SHOW_BUTTONS, 
00978     BP_HIDE_BUTTONS, 
00979   };
00980 
00981 public:
00982   VehicleListWindow(const WindowDesc *desc, WindowNumber window_number) : BaseVehicleListWindow()
00983   {
00984     uint16 window_type = window_number & VLW_MASK;
00985     CompanyID company = (CompanyID)GB(window_number, 0, 8);
00986 
00987     this->vehicle_type = (VehicleType)GB(window_number, 11, 5);
00988 
00989     /* Set up sorting. Make the window-specific _sorting variable
00990      * point to the correct global _sorting struct so we are freed
00991      * from having conditionals during window operation */
00992     switch (this->vehicle_type) {
00993       case VEH_TRAIN:    this->sorting = &_sorting.train; break;
00994       case VEH_ROAD:     this->sorting = &_sorting.roadveh; break;
00995       case VEH_SHIP:     this->sorting = &_sorting.ship; break;
00996       case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break;
00997       default: NOT_REACHED();
00998     }
00999 
01000     this->vehicles.SetListing(*this->sorting);
01001     this->vehicles.ForceRebuild();
01002     this->vehicles.NeedResort();
01003     this->BuildVehicleList(company, GB(window_number, 16, 16), window_type);
01004     this->SortVehicleList();
01005 
01006     this->CreateNestedTree(desc);
01007 
01008     /* Set up the window widgets */
01009     this->GetWidget<NWidgetCore>(VLW_WIDGET_LIST)->tool_tip                  = STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP + this->vehicle_type;
01010     this->GetWidget<NWidgetCore>(VLW_WIDGET_AVAILABLE_VEHICLES)->widget_data = STR_VEHICLE_LIST_AVAILABLE_TRAINS + this->vehicle_type;
01011 
01012     if (window_type == VLW_SHARED_ORDERS) {
01013       this->GetWidget<NWidgetCore>(VLW_WIDGET_CAPTION)->widget_data = STR_VEHICLE_LIST_SHARED_ORDERS_LIST_CAPTION;
01014     } else {
01015       this->GetWidget<NWidgetCore>(VLW_WIDGET_CAPTION)->widget_data = STR_VEHICLE_LIST_TRAIN_CAPTION + this->vehicle_type;
01016     }
01017 
01018     this->FinishInitNested(desc, window_number);
01019     this->owner = company;
01020 
01021     if (this->vehicle_type == VEH_TRAIN) ResizeWindow(this, 65, 0);
01022   }
01023 
01024   ~VehicleListWindow()
01025   {
01026     *this->sorting = this->vehicles.GetListing();
01027   }
01028 
01029   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01030   {
01031     if (widget != VLW_WIDGET_LIST) return;
01032 
01033     resize->width = 0;
01034     resize->height = GetVehicleListHeight(this->vehicle_type, 1);
01035 
01036     switch (this->vehicle_type) {
01037       case VEH_TRAIN:
01038         resize->width = 1;
01039         /* Fallthrough */
01040       case VEH_ROAD:
01041         size->height = 6 * resize->height;
01042         break;
01043       case VEH_SHIP:
01044       case VEH_AIRCRAFT:
01045         size->height = 4 * resize->height;
01046         break;
01047       default: NOT_REACHED();
01048     }
01049   }
01050 
01051   virtual void SetStringParameters(int widget) const
01052   {
01053     if (widget != VLW_WIDGET_CAPTION) return;
01054 
01055     const uint16 index = GB(this->window_number, 16, 16);
01056     switch (this->window_number & VLW_MASK) {
01057       case VLW_SHARED_ORDERS: // Shared Orders
01058         if (this->vehicles.Length() == 0) {
01059           /* We can't open this window without vehicles using this order
01060            * and we should close the window when deleting the order      */
01061           NOT_REACHED();
01062         }
01063         SetDParam(0, this->vscroll.GetCount());
01064         break;
01065 
01066       case VLW_STANDARD: // Company Name
01067         SetDParam(0, STR_COMPANY_NAME);
01068         SetDParam(1, index);
01069         SetDParam(2, this->vscroll.GetCount());
01070         break;
01071 
01072       case VLW_WAYPOINT_LIST:
01073         SetDParam(0, STR_WAYPOINT_NAME);
01074         SetDParam(1, index);
01075         SetDParam(2, this->vscroll.GetCount());
01076         break;
01077 
01078       case VLW_STATION_LIST: // Station Name
01079         SetDParam(0, STR_STATION_NAME);
01080         SetDParam(1, index);
01081         SetDParam(2, this->vscroll.GetCount());
01082         break;
01083 
01084       case VLW_DEPOT_LIST:
01085         SetDParam(0, STR_DEPOT_TRAIN_CAPTION + this->vehicle_type);
01086         if (this->vehicle_type == VEH_AIRCRAFT) {
01087           SetDParam(1, index); // Airport name
01088         } else {
01089           SetDParam(1, Depot::Get(index)->town_index);
01090         }
01091         SetDParam(2, this->vscroll.GetCount());
01092         break;
01093       default: NOT_REACHED();
01094     }
01095   }
01096 
01097   virtual void DrawWidget(const Rect &r, int widget) const
01098   {
01099     switch (widget) {
01100       case VLW_WIDGET_SORT_ORDER:
01101         /* draw arrow pointing up/down for ascending/descending sorting */
01102         this->DrawSortButtonState(widget, this->vehicles.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
01103         break;
01104 
01105       case VLW_WIDGET_LIST:
01106         this->DrawVehicleListItems(INVALID_VEHICLE, this->resize.step_height, r);
01107         break;
01108     }
01109   }
01110 
01111   virtual void OnPaint()
01112   {
01113     const uint16 window_type = this->window_number & VLW_MASK;
01114 
01115     this->BuildVehicleList(this->owner, GB(this->window_number, 16, 16), window_type);
01116     this->SortVehicleList();
01117 
01118     if (this->vehicles.Length() == 0) HideDropDownMenu(this);
01119 
01120     /* Hide the widgets that we will not use in this window
01121      * Some windows contains actions only fit for the owner */
01122     int plane_to_show = (this->owner == _local_company) ? BP_SHOW_BUTTONS : BP_HIDE_BUTTONS;
01123     NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(VLW_WIDGET_HIDE_BUTTONS);
01124     if (plane_to_show != nwi->shown_plane) {
01125       nwi->SetDisplayedPlane(plane_to_show);
01126       nwi->SetDirty(this);
01127     }
01128     if (this->owner == _local_company) {
01129       this->SetWidgetDisabledState(VLW_WIDGET_AVAILABLE_VEHICLES, window_type != VLW_STANDARD);
01130       this->SetWidgetsDisabledState(this->vehicles.Length() == 0,
01131         VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
01132         VLW_WIDGET_STOP_ALL,
01133         VLW_WIDGET_START_ALL,
01134         WIDGET_LIST_END);
01135     }
01136 
01137     /* Set text of sort by dropdown widget. */
01138     this->GetWidget<NWidgetCore>(VLW_WIDGET_SORT_BY_PULLDOWN)->widget_data = this->vehicle_sorter_names[this->vehicles.SortType()];
01139 
01140     this->DrawWidgets();
01141   }
01142 
01143   virtual void OnClick(Point pt, int widget)
01144   {
01145     switch (widget) {
01146       case VLW_WIDGET_SORT_ORDER: // Flip sorting method ascending/descending
01147         this->vehicles.ToggleSortOrder();
01148         this->SetDirty();
01149         break;
01150 
01151       case VLW_WIDGET_SORT_BY_PULLDOWN:// Select sorting criteria dropdown menu
01152         ShowDropDownMenu(this, this->vehicle_sorter_names, this->vehicles.SortType(), VLW_WIDGET_SORT_BY_PULLDOWN, 0,
01153             (this->vehicle_type == VEH_TRAIN || this->vehicle_type == VEH_ROAD) ? 0 : (1 << 10));
01154         return;
01155 
01156       case VLW_WIDGET_LIST: { // Matrix to show vehicles
01157         uint32 id_v = (pt.y - this->GetWidget<NWidgetBase>(VLW_WIDGET_LIST)->pos_y) / this->resize.step_height;
01158         const Vehicle *v;
01159 
01160         if (id_v >= this->vscroll.GetCapacity()) return; // click out of bounds
01161 
01162         id_v += this->vscroll.GetPosition();
01163 
01164         if (id_v >= this->vehicles.Length()) return; // click out of list bound
01165 
01166         v = this->vehicles[id_v];
01167 
01168         ShowVehicleViewWindow(v);
01169       } break;
01170 
01171       case VLW_WIDGET_AVAILABLE_VEHICLES:
01172         ShowBuildVehicleWindow(INVALID_TILE, this->vehicle_type);
01173         break;
01174 
01175       case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
01176         static StringID action_str[] = {
01177           STR_VEHICLE_LIST_REPLACE_VEHICLES,
01178           STR_VEHICLE_LIST_SEND_FOR_SERVICING,
01179           STR_NULL,
01180           INVALID_STRING_ID
01181         };
01182 
01183         static const StringID depot_name[] = {
01184           STR_VEHICLE_LIST_SEND_TRAIN_TO_DEPOT,
01185           STR_VEHICLE_LIST_SEND_ROAD_VEHICLE_TO_DEPOT,
01186           STR_VEHICLE_LIST_SEND_SHIP_TO_DEPOT,
01187           STR_VEHICLE_LIST_SEND_AIRCRAFT_TO_HANGAR
01188         };
01189 
01190         /* XXX - Substite string since the dropdown cannot handle dynamic strings */
01191         action_str[2] = depot_name[this->vehicle_type];
01192         ShowDropDownMenu(this, action_str, 0, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN, 0, (this->window_number & VLW_MASK) == VLW_STANDARD ? 0 : 1);
01193         break;
01194       }
01195 
01196       case VLW_WIDGET_STOP_ALL:
01197       case VLW_WIDGET_START_ALL:
01198         DoCommandP(0, GB(this->window_number, 16, 16),
01199             (this->window_number & VLW_MASK) | (1 << 6) | (widget == VLW_WIDGET_START_ALL ? (1 << 5) : 0) | this->vehicle_type, CMD_MASS_START_STOP);
01200         break;
01201     }
01202   }
01203 
01204   virtual void OnDropdownSelect(int widget, int index)
01205   {
01206     switch (widget) {
01207       case VLW_WIDGET_SORT_BY_PULLDOWN:
01208         this->vehicles.SetSortType(index);
01209         break;
01210       case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN:
01211         assert(this->vehicles.Length() != 0);
01212 
01213         switch (index) {
01214           case 0: // Replace window
01215             ShowReplaceGroupVehicleWindow(DEFAULT_GROUP, this->vehicle_type);
01216             break;
01217           case 1: // Send for servicing
01218             DoCommandP(0, GB(this->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
01219               (this->window_number & VLW_MASK) | DEPOT_MASS_SEND | DEPOT_SERVICE, GetCmdSendToDepot(this->vehicle_type));
01220             break;
01221           case 2: // Send to Depots
01222             DoCommandP(0, GB(this->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
01223               (this->window_number & VLW_MASK) | DEPOT_MASS_SEND, GetCmdSendToDepot(this->vehicle_type));
01224             break;
01225 
01226           default: NOT_REACHED();
01227         }
01228         break;
01229       default: NOT_REACHED();
01230     }
01231     this->SetDirty();
01232   }
01233 
01234   virtual void OnTick()
01235   {
01236     if (_pause_mode != PM_UNPAUSED) return;
01237     if (this->vehicles.NeedResort()) {
01238       StationID station = ((this->window_number & VLW_MASK) == VLW_STATION_LIST) ? GB(this->window_number, 16, 16) : INVALID_STATION;
01239 
01240       DEBUG(misc, 3, "Periodic resort %d list company %d at station %d", this->vehicle_type, this->owner, station);
01241       this->SetDirty();
01242     }
01243   }
01244 
01245   virtual void OnResize()
01246   {
01247     this->vscroll.SetCapacityFromWidget(this, VLW_WIDGET_LIST);
01248     this->GetWidget<NWidgetCore>(VLW_WIDGET_LIST)->widget_data = (this->vscroll.GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
01249   }
01250 
01251   virtual void OnInvalidateData(int data)
01252   {
01253     if (HasBit(data, 15) && (this->window_number & VLW_MASK) == VLW_SHARED_ORDERS) {
01254       SB(this->window_number, 16, 16, GB(data, 16, 16));
01255       this->vehicles.ForceRebuild();
01256       return;
01257     }
01258 
01259     if (data == 0) {
01260       this->vehicles.ForceRebuild();
01261     } else {
01262       this->vehicles.ForceResort();
01263     }
01264   }
01265 };
01266 
01267 static WindowDesc _vehicle_list_desc(
01268   WDP_AUTO, 260, 246,
01269   WC_INVALID, WC_NONE,
01270   WDF_UNCLICK_BUTTONS,
01271   _nested_vehicle_list, lengthof(_nested_vehicle_list)
01272 );
01273 
01274 static void ShowVehicleListWindowLocal(CompanyID company, uint16 VLW_flag, VehicleType vehicle_type, uint16 unique_number)
01275 {
01276   if (!Company::IsValidID(company)) return;
01277 
01278   _vehicle_list_desc.cls = GetWindowClassForVehicleType(vehicle_type);
01279   WindowNumber num = (unique_number << 16) | (vehicle_type << 11) | VLW_flag | company;
01280   AllocateWindowDescFront<VehicleListWindow>(&_vehicle_list_desc, num);
01281 }
01282 
01283 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type)
01284 {
01285   /* If _settings_client.gui.advanced_vehicle_list > 1, display the Advanced list
01286    * if _settings_client.gui.advanced_vehicle_list == 1, display Advanced list only for local company
01287    * if _ctrl_pressed, do the opposite action (Advanced list x Normal list)
01288    */
01289 
01290   if ((_settings_client.gui.advanced_vehicle_list > (uint)(company != _local_company)) != _ctrl_pressed) {
01291     ShowCompanyGroup(company, vehicle_type);
01292   } else {
01293     ShowVehicleListWindowLocal(company, VLW_STANDARD, vehicle_type, company);
01294   }
01295 }
01296 
01297 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, const Waypoint *wp)
01298 {
01299   if (wp == NULL) return;
01300   ShowVehicleListWindowLocal(company, VLW_WAYPOINT_LIST, vehicle_type, wp->index);
01301 }
01302 
01303 void ShowVehicleListWindow(const Vehicle *v)
01304 {
01305   ShowVehicleListWindowLocal(v->owner, VLW_SHARED_ORDERS, v->type, v->FirstShared()->index);
01306 }
01307 
01308 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station)
01309 {
01310   ShowVehicleListWindowLocal(company, VLW_STATION_LIST, vehicle_type, station);
01311 }
01312 
01313 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile)
01314 {
01315   uint16 depot_airport_index;
01316 
01317   if (vehicle_type == VEH_AIRCRAFT) {
01318     depot_airport_index = GetStationIndex(depot_tile);
01319   } else {
01320     depot_airport_index = GetDepotIndex(depot_tile);
01321   }
01322   ShowVehicleListWindowLocal(company, VLW_DEPOT_LIST, vehicle_type, depot_airport_index);
01323 }
01324 
01325 
01326 /* Unified vehicle GUI - Vehicle Details Window */
01327 
01329 enum VehicleDetailsWindowWidgets {
01330   VLD_WIDGET_CAPTION,
01331   VLD_WIDGET_RENAME_VEHICLE,
01332   VLD_WIDGET_TOP_DETAILS,
01333   VLD_WIDGET_INCREASE_SERVICING_INTERVAL,
01334   VLD_WIDGET_DECREASE_SERVICING_INTERVAL,
01335   VLD_WIDGET_SERVICING_INTERVAL,
01336   VLD_WIDGET_MIDDLE_DETAILS,
01337   VLD_WIDGET_MATRIX,
01338   VLD_WIDGET_SCROLLBAR,
01339   VLD_WIDGET_DETAILS_CARGO_CARRIED,
01340   VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
01341   VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
01342   VLD_WIDGET_DETAILS_TOTAL_CARGO,
01343 };
01344 
01345 assert_compile(VLD_WIDGET_DETAILS_CARGO_CARRIED    == VLD_WIDGET_DETAILS_CARGO_CARRIED + TDW_TAB_CARGO   );
01346 assert_compile(VLD_WIDGET_DETAILS_TRAIN_VEHICLES   == VLD_WIDGET_DETAILS_CARGO_CARRIED + TDW_TAB_INFO    );
01347 assert_compile(VLD_WIDGET_DETAILS_CAPACITY_OF_EACH == VLD_WIDGET_DETAILS_CARGO_CARRIED + TDW_TAB_CAPACITY);
01348 assert_compile(VLD_WIDGET_DETAILS_TOTAL_CARGO      == VLD_WIDGET_DETAILS_CARGO_CARRIED + TDW_TAB_TOTALS  );
01349 
01351 static const NWidgetPart _nested_nontrain_vehicle_details_widgets[] = {
01352   NWidget(NWID_HORIZONTAL),
01353     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
01354     NWidget(WWT_CAPTION, COLOUR_GREY, VLD_WIDGET_CAPTION), SetDataTip(STR_VEHICLE_DETAILS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01355     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 */),
01356     NWidget(WWT_SHADEBOX, COLOUR_GREY),
01357     NWidget(WWT_STICKYBOX, COLOUR_GREY),
01358   EndContainer(),
01359   NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_TOP_DETAILS), SetMinimalSize(405, 42), SetResize(1, 0), EndContainer(),
01360   NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_MIDDLE_DETAILS), SetMinimalSize(405, 45), SetResize(1, 0), EndContainer(),
01361   NWidget(NWID_HORIZONTAL),
01362     NWidget(NWID_BUTTON_ARROW, COLOUR_GREY, VLD_WIDGET_DECREASE_SERVICING_INTERVAL), SetFill(0, 1),
01363         SetDataTip(AWV_DECREASE, STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP),
01364     NWidget(NWID_BUTTON_ARROW, COLOUR_GREY, VLD_WIDGET_INCREASE_SERVICING_INTERVAL), SetFill(0, 1),
01365         SetDataTip(AWV_INCREASE, STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP),
01366     NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_SERVICING_INTERVAL), SetFill(1, 1), SetResize(1, 0), EndContainer(),
01367     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
01368   EndContainer(),
01369 };
01370 
01372 static const NWidgetPart _nested_train_vehicle_details_widgets[] = {
01373   NWidget(NWID_HORIZONTAL),
01374     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
01375     NWidget(WWT_CAPTION, COLOUR_GREY, VLD_WIDGET_CAPTION), SetDataTip(STR_VEHICLE_DETAILS_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01376     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 */),
01377     NWidget(WWT_SHADEBOX, COLOUR_GREY),
01378     NWidget(WWT_STICKYBOX, COLOUR_GREY),
01379   EndContainer(),
01380   NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_TOP_DETAILS), SetResize(1, 0), SetMinimalSize(405, 42), EndContainer(),
01381   NWidget(NWID_HORIZONTAL),
01382     NWidget(WWT_MATRIX, COLOUR_GREY, VLD_WIDGET_MATRIX), SetResize(1, 1), SetMinimalSize(393, 45), SetDataTip(0x701, STR_NULL), SetFill(1, 0),
01383     NWidget(WWT_SCROLLBAR, COLOUR_GREY, VLD_WIDGET_SCROLLBAR),
01384   EndContainer(),
01385   NWidget(NWID_HORIZONTAL),
01386     NWidget(NWID_BUTTON_ARROW, COLOUR_GREY, VLD_WIDGET_DECREASE_SERVICING_INTERVAL), SetFill(0, 1),
01387         SetDataTip(AWV_DECREASE, STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP),
01388     NWidget(NWID_BUTTON_ARROW, COLOUR_GREY, VLD_WIDGET_INCREASE_SERVICING_INTERVAL), SetFill(0, 1),
01389         SetDataTip(AWV_INCREASE, STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP),
01390     NWidget(WWT_PANEL, COLOUR_GREY, VLD_WIDGET_SERVICING_INTERVAL), SetFill(1, 1), SetResize(1, 0), EndContainer(),
01391   EndContainer(),
01392   NWidget(NWID_HORIZONTAL),
01393     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_DETAILS_CARGO_CARRIED), SetMinimalSize(96, 12),
01394         SetDataTip(STR_VEHICLE_DETAIL_TAB_CARGO, STR_VEHICLE_DETAILS_TRAIN_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01395     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_DETAILS_TRAIN_VEHICLES), SetMinimalSize(99, 12),
01396         SetDataTip(STR_VEHICLE_DETAIL_TAB_INFORMATION, STR_VEHICLE_DETAILS_TRAIN_INFORMATION_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01397     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_DETAILS_CAPACITY_OF_EACH), SetMinimalSize(99, 12),
01398         SetDataTip(STR_VEHICLE_DETAIL_TAB_CAPACITIES, STR_VEHICLE_DETAILS_TRAIN_CAPACITIES_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01399     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, VLD_WIDGET_DETAILS_TOTAL_CARGO), SetMinimalSize(99, 12),
01400         SetDataTip(STR_VEHICLE_DETAIL_TAB_TOTAL_CARGO, STR_VEHICLE_DETAILS_TRAIN_TOTAL_CARGO_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
01401     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
01402   EndContainer(),
01403 };
01404 
01405 
01406 extern int GetTrainDetailsWndVScroll(VehicleID veh_id, TrainDetailsWindowTabs det_tab);
01407 extern void DrawTrainDetails(const Train *v, int left, int right, int y, int vscroll_pos, uint16 vscroll_cap, TrainDetailsWindowTabs det_tab);
01408 extern void DrawRoadVehDetails(const Vehicle *v, int left, int right, int y);
01409 extern void DrawShipDetails(const Vehicle *v, int left, int right, int y);
01410 extern void DrawAircraftDetails(const Aircraft *v, int left, int right, int y);
01411 
01413 struct VehicleDetailsWindow : Window {
01414   TrainDetailsWindowTabs tab; 
01415 
01417   VehicleDetailsWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
01418   {
01419     this->InitNested(desc, window_number);
01420 
01421     const Vehicle *v = Vehicle::Get(this->window_number);
01422 
01423     this->GetWidget<NWidgetCore>(VLD_WIDGET_RENAME_VEHICLE)->tool_tip = STR_VEHICLE_DETAILS_TRAIN_RENAME + v->type;
01424 
01425     this->owner = v->owner;
01426     this->tab = TDW_TAB_CARGO;
01427   }
01428 
01429   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01430   {
01431     switch (widget) {
01432       case VLD_WIDGET_TOP_DETAILS: {
01433         Dimension dim = { 0, 0 };
01434         size->height = WD_FRAMERECT_TOP + 4 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
01435 
01436         for (uint i = 0; i < 4; i++) SetDParam(i, INT16_MAX);
01437         static const StringID info_strings[] = {
01438           STR_VEHICLE_INFO_MAX_SPEED,
01439           STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED,
01440           STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE,
01441           STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR,
01442           STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS
01443         };
01444         for (uint i = 0; i < lengthof(info_strings); i++) {
01445           dim = maxdim(dim, GetStringBoundingBox(info_strings[i]));
01446         }
01447         SetDParam(0, STR_VEHICLE_INFO_AGE);
01448         dim = maxdim(dim, GetStringBoundingBox(STR_VEHICLE_INFO_AGE_RUNNING_COST_YR));
01449         size->width = dim.width + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01450       } break;
01451 
01452       case VLD_WIDGET_MIDDLE_DETAILS: {
01453         const Vehicle *v = Vehicle::Get(this->window_number);
01454         switch (v->type) {
01455           case VEH_ROAD:
01456             if (RoadVehicle::From(v)->HasArticulatedPart()) {
01457               /* An articulated RV has its text drawn under the sprite instead of after it, hence 15 pixels extra. */
01458               size->height = WD_FRAMERECT_TOP + 15 + 3 * FONT_HEIGHT_NORMAL + 2 + WD_FRAMERECT_BOTTOM;
01459               /* Add space for the cargo amount for each part. */
01460               for (const Vehicle *u = v; u != NULL; u = u->Next()) {
01461                 if (u->cargo_cap != 0) size->height += FONT_HEIGHT_NORMAL + 1;
01462               }
01463             } else {
01464               size->height = WD_FRAMERECT_TOP + 4 * FONT_HEIGHT_NORMAL + 3 + WD_FRAMERECT_BOTTOM;
01465             }
01466             break;
01467 
01468           case VEH_SHIP:
01469             size->height = WD_FRAMERECT_TOP + 4 * FONT_HEIGHT_NORMAL + 3 + WD_FRAMERECT_BOTTOM;
01470             break;
01471 
01472           case VEH_AIRCRAFT:
01473             size->height = WD_FRAMERECT_TOP + 5 * FONT_HEIGHT_NORMAL + 4 + WD_FRAMERECT_BOTTOM;
01474             break;
01475 
01476           default:
01477             NOT_REACHED(); // Train uses VLD_WIDGET_MATRIX instead.
01478         }
01479         break;
01480       }
01481 
01482       case VLD_WIDGET_MATRIX:
01483         resize->height = WD_MATRIX_TOP + FONT_HEIGHT_NORMAL + WD_MATRIX_BOTTOM;
01484         size->height = 4 * resize->height;
01485         break;
01486 
01487       case VLD_WIDGET_SERVICING_INTERVAL:
01488         SetDParam(0, 9999); // Roughly the maximum interval
01489         SetDParam(1, MAX_YEAR * DAYS_IN_YEAR); // Roughly the maximum year
01490         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;
01491         size->height = WD_FRAMERECT_TOP + FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
01492         break;
01493     }
01494   }
01495 
01497   static bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type, CompanyID company_id)
01498   {
01499     const VehicleDefaultSettings *vds = &Company::Get(company_id)->settings.vehicle;
01500     switch (vehicle_type) {
01501       default: NOT_REACHED();
01502       case VEH_TRAIN:    return vds->servint_trains   != 0;
01503       case VEH_ROAD:     return vds->servint_roadveh  != 0;
01504       case VEH_SHIP:     return vds->servint_ships    != 0;
01505       case VEH_AIRCRAFT: return vds->servint_aircraft != 0;
01506     }
01507   }
01508 
01520   static void DrawVehicleDetails(const Vehicle *v, int left, int right, int y, int vscroll_pos, uint vscroll_cap, TrainDetailsWindowTabs det_tab)
01521   {
01522     switch (v->type) {
01523       case VEH_TRAIN:    DrawTrainDetails(Train::From(v), left, right, y, vscroll_pos, vscroll_cap, det_tab);  break;
01524       case VEH_ROAD:     DrawRoadVehDetails(v, left, right, y);  break;
01525       case VEH_SHIP:     DrawShipDetails(v, left, right, y);     break;
01526       case VEH_AIRCRAFT: DrawAircraftDetails(Aircraft::From(v), left, right, y); break;
01527       default: NOT_REACHED();
01528     }
01529   }
01530 
01531   virtual void SetStringParameters(int widget) const
01532   {
01533     if (widget == VLD_WIDGET_CAPTION) SetDParam(0, Vehicle::Get(this->window_number)->index);
01534   }
01535 
01536   virtual void DrawWidget(const Rect &r, int widget) const
01537   {
01538     const Vehicle *v = Vehicle::Get(this->window_number);
01539 
01540     switch (widget) {
01541       case VLD_WIDGET_TOP_DETAILS: {
01542         int y = r.top + WD_FRAMERECT_TOP;
01543 
01544         /* Draw running cost */
01545         SetDParam(1, v->age / DAYS_IN_LEAP_YEAR);
01546         SetDParam(0, (v->age + DAYS_IN_YEAR < v->max_age) ? STR_VEHICLE_INFO_AGE : STR_VEHICLE_INFO_AGE_RED);
01547         SetDParam(2, v->max_age / DAYS_IN_LEAP_YEAR);
01548         SetDParam(3, v->GetDisplayRunningCost());
01549         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_AGE_RUNNING_COST_YR);
01550         y += FONT_HEIGHT_NORMAL;
01551 
01552         /* Draw max speed */
01553         switch (v->type) {
01554           case VEH_TRAIN:
01555             SetDParam(2, v->GetDisplayMaxSpeed());
01556             SetDParam(1, Train::From(v)->tcache.cached_power);
01557             SetDParam(0, Train::From(v)->tcache.cached_weight);
01558             SetDParam(3, Train::From(v)->tcache.cached_max_te / 1000);
01559             DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, (_settings_game.vehicle.train_acceleration_model != TAM_ORIGINAL && Train::From(v)->railtype != RAILTYPE_MAGLEV) ?
01560                 STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE : STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED);
01561             break;
01562 
01563           case VEH_ROAD:
01564           case VEH_SHIP:
01565           case VEH_AIRCRAFT:
01566             SetDParam(0, v->GetDisplayMaxSpeed());
01567             DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_MAX_SPEED);
01568             break;
01569 
01570           default: NOT_REACHED();
01571         }
01572         y += FONT_HEIGHT_NORMAL;
01573 
01574         /* Draw profit */
01575         SetDParam(0, v->GetDisplayProfitThisYear());
01576         SetDParam(1, v->GetDisplayProfitLastYear());
01577         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR);
01578         y += FONT_HEIGHT_NORMAL;
01579 
01580         /* Draw breakdown & reliability */
01581         SetDParam(0, ToPercent16(v->reliability));
01582         SetDParam(1, v->breakdowns_since_last_service);
01583         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS);
01584         break;
01585       }
01586 
01587       case VLD_WIDGET_MATRIX:
01588         /* For trains only. */
01589         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);
01590         break;
01591 
01592       case VLD_WIDGET_MIDDLE_DETAILS: {
01593         /* For other vehicles, at the place of the matrix. */
01594         bool rtl = _dynlang.text_dir == TD_RTL;
01595         uint sprite_width = max<uint>(GetSprite(v->GetImage(rtl ? DIR_E : DIR_W), ST_NORMAL)->width, 70U) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
01596 
01597         uint text_left  = r.left  + (rtl ? 0 : sprite_width);
01598         uint text_right = r.right - (rtl ? sprite_width : 0);
01599 
01600         uint sprite_left  = rtl ? text_right : r.left;
01601         uint sprite_right = rtl ? r.right : text_left;
01602 
01603         DrawVehicleImage(v, sprite_left + WD_FRAMERECT_LEFT, sprite_right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, INVALID_VEHICLE, 0);
01604         DrawVehicleDetails(v, text_left + WD_FRAMERECT_LEFT, text_right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, this->vscroll.GetPosition(), this->vscroll.GetCapacity(), this->tab);
01605       } break;
01606 
01607       case VLD_WIDGET_SERVICING_INTERVAL:
01608         /* Draw service interval text */
01609         SetDParam(0, v->service_interval);
01610         SetDParam(1, v->date_of_last_service);
01611         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + (r.bottom - r.top + 1 - FONT_HEIGHT_NORMAL) / 2,
01612             Company::Get(v->owner)->settings.vehicle.servint_ispercent ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS);
01613         break;
01614     }
01615   }
01616 
01618   virtual void OnPaint()
01619   {
01620     const Vehicle *v = Vehicle::Get(this->window_number);
01621 
01622     this->SetWidgetDisabledState(VLD_WIDGET_RENAME_VEHICLE, v->owner != _local_company);
01623 
01624     if (v->type == VEH_TRAIN) {
01625       this->DisableWidget(this->tab + VLD_WIDGET_DETAILS_CARGO_CARRIED);
01626       this->vscroll.SetCount(GetTrainDetailsWndVScroll(v->index, this->tab));
01627     }
01628 
01629     /* Disable service-scroller when interval is set to disabled */
01630     this->SetWidgetsDisabledState(!IsVehicleServiceIntervalEnabled(v->type, v->owner),
01631       VLD_WIDGET_INCREASE_SERVICING_INTERVAL,
01632       VLD_WIDGET_DECREASE_SERVICING_INTERVAL,
01633       WIDGET_LIST_END);
01634 
01635     this->DrawWidgets();
01636   }
01637 
01638   virtual void OnClick(Point pt, int widget)
01639   {
01640     switch (widget) {
01641       case VLD_WIDGET_RENAME_VEHICLE: { // rename
01642         const Vehicle *v = Vehicle::Get(this->window_number);
01643         SetDParam(0, v->index);
01644         ShowQueryString(STR_VEHICLE_NAME, STR_QUERY_RENAME_TRAIN_CAPTION + v->type,
01645             MAX_LENGTH_VEHICLE_NAME_BYTES, MAX_LENGTH_VEHICLE_NAME_PIXELS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT);
01646       } break;
01647 
01648       case VLD_WIDGET_INCREASE_SERVICING_INTERVAL:   // increase int
01649       case VLD_WIDGET_DECREASE_SERVICING_INTERVAL: { // decrease int
01650         int mod = _ctrl_pressed ? 5 : 10;
01651         const Vehicle *v = Vehicle::Get(this->window_number);
01652 
01653         mod = (widget == VLD_WIDGET_DECREASE_SERVICING_INTERVAL) ? -mod : mod;
01654         mod = GetServiceIntervalClamped(mod + v->service_interval, v->owner);
01655         if (mod == v->service_interval) return;
01656 
01657         DoCommandP(v->tile, v->index, mod, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_ERROR_CAN_T_CHANGE_SERVICING));
01658       } break;
01659 
01660       case VLD_WIDGET_DETAILS_CARGO_CARRIED:
01661       case VLD_WIDGET_DETAILS_TRAIN_VEHICLES:
01662       case VLD_WIDGET_DETAILS_CAPACITY_OF_EACH:
01663       case VLD_WIDGET_DETAILS_TOTAL_CARGO:
01664         this->SetWidgetsDisabledState(false,
01665           VLD_WIDGET_DETAILS_CARGO_CARRIED,
01666           VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
01667           VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
01668           VLD_WIDGET_DETAILS_TOTAL_CARGO,
01669           widget,
01670           WIDGET_LIST_END);
01671 
01672         this->tab = (TrainDetailsWindowTabs)(widget - VLD_WIDGET_DETAILS_CARGO_CARRIED);
01673         this->SetDirty();
01674         break;
01675     }
01676   }
01677 
01678   virtual void OnQueryTextFinished(char *str)
01679   {
01680     if (str == NULL) return;
01681 
01682     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);
01683   }
01684 
01685   virtual void OnResize()
01686   {
01687     NWidgetCore *nwi = this->GetWidget<NWidgetCore>(VLD_WIDGET_MATRIX);
01688     if (nwi != NULL) {
01689       this->vscroll.SetCapacityFromWidget(this, VLD_WIDGET_MATRIX);
01690       nwi->widget_data = (this->vscroll.GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
01691     }
01692   }
01693 };
01694 
01696 static const WindowDesc _train_vehicle_details_desc(
01697   WDP_AUTO, 405, 178,
01698   WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW,
01699   WDF_UNCLICK_BUTTONS,
01700   _nested_train_vehicle_details_widgets, lengthof(_nested_train_vehicle_details_widgets)
01701 );
01702 
01704 static const WindowDesc _nontrain_vehicle_details_desc(
01705   WDP_AUTO, 405, 113,
01706   WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW,
01707   WDF_UNCLICK_BUTTONS,
01708   _nested_nontrain_vehicle_details_widgets, lengthof(_nested_nontrain_vehicle_details_widgets)
01709 );
01710 
01712 static void ShowVehicleDetailsWindow(const Vehicle *v)
01713 {
01714   DeleteWindowById(WC_VEHICLE_ORDERS, v->index, false);
01715   DeleteWindowById(WC_VEHICLE_TIMETABLE, v->index, false);
01716   AllocateWindowDescFront<VehicleDetailsWindow>((v->type == VEH_TRAIN) ? &_train_vehicle_details_desc : &_nontrain_vehicle_details_desc, v->index);
01717 }
01718 
01719 
01720 /* Unified vehicle GUI - Vehicle View Window */
01721 
01723 static const NWidgetPart _nested_vehicle_view_widgets[] = {
01724   NWidget(NWID_HORIZONTAL),
01725     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
01726     NWidget(WWT_CAPTION, COLOUR_GREY, VVW_WIDGET_CAPTION), SetDataTip(STR_VEHICLE_VIEW_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
01727     NWidget(WWT_SHADEBOX, COLOUR_GREY),
01728     NWidget(WWT_STICKYBOX, COLOUR_GREY),
01729   EndContainer(),
01730   NWidget(NWID_HORIZONTAL),
01731     NWidget(WWT_PANEL, COLOUR_GREY),
01732       NWidget(WWT_INSET, COLOUR_GREY), SetPadding(2, 2, 2, 2),
01733         NWidget(NWID_VIEWPORT, INVALID_COLOUR, VVW_WIDGET_VIEWPORT), SetMinimalSize(226, 84), SetResize(1, 1), SetPadding(1, 1, 1, 1),
01734       EndContainer(),
01735     EndContainer(),
01736     NWidget(NWID_VERTICAL),
01737       NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_CENTER_MAIN_VIEH), SetMinimalSize(18, 18), SetFill(1, 1), SetDataTip(SPR_CENTRE_VIEW_VEHICLE, 0x0 /* filled later */),
01738       NWidget(NWID_SELECTION, INVALID_COLOUR, VVW_WIDGET_SELECT_DEPOT_CLONE),
01739         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_GOTO_DEPOT), SetMinimalSize(18, 18), SetFill(1, 1), SetDataTip(0x0 /* filled later */, 0x0 /* filled later */),
01740         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_CLONE_VEH), SetMinimalSize(18, 18), SetFill(1, 1), SetDataTip(0x0 /* filled later */, 0x0 /* filled later */),
01741       EndContainer(),
01742       /* For trains only, 'ignore signal' button. */
01743       NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_FORCE_PROCEED), SetMinimalSize(18, 18), SetFill(1, 1),
01744                       SetDataTip(SPR_IGNORE_SIGNALS, STR_VEHICLE_VIEW_TRAIN_IGNORE_SIGNAL_TOOLTIP),
01745       NWidget(NWID_SELECTION, INVALID_COLOUR, VVW_WIDGET_SELECT_REFIT_TURN),
01746         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_REFIT_VEH), SetMinimalSize(18, 18), SetFill(1, 1), SetDataTip(SPR_REFIT_VEHICLE, 0x0 /* filled later */),
01747         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_TURN_AROUND), SetMinimalSize(18, 18), SetFill(1, 1),
01748                         SetDataTip(SPR_FORCE_VEHICLE_TURN, STR_VEHICLE_VIEW_ROAD_VEHICLE_REVERSE_TOOLTIP),
01749       EndContainer(),
01750       NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_SHOW_ORDERS), SetFill(1, 1), SetMinimalSize(18, 18), SetDataTip(SPR_SHOW_ORDERS, 0x0 /* filled later */),
01751       NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, VVW_WIDGET_SHOW_DETAILS), SetFill(1, 1), SetMinimalSize(18, 18), SetDataTip(SPR_SHOW_VEHICLE_DETAILS, 0x0 /* filled later */),
01752       NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetMinimalSize(18, 0), SetResize(0, 1), EndContainer(),
01753     EndContainer(),
01754   EndContainer(),
01755   NWidget(NWID_HORIZONTAL),
01756     NWidget(WWT_PUSHBTN, COLOUR_GREY, VVW_WIDGET_START_STOP_VEH), SetMinimalTextLines(1, WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM), SetResize(1, 0), SetFill(1, 0),
01757     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
01758   EndContainer(),
01759 };
01760 
01762 static const WindowDesc _vehicle_view_desc(
01763   WDP_AUTO, 250, 116,
01764   WC_VEHICLE_VIEW, WC_NONE,
01765   WDF_UNCLICK_BUTTONS,
01766   _nested_vehicle_view_widgets, lengthof(_nested_vehicle_view_widgets)
01767 );
01768 
01772 static const WindowDesc _train_view_desc(
01773   WDP_AUTO, 250, 134,
01774   WC_VEHICLE_VIEW, WC_NONE,
01775   WDF_UNCLICK_BUTTONS,
01776   _nested_vehicle_view_widgets, lengthof(_nested_vehicle_view_widgets)
01777 );
01778 
01779 
01780 /* Just to make sure, nobody has changed the vehicle type constants, as we are
01781    using them for array indexing in a number of places here. */
01782 assert_compile(VEH_TRAIN == 0);
01783 assert_compile(VEH_ROAD == 1);
01784 assert_compile(VEH_SHIP == 2);
01785 assert_compile(VEH_AIRCRAFT == 3);
01786 
01788 static const ZoomLevel _vehicle_view_zoom_levels[] = {
01789   ZOOM_LVL_TRAIN,
01790   ZOOM_LVL_ROADVEH,
01791   ZOOM_LVL_SHIP,
01792   ZOOM_LVL_AIRCRAFT,
01793 };
01794 
01795 /* Constants for geometry of vehicle view viewport */
01796 static const int VV_INITIAL_VIEWPORT_WIDTH = 226;
01797 static const int VV_INITIAL_VIEWPORT_HEIGHT = 84;
01798 static const int VV_INITIAL_VIEWPORT_HEIGHT_TRAIN = 102;
01799 
01801 enum VehicleCommandTranslation {
01802   VCT_CMD_START_STOP = 0,
01803   VCT_CMD_CLONE_VEH,
01804   VCT_CMD_TURN_AROUND,
01805 };
01806 
01808 static const uint32 _vehicle_command_translation_table[][4] = {
01809   { // VCT_CMD_START_STOP
01810     CMD_START_STOP_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_STOP_START_TRAIN),
01811     CMD_START_STOP_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_STOP_START_ROAD_VEHICLE),
01812     CMD_START_STOP_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_STOP_START_SHIP),
01813     CMD_START_STOP_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_STOP_START_AIRCRAFT)
01814   },
01815   { // VCT_CMD_CLONE_VEH
01816     CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_TRAIN),
01817     CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_ROAD_VEHICLE),
01818     CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_SHIP),
01819     CMD_CLONE_VEHICLE | CMD_MSG(STR_ERROR_CAN_T_BUY_AIRCRAFT)
01820   },
01821   { // VCT_CMD_TURN_AROUND
01822     CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN),
01823     CMD_TURN_ROADVEH            | CMD_MSG(STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN),
01824     0xffffffff, // invalid for ships
01825     0xffffffff  // invalid for aircrafts
01826   },
01827 };
01828 
01830 static bool IsVehicleRefitable(const Vehicle *v)
01831 {
01832   if (!v->IsStoppedInDepot()) return false;
01833 
01834   do {
01835     if (IsEngineRefittable(v->engine_type)) return true;
01836   } while ((v->type == VEH_TRAIN || v->type == VEH_ROAD) && (v = v->Next()) != NULL);
01837 
01838   return false;
01839 }
01840 
01842 struct VehicleViewWindow : Window {
01843 private:
01845   enum PlaneSelections {
01846     SEL_DC_GOTO_DEPOT,  
01847     SEL_DC_CLONE,       
01848 
01849     SEL_RT_REFIT,       
01850     SEL_RT_TURN_AROUND, 
01851 
01852     SEL_DC_BASEPLANE = SEL_DC_GOTO_DEPOT, 
01853     SEL_RT_BASEPLANE = SEL_RT_REFIT,      
01854   };
01855 
01859   void SelectPlane(PlaneSelections plane)
01860   {
01861     switch (plane) {
01862       case SEL_DC_GOTO_DEPOT:
01863       case SEL_DC_CLONE:
01864         this->GetWidget<NWidgetStacked>(VVW_WIDGET_SELECT_DEPOT_CLONE)->SetDisplayedPlane(plane - SEL_DC_BASEPLANE);
01865         break;
01866 
01867       case SEL_RT_REFIT:
01868       case SEL_RT_TURN_AROUND:
01869         this->GetWidget<NWidgetStacked>(VVW_WIDGET_SELECT_REFIT_TURN)->SetDisplayedPlane(plane - SEL_RT_BASEPLANE);
01870         break;
01871 
01872       default:
01873         NOT_REACHED();
01874     }
01875   }
01876 
01877 public:
01878   VehicleViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
01879   {
01880     this->CreateNestedTree(desc);
01881 
01882     /* Sprites for the 'send to depot' button indexed by vehicle type. */
01883     static const SpriteID vehicle_view_goto_depot_sprites[] = {
01884       SPR_SEND_TRAIN_TODEPOT,
01885       SPR_SEND_ROADVEH_TODEPOT,
01886       SPR_SEND_SHIP_TODEPOT,
01887       SPR_SEND_AIRCRAFT_TODEPOT,
01888     };
01889     const Vehicle *v = Vehicle::Get(window_number);
01890     this->GetWidget<NWidgetCore>(VVW_WIDGET_GOTO_DEPOT)->widget_data = vehicle_view_goto_depot_sprites[v->type];
01891 
01892     /* Sprites for the 'clone vehicle' button indexed by vehicle type. */
01893     static const SpriteID vehicle_view_clone_sprites[] = {
01894       SPR_CLONE_TRAIN,
01895       SPR_CLONE_ROADVEH,
01896       SPR_CLONE_SHIP,
01897       SPR_CLONE_AIRCRAFT,
01898     };
01899     this->GetWidget<NWidgetCore>(VVW_WIDGET_CLONE_VEH)->widget_data = vehicle_view_clone_sprites[v->type];
01900 
01901     switch (v->type) {
01902       case VEH_TRAIN:
01903         this->GetWidget<NWidgetCore>(VVW_WIDGET_TURN_AROUND)->tool_tip = STR_VEHICLE_VIEW_TRAIN_REVERSE_TOOLTIP;
01904         break;
01905 
01906       case VEH_ROAD:
01907         break;
01908 
01909       case VEH_SHIP:
01910       case VEH_AIRCRAFT:
01911         this->SelectPlane(SEL_RT_REFIT);
01912         break;
01913 
01914       default: NOT_REACHED();
01915     }
01916     this->FinishInitNested(desc, window_number);
01917     this->owner = v->owner;
01918     this->GetWidget<NWidgetViewport>(VVW_WIDGET_VIEWPORT)->InitializeViewport(this, this->window_number | (1 << 31), _vehicle_view_zoom_levels[v->type]);
01919 
01920     this->GetWidget<NWidgetCore>(VVW_WIDGET_START_STOP_VEH)->tool_tip   = STR_VEHICLE_VIEW_TRAIN_STATE_START_STOP_TOOLTIP + v->type;
01921     this->GetWidget<NWidgetCore>(VVW_WIDGET_CENTER_MAIN_VIEH)->tool_tip = STR_VEHICLE_VIEW_TRAIN_LOCATION_TOOLTIP + v->type;
01922     this->GetWidget<NWidgetCore>(VVW_WIDGET_REFIT_VEH)->tool_tip        = STR_VEHICLE_VIEW_TRAIN_REFIT_TOOLTIP + v->type;
01923     this->GetWidget<NWidgetCore>(VVW_WIDGET_GOTO_DEPOT)->tool_tip       = STR_VEHICLE_VIEW_TRAIN_SEND_TO_DEPOT_TOOLTIP + v->type;
01924     this->GetWidget<NWidgetCore>(VVW_WIDGET_SHOW_ORDERS)->tool_tip      = STR_VEHICLE_VIEW_TRAIN_ORDERS_TOOLTIP + v->type;
01925     this->GetWidget<NWidgetCore>(VVW_WIDGET_SHOW_DETAILS)->tool_tip     = STR_VEHICLE_VIEW_TRAIN_SHOW_DETAILS_TOOLTIP + v->type;
01926     this->GetWidget<NWidgetCore>(VVW_WIDGET_CLONE_VEH)->tool_tip        = STR_VEHICLE_VIEW_CLONE_TRAIN_INFO + v->type;
01927   }
01928 
01929   ~VehicleViewWindow()
01930   {
01931     DeleteWindowById(WC_VEHICLE_ORDERS, this->window_number, false);
01932     DeleteWindowById(WC_VEHICLE_REFIT, this->window_number, false);
01933     DeleteWindowById(WC_VEHICLE_DETAILS, this->window_number, false);
01934     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false);
01935   }
01936 
01937   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01938   {
01939     const Vehicle *v = Vehicle::Get(this->window_number);
01940     switch (widget) {
01941       case VVW_WIDGET_FORCE_PROCEED:
01942         if (v->type != VEH_TRAIN) {
01943           size->height = 0;
01944           size->width = 0;
01945         } break;
01946 
01947       case VVW_WIDGET_VIEWPORT:
01948         size->width = VV_INITIAL_VIEWPORT_WIDTH;
01949         size->height = (v->type == VEH_TRAIN) ? VV_INITIAL_VIEWPORT_HEIGHT_TRAIN : VV_INITIAL_VIEWPORT_HEIGHT;
01950         break;
01951     }
01952   }
01953 
01954   virtual void OnPaint()
01955   {
01956     const Vehicle *v = Vehicle::Get(this->window_number);
01957     bool is_localcompany = v->owner == _local_company;
01958     bool can_control = IsVehicleControlAllowed(v, _local_company);
01959     bool refitable_and_stopped_in_depot = IsVehicleRefitable(v);
01960 
01961     this->SetWidgetDisabledState(VVW_WIDGET_GOTO_DEPOT, !is_localcompany);
01962     this->SetWidgetDisabledState(VVW_WIDGET_REFIT_VEH, !refitable_and_stopped_in_depot || !is_localcompany);
01963     this->SetWidgetDisabledState(VVW_WIDGET_CLONE_VEH, !is_localcompany);
01964 
01965     if (v->type == VEH_TRAIN) {
01966       this->SetWidgetLoweredState(VVW_WIDGET_FORCE_PROCEED, Train::From(v)->force_proceed == 2);
01967       this->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !can_control);
01968       this->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !can_control);
01969     }
01970 
01971     this->DrawWidgets();
01972   }
01973 
01974   virtual void SetStringParameters(int widget) const
01975   {
01976     if (widget != VVW_WIDGET_CAPTION) return;
01977 
01978     const Vehicle *v = Vehicle::Get(this->window_number);
01979     SetDParam(0, v->index);
01980   }
01981 
01982   virtual void DrawWidget(const Rect &r, int widget) const
01983   {
01984     if (widget != VVW_WIDGET_START_STOP_VEH) return;
01985 
01986     const Vehicle *v = Vehicle::Get(this->window_number);
01987     StringID str;
01988     if (v->vehstatus & VS_CRASHED) {
01989       str = STR_VEHICLE_STATUS_CRASHED;
01990     } else if (v->type != VEH_AIRCRAFT && v->breakdown_ctr == 1) { // check for aircraft necessary?
01991       str = STR_VEHICLE_STATUS_BROKEN_DOWN;
01992     } else if (v->vehstatus & VS_STOPPED) {
01993       if (v->type == VEH_TRAIN) {
01994         if (v->cur_speed == 0) {
01995           if (Train::From(v)->tcache.cached_power == 0) {
01996             str = STR_VEHICLE_STATUS_TRAIN_NO_POWER;
01997           } else {
01998             str = STR_VEHICLE_STATUS_STOPPED;
01999           }
02000         } else {
02001           SetDParam(0, v->GetDisplaySpeed());
02002           str = STR_VEHICLE_STATUS_TRAIN_STOPPING + _settings_client.gui.vehicle_speed;
02003         }
02004       } else { // no train
02005         str = STR_VEHICLE_STATUS_STOPPED;
02006       }
02007     } else if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_TRAIN_STUCK) && !v->current_order.IsType(OT_LOADING)) {
02008       str = STR_VEHICLE_STATUS_TRAIN_STUCK;
02009     } else { // vehicle is in a "normal" state, show current order
02010       switch (v->current_order.GetType()) {
02011         case OT_GOTO_STATION: {
02012           SetDParam(0, v->current_order.GetDestination());
02013           SetDParam(1, v->GetDisplaySpeed());
02014           str = STR_VEHICLE_STATUS_HEADING_FOR_STATION + _settings_client.gui.vehicle_speed;
02015         } break;
02016 
02017         case OT_GOTO_DEPOT: {
02018           if (v->type == VEH_AIRCRAFT) {
02019             /* Aircrafts always go to a station, even if you say depot */
02020             SetDParam(0, v->current_order.GetDestination());
02021             SetDParam(1, v->GetDisplaySpeed());
02022           } else {
02023             Depot *depot = Depot::Get(v->current_order.GetDestination());
02024             SetDParam(0, depot->town_index);
02025             SetDParam(1, v->GetDisplaySpeed());
02026           }
02027           if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
02028             str = STR_VEHICLE_STATUS_HEADING_FOR_TRAIN_DEPOT + 2 * v->type + _settings_client.gui.vehicle_speed;
02029           } else {
02030             str = STR_VEHICLE_STATUS_HEADING_FOR_TRAIN_DEPOT_SERVICE + 2 * v->type + _settings_client.gui.vehicle_speed;
02031           }
02032         } break;
02033 
02034         case OT_LOADING:
02035           str = STR_VEHICLE_STATUS_LOADING_UNLOADING;
02036           break;
02037 
02038         case OT_GOTO_WAYPOINT: {
02039           assert(v->type == VEH_TRAIN || v->type == VEH_SHIP);
02040           SetDParam(0, v->current_order.GetDestination());
02041           str = STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT + _settings_client.gui.vehicle_speed;
02042           SetDParam(1, v->GetDisplaySpeed());
02043           break;
02044         }
02045 
02046         case OT_LEAVESTATION:
02047           if (v->type != VEH_AIRCRAFT) {
02048             str = STR_VEHICLE_STATUS_LEAVING;
02049             break;
02050           }
02051           /* fall-through if aircraft. Does this even happen? */
02052 
02053         default:
02054           if (v->GetNumOrders() == 0) {
02055             str = STR_VEHICLE_STATUS_NO_ORDERS + _settings_client.gui.vehicle_speed;
02056             SetDParam(0, v->GetDisplaySpeed());
02057           } else {
02058             str = STR_EMPTY;
02059           }
02060           break;
02061       }
02062     }
02063 
02064     /* draw the flag plus orders */
02065     DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, WD_FRAMERECT_LEFT, r.top + WD_FRAMERECT_TOP);
02066     DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_FROMSTRING, SA_CENTER);
02067   }
02068 
02069   virtual void OnClick(Point pt, int widget)
02070   {
02071     const Vehicle *v = Vehicle::Get(this->window_number);
02072 
02073     switch (widget) {
02074       case VVW_WIDGET_START_STOP_VEH: // start stop
02075         DoCommandP(v->tile, v->index, 0,
02076                     _vehicle_command_translation_table[VCT_CMD_START_STOP][v->type]);
02077         break;
02078       case VVW_WIDGET_CENTER_MAIN_VIEH: {// center main view
02079         const Window *mainwindow = FindWindowById(WC_MAIN_WINDOW, 0);
02080         /* code to allow the main window to 'follow' the vehicle if the ctrl key is pressed */
02081         if (_ctrl_pressed && mainwindow->viewport->zoom == ZOOM_LVL_NORMAL) {
02082           mainwindow->viewport->follow_vehicle = v->index;
02083         } else {
02084           ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos);
02085         }
02086       } break;
02087 
02088       case VVW_WIDGET_GOTO_DEPOT: // goto hangar
02089         DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0, GetCmdSendToDepot(v));
02090         break;
02091       case VVW_WIDGET_REFIT_VEH: // refit
02092         ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID, this);
02093         break;
02094       case VVW_WIDGET_SHOW_ORDERS: // show orders
02095         if (_ctrl_pressed) {
02096           ShowTimetableWindow(v);
02097         } else {
02098           ShowOrdersWindow(v);
02099         }
02100         break;
02101       case VVW_WIDGET_SHOW_DETAILS: // show details
02102         ShowVehicleDetailsWindow(v);
02103         break;
02104       case VVW_WIDGET_CLONE_VEH: // clone vehicle
02105         DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0,
02106                     _vehicle_command_translation_table[VCT_CMD_CLONE_VEH][v->type],
02107                     CcCloneVehicle);
02108         break;
02109       case VVW_WIDGET_TURN_AROUND: // turn around
02110         assert(v->type == VEH_TRAIN || v->type == VEH_ROAD);
02111         DoCommandP(v->tile, v->index, 0,
02112                     _vehicle_command_translation_table[VCT_CMD_TURN_AROUND][v->type]);
02113         break;
02114       case VVW_WIDGET_FORCE_PROCEED: // force proceed
02115         assert(v->type == VEH_TRAIN);
02116         DoCommandP(v->tile, v->index, 0, CMD_FORCE_TRAIN_PROCEED | CMD_MSG(STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL));
02117         break;
02118     }
02119   }
02120 
02121   virtual void OnResize()
02122   {
02123     if (this->viewport != NULL) {
02124       NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(VVW_WIDGET_VIEWPORT);
02125       nvp->UpdateViewportCoordinates(this);
02126     }
02127   }
02128 
02129   virtual void OnTick()
02130   {
02131     const Vehicle *v = Vehicle::Get(this->window_number);
02132     bool veh_stopped = v->IsStoppedInDepot();
02133 
02134     /* Widget VVW_WIDGET_GOTO_DEPOT must be hidden if the vehicle is already stopped in depot.
02135      * Widget VVW_WIDGET_CLONE_VEH should then be shown, since cloning is allowed only while in depot and stopped.
02136      */
02137     PlaneSelections plane = veh_stopped ? SEL_DC_CLONE : SEL_DC_GOTO_DEPOT;
02138     NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(VVW_WIDGET_SELECT_DEPOT_CLONE); // Selection widget 'send to depot' / 'clone'.
02139     if (nwi->shown_plane + SEL_DC_BASEPLANE != plane) {
02140       this->SelectPlane(plane);
02141       this->SetWidgetDirty(VVW_WIDGET_SELECT_DEPOT_CLONE);
02142     }
02143     /* The same system applies to widget VVW_WIDGET_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/
02144     if (v->type == VEH_ROAD || v->type == VEH_TRAIN) {
02145       PlaneSelections plane = veh_stopped ? SEL_RT_REFIT : SEL_RT_TURN_AROUND;
02146       NWidgetStacked *nwi = this->GetWidget<NWidgetStacked>(VVW_WIDGET_SELECT_REFIT_TURN);
02147       if (nwi->shown_plane + SEL_RT_BASEPLANE != plane) {
02148         this->SelectPlane(plane);
02149         this->SetWidgetDirty(VVW_WIDGET_SELECT_REFIT_TURN);
02150       }
02151     }
02152   }
02153 };
02154 
02155 
02157 void ShowVehicleViewWindow(const Vehicle *v)
02158 {
02159   AllocateWindowDescFront<VehicleViewWindow>((v->type == VEH_TRAIN) ? &_train_view_desc : &_vehicle_view_desc, v->index);
02160 }
02161 
02162 void StopGlobalFollowVehicle(const Vehicle *v)
02163 {
02164   Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02165   if (w != NULL && w->viewport->follow_vehicle == v->index) {
02166     ScrollMainWindowTo(v->x_pos, v->y_pos, v->z_pos, true); // lock the main view on the vehicle's last position
02167     w->viewport->follow_vehicle = INVALID_VEHICLE;
02168   }
02169 }

Generated on Sat Dec 26 20:06:07 2009 for OpenTTD by  doxygen 1.5.6