vehicle_gui.cpp

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

Generated on Tue Jul 21 18:48:28 2009 for OpenTTD by  doxygen 1.5.6