vehicle_gui.cpp

Go to the documentation of this file.
00001 /* $Id: vehicle_gui.cpp 15480 2009-02-14 18:42:03Z michi_cc $ */
00002 
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "company_func.h"
00009 #include "gui.h"
00010 #include "window_gui.h"
00011 #include "textbuf_gui.h"
00012 #include "command_func.h"
00013 #include "vehicle_gui.h"
00014 #include "vehicle_gui_base.h"
00015 #include "viewport_func.h"
00016 #include "gfx_func.h"
00017 #include "newgrf_engine.h"
00018 #include "newgrf_text.h"
00019 #include "station_map.h"
00020 #include "roadveh.h"
00021 #include "depot_base.h"
00022 #include "group_gui.h"
00023 #include "strings_func.h"
00024 #include "window_func.h"
00025 #include "vehicle_func.h"
00026 #include "autoreplace_gui.h"
00027 #include "string_func.h"
00028 #include "widgets/dropdown_func.h"
00029 #include "timetable.h"
00030 #include "vehiclelist.h"
00031 #include "settings_type.h"
00032 
00033 #include "table/sprites.h"
00034 #include "table/strings.h"
00035 
00036 Sorting _sorting;
00037 
00038 static GUIVehicleList::SortFunction VehicleNumberSorter;
00039 static GUIVehicleList::SortFunction VehicleNameSorter;
00040 static GUIVehicleList::SortFunction VehicleAgeSorter;
00041 static GUIVehicleList::SortFunction VehicleProfitThisYearSorter;
00042 static GUIVehicleList::SortFunction VehicleProfitLastYearSorter;
00043 static GUIVehicleList::SortFunction VehicleCargoSorter;
00044 static GUIVehicleList::SortFunction VehicleReliabilitySorter;
00045 static GUIVehicleList::SortFunction VehicleMaxSpeedSorter;
00046 static GUIVehicleList::SortFunction VehicleModelSorter;
00047 static GUIVehicleList::SortFunction VehicleValueSorter;
00048 static GUIVehicleList::SortFunction VehicleLengthSorter;
00049 static GUIVehicleList::SortFunction VehicleTimeToLiveSorter;
00050 
00051 GUIVehicleList::SortFunction * const BaseVehicleListWindow::vehicle_sorter_funcs[] = {
00052   &VehicleNumberSorter,
00053   &VehicleNameSorter,
00054   &VehicleAgeSorter,
00055   &VehicleProfitThisYearSorter,
00056   &VehicleProfitLastYearSorter,
00057   &VehicleCargoSorter,
00058   &VehicleReliabilitySorter,
00059   &VehicleMaxSpeedSorter,
00060   &VehicleModelSorter,
00061   &VehicleValueSorter,
00062   &VehicleLengthSorter,
00063   &VehicleTimeToLiveSorter,
00064 };
00065 
00066 const StringID BaseVehicleListWindow::vehicle_sorter_names[] = {
00067   STR_SORT_BY_NUMBER,
00068   STR_SORT_BY_DROPDOWN_NAME,
00069   STR_SORT_BY_AGE,
00070   STR_SORT_BY_PROFIT_THIS_YEAR,
00071   STR_SORT_BY_PROFIT_LAST_YEAR,
00072   STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE,
00073   STR_SORT_BY_RELIABILITY,
00074   STR_SORT_BY_MAX_SPEED,
00075   STR_SORT_BY_MODEL,
00076   STR_SORT_BY_VALUE,
00077   STR_SORT_BY_LENGTH,
00078   STR_SORT_BY_LIFE_TIME,
00079   INVALID_STRING_ID
00080 };
00081 
00082 void BaseVehicleListWindow::BuildVehicleList(Owner owner, uint16 index, uint16 window_type)
00083 {
00084   if (!this->vehicles.NeedRebuild()) return;
00085 
00086   DEBUG(misc, 3, "Building vehicle list for company %d at station %d", owner, index);
00087 
00088   GenerateVehicleSortList(&this->vehicles, this->vehicle_type, owner, index, window_type);
00089 
00090   this->vehicles.RebuildDone();
00091 }
00092 
00093 /* cached values for VehicleNameSorter to spare many GetString() calls */
00094 static const Vehicle *_last_vehicle[2] = { NULL, NULL };
00095 
00096 void BaseVehicleListWindow::SortVehicleList()
00097 {
00098   if (this->vehicles.Sort()) return;
00099 
00100   /* invalidate cached values for name sorter - vehicle names could change */
00101   _last_vehicle[0] = _last_vehicle[1] = NULL;
00102 }
00103 
00104 void DepotSortList(VehicleList *list)
00105 {
00106   if (list->Length() < 2) return;
00107   QSortT(list->Begin(), list->Length(), &VehicleNumberSorter);
00108 }
00109 
00111 void DrawVehicleProfitButton(const Vehicle *v, int x, int y)
00112 {
00113   SpriteID pal;
00114 
00115   /* draw profit-based coloured icons */
00116   if (v->age <= DAYS_IN_YEAR * 2) {
00117     pal = PALETTE_TO_GREY;
00118   } else if (v->GetDisplayProfitLastYear() < 0) {
00119     pal = PALETTE_TO_RED;
00120   } else if (v->GetDisplayProfitLastYear() < 10000) {
00121     pal = PALETTE_TO_YELLOW;
00122   } else {
00123     pal = PALETTE_TO_GREEN;
00124   }
00125   DrawSprite(SPR_BLOT, pal, x, y);
00126 }
00127 
00128 struct RefitOption {
00129   CargoID cargo;
00130   byte subtype;
00131   uint16 value;
00132   EngineID engine;
00133 };
00134 
00135 struct RefitList {
00136   uint num_lines;
00137   RefitOption *items;
00138 };
00139 
00140 static RefitList *BuildRefitList(const Vehicle *v)
00141 {
00142   uint max_lines = 256;
00143   RefitOption *refit = CallocT<RefitOption>(max_lines);
00144   RefitList *list = CallocT<RefitList>(1);
00145   Vehicle *u = (Vehicle*)v;
00146   uint num_lines = 0;
00147   uint i;
00148 
00149   do {
00150     uint32 cmask = EngInfo(u->engine_type)->refit_mask;
00151     byte callbackmask = EngInfo(u->engine_type)->callbackmask;
00152 
00153     /* Skip this engine if it has no capacity */
00154     if (u->cargo_cap == 0) continue;
00155 
00156     /* Loop through all cargos in the refit mask */
00157     for (CargoID cid = 0; cid < NUM_CARGO && num_lines < max_lines; cid++) {
00158       /* Skip cargo type if it's not listed */
00159       if (!HasBit(cmask, cid)) continue;
00160 
00161       /* Check the vehicle's callback mask for cargo suffixes */
00162       if (HasBit(callbackmask, CBM_VEHICLE_CARGO_SUFFIX)) {
00163         /* Make a note of the original cargo type. It has to be
00164          * changed to test the cargo & subtype... */
00165         CargoID temp_cargo = u->cargo_type;
00166         byte temp_subtype  = u->cargo_subtype;
00167         byte refit_cyc;
00168 
00169         u->cargo_type = cid;
00170 
00171         for (refit_cyc = 0; refit_cyc < 16 && num_lines < max_lines; refit_cyc++) {
00172           bool duplicate = false;
00173           uint16 callback;
00174 
00175           u->cargo_subtype = refit_cyc;
00176           callback = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, u->engine_type, u);
00177 
00178           if (callback == 0xFF) callback = CALLBACK_FAILED;
00179           if (refit_cyc != 0 && callback == CALLBACK_FAILED) break;
00180 
00181           /* Check if this cargo and subtype combination are listed */
00182           for (i = 0; i < num_lines && !duplicate; i++) {
00183             if (refit[i].cargo == cid && refit[i].value == callback) duplicate = true;
00184           }
00185 
00186           if (duplicate) continue;
00187 
00188           refit[num_lines].cargo   = cid;
00189           refit[num_lines].subtype = refit_cyc;
00190           refit[num_lines].value   = callback;
00191           refit[num_lines].engine  = u->engine_type;
00192           num_lines++;
00193         }
00194 
00195         /* Reset the vehicle's cargo type */
00196         u->cargo_type    = temp_cargo;
00197         u->cargo_subtype = temp_subtype;
00198       } else {
00199         /* No cargo suffix callback -- use no subtype */
00200         bool duplicate = false;
00201 
00202         for (i = 0; i < num_lines && !duplicate; i++) {
00203           if (refit[i].cargo == cid && refit[i].value == CALLBACK_FAILED) duplicate = true;
00204         }
00205 
00206         if (!duplicate) {
00207           refit[num_lines].cargo   = cid;
00208           refit[num_lines].subtype = 0;
00209           refit[num_lines].value   = CALLBACK_FAILED;
00210           refit[num_lines].engine  = INVALID_ENGINE;
00211           num_lines++;
00212         }
00213       }
00214     }
00215   } while ((v->type == VEH_TRAIN || v->type == VEH_ROAD) && (u = u->Next()) != NULL && num_lines < max_lines);
00216 
00217   list->num_lines = num_lines;
00218   list->items = refit;
00219 
00220   return list;
00221 }
00222 
00232 static RefitOption *DrawVehicleRefitWindow(const RefitList *list, int sel, uint pos, uint rows, uint delta)
00233 {
00234   RefitOption *refit = list->items;
00235   RefitOption *selected = NULL;
00236   uint num_lines = list->num_lines;
00237   uint y = 31;
00238   uint i;
00239 
00240   /* Draw the list, and find the selected cargo (by its position in list) */
00241   for (i = 0; i < num_lines; i++) {
00242     TextColour colour = TC_BLACK;
00243     if (sel == 0) {
00244       selected = &refit[i];
00245       colour = TC_WHITE;
00246     }
00247 
00248     if (i >= pos && i < pos + rows) {
00249       /* Draw the cargo name */
00250       int last_x = DrawString(2, y, GetCargo(refit[i].cargo)->name, colour);
00251 
00252       /* If the callback succeeded, draw the cargo suffix */
00253       if (refit[i].value != CALLBACK_FAILED) {
00254         DrawString(last_x + 1, y, GetGRFStringID(GetEngineGRFID(refit[i].engine), 0xD000 + refit[i].value), colour);
00255       }
00256       y += delta;
00257     }
00258 
00259     sel--;
00260   }
00261 
00262   return selected;
00263 }
00264 
00265 struct RefitWindow : public Window {
00266   int sel;
00267   RefitOption *cargo;
00268   RefitList *list;
00269   uint length;
00270   VehicleOrderID order;
00271 
00272   RefitWindow(const WindowDesc *desc, const Vehicle *v, VehicleOrderID order) : Window(desc, v->index)
00273   {
00274     this->owner = v->owner;
00275     this->vscroll.cap = 8;
00276     this->resize.step_height = 14;
00277 
00278     this->order = order;
00279     this->sel  = -1;
00280     this->list = BuildRefitList(v);
00281     if (v->type == VEH_TRAIN) this->length = CountVehiclesInChain(v);
00282     SetVScrollCount(this, this->list->num_lines);
00283 
00284     switch (v->type) {
00285       case VEH_TRAIN:
00286         this->widget[3].tooltips = STR_RAIL_SELECT_TYPE_OF_CARGO_FOR;
00287         this->widget[6].data     = STR_RAIL_REFIT_VEHICLE;
00288         this->widget[6].tooltips = STR_RAIL_REFIT_TO_CARRY_HIGHLIGHTED;
00289         break;
00290 
00291       case VEH_ROAD:
00292         this->widget[3].tooltips = STR_ROAD_SELECT_TYPE_OF_CARGO_FOR;
00293         this->widget[6].data     = STR_REFIT_ROAD_VEHICLE;
00294         this->widget[6].tooltips = STR_REFIT_ROAD_VEHICLE_TO_CARRY_HIGHLIGHTED;
00295         break;
00296 
00297       case VEH_SHIP:
00298         this->widget[3].tooltips = STR_983D_SELECT_TYPE_OF_CARGO_FOR;
00299         this->widget[6].data     = STR_983C_REFIT_SHIP;
00300         this->widget[6].tooltips = STR_983E_REFIT_SHIP_TO_CARRY_HIGHLIGHTED;
00301         break;
00302 
00303       case VEH_AIRCRAFT:
00304         this->widget[3].tooltips = STR_A03E_SELECT_TYPE_OF_CARGO_FOR;
00305         this->widget[6].data     = STR_A03D_REFIT_AIRCRAFT;
00306         this->widget[6].tooltips = STR_A03F_REFIT_AIRCRAFT_TO_CARRY;
00307         break;
00308 
00309       default: NOT_REACHED();
00310     }
00311 
00312     this->FindWindowPlacementAndResize(desc);
00313   }
00314 
00315   ~RefitWindow()
00316   {
00317     free(this->list->items);
00318     free(this->list);
00319   }
00320 
00321   virtual void OnPaint()
00322   {
00323     Vehicle *v = GetVehicle(this->window_number);
00324 
00325     if (v->type == VEH_TRAIN) {
00326       uint length = CountVehiclesInChain(v);
00327 
00328       if (length != this->length) {
00329         /* Consist length has changed, so rebuild the refit list */
00330         free(this->list->items);
00331         free(this->list);
00332         this->list = BuildRefitList(v);
00333         this->length = length;
00334       }
00335     }
00336 
00337     SetVScrollCount(this, this->list->num_lines);
00338 
00339     SetDParam(0, v->index);
00340     this->DrawWidgets();
00341 
00342     this->cargo = DrawVehicleRefitWindow(this->list, this->sel, this->vscroll.pos, this->vscroll.cap, this->resize.step_height);
00343 
00344     if (this->cargo != NULL) {
00345       CommandCost cost;
00346 
00347       cost = DoCommand(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8,
00348                DC_QUERY_COST, GetCmdRefitVeh(v->type));
00349 
00350       if (CmdSucceeded(cost)) {
00351         SetDParam(0, this->cargo->cargo);
00352         SetDParam(1, _returned_refit_capacity);
00353         SetDParam(2, cost.GetCost());
00354         DrawString(2, this->widget[5].top + 1, STR_9840_NEW_CAPACITY_COST_OF_REFIT, TC_FROMSTRING);
00355       }
00356     }
00357   }
00358 
00359   virtual void OnClick(Point pt, int widget)
00360   {
00361     switch (widget) {
00362       case 3: { // listbox
00363         int y = pt.y - this->widget[3].top;
00364         if (y >= 0) {
00365           this->sel = (y / (int)this->resize.step_height) + this->vscroll.pos;
00366           this->SetDirty();
00367         }
00368         break;
00369       }
00370 
00371       case 6: // refit button
00372         if (this->cargo != NULL) {
00373           const Vehicle *v = GetVehicle(this->window_number);
00374 
00375           if (this->order == INVALID_VEH_ORDER_ID) {
00376             int command = 0;
00377 
00378             switch (v->type) {
00379               default: NOT_REACHED();
00380               case VEH_TRAIN:    command = CMD_REFIT_RAIL_VEHICLE | CMD_MSG(STR_RAIL_CAN_T_REFIT_VEHICLE);  break;
00381               case VEH_ROAD:     command = CMD_REFIT_ROAD_VEH     | CMD_MSG(STR_REFIT_ROAD_VEHICLE_CAN_T);  break;
00382               case VEH_SHIP:     command = CMD_REFIT_SHIP         | CMD_MSG(STR_9841_CAN_T_REFIT_SHIP);     break;
00383               case VEH_AIRCRAFT: command = CMD_REFIT_AIRCRAFT     | CMD_MSG(STR_A042_CAN_T_REFIT_AIRCRAFT); break;
00384             }
00385             if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8, command)) delete this;
00386           } else {
00387             if (DoCommandP(v->tile, v->index, this->cargo->cargo | this->cargo->subtype << 8 | this->order << 16, CMD_ORDER_REFIT)) delete this;
00388           }
00389         }
00390         break;
00391     }
00392   }
00393 
00394   virtual void OnResize(Point new_size, Point delta)
00395   {
00396     this->vscroll.cap += delta.y / (int)this->resize.step_height;
00397     this->widget[3].data = (this->vscroll.cap << 8) + 1;
00398   }
00399 };
00400 
00401 
00402 static const Widget _vehicle_refit_widgets[] = {
00403   {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,                            STR_018B_CLOSE_WINDOW},
00404   {    WWT_CAPTION,   RESIZE_NONE,  COLOUR_GREY,    11,   239,     0,    13, STR_983B_REFIT,                      STR_018C_WINDOW_TITLE_DRAG_THIS},
00405   {    WWT_TEXTBTN,   RESIZE_NONE,  COLOUR_GREY,     0,   239,    14,    27, STR_983F_SELECT_CARGO_TYPE_TO_CARRY, STR_983D_SELECT_TYPE_OF_CARGO_FOR},
00406   {     WWT_MATRIX, RESIZE_BOTTOM,  COLOUR_GREY,     0,   227,    28,   139, 0x801,                               STR_EMPTY},
00407   {  WWT_SCROLLBAR, RESIZE_BOTTOM,  COLOUR_GREY,   228,   239,    28,   139, 0x0,                                 STR_0190_SCROLL_BAR_SCROLLS_LIST},
00408   {      WWT_PANEL,     RESIZE_TB,  COLOUR_GREY,     0,   239,   140,   161, 0x0,                                 STR_NULL},
00409   { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,     0,   227,   162,   173, 0x0,                                 STR_NULL},
00410   {  WWT_RESIZEBOX,     RESIZE_TB,  COLOUR_GREY,   228,   239,   162,   173, 0x0,                                 STR_RESIZE_BUTTON},
00411   {   WIDGETS_END},
00412 };
00413 
00414 static const WindowDesc _vehicle_refit_desc = {
00415   WDP_AUTO, WDP_AUTO, 240, 174, 240, 174,
00416   WC_VEHICLE_REFIT, WC_VEHICLE_VIEW,
00417   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE | WDF_CONSTRUCTION,
00418   _vehicle_refit_widgets,
00419 };
00420 
00425 void ShowVehicleRefitWindow(const Vehicle *v, VehicleOrderID order, Window *parent)
00426 {
00427   DeleteWindowById(WC_VEHICLE_REFIT, v->index);
00428   RefitWindow *w = new RefitWindow(&_vehicle_refit_desc, v, order);
00429   w->parent = parent;
00430 }
00431 
00433 uint ShowAdditionalText(int x, int y, uint w, EngineID engine)
00434 {
00435   uint16 callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, NULL);
00436   if (callback == CALLBACK_FAILED) return 0;
00437 
00438   /* STR_02BD is used to start the string with {BLACK} */
00439   SetDParam(0, GetGRFStringID(GetEngineGRFID(engine), 0xD000 + callback));
00440   PrepareTextRefStackUsage(0);
00441   uint result = DrawStringMultiLine(x, y, STR_02BD, w);
00442   StopTextRefStackUsage();
00443   return result;
00444 }
00445 
00447 uint ShowRefitOptionsList(int x, int y, uint w, EngineID engine)
00448 {
00449   /* List of cargo types of this engine */
00450   uint32 cmask = EngInfo(engine)->refit_mask;
00451   /* List of cargo types available in this climate */
00452   uint32 lmask = _cargo_mask;
00453   char string[512];
00454   char *b = string;
00455 
00456   /* Draw nothing if the engine is not refittable */
00457   if (CountBits(cmask) <= 1) return 0;
00458 
00459   b = InlineString(b, STR_PURCHASE_INFO_REFITTABLE_TO);
00460 
00461   if (cmask == lmask) {
00462     /* Engine can be refitted to all types in this climate */
00463     b = InlineString(b, STR_PURCHASE_INFO_ALL_TYPES);
00464   } else {
00465     /* Check if we are able to refit to more cargo types and unable to. If
00466      * so, invert the cargo types to list those that we can't refit to. */
00467     if (CountBits(cmask ^ lmask) < CountBits(cmask)) {
00468       cmask ^= lmask;
00469       b = InlineString(b, STR_PURCHASE_INFO_ALL_BUT);
00470     }
00471 
00472     bool first = true;
00473 
00474     /* Add each cargo type to the list */
00475     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00476       if (!HasBit(cmask, cid)) continue;
00477 
00478       if (b >= lastof(string) - (2 + 2 * 4)) break; // ", " and two calls to Utf8Encode()
00479 
00480       if (!first) b = strecpy(b, ", ", lastof(string));
00481       first = false;
00482 
00483       b = InlineString(b, GetCargo(cid)->name);
00484     }
00485   }
00486 
00487   /* Terminate and display the completed string */
00488   *b = '\0';
00489 
00490   /* Make sure we detect any buffer overflow */
00491   assert(b < endof(string));
00492 
00493   SetDParamStr(0, string);
00494   return DrawStringMultiLine(x, y, STR_JUST_RAW_STRING, w);
00495 }
00496 
00498 StringID GetCargoSubtypeText(const Vehicle *v)
00499 {
00500   if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_CARGO_SUFFIX)) {
00501     uint16 cb = GetVehicleCallback(CBID_VEHICLE_CARGO_SUFFIX, 0, 0, v->engine_type, v);
00502     if (cb != CALLBACK_FAILED) {
00503       return GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + cb);
00504     }
00505   }
00506   return STR_EMPTY;
00507 }
00508 
00510 static int CDECL VehicleNumberSorter(const Vehicle * const *a, const Vehicle * const *b)
00511 {
00512   return (*a)->unitnumber - (*b)->unitnumber;
00513 }
00514 
00516 static int CDECL VehicleNameSorter(const Vehicle * const *a, const Vehicle * const *b)
00517 {
00518   static char last_name[2][64];
00519 
00520   if (*a != _last_vehicle[0]) {
00521     _last_vehicle[0] = *a;
00522     SetDParam(0, (*a)->index);
00523     GetString(last_name[0], STR_VEHICLE_NAME, lastof(last_name[0]));
00524   }
00525 
00526   if (*b != _last_vehicle[1]) {
00527     _last_vehicle[1] = *b;
00528     SetDParam(0, (*b)->index);
00529     GetString(last_name[1], STR_VEHICLE_NAME, lastof(last_name[1]));
00530   }
00531 
00532   int r = strcmp(last_name[0], last_name[1]);
00533   return (r != 0) ? r : VehicleNumberSorter(a, b);
00534 }
00535 
00537 static int CDECL VehicleAgeSorter(const Vehicle * const *a, const Vehicle * const *b)
00538 {
00539   int r = (*a)->age - (*b)->age;
00540   return (r != 0) ? r : VehicleNumberSorter(a, b);
00541 }
00542 
00544 static int CDECL VehicleProfitThisYearSorter(const Vehicle * const *a, const Vehicle * const *b)
00545 {
00546   int r = ClampToI32((*a)->GetDisplayProfitThisYear() - (*b)->GetDisplayProfitThisYear());
00547   return (r != 0) ? r : VehicleNumberSorter(a, b);
00548 }
00549 
00551 static int CDECL VehicleProfitLastYearSorter(const Vehicle * const *a, const Vehicle * const *b)
00552 {
00553   int r = ClampToI32((*a)->GetDisplayProfitLastYear() - (*b)->GetDisplayProfitLastYear());
00554   return (r != 0) ? r : VehicleNumberSorter(a, b);
00555 }
00556 
00558 static int CDECL VehicleCargoSorter(const Vehicle * const *a, const Vehicle * const *b)
00559 {
00560   const Vehicle *v;
00561   AcceptedCargo diff;
00562   memset(diff, 0, sizeof(diff));
00563 
00564   /* Append the cargo of the connected weagons */
00565   for (v = *a; v != NULL; v = v->Next()) diff[v->cargo_type] += v->cargo_cap;
00566   for (v = *b; v != NULL; v = v->Next()) diff[v->cargo_type] -= v->cargo_cap;
00567 
00568   int r = 0;
00569   for (CargoID i = 0; i < NUM_CARGO; i++) {
00570     r = diff[i];
00571     if (r != 0) break;
00572   }
00573 
00574   return (r != 0) ? r : VehicleNumberSorter(a, b);
00575 }
00576 
00578 static int CDECL VehicleReliabilitySorter(const Vehicle * const *a, const Vehicle * const *b)
00579 {
00580   int r = (*a)->reliability - (*b)->reliability;
00581   return (r != 0) ? r : VehicleNumberSorter(a, b);
00582 }
00583 
00585 static int CDECL VehicleMaxSpeedSorter(const Vehicle * const *a, const Vehicle * const *b)
00586 {
00587   int r = 0;
00588   if ((*a)->type == VEH_TRAIN && (*b)->type == VEH_TRAIN) {
00589     r = (*a)->u.rail.cached_max_speed - (*b)->u.rail.cached_max_speed;
00590   } else {
00591     r = (*a)->max_speed - (*b)->max_speed;
00592   }
00593   return (r != 0) ? r : VehicleNumberSorter(a, b);
00594 }
00595 
00597 static int CDECL VehicleModelSorter(const Vehicle * const *a, const Vehicle * const *b)
00598 {
00599   int r = (*a)->engine_type - (*b)->engine_type;
00600   return (r != 0) ? r : VehicleNumberSorter(a, b);
00601 }
00602 
00604 static int CDECL VehicleValueSorter(const Vehicle * const *a, const Vehicle * const *b)
00605 {
00606   const Vehicle *u;
00607   Money diff = 0;
00608 
00609   for (u = *a; u != NULL; u = u->Next()) diff += u->value;
00610   for (u = *b; u != NULL; u = u->Next()) diff -= u->value;
00611 
00612   int r = ClampToI32(diff);
00613   return (r != 0) ? r : VehicleNumberSorter(a, b);
00614 }
00615 
00617 static int CDECL VehicleLengthSorter(const Vehicle * const *a, const Vehicle * const *b)
00618 {
00619   int r = 0;
00620   switch ((*a)->type) {
00621     case VEH_TRAIN:
00622       r = (*a)->u.rail.cached_total_length - (*b)->u.rail.cached_total_length;
00623       break;
00624 
00625     case VEH_ROAD: {
00626       const Vehicle *u;
00627       for (u = *a; u != NULL; u = u->Next()) r += u->u.road.cached_veh_length;
00628       for (u = *b; u != NULL; u = u->Next()) r -= u->u.road.cached_veh_length;
00629     } break;
00630 
00631     default: NOT_REACHED();
00632   }
00633   return (r != 0) ? r : VehicleNumberSorter(a, b);
00634 }
00635 
00637 static int CDECL VehicleTimeToLiveSorter(const Vehicle * const *a, const Vehicle * const *b)
00638 {
00639   int r = ClampToI32(((*a)->max_age - (*a)->age) - ((*b)->max_age - (*b)->age));
00640   return (r != 0) ? r : VehicleNumberSorter(a, b);
00641 }
00642 
00643 void InitializeGUI()
00644 {
00645   MemSetT(&_sorting, 0);
00646 }
00647 
00654 static inline void ChangeVehicleWindow(WindowClass window_class, VehicleID from_index, VehicleID to_index)
00655 {
00656   Window *w = FindWindowById(window_class, from_index);
00657   if (w != NULL) {
00658     w->window_number = to_index;
00659     if (w->viewport != NULL) w->viewport->follow_vehicle = to_index;
00660     if (to_index != INVALID_VEHICLE) InvalidateThisWindowData(w, 0);
00661   }
00662 }
00663 
00669 void ChangeVehicleViewWindow(VehicleID from_index, VehicleID to_index)
00670 {
00671   ChangeVehicleWindow(WC_VEHICLE_VIEW,      from_index, to_index);
00672   ChangeVehicleWindow(WC_VEHICLE_ORDERS,    from_index, to_index);
00673   ChangeVehicleWindow(WC_VEHICLE_REFIT,     from_index, to_index);
00674   ChangeVehicleWindow(WC_VEHICLE_DETAILS,   from_index, to_index);
00675   ChangeVehicleWindow(WC_VEHICLE_TIMETABLE, from_index, to_index);
00676 }
00677 
00678 enum VehicleListWindowWidgets {
00679   VLW_WIDGET_CLOSEBOX = 0,
00680   VLW_WIDGET_CAPTION,
00681   VLW_WIDGET_STICKY,
00682   VLW_WIDGET_SORT_ORDER,
00683   VLW_WIDGET_SORT_BY_PULLDOWN,
00684   VLW_WIDGET_EMPTY_TOP_RIGHT,
00685   VLW_WIDGET_LIST,
00686   VLW_WIDGET_SCROLLBAR,
00687   VLW_WIDGET_OTHER_COMPANY_FILLER,
00688   VLW_WIDGET_AVAILABLE_VEHICLES,
00689   VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
00690   VLW_WIDGET_STOP_ALL,
00691   VLW_WIDGET_START_ALL,
00692   VLW_WIDGET_EMPTY_BOTTOM_RIGHT,
00693   VLW_WIDGET_RESIZE,
00694 };
00695 
00696 static const Widget _vehicle_list_widgets[] = {
00697   {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,             STR_018B_CLOSE_WINDOW},
00698   {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   247,     0,    13, 0x0,                  STR_018C_WINDOW_TITLE_DRAG_THIS},
00699   {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_GREY,   248,   259,     0,    13, 0x0,                  STR_STICKY_BUTTON},
00700   { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_GREY,     0,    80,    14,    25, STR_SORT_BY,          STR_SORT_ORDER_TIP},
00701   {   WWT_DROPDOWN,   RESIZE_NONE,  COLOUR_GREY,    81,   247,    14,    25, 0x0,                  STR_SORT_CRITERIA_TIP},
00702   {      WWT_PANEL,  RESIZE_RIGHT,  COLOUR_GREY,   248,   259,    14,    25, 0x0,                  STR_NULL},
00703   {     WWT_MATRIX,     RESIZE_RB,  COLOUR_GREY,     0,   247,    26,   181, 0x0,                  STR_NULL},
00704   {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY,   248,   259,    26,   181, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
00705   /* Widget to be shown for other companies hiding the following 6 widgets */
00706   {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,     0,   247,   182,   193, 0x0,                  STR_NULL},
00707 
00708   { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,     0,   105,   182,   193, 0x0,                  STR_AVAILABLE_ENGINES_TIP},
00709   {   WWT_DROPDOWN,     RESIZE_TB,  COLOUR_GREY,   106,   223,   182,   193, STR_MANAGE_LIST,      STR_MANAGE_LIST_TIP},
00710 
00711   { WWT_PUSHIMGBTN,     RESIZE_TB,  COLOUR_GREY,   224,   235,   182,   193, SPR_FLAG_VEH_STOPPED, STR_MASS_STOP_LIST_TIP},
00712   { WWT_PUSHIMGBTN,     RESIZE_TB,  COLOUR_GREY,   236,   247,   182,   193, SPR_FLAG_VEH_RUNNING, STR_MASS_START_LIST_TIP},
00713   {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,   248,   247,   182,   193, 0x0,                  STR_NULL},
00714   {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   248,   259,   182,   193, 0x0,                  STR_RESIZE_BUTTON},
00715   {   WIDGETS_END},
00716 };
00717 
00718 static void DrawSmallOrderList(const Vehicle *v, int x, int y)
00719 {
00720   const Order *order;
00721   int sel, i = 0;
00722 
00723   sel = v->cur_order_index;
00724 
00725   FOR_VEHICLE_ORDERS(v, order) {
00726     if (sel == 0) DrawString(x - 6, y, STR_SMALL_RIGHT_ARROW, TC_BLACK);
00727     sel--;
00728 
00729     if (order->IsType(OT_GOTO_STATION)) {
00730       if (v->type == VEH_SHIP && GetStation(order->GetDestination())->IsBuoy()) continue;
00731 
00732       SetDParam(0, order->GetDestination());
00733       DrawString(x, y, STR_A036, TC_FROMSTRING);
00734 
00735       y += 6;
00736       if (++i == 4) break;
00737     }
00738   }
00739 }
00740 
00741 static void DrawVehicleImage(const Vehicle *v, int x, int y, VehicleID selection, int count, int skip)
00742 {
00743   switch (v->type) {
00744     case VEH_TRAIN:    DrawTrainImage(v, x, y, selection, count, skip); break;
00745     case VEH_ROAD:     DrawRoadVehImage(v, x, y, selection, count);     break;
00746     case VEH_SHIP:     DrawShipImage(v, x, y, selection);               break;
00747     case VEH_AIRCRAFT: DrawAircraftImage(v, x, y, selection);           break;
00748     default: NOT_REACHED();
00749   }
00750 }
00751 
00757 void BaseVehicleListWindow::DrawVehicleListItems(int x, VehicleID selected_vehicle)
00758 {
00759   int y = PLY_WND_PRC__OFFSET_TOP_WIDGET;
00760   uint max = min(this->vscroll.pos + this->vscroll.cap, this->vehicles.Length());
00761   for (uint i = this->vscroll.pos; i < max; ++i) {
00762     const Vehicle *v = this->vehicles[i];
00763     StringID str;
00764 
00765     SetDParam(0, v->GetDisplayProfitThisYear());
00766     SetDParam(1, v->GetDisplayProfitLastYear());
00767 
00768     DrawVehicleImage(v, x + 19, y + 6, selected_vehicle, this->widget[VLW_WIDGET_LIST].right - this->widget[VLW_WIDGET_LIST].left - 20, 0);
00769     DrawString(x + 19, y + this->resize.step_height - 8, STR_0198_PROFIT_THIS_YEAR_LAST_YEAR, TC_FROMSTRING);
00770 
00771     if (v->name != NULL) {
00772       /* The vehicle got a name so we will print it */
00773       SetDParam(0, v->index);
00774       DrawString(x + 19, y, STR_01AB, TC_FROMSTRING);
00775     } else if (v->group_id != DEFAULT_GROUP) {
00776       /* The vehicle has no name, but is member of a group, so print group name */
00777       SetDParam(0, v->group_id);
00778       DrawString(x + 19, y, STR_GROUP_TINY_NAME, TC_BLACK);
00779     }
00780 
00781     if (this->resize.step_height == PLY_WND_PRC__SIZE_OF_ROW_BIG) DrawSmallOrderList(v, x + 138, y);
00782 
00783     if (v->IsInDepot()) {
00784       str = STR_021F;
00785     } else {
00786       str = (v->age > v->max_age - DAYS_IN_LEAP_YEAR) ? STR_00E3 : STR_00E2;
00787     }
00788 
00789     SetDParam(0, v->unitnumber);
00790     DrawString(x, y + 2, str, TC_FROMSTRING);
00791 
00792     DrawVehicleProfitButton(v, x, y + 13);
00793 
00794     y += this->resize.step_height;
00795   }
00796 }
00797 
00807 struct VehicleListWindow : public BaseVehicleListWindow {
00808 
00809   VehicleListWindow(const WindowDesc *desc, WindowNumber window_number) : BaseVehicleListWindow(desc, window_number)
00810   {
00811     uint16 window_type = this->window_number & VLW_MASK;
00812     CompanyID company = (CompanyID)GB(this->window_number, 0, 8);
00813 
00814     this->vehicle_type = (VehicleType)GB(this->window_number, 11, 5);
00815     this->owner = company;
00816 
00817     /* Hide the widgets that we will not use in this window
00818     * Some windows contains actions only fit for the owner */
00819     if (company == _local_company) {
00820       this->HideWidget(VLW_WIDGET_OTHER_COMPANY_FILLER);
00821       this->SetWidgetDisabledState(VLW_WIDGET_AVAILABLE_VEHICLES, window_type != VLW_STANDARD);
00822     } else {
00823       this->SetWidgetsHiddenState(true,
00824         VLW_WIDGET_AVAILABLE_VEHICLES,
00825         VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
00826         VLW_WIDGET_STOP_ALL,
00827         VLW_WIDGET_START_ALL,
00828         VLW_WIDGET_EMPTY_BOTTOM_RIGHT,
00829         WIDGET_LIST_END);
00830     }
00831 
00832     /* Set up the window widgets */
00833     switch (this->vehicle_type) {
00834       case VEH_TRAIN:
00835         this->widget[VLW_WIDGET_LIST].tooltips          = STR_883D_TRAINS_CLICK_ON_TRAIN_FOR;
00836         this->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_TRAINS;
00837         break;
00838 
00839       case VEH_ROAD:
00840         this->widget[VLW_WIDGET_LIST].tooltips          = STR_901A_ROAD_VEHICLES_CLICK_ON;
00841         this->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_ROAD_VEHICLES;
00842         break;
00843 
00844       case VEH_SHIP:
00845         this->widget[VLW_WIDGET_LIST].tooltips          = STR_9823_SHIPS_CLICK_ON_SHIP_FOR;
00846         this->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_SHIPS;
00847         break;
00848 
00849       case VEH_AIRCRAFT:
00850         this->widget[VLW_WIDGET_LIST].tooltips          = STR_A01F_AIRCRAFT_CLICK_ON_AIRCRAFT;
00851         this->widget[VLW_WIDGET_AVAILABLE_VEHICLES].data = STR_AVAILABLE_AIRCRAFT;
00852         break;
00853 
00854       default: NOT_REACHED();
00855     }
00856 
00857     switch (window_type) {
00858       case VLW_SHARED_ORDERS:
00859         this->widget[VLW_WIDGET_CAPTION].data  = STR_VEH_WITH_SHARED_ORDERS_LIST;
00860         break;
00861 
00862       case VLW_STANDARD: /* Company Name - standard widget setup */
00863         switch (this->vehicle_type) {
00864           case VEH_TRAIN:    this->widget[VLW_WIDGET_CAPTION].data = STR_881B_TRAINS;        break;
00865           case VEH_ROAD:     this->widget[VLW_WIDGET_CAPTION].data = STR_9001_ROAD_VEHICLES; break;
00866           case VEH_SHIP:     this->widget[VLW_WIDGET_CAPTION].data = STR_9805_SHIPS;         break;
00867           case VEH_AIRCRAFT: this->widget[VLW_WIDGET_CAPTION].data = STR_A009_AIRCRAFT;      break;
00868           default: NOT_REACHED(); break;
00869         }
00870         break;
00871 
00872       case VLW_WAYPOINT_LIST:
00873         this->widget[VLW_WIDGET_CAPTION].data = STR_WAYPOINT_VIEWPORT_LIST;
00874         break;
00875 
00876       case VLW_STATION_LIST: /* Station Name */
00877         switch (this->vehicle_type) {
00878           case VEH_TRAIN:    this->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_TRAINS;        break;
00879           case VEH_ROAD:     this->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_ROAD_VEHICLES; break;
00880           case VEH_SHIP:     this->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_SHIPS;         break;
00881           case VEH_AIRCRAFT: this->widget[VLW_WIDGET_CAPTION].data = STR_SCHEDULED_AIRCRAFT;      break;
00882           default: NOT_REACHED(); break;
00883         }
00884         break;
00885 
00886       case VLW_DEPOT_LIST:
00887         switch (this->vehicle_type) {
00888           case VEH_TRAIN:    this->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_TRAIN_DEPOT;    break;
00889           case VEH_ROAD:     this->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_ROADVEH_DEPOT;  break;
00890           case VEH_SHIP:     this->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_SHIP_DEPOT;     break;
00891           case VEH_AIRCRAFT: this->widget[VLW_WIDGET_CAPTION].data = STR_VEHICLE_LIST_AIRCRAFT_DEPOT; break;
00892           default: NOT_REACHED(); break;
00893         }
00894         break;
00895       default: NOT_REACHED(); break;
00896     }
00897 
00898     switch (this->vehicle_type) {
00899       case VEH_TRAIN:
00900         this->resize.step_width = 1;
00901         /* Fallthrough */
00902       case VEH_ROAD:
00903         this->vscroll.cap = 6;
00904         this->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_SMALL;
00905         break;
00906       case VEH_SHIP:
00907       case VEH_AIRCRAFT:
00908         this->vscroll.cap = 4;
00909         this->resize.step_height = PLY_WND_PRC__SIZE_OF_ROW_BIG;
00910         break;
00911       default: NOT_REACHED();
00912     }
00913 
00914 
00915     this->widget[VLW_WIDGET_LIST].data = (this->vscroll.cap << 8) + 1;
00916 
00917     /* Set up sorting. Make the window-specific _sorting variable
00918       * point to the correct global _sorting struct so we are freed
00919       * from having conditionals during window operation */
00920     switch (this->vehicle_type) {
00921       case VEH_TRAIN:    this->sorting = &_sorting.train; break;
00922       case VEH_ROAD:     this->sorting = &_sorting.roadveh; break;
00923       case VEH_SHIP:     this->sorting = &_sorting.ship; break;
00924       case VEH_AIRCRAFT: this->sorting = &_sorting.aircraft; break;
00925       default: NOT_REACHED(); break;
00926     }
00927 
00928     this->vehicles.SetListing(*this->sorting);
00929     this->vehicles.ForceRebuild();
00930     this->vehicles.NeedResort();
00931 
00932     this->FindWindowPlacementAndResize(desc);
00933     if (this->vehicle_type == VEH_TRAIN) ResizeWindow(this, 65, 0);
00934   }
00935 
00936   ~VehicleListWindow()
00937   {
00938     *this->sorting = this->vehicles.GetListing();
00939   }
00940 
00941   virtual void OnPaint()
00942   {
00943     int x = 2;
00944     const Owner owner = this->owner;
00945     const uint16 window_type = this->window_number & VLW_MASK;
00946     const uint16 index = GB(this->window_number, 16, 16);
00947 
00948     this->BuildVehicleList(owner, index, window_type);
00949     this->SortVehicleList();
00950     SetVScrollCount(this, this->vehicles.Length());
00951 
00952     if (this->vehicles.Length() == 0) HideDropDownMenu(this);
00953 
00954     /* draw the widgets */
00955     switch (window_type) {
00956       case VLW_SHARED_ORDERS: /* Shared Orders */
00957         if (this->vehicles.Length() == 0) {
00958           /* We can't open this window without vehicles using this order
00959           * and we should close the window when deleting the order      */
00960           NOT_REACHED();
00961         }
00962         SetDParam(0, this->vscroll.count);
00963         break;
00964 
00965       case VLW_STANDARD: /* Company Name */
00966         SetDParam(0, owner);
00967         SetDParam(1, this->vscroll.count);
00968         break;
00969 
00970       case VLW_WAYPOINT_LIST:
00971         SetDParam(0, index);
00972         SetDParam(1, this->vscroll.count);
00973         break;
00974 
00975       case VLW_STATION_LIST: /* Station Name */
00976         SetDParam(0, index);
00977         SetDParam(1, this->vscroll.count);
00978         break;
00979 
00980       case VLW_DEPOT_LIST:
00981         switch (this->vehicle_type) {
00982           case VEH_TRAIN:    SetDParam(0, STR_8800_TRAIN_DEPOT);        break;
00983           case VEH_ROAD:     SetDParam(0, STR_9003_ROAD_VEHICLE_DEPOT); break;
00984           case VEH_SHIP:     SetDParam(0, STR_9803_SHIP_DEPOT);         break;
00985           case VEH_AIRCRAFT: SetDParam(0, STR_A002_AIRCRAFT_HANGAR);    break;
00986           default: NOT_REACHED(); break;
00987         }
00988         if (this->vehicle_type == VEH_AIRCRAFT) {
00989           SetDParam(1, index); // Airport name
00990         } else {
00991           SetDParam(1, GetDepot(index)->town_index);
00992         }
00993         SetDParam(2, this->vscroll.count);
00994         break;
00995       default: NOT_REACHED(); break;
00996     }
00997 
00998     this->SetWidgetsDisabledState(this->vehicles.Length() == 0,
00999       VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN,
01000       VLW_WIDGET_STOP_ALL,
01001       VLW_WIDGET_START_ALL,
01002       WIDGET_LIST_END);
01003 
01004     this->DrawWidgets();
01005 
01006     /* draw sorting criteria string */
01007     DrawString(85, 15, this->vehicle_sorter_names[this->vehicles.SortType()], TC_BLACK);
01008     /* draw arrow pointing up/down for ascending/descending sorting */
01009     this->DrawSortButtonState(VLW_WIDGET_SORT_ORDER, this->vehicles.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
01010 
01011     this->DrawVehicleListItems(x,  INVALID_VEHICLE);
01012   }
01013 
01014   virtual void OnClick(Point pt, int widget)
01015   {
01016     switch (widget) {
01017       case VLW_WIDGET_SORT_ORDER: /* Flip sorting method ascending/descending */
01018         this->vehicles.ToggleSortOrder();
01019         this->SetDirty();
01020         break;
01021       case VLW_WIDGET_SORT_BY_PULLDOWN:/* Select sorting criteria dropdown menu */
01022         ShowDropDownMenu(this, this->vehicle_sorter_names, this->vehicles.SortType(), VLW_WIDGET_SORT_BY_PULLDOWN, 0, (this->vehicle_type == VEH_TRAIN || this->vehicle_type == VEH_ROAD) ? 0 : (1 << 10));
01023         return;
01024       case VLW_WIDGET_LIST: { /* Matrix to show vehicles */
01025         uint32 id_v = (pt.y - PLY_WND_PRC__OFFSET_TOP_WIDGET) / this->resize.step_height;
01026         const Vehicle *v;
01027 
01028         if (id_v >= this->vscroll.cap) return; // click out of bounds
01029 
01030         id_v += this->vscroll.pos;
01031 
01032         if (id_v >= this->vehicles.Length()) return; // click out of list bound
01033 
01034         v = this->vehicles[id_v];
01035 
01036         ShowVehicleViewWindow(v);
01037       } break;
01038 
01039       case VLW_WIDGET_AVAILABLE_VEHICLES:
01040         ShowBuildVehicleWindow(INVALID_TILE, this->vehicle_type);
01041         break;
01042 
01043       case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN: {
01044         static StringID action_str[] = {
01045           STR_REPLACE_VEHICLES,
01046           STR_SEND_FOR_SERVICING,
01047           STR_NULL,
01048           INVALID_STRING_ID
01049         };
01050 
01051         static const StringID depot_name[] = {
01052           STR_SEND_TRAIN_TO_DEPOT,
01053           STR_SEND_ROAD_VEHICLE_TO_DEPOT,
01054           STR_SEND_SHIP_TO_DEPOT,
01055           STR_SEND_AIRCRAFT_TO_HANGAR
01056         };
01057 
01058         /* XXX - Substite string since the dropdown cannot handle dynamic strings */
01059         action_str[2] = depot_name[this->vehicle_type];
01060         ShowDropDownMenu(this, action_str, 0, VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN, 0, (this->window_number & VLW_MASK) == VLW_STANDARD ? 0 : 1);
01061         break;
01062       }
01063 
01064       case VLW_WIDGET_STOP_ALL:
01065       case VLW_WIDGET_START_ALL:
01066         DoCommandP(0, GB(this->window_number, 16, 16), (this->window_number & VLW_MASK) | (1 << 6) | (widget == VLW_WIDGET_START_ALL ? (1 << 5) : 0) | this->vehicle_type, CMD_MASS_START_STOP);
01067         break;
01068     }
01069   }
01070 
01071   virtual void OnDropdownSelect(int widget, int index)
01072   {
01073     switch (widget) {
01074       case VLW_WIDGET_SORT_BY_PULLDOWN:
01075         this->vehicles.SetSortType(index);
01076         break;
01077       case VLW_WIDGET_MANAGE_VEHICLES_DROPDOWN:
01078         assert(this->vehicles.Length() != 0);
01079 
01080         switch (index) {
01081           case 0: /* Replace window */
01082             ShowReplaceGroupVehicleWindow(DEFAULT_GROUP, this->vehicle_type);
01083             break;
01084           case 1: /* Send for servicing */
01085             DoCommandP(0, GB(this->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
01086               (this->window_number & VLW_MASK) | DEPOT_MASS_SEND | DEPOT_SERVICE,
01087               GetCmdSendToDepot(this->vehicle_type));
01088             break;
01089           case 2: /* Send to Depots */
01090             DoCommandP(0, GB(this->window_number, 16, 16) /* StationID or OrderID (depending on VLW) */,
01091               (this->window_number & VLW_MASK) | DEPOT_MASS_SEND,
01092               GetCmdSendToDepot(this->vehicle_type));
01093             break;
01094 
01095           default: NOT_REACHED();
01096         }
01097         break;
01098       default: NOT_REACHED();
01099     }
01100     this->SetDirty();
01101   }
01102 
01103   virtual void OnTick()
01104   {
01105     if (_pause_game != 0) return;
01106     if (this->vehicles.NeedResort()) {
01107       StationID station = ((this->window_number & VLW_MASK) == VLW_STATION_LIST) ? GB(this->window_number, 16, 16) : INVALID_STATION;
01108 
01109       DEBUG(misc, 3, "Periodic resort %d list company %d at station %d", this->vehicle_type, this->owner, station);
01110       this->SetDirty();
01111     }
01112   }
01113 
01114   virtual void OnResize(Point new_size, Point delta)
01115   {
01116     this->vscroll.cap += delta.y / (int)this->resize.step_height;
01117     this->widget[VLW_WIDGET_LIST].data = (this->vscroll.cap << 8) + 1;
01118   }
01119 
01120   virtual void OnInvalidateData(int data)
01121   {
01122     if (HasBit(data, 15) && (this->window_number & VLW_MASK) == VLW_SHARED_ORDERS) {
01123       SB(this->window_number, 16, 16, GB(data, 16, 16));
01124       this->vehicles.ForceRebuild();
01125       return;
01126     }
01127 
01128     if (data == 0) {
01129       this->vehicles.ForceRebuild();
01130     } else {
01131       this->vehicles.ForceResort();
01132     }
01133   }
01134 };
01135 
01136 static WindowDesc _vehicle_list_desc = {
01137   WDP_AUTO, WDP_AUTO, 260, 194, 260, 246,
01138   WC_INVALID, WC_NONE,
01139   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
01140   _vehicle_list_widgets,
01141 };
01142 
01143 static void ShowVehicleListWindowLocal(CompanyID company, uint16 VLW_flag, VehicleType vehicle_type, uint16 unique_number)
01144 {
01145   if (!IsValidCompanyID(company)) return;
01146 
01147   _vehicle_list_desc.cls = GetWindowClassForVehicleType(vehicle_type);
01148   WindowNumber num = (unique_number << 16) | (vehicle_type << 11) | VLW_flag | company;
01149   AllocateWindowDescFront<VehicleListWindow>(&_vehicle_list_desc, num);
01150 }
01151 
01152 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type)
01153 {
01154   /* If _settings_client.gui.advanced_vehicle_list > 1, display the Advanced list
01155    * if _settings_client.gui.advanced_vehicle_list == 1, display Advanced list only for local company
01156    * if _ctrl_pressed, do the opposite action (Advanced list x Normal list)
01157    */
01158 
01159   if ((_settings_client.gui.advanced_vehicle_list > (uint)(company != _local_company)) != _ctrl_pressed) {
01160     ShowCompanyGroup(company, vehicle_type);
01161   } else {
01162     ShowVehicleListWindowLocal(company, VLW_STANDARD, vehicle_type, 0);
01163   }
01164 }
01165 
01166 void ShowVehicleListWindow(const Waypoint *wp)
01167 {
01168   if (wp == NULL) return;
01169   ShowVehicleListWindowLocal(wp->owner, VLW_WAYPOINT_LIST, VEH_TRAIN, wp->index);
01170 }
01171 
01172 void ShowVehicleListWindow(const Vehicle *v)
01173 {
01174   ShowVehicleListWindowLocal(v->owner, VLW_SHARED_ORDERS, v->type, v->FirstShared()->index);
01175 }
01176 
01177 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, StationID station)
01178 {
01179   ShowVehicleListWindowLocal(company, VLW_STATION_LIST, vehicle_type, station);
01180 }
01181 
01182 void ShowVehicleListWindow(CompanyID company, VehicleType vehicle_type, TileIndex depot_tile)
01183 {
01184   uint16 depot_airport_index;
01185 
01186   if (vehicle_type == VEH_AIRCRAFT) {
01187     depot_airport_index = GetStationIndex(depot_tile);
01188   } else {
01189     Depot *depot = GetDepotByTile(depot_tile);
01190     if (depot == NULL) return; // no depot to show
01191     depot_airport_index = depot->index;
01192   }
01193   ShowVehicleListWindowLocal(company, VLW_DEPOT_LIST, vehicle_type, depot_airport_index);
01194 }
01195 
01196 
01197 /* Unified vehicle GUI - Vehicle Details Window */
01198 
01200 enum VehicleDetailsWindowWidgets {
01201   VLD_WIDGET_CLOSEBOX = 0,
01202   VLD_WIDGET_CAPTION,
01203   VLD_WIDGET_RENAME_VEHICLE,
01204   VLD_WIDGET_STICKY,
01205   VLD_WIDGET_TOP_DETAILS,
01206   VLD_WIDGET_INCREASE_SERVICING_INTERVAL,
01207   VLD_WIDGET_DECREASE_SERVICING_INTERVAL,
01208   VLD_WIDGET_BOTTOM_RIGHT,
01209   VLD_WIDGET_MIDDLE_DETAILS,
01210   VLD_WIDGET_SCROLLBAR,
01211   VLD_WIDGET_DETAILS_CARGO_CARRIED,
01212   VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
01213   VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
01214   VLD_WIDGET_DETAILS_TOTAL_CARGO,
01215   VLD_WIDGET_RESIZE,
01216 };
01217 
01219 static const Widget _vehicle_details_widgets[] = {
01220   {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,   0,  10,   0,  13, STR_00C5,             STR_018B_CLOSE_WINDOW},                  // VLD_WIDGET_CLOSEBOX
01221   {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,  11, 352,   0,  13, 0x0,                  STR_018C_WINDOW_TITLE_DRAG_THIS},        // VLD_WIDGET_CAPTION
01222   { WWT_PUSHTXTBTN,     RESIZE_LR,  COLOUR_GREY, 353, 392,   0,  13, STR_01AA_NAME,        STR_NULL /* filled in later */},         // VLD_WIDGET_RENAME_VEHICLE
01223   {  WWT_STICKYBOX,     RESIZE_LR,  COLOUR_GREY, 393, 404,   0,  13, STR_NULL,             STR_STICKY_BUTTON},                      // VLD_WIDGET_STICKY
01224   {      WWT_PANEL,  RESIZE_RIGHT,  COLOUR_GREY,   0, 404,  14,  55, 0x0,                  STR_NULL},                               // VLD_WIDGET_TOP_DETAILS
01225   { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,   0,  10, 101, 106, STR_0188,             STR_884D_INCREASE_SERVICING_INTERVAL},   // VLD_WIDGET_INCREASE_SERVICING_INTERVAL
01226   { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,   0,  10, 107, 112, STR_0189,             STR_884E_DECREASE_SERVICING_INTERVAL},   // VLD_WIDGET_DECREASE_SERVICING_INTERVAL
01227   {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,  11, 404, 101, 112, 0x0,                  STR_NULL},                               // VLD_WIDGET_BOTTOM_RIGHT
01228   {     WWT_MATRIX,     RESIZE_RB,  COLOUR_GREY,   0, 392,  56, 100, 0x701,                STR_NULL},                               // VLD_WIDGET_MIDDLE_DETAILS
01229   {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY, 393, 404,  56, 100, 0x0,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},       // VLD_WIDGET_SCROLLBAR
01230   { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,   0,  95, 113, 124, STR_013C_CARGO,       STR_884F_SHOW_DETAILS_OF_CARGO_CARRIED}, // VLD_WIDGET_DETAILS_CARGO_CARRIED
01231   { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,  96, 194, 113, 124, STR_013D_INFORMATION, STR_8850_SHOW_DETAILS_OF_TRAIN_VEHICLES},// VLD_WIDGET_DETAILS_TRAIN_VEHICLES
01232   { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY, 195, 293, 113, 124, STR_013E_CAPACITIES,  STR_8851_SHOW_CAPACITIES_OF_EACH},       // VLD_WIDGET_DETAILS_CAPACITY_OF_EACH
01233   { WWT_PUSHTXTBTN,    RESIZE_RTB,  COLOUR_GREY, 294, 392, 113, 124, STR_TOTAL_CARGO,      STR_SHOW_TOTAL_CARGO},                   // VLD_WIDGET_DETAILS_TOTAL_CARGO
01234   {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY, 393, 404, 113, 124, 0x0,                  STR_RESIZE_BUTTON},                      // VLD_RESIZE
01235   {   WIDGETS_END},
01236 };
01237 
01238 
01240 enum VehicleStringTranslation {
01241   VST_VEHICLE_AGE_RUNNING_COST_YR,
01242   VST_VEHICLE_MAX_SPEED,
01243   VST_VEHICLE_PROFIT_THIS_YEAR_LAST_YEAR,
01244   VST_VEHICLE_RELIABILITY_BREAKDOWNS,
01245 };
01246 
01248 static const StringID _vehicle_translation_table[][4] = {
01249   { // VST_VEHICLE_AGE_RUNNING_COST_YR
01250     STR_885D_AGE_RUNNING_COST_YR,
01251     STR_900D_AGE_RUNNING_COST_YR,
01252     STR_9812_AGE_RUNNING_COST_YR,
01253     STR_A00D_AGE_RUNNING_COST_YR,
01254   },
01255   { // VST_VEHICLE_MAX_SPEED
01256     STR_NULL,
01257     STR_900E_MAX_SPEED,
01258     STR_9813_MAX_SPEED,
01259     STR_A00E_MAX_SPEED,
01260   },
01261   { // VST_VEHICLE_PROFIT_THIS_YEAR_LAST_YEAR
01262     STR_885F_PROFIT_THIS_YEAR_LAST_YEAR,
01263     STR_900F_PROFIT_THIS_YEAR_LAST_YEAR,
01264     STR_9814_PROFIT_THIS_YEAR_LAST_YEAR,
01265     STR_A00F_PROFIT_THIS_YEAR_LAST_YEAR,
01266   },
01267   { // VST_VEHICLE_RELIABILITY_BREAKDOWNS
01268     STR_8860_RELIABILITY_BREAKDOWNS,
01269     STR_9010_RELIABILITY_BREAKDOWNS,
01270     STR_9815_RELIABILITY_BREAKDOWNS,
01271     STR_A010_RELIABILITY_BREAKDOWNS,
01272   },
01273 };
01274 
01275 
01276 extern int GetTrainDetailsWndVScroll(VehicleID veh_id, byte det_tab);
01277 extern void DrawTrainDetails(const Vehicle *v, int x, int y, int vscroll_pos, uint16 vscroll_cap, byte det_tab);
01278 extern void DrawRoadVehDetails(const Vehicle *v, int x, int y);
01279 extern void DrawShipDetails(const Vehicle *v, int x, int y);
01280 extern void DrawAircraftDetails(const Vehicle *v, int x, int y);
01281 
01282 struct VehicleDetailsWindow : Window {
01283   int tab;
01284 
01286   VehicleDetailsWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
01287   {
01288     const Vehicle *v = GetVehicle(this->window_number);
01289 
01290     switch (v->type) {
01291       case VEH_TRAIN:
01292         ResizeWindow(this, 0, 39);
01293 
01294         this->vscroll.cap = 6;
01295         this->height += 12;
01296         this->resize.step_height = 14;
01297         this->resize.height = this->height - 14 * 2; // Minimum of 4 wagons in the display
01298 
01299         this->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_8867_NAME_TRAIN;
01300         this->widget[VLD_WIDGET_CAPTION].data = STR_8802_DETAILS;
01301         break;
01302 
01303       case VEH_ROAD: {
01304         this->widget[VLD_WIDGET_CAPTION].data = STR_900C_DETAILS;
01305         this->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_902E_NAME_ROAD_VEHICLE;
01306 
01307         if (!RoadVehHasArticPart(v)) break;
01308 
01309         /* Draw the text under the vehicle instead of next to it, minus the
01310          * height already allocated for the cargo of the first vehicle. */
01311         uint height_extension = 15 - 11;
01312 
01313         /* Add space for the cargo amount for each part. */
01314         for (const Vehicle *u = v; u != NULL; u = u->Next()) {
01315           if (u->cargo_cap != 0) height_extension += 11;
01316         }
01317 
01318         ResizeWindow(this, 0, height_extension);
01319       } break;
01320 
01321       case VEH_SHIP:
01322         this->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_982F_NAME_SHIP;
01323         this->widget[VLD_WIDGET_CAPTION].data = STR_9811_DETAILS;
01324         break;
01325 
01326       case VEH_AIRCRAFT:
01327         ResizeWindow(this, 0, 11);
01328         this->widget[VLD_WIDGET_RENAME_VEHICLE].tooltips = STR_A032_NAME_AIRCRAFT;
01329         this->widget[VLD_WIDGET_CAPTION].data = STR_A00C_DETAILS;
01330         break;
01331       default: NOT_REACHED();
01332     }
01333 
01334     if (v->type != VEH_TRAIN) {
01335       this->vscroll.cap = 1;
01336       this->widget[VLD_WIDGET_MIDDLE_DETAILS].right += 12;
01337     }
01338 
01339     this->widget[VLD_WIDGET_MIDDLE_DETAILS].data = (this->vscroll.cap << 8) + 1;
01340     this->owner = v->owner;
01341 
01342     this->tab = 0;
01343 
01344     this->FindWindowPlacementAndResize(desc);
01345   }
01346 
01348   static bool IsVehicleServiceIntervalEnabled(const VehicleType vehicle_type)
01349   {
01350     switch (vehicle_type) {
01351       default: NOT_REACHED();
01352       case VEH_TRAIN:    return _settings_game.vehicle.servint_trains   != 0; break;
01353       case VEH_ROAD:     return _settings_game.vehicle.servint_roadveh  != 0; break;
01354       case VEH_SHIP:     return _settings_game.vehicle.servint_ships    != 0; break;
01355       case VEH_AIRCRAFT: return _settings_game.vehicle.servint_aircraft != 0; break;
01356     }
01357     return false; // kill a compiler warning
01358   }
01359 
01370   static void DrawVehicleDetails(const Vehicle *v, int x, int y, int vscroll_pos, uint vscroll_cap, byte det_tab)
01371   {
01372     switch (v->type) {
01373       case VEH_TRAIN:    DrawTrainDetails(v, x, y, vscroll_pos, vscroll_cap, det_tab);  break;
01374       case VEH_ROAD:     DrawRoadVehDetails(v, x, y);  break;
01375       case VEH_SHIP:     DrawShipDetails(v, x, y);     break;
01376       case VEH_AIRCRAFT: DrawAircraftDetails(v, x, y); break;
01377       default: NOT_REACHED();
01378     }
01379   }
01380 
01382   virtual void OnPaint()
01383   {
01384     const Vehicle *v = GetVehicle(this->window_number);
01385     byte det_tab = this->tab;
01386 
01387     this->SetWidgetDisabledState(VLD_WIDGET_RENAME_VEHICLE, v->owner != _local_company);
01388 
01389     if (v->type == VEH_TRAIN) {
01390       this->DisableWidget(det_tab + VLD_WIDGET_DETAILS_CARGO_CARRIED);
01391       SetVScrollCount(this, GetTrainDetailsWndVScroll(v->index, det_tab));
01392     }
01393 
01394     this->SetWidgetsHiddenState(v->type != VEH_TRAIN,
01395       VLD_WIDGET_SCROLLBAR,
01396       VLD_WIDGET_DETAILS_CARGO_CARRIED,
01397       VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
01398       VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
01399       VLD_WIDGET_DETAILS_TOTAL_CARGO,
01400       VLD_WIDGET_RESIZE,
01401       WIDGET_LIST_END);
01402 
01403     /* Disable service-scroller when interval is set to disabled */
01404     this->SetWidgetsDisabledState(!IsVehicleServiceIntervalEnabled(v->type),
01405       VLD_WIDGET_INCREASE_SERVICING_INTERVAL,
01406       VLD_WIDGET_DECREASE_SERVICING_INTERVAL,
01407       WIDGET_LIST_END);
01408 
01409 
01410     SetDParam(0, v->index);
01411     this->DrawWidgets();
01412 
01413     /* Draw running cost */
01414     SetDParam(1, v->age / DAYS_IN_LEAP_YEAR);
01415     SetDParam(0, (v->age + DAYS_IN_YEAR < v->max_age) ? STR_AGE : STR_AGE_RED);
01416     SetDParam(2, v->max_age / DAYS_IN_LEAP_YEAR);
01417     SetDParam(3, v->GetDisplayRunningCost());
01418     DrawString(2, 15, _vehicle_translation_table[VST_VEHICLE_AGE_RUNNING_COST_YR][v->type], TC_FROMSTRING);
01419 
01420     /* Draw max speed */
01421     switch (v->type) {
01422       case VEH_TRAIN:
01423         SetDParam(2, v->GetDisplayMaxSpeed());
01424         SetDParam(1, v->u.rail.cached_power);
01425         SetDParam(0, v->u.rail.cached_weight);
01426         SetDParam(3, v->u.rail.cached_max_te / 1000);
01427         DrawString(2, 25, (_settings_game.vehicle.train_acceleration_model != TAM_ORIGINAL && v->u.rail.railtype != RAILTYPE_MAGLEV) ?
01428           STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :
01429           STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED, TC_FROMSTRING);
01430         break;
01431 
01432       case VEH_ROAD:
01433       case VEH_SHIP:
01434       case VEH_AIRCRAFT:
01435         SetDParam(0, v->GetDisplayMaxSpeed());
01436         DrawString(2, 25, _vehicle_translation_table[VST_VEHICLE_MAX_SPEED][v->type], TC_FROMSTRING);
01437         break;
01438 
01439       default: NOT_REACHED();
01440     }
01441 
01442     /* Draw profit */
01443     SetDParam(0, v->GetDisplayProfitThisYear());
01444     SetDParam(1, v->GetDisplayProfitLastYear());
01445     DrawString(2, 35, _vehicle_translation_table[VST_VEHICLE_PROFIT_THIS_YEAR_LAST_YEAR][v->type], TC_FROMSTRING);
01446 
01447     /* Draw breakdown & reliability */
01448     SetDParam(0, v->reliability * 100 >> 16);
01449     SetDParam(1, v->breakdowns_since_last_service);
01450     DrawString(2, 45, _vehicle_translation_table[VST_VEHICLE_RELIABILITY_BREAKDOWNS][v->type], TC_FROMSTRING);
01451 
01452     /* Draw service interval text */
01453     SetDParam(0, v->service_interval);
01454     SetDParam(1, v->date_of_last_service);
01455     DrawString(13, this->height - (v->type != VEH_TRAIN ? 11 : 23), _settings_game.vehicle.servint_ispercent ? STR_SERVICING_INTERVAL_PERCENT : STR_883C_SERVICING_INTERVAL_DAYS, TC_FROMSTRING);
01456 
01457     switch (v->type) {
01458       case VEH_TRAIN:
01459         DrawVehicleDetails(v, 2, 57, this->vscroll.pos, this->vscroll.cap, det_tab);
01460         break;
01461 
01462       case VEH_ROAD:
01463       case VEH_SHIP:
01464       case VEH_AIRCRAFT:
01465         DrawVehicleImage(v, 3, 57, INVALID_VEHICLE, 0, 0);
01466         DrawVehicleDetails(v, 75, 57, this->vscroll.pos, this->vscroll.cap, det_tab);
01467         break;
01468 
01469       default: NOT_REACHED();
01470     }
01471   }
01472 
01473   virtual void OnClick(Point pt, int widget)
01474   {
01476     static const StringID _name_vehicle_title[] = {
01477       STR_8865_NAME_TRAIN,
01478       STR_902C_NAME_ROAD_VEHICLE,
01479       STR_9831_NAME_SHIP,
01480       STR_A030_NAME_AIRCRAFT
01481     };
01482 
01483     switch (widget) {
01484       case VLD_WIDGET_RENAME_VEHICLE: {// rename
01485         const Vehicle *v = GetVehicle(this->window_number);
01486         SetDParam(0, v->index);
01487         ShowQueryString(STR_VEHICLE_NAME, _name_vehicle_title[v->type], MAX_LENGTH_VEHICLE_NAME_BYTES, MAX_LENGTH_VEHICLE_NAME_PIXELS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT);
01488       } break;
01489 
01490       case VLD_WIDGET_INCREASE_SERVICING_INTERVAL:   // increase int
01491       case VLD_WIDGET_DECREASE_SERVICING_INTERVAL: { // decrease int
01492         int mod = _ctrl_pressed ? 5 : 10;
01493         const Vehicle *v = GetVehicle(this->window_number);
01494 
01495         mod = (widget == VLD_WIDGET_DECREASE_SERVICING_INTERVAL) ? -mod : mod;
01496         mod = GetServiceIntervalClamped(mod + v->service_interval);
01497         if (mod == v->service_interval) return;
01498 
01499         DoCommandP(v->tile, v->index, mod, CMD_CHANGE_SERVICE_INT | CMD_MSG(STR_018A_CAN_T_CHANGE_SERVICING));
01500       } break;
01501 
01502       case VLD_WIDGET_DETAILS_CARGO_CARRIED:
01503       case VLD_WIDGET_DETAILS_TRAIN_VEHICLES:
01504       case VLD_WIDGET_DETAILS_CAPACITY_OF_EACH:
01505       case VLD_WIDGET_DETAILS_TOTAL_CARGO:
01506         this->SetWidgetsDisabledState(false,
01507           VLD_WIDGET_DETAILS_CARGO_CARRIED,
01508           VLD_WIDGET_DETAILS_TRAIN_VEHICLES,
01509           VLD_WIDGET_DETAILS_CAPACITY_OF_EACH,
01510           VLD_WIDGET_DETAILS_TOTAL_CARGO,
01511           widget,
01512           WIDGET_LIST_END);
01513 
01514         this->tab = widget - VLD_WIDGET_DETAILS_CARGO_CARRIED;
01515         this->SetDirty();
01516         break;
01517     }
01518   }
01519 
01520   virtual void OnQueryTextFinished(char *str)
01521   {
01523     static const StringID _name_vehicle_error[] = {
01524       STR_8866_CAN_T_NAME_TRAIN,
01525       STR_902D_CAN_T_NAME_ROAD_VEHICLE,
01526       STR_9832_CAN_T_NAME_SHIP,
01527       STR_A031_CAN_T_NAME_AIRCRAFT
01528     };
01529 
01530     if (str == NULL) return;
01531 
01532     DoCommandP(0, this->window_number, 0, CMD_RENAME_VEHICLE | CMD_MSG(_name_vehicle_error[GetVehicle(this->window_number)->type]), NULL, str);
01533   }
01534 
01535   virtual void OnResize(Point new_size, Point delta)
01536   {
01537     if (delta.x != 0) ResizeButtons(this, VLD_WIDGET_DETAILS_CARGO_CARRIED, VLD_WIDGET_DETAILS_TOTAL_CARGO);
01538     if (delta.y == 0) return;
01539 
01540     this->vscroll.cap += delta.y / 14;
01541     this->widget[VLD_WIDGET_MIDDLE_DETAILS].data = (this->vscroll.cap << 8) + 1;
01542   }
01543 };
01544 
01546 static const WindowDesc _vehicle_details_desc = {
01547   WDP_AUTO, WDP_AUTO, 405, 113, 405, 113,
01548   WC_VEHICLE_DETAILS, WC_VEHICLE_VIEW,
01549   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
01550   _vehicle_details_widgets,
01551 };
01552 
01554 static void ShowVehicleDetailsWindow(const Vehicle *v)
01555 {
01556   DeleteWindowById(WC_VEHICLE_ORDERS, v->index, false);
01557   DeleteWindowById(WC_VEHICLE_TIMETABLE, v->index, false);
01558   AllocateWindowDescFront<VehicleDetailsWindow>(&_vehicle_details_desc, v->index);
01559 }
01560 
01561 
01562 /* Unified vehicle GUI - Vehicle View Window */
01563 
01565 static const Widget _vehicle_view_widgets[] = {
01566   {   WWT_CLOSEBOX,  RESIZE_NONE,  COLOUR_GREY,   0,  10,   0,  13, STR_00C5,                 STR_018B_CLOSE_WINDOW },           // VVW_WIDGET_CLOSEBOX
01567   {    WWT_CAPTION, RESIZE_RIGHT,  COLOUR_GREY,  11, 237,   0,  13, 0x0 /* filled later */,   STR_018C_WINDOW_TITLE_DRAG_THIS }, // VVW_WIDGET_CAPTION
01568   {  WWT_STICKYBOX,    RESIZE_LR,  COLOUR_GREY, 238, 249,   0,  13, 0x0,                      STR_STICKY_BUTTON },               // VVW_WIDGET_STICKY
01569   {      WWT_PANEL,    RESIZE_RB,  COLOUR_GREY,   0, 231,  14, 103, 0x0,                      STR_NULL },                        // VVW_WIDGET_PANEL
01570   {      WWT_INSET,    RESIZE_RB,  COLOUR_GREY,   2, 229,  16, 101, 0x0,                      STR_NULL },                        // VVW_WIDGET_VIEWPORT
01571   {    WWT_PUSHBTN,   RESIZE_RTB,  COLOUR_GREY,   0, 237, 104, 115, 0x0,                      0x0 /* filled later */ },          // VVW_WIDGET_START_STOP_VEH
01572   { WWT_PUSHIMGBTN,    RESIZE_LR,  COLOUR_GREY, 232, 249,  14,  31, SPR_CENTRE_VIEW_VEHICLE,  0x0 /* filled later */ },          // VVW_WIDGET_CENTER_MAIN_VIEH
01573   { WWT_PUSHIMGBTN,    RESIZE_LR,  COLOUR_GREY, 232, 249,  32,  49, 0x0 /* filled later */,   0x0 /* filled later */ },          // VVW_WIDGET_GOTO_DEPOT
01574   { WWT_PUSHIMGBTN,    RESIZE_LR,  COLOUR_GREY, 232, 249,  50,  67, SPR_REFIT_VEHICLE,        0x0 /* filled later */ },          // VVW_WIDGET_REFIT_VEH
01575   { WWT_PUSHIMGBTN,    RESIZE_LR,  COLOUR_GREY, 232, 249,  68,  85, SPR_SHOW_ORDERS,          0x0 /* filled later */ },          // VVW_WIDGET_SHOW_ORDERS
01576   { WWT_PUSHIMGBTN,    RESIZE_LR,  COLOUR_GREY, 232, 249,  86, 103, SPR_SHOW_VEHICLE_DETAILS, 0x0 /* filled later */ },          // VVW_WIDGET_SHOW_DETAILS
01577   { WWT_PUSHIMGBTN,    RESIZE_LR,  COLOUR_GREY, 232, 249,  32,  49, 0x0 /* filled later */,   0x0 /* filled later */ },          // VVW_WIDGET_CLONE_VEH
01578   {      WWT_PANEL,   RESIZE_LRB,  COLOUR_GREY, 232, 249, 104, 103, 0x0,                      STR_NULL },                        // VVW_WIDGET_EMPTY_BOTTOM_RIGHT
01579   {  WWT_RESIZEBOX,  RESIZE_LRTB,  COLOUR_GREY, 238, 249, 104, 115, 0x0,                      STR_NULL },                        // VVW_WIDGET_RESIZE
01580   { WWT_PUSHIMGBTN,    RESIZE_LR,  COLOUR_GREY, 232, 249,  50,  67, SPR_FORCE_VEHICLE_TURN,   STR_9020_FORCE_VEHICLE_TO_TURN_AROUND }, // VVW_WIDGET_TURN_AROUND
01581   { WWT_PUSHIMGBTN,    RESIZE_LR,  COLOUR_GREY, 232, 249,  50,  67, SPR_IGNORE_SIGNALS,       STR_884A_FORCE_TRAIN_TO_PROCEED },       // VVW_WIDGET_FORCE_PROCEED
01582 {   WIDGETS_END},
01583 };
01584 
01585 
01587 static const WindowDesc _vehicle_view_desc = {
01588   WDP_AUTO, WDP_AUTO, 250, 116, 250, 116,
01589   WC_VEHICLE_VIEW, WC_NONE,
01590   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
01591   _vehicle_view_widgets,
01592 };
01593 
01597 static const WindowDesc _train_view_desc = {
01598   WDP_AUTO, WDP_AUTO, 250, 134, 250, 134,
01599   WC_VEHICLE_VIEW, WC_NONE,
01600   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE,
01601   _vehicle_view_widgets,
01602 };
01603 
01604 
01605 /* Just to make sure, nobody has changed the vehicle type constants, as we are
01606    using them for array indexing in a number of places here. */
01607 assert_compile(VEH_TRAIN == 0);
01608 assert_compile(VEH_ROAD == 1);
01609 assert_compile(VEH_SHIP == 2);
01610 assert_compile(VEH_AIRCRAFT == 3);
01611 
01613 static const ZoomLevel _vehicle_view_zoom_levels[] = {
01614   ZOOM_LVL_TRAIN,
01615   ZOOM_LVL_ROADVEH,
01616   ZOOM_LVL_SHIP,
01617   ZOOM_LVL_AIRCRAFT,
01618 };
01619 
01620 /* Constants for geometry of vehicle view viewport */
01621 static const int VV_VIEWPORT_X = 3;
01622 static const int VV_VIEWPORT_Y = 17;
01623 static const int VV_INITIAL_VIEWPORT_WIDTH = 226;
01624 static const int VV_INITIAL_VIEWPORT_HEIGHT = 84;
01625 static const int VV_INITIAL_VIEWPORT_HEIGHT_TRAIN = 102;
01626 
01628 enum VehicleCommandTranslation {
01629   VCT_CMD_START_STOP = 0,
01630   VCT_CMD_GOTO_DEPOT,
01631   VCT_CMD_CLONE_VEH,
01632   VCT_CMD_TURN_AROUND,
01633 };
01634 
01636 static const uint32 _vehicle_command_translation_table[][4] = {
01637   { // VCT_CMD_START_STOP
01638     CMD_START_STOP_VEHICLE | CMD_MSG(STR_883B_CAN_T_STOP_START_TRAIN),
01639     CMD_START_STOP_VEHICLE | CMD_MSG(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE),
01640     CMD_START_STOP_VEHICLE | CMD_MSG(STR_9818_CAN_T_STOP_START_SHIP),
01641     CMD_START_STOP_VEHICLE | CMD_MSG(STR_A016_CAN_T_STOP_START_AIRCRAFT)
01642   },
01643   { // VCT_CMD_GOTO_DEPOT
01644     /* TrainGotoDepot has a nice randomizer in the pathfinder, which causes desyncs... */
01645     CMD_SEND_TRAIN_TO_DEPOT | CMD_NO_TEST_IF_IN_NETWORK | CMD_MSG(STR_8830_CAN_T_SEND_TRAIN_TO_DEPOT),
01646     CMD_SEND_ROADVEH_TO_DEPOT | CMD_MSG(STR_9018_CAN_T_SEND_VEHICLE_TO_DEPOT),
01647     CMD_SEND_SHIP_TO_DEPOT | CMD_MSG(STR_9819_CAN_T_SEND_SHIP_TO_DEPOT),
01648     CMD_SEND_AIRCRAFT_TO_HANGAR | CMD_MSG(STR_A012_CAN_T_SEND_AIRCRAFT_TO)
01649   },
01650   { // VCT_CMD_CLONE_VEH
01651     CMD_CLONE_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE),
01652     CMD_CLONE_VEHICLE | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE),
01653     CMD_CLONE_VEHICLE | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP),
01654     CMD_CLONE_VEHICLE | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT)
01655   },
01656   { // VCT_CMD_TURN_AROUND
01657     CMD_REVERSE_TRAIN_DIRECTION | CMD_MSG(STR_8869_CAN_T_REVERSE_DIRECTION),
01658     CMD_TURN_ROADVEH | CMD_MSG(STR_9033_CAN_T_MAKE_VEHICLE_TURN),
01659     0xffffffff, // invalid for ships
01660     0xffffffff  // invalid for aircrafts
01661   },
01662 };
01663 
01665 static bool IsVehicleRefitable(const Vehicle *v)
01666 {
01667   if (!v->IsStoppedInDepot()) return false;
01668 
01669   do {
01670     /* Skip this vehicle if it has no capacity */
01671     if (v->cargo_cap == 0) continue;
01672 
01673     if (IsEngineRefittable(v->engine_type)) return true;
01674   } while ((v->type == VEH_TRAIN || v->type == VEH_ROAD) && (v = v->Next()) != NULL);
01675 
01676   return false;
01677 }
01678 
01679 struct VehicleViewWindow : Window {
01680   VehicleViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
01681   {
01682     const Vehicle *v = GetVehicle(this->window_number);
01683 
01684     this->owner = v->owner;
01685     InitializeWindowViewport(this, VV_VIEWPORT_X, VV_VIEWPORT_Y, VV_INITIAL_VIEWPORT_WIDTH,
01686                          (v->type == VEH_TRAIN) ? VV_INITIAL_VIEWPORT_HEIGHT_TRAIN : VV_INITIAL_VIEWPORT_HEIGHT,
01687                          this->window_number | (1 << 31), _vehicle_view_zoom_levels[v->type]);
01688 
01689     /*
01690      * fill in data and tooltip codes for the widgets and
01691      * move some of the buttons for trains
01692      */
01693     switch (v->type) {
01694       case VEH_TRAIN:
01695         this->widget[VVW_WIDGET_CAPTION].data = STR_882E;
01696 
01697         this->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_8846_CURRENT_TRAIN_ACTION_CLICK;
01698 
01699         this->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_8848_CENTER_MAIN_VIEW_ON_TRAIN;
01700 
01701         this->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_TRAIN_TODEPOT;
01702         this->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_8849_SEND_TRAIN_TO_DEPOT;
01703 
01704         this->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_RAIL_REFIT_VEHICLE_TO_CARRY;
01705 
01706         this->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_8847_SHOW_TRAIN_S_ORDERS;
01707 
01708         this->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_884C_SHOW_TRAIN_DETAILS;
01709 
01710         this->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_TRAIN;
01711         this->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_TRAIN_INFO;
01712 
01713         this->widget[VVW_WIDGET_TURN_AROUND].tooltips = STR_884B_REVERSE_DIRECTION_OF_TRAIN;
01714 
01715 
01716         /* due to more buttons we must modify the layout a bit for trains */
01717         this->widget[VVW_WIDGET_PANEL].bottom = 121;
01718         this->widget[VVW_WIDGET_VIEWPORT].bottom = 119;
01719 
01720         this->widget[VVW_WIDGET_START_STOP_VEH].top = 122;
01721         this->widget[VVW_WIDGET_START_STOP_VEH].bottom = 133;
01722 
01723         this->widget[VVW_WIDGET_REFIT_VEH].top = 68;
01724         this->widget[VVW_WIDGET_REFIT_VEH].bottom = 85;
01725 
01726         this->widget[VVW_WIDGET_SHOW_ORDERS].top = 86;
01727         this->widget[VVW_WIDGET_SHOW_ORDERS].bottom = 103;
01728 
01729         this->widget[VVW_WIDGET_SHOW_DETAILS].top = 104;
01730         this->widget[VVW_WIDGET_SHOW_DETAILS].bottom = 121;
01731 
01732         this->widget[VVW_WIDGET_EMPTY_BOTTOM_RIGHT].top = 122;
01733         this->widget[VVW_WIDGET_EMPTY_BOTTOM_RIGHT].bottom = 121;
01734 
01735         this->widget[VVW_WIDGET_RESIZE].top = 122;
01736         this->widget[VVW_WIDGET_RESIZE].bottom = 133;
01737 
01738         this->widget[VVW_WIDGET_TURN_AROUND].top = 68;
01739         this->widget[VVW_WIDGET_TURN_AROUND].bottom = 85;
01740         break;
01741 
01742       case VEH_ROAD:
01743         this->widget[VVW_WIDGET_CAPTION].data = STR_9002;
01744 
01745         this->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_901C_CURRENT_VEHICLE_ACTION;
01746 
01747         this->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_901E_CENTER_MAIN_VIEW_ON_VEHICLE;
01748 
01749         this->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_ROADVEH_TODEPOT;
01750         this->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_901F_SEND_VEHICLE_TO_DEPOT;
01751 
01752         this->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_REFIT_ROAD_VEHICLE_TO_CARRY;
01753 
01754         this->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_901D_SHOW_VEHICLE_S_ORDERS;
01755 
01756         this->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_9021_SHOW_ROAD_VEHICLE_DETAILS;
01757 
01758         this->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_ROADVEH;
01759         this->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_ROAD_VEHICLE_INFO;
01760 
01761         this->SetWidgetHiddenState(VVW_WIDGET_FORCE_PROCEED, true);
01762         break;
01763 
01764       case VEH_SHIP:
01765         this->widget[VVW_WIDGET_CAPTION].data = STR_980F;
01766 
01767         this->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_9827_CURRENT_SHIP_ACTION_CLICK;
01768 
01769         this->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_9829_CENTER_MAIN_VIEW_ON_SHIP;
01770 
01771         this->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_SHIP_TODEPOT;
01772         this->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_982A_SEND_SHIP_TO_DEPOT;
01773 
01774         this->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_983A_REFIT_CARGO_SHIP_TO_CARRY;
01775 
01776         this->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_9828_SHOW_SHIP_S_ORDERS;
01777 
01778         this->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_982B_SHOW_SHIP_DETAILS;
01779 
01780         this->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_SHIP;
01781         this->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_SHIP_INFO;
01782 
01783         this->SetWidgetsHiddenState(true,
01784                                     VVW_WIDGET_TURN_AROUND,
01785                                     VVW_WIDGET_FORCE_PROCEED,
01786                                     WIDGET_LIST_END);
01787         break;
01788 
01789       case VEH_AIRCRAFT:
01790         this->widget[VVW_WIDGET_CAPTION].data = STR_A00A;
01791 
01792         this->widget[VVW_WIDGET_START_STOP_VEH].tooltips = STR_A027_CURRENT_AIRCRAFT_ACTION;
01793 
01794         this->widget[VVW_WIDGET_CENTER_MAIN_VIEH].tooltips = STR_A029_CENTER_MAIN_VIEW_ON_AIRCRAFT;
01795 
01796         this->widget[VVW_WIDGET_GOTO_DEPOT].data = SPR_SEND_AIRCRAFT_TODEPOT;
01797         this->widget[VVW_WIDGET_GOTO_DEPOT].tooltips = STR_A02A_SEND_AIRCRAFT_TO_HANGAR;
01798 
01799         this->widget[VVW_WIDGET_REFIT_VEH].tooltips = STR_A03B_REFIT_AIRCRAFT_TO_CARRY;
01800 
01801         this->widget[VVW_WIDGET_SHOW_ORDERS].tooltips = STR_A028_SHOW_AIRCRAFT_S_ORDERS;
01802 
01803         this->widget[VVW_WIDGET_SHOW_DETAILS].tooltips = STR_A02B_SHOW_AIRCRAFT_DETAILS;
01804 
01805         this->widget[VVW_WIDGET_CLONE_VEH].data = SPR_CLONE_AIRCRAFT;
01806         this->widget[VVW_WIDGET_CLONE_VEH].tooltips = STR_CLONE_AIRCRAFT_INFO;
01807 
01808         this->SetWidgetsHiddenState(true,
01809                                     VVW_WIDGET_TURN_AROUND,
01810                                     VVW_WIDGET_FORCE_PROCEED,
01811                                     WIDGET_LIST_END);
01812         break;
01813 
01814         default: NOT_REACHED();
01815     }
01816 
01817     this->FindWindowPlacementAndResize(desc);
01818   }
01819 
01820   ~VehicleViewWindow()
01821   {
01822     DeleteWindowById(WC_VEHICLE_ORDERS, this->window_number, false);
01823     DeleteWindowById(WC_VEHICLE_REFIT, this->window_number, false);
01824     DeleteWindowById(WC_VEHICLE_DETAILS, this->window_number, false);
01825     DeleteWindowById(WC_VEHICLE_TIMETABLE, this->window_number, false);
01826   }
01827 
01828   virtual void OnPaint()
01829   {
01831     static const StringID _heading_for_depot_strings[] = {
01832       STR_HEADING_FOR_TRAIN_DEPOT,
01833       STR_HEADING_FOR_ROAD_DEPOT,
01834       STR_HEADING_FOR_SHIP_DEPOT,
01835       STR_HEADING_FOR_HANGAR,
01836     };
01837 
01839     static const StringID _heading_for_depot_service_strings[] = {
01840       STR_HEADING_FOR_TRAIN_DEPOT_SERVICE,
01841       STR_HEADING_FOR_ROAD_DEPOT_SERVICE,
01842       STR_HEADING_FOR_SHIP_DEPOT_SERVICE,
01843       STR_HEADING_FOR_HANGAR_SERVICE,
01844     };
01845 
01846     const Vehicle *v = GetVehicle(this->window_number);
01847     StringID str;
01848     bool is_localcompany = v->owner == _local_company;
01849     bool refitable_and_stopped_in_depot = IsVehicleRefitable(v);
01850 
01851     this->SetWidgetDisabledState(VVW_WIDGET_GOTO_DEPOT, !is_localcompany);
01852     this->SetWidgetDisabledState(VVW_WIDGET_REFIT_VEH,
01853                                 !refitable_and_stopped_in_depot || !is_localcompany);
01854     this->SetWidgetDisabledState(VVW_WIDGET_CLONE_VEH, !is_localcompany);
01855 
01856     if (v->type == VEH_TRAIN) {
01857       this->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !is_localcompany);
01858       this->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !is_localcompany);
01859     }
01860 
01861     /* draw widgets & caption */
01862     SetDParam(0, v->index);
01863     this->DrawWidgets();
01864 
01865     if (v->vehstatus & VS_CRASHED) {
01866       str = STR_8863_CRASHED;
01867     } else if (v->type != VEH_AIRCRAFT && v->breakdown_ctr == 1) { // check for aircraft necessary?
01868       str = STR_885C_BROKEN_DOWN;
01869     } else if (v->vehstatus & VS_STOPPED) {
01870       if (v->type == VEH_TRAIN) {
01871         if (v->cur_speed == 0) {
01872           if (v->u.rail.cached_power == 0) {
01873             str = STR_TRAIN_NO_POWER;
01874           } else {
01875             str = STR_8861_STOPPED;
01876           }
01877         } else {
01878           SetDParam(0, v->GetDisplaySpeed());
01879           str = STR_TRAIN_STOPPING + _settings_client.gui.vehicle_speed;
01880         }
01881       } else { // no train
01882         str = STR_8861_STOPPED;
01883       }
01884     } else if (v->type == VEH_TRAIN && HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) {
01885       str = STR_TRAIN_STUCK;
01886     } else { // vehicle is in a "normal" state, show current order
01887       switch (v->current_order.GetType()) {
01888         case OT_GOTO_STATION: {
01889           SetDParam(0, v->current_order.GetDestination());
01890           SetDParam(1, v->GetDisplaySpeed());
01891           str = STR_HEADING_FOR_STATION + _settings_client.gui.vehicle_speed;
01892         } break;
01893 
01894         case OT_GOTO_DEPOT: {
01895           if (v->type == VEH_AIRCRAFT) {
01896             /* Aircrafts always go to a station, even if you say depot */
01897             SetDParam(0, v->current_order.GetDestination());
01898             SetDParam(1, v->GetDisplaySpeed());
01899           } else {
01900             Depot *depot = GetDepot(v->current_order.GetDestination());
01901             SetDParam(0, depot->town_index);
01902             SetDParam(1, v->GetDisplaySpeed());
01903           }
01904           if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
01905             str = _heading_for_depot_strings[v->type] + _settings_client.gui.vehicle_speed;
01906           } else {
01907             str = _heading_for_depot_service_strings[v->type] + _settings_client.gui.vehicle_speed;
01908           }
01909         } break;
01910 
01911         case OT_LOADING:
01912           str = STR_882F_LOADING_UNLOADING;
01913           break;
01914 
01915         case OT_GOTO_WAYPOINT: {
01916           assert(v->type == VEH_TRAIN);
01917           SetDParam(0, v->current_order.GetDestination());
01918           str = STR_HEADING_FOR_WAYPOINT + _settings_client.gui.vehicle_speed;
01919           SetDParam(1, v->GetDisplaySpeed());
01920           break;
01921         }
01922 
01923         case OT_LEAVESTATION:
01924           if (v->type != VEH_AIRCRAFT) {
01925             str = STR_LEAVING;
01926             break;
01927           }
01928           /* fall-through if aircraft. Does this even happen? */
01929 
01930         default:
01931           if (v->GetNumOrders() == 0) {
01932             str = STR_NO_ORDERS + _settings_client.gui.vehicle_speed;
01933             SetDParam(0, v->GetDisplaySpeed());
01934           } else {
01935             str = STR_EMPTY;
01936           }
01937           break;
01938       }
01939     }
01940 
01941     /* draw the flag plus orders */
01942     DrawSprite(v->vehstatus & VS_STOPPED ? SPR_FLAG_VEH_STOPPED : SPR_FLAG_VEH_RUNNING, PAL_NONE, 2, this->widget[VVW_WIDGET_START_STOP_VEH].top + 1);
01943     DrawStringCenteredTruncated(this->widget[VVW_WIDGET_START_STOP_VEH].left + 8, this->widget[VVW_WIDGET_START_STOP_VEH].right, this->widget[VVW_WIDGET_START_STOP_VEH].top + 1, str, TC_FROMSTRING);
01944     this->DrawViewport();
01945   }
01946 
01947   virtual void OnClick(Point pt, int widget)
01948   {
01949     const Vehicle *v = GetVehicle(this->window_number);
01950 
01951     switch (widget) {
01952       case VVW_WIDGET_START_STOP_VEH: // start stop
01953         DoCommandP(v->tile, v->index, 0,
01954                     _vehicle_command_translation_table[VCT_CMD_START_STOP][v->type]);
01955         break;
01956       case VVW_WIDGET_CENTER_MAIN_VIEH: {/* center main view */
01957         const Window *mainwindow = FindWindowById(WC_MAIN_WINDOW, 0);
01958         /* code to allow the main window to 'follow' the vehicle if the ctrl key is pressed */
01959         if (_ctrl_pressed && mainwindow->viewport->zoom == ZOOM_LVL_NORMAL) {
01960           mainwindow->viewport->follow_vehicle = v->index;
01961         } else {
01962           ScrollMainWindowTo(v->x_pos, v->y_pos);
01963         }
01964       } break;
01965 
01966       case VVW_WIDGET_GOTO_DEPOT: // goto hangar
01967         DoCommandP(v->tile, v->index, _ctrl_pressed ? DEPOT_SERVICE : 0,
01968           _vehicle_command_translation_table[VCT_CMD_GOTO_DEPOT][v->type]);
01969         break;
01970       case VVW_WIDGET_REFIT_VEH: // refit
01971         ShowVehicleRefitWindow(v, INVALID_VEH_ORDER_ID, this);
01972         break;
01973       case VVW_WIDGET_SHOW_ORDERS: // show orders
01974         if (_ctrl_pressed) {
01975           ShowTimetableWindow(v);
01976         } else {
01977           ShowOrdersWindow(v);
01978         }
01979         break;
01980       case VVW_WIDGET_SHOW_DETAILS: // show details
01981         ShowVehicleDetailsWindow(v);
01982         break;
01983       case VVW_WIDGET_CLONE_VEH: // clone vehicle
01984         DoCommandP(v->tile, v->index, _ctrl_pressed ? 1 : 0,
01985                     _vehicle_command_translation_table[VCT_CMD_CLONE_VEH][v->type],
01986                     CcCloneVehicle);
01987         break;
01988       case VVW_WIDGET_TURN_AROUND: // turn around
01989         assert(v->type == VEH_TRAIN || v->type == VEH_ROAD);
01990         DoCommandP(v->tile, v->index, 0,
01991                     _vehicle_command_translation_table[VCT_CMD_TURN_AROUND][v->type]);
01992         break;
01993       case VVW_WIDGET_FORCE_PROCEED: // force proceed
01994         assert(v->type == VEH_TRAIN);
01995         DoCommandP(v->tile, v->index, 0, CMD_FORCE_TRAIN_PROCEED | CMD_MSG(STR_8862_CAN_T_MAKE_TRAIN_PASS_SIGNAL));
01996         break;
01997     }
01998   }
01999 
02000   virtual void OnResize(Point new_size, Point delta)
02001   {
02002     this->viewport->width          += delta.x;
02003     this->viewport->height         += delta.y;
02004     this->viewport->virtual_width  += delta.x;
02005     this->viewport->virtual_height += delta.y;
02006   }
02007 
02008   virtual void OnTick()
02009   {
02010     const Vehicle *v = GetVehicle(this->window_number);
02011     bool veh_stopped = v->IsStoppedInDepot();
02012 
02013     /* Widget VVW_WIDGET_GOTO_DEPOT must be hidden if the vehicle is already
02014      * stopped in depot.
02015      * Widget VVW_WIDGET_CLONE_VEH should then be shown, since cloning is
02016      * allowed only while in depot and stopped.
02017      * This sytem allows to have two buttons, on top of each other.
02018      * The same system applies to widget VVW_WIDGET_REFIT_VEH and VVW_WIDGET_TURN_AROUND.*/
02019     if (veh_stopped != this->IsWidgetHidden(VVW_WIDGET_GOTO_DEPOT) || veh_stopped == this->IsWidgetHidden(VVW_WIDGET_CLONE_VEH)) {
02020       this->SetWidgetHiddenState( VVW_WIDGET_GOTO_DEPOT, veh_stopped);  // send to depot
02021       this->SetWidgetHiddenState(VVW_WIDGET_CLONE_VEH, !veh_stopped); // clone
02022       if (v->type == VEH_ROAD || v->type == VEH_TRAIN) {
02023         this->SetWidgetHiddenState( VVW_WIDGET_REFIT_VEH, !veh_stopped); // refit
02024         this->SetWidgetHiddenState(VVW_WIDGET_TURN_AROUND, veh_stopped);  // force turn around
02025       }
02026       this->SetDirty();
02027     }
02028   }
02029 };
02030 
02031 
02033 void ShowVehicleViewWindow(const Vehicle *v)
02034 {
02035   AllocateWindowDescFront<VehicleViewWindow>((v->type == VEH_TRAIN) ? &_train_view_desc : &_vehicle_view_desc, v->index);
02036 }
02037 
02038 void StopGlobalFollowVehicle(const Vehicle *v)
02039 {
02040   Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
02041   if (w != NULL && w->viewport->follow_vehicle == v->index) {
02042     ScrollMainWindowTo(v->x_pos, v->y_pos, true); // lock the main view on the vehicle's last position
02043     w->viewport->follow_vehicle = INVALID_VEHICLE;
02044   }
02045 }

Generated on Mon Feb 16 23:12:12 2009 for openttd by  doxygen 1.5.6