vehicle_gui.cpp

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

Generated on Wed Dec 23 23:27:56 2009 for OpenTTD by  doxygen 1.5.6