# build_vehicle_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: build_vehicle_gui.cpp 15723 2009-03-15 15:12:06Z rubidium $ */
    00002
    00005 #include "train.h"
    00006 #include "roadveh.h"
    00007 #include "ship.h"
    00008 #include "aircraft.h"
    00009 #include "articulated_vehicles.h"
    00010 #include "textbuf_gui.h"
    00011 #include "command_func.h"
    00012 #include "company_func.h"
    00013 #include "vehicle_gui.h"
    00014 #include "newgrf_engine.h"
    00015 #include "group.h"
    00016 #include "strings_func.h"
    00017 #include "window_func.h"
    00018 #include "date_func.h"
    00019 #include "vehicle_func.h"
    00020 #include "gfx_func.h"
    00021 #include "widgets/dropdown_func.h"
    00022 #include "window_gui.h"
    00023 #include "engine_gui.h"
    00024 #include "settings_type.h"
    00025
    00026 #include "table/sprites.h"
    00027 #include "table/strings.h"
    00028
    00029 enum BuildVehicleWidgets {
    00030   BUILD_VEHICLE_WIDGET_CLOSEBOX = 0,
    00031   BUILD_VEHICLE_WIDGET_CAPTION,
    00032   BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING,
    00033   BUILD_VEHICLE_WIDGET_SORT_DROPDOWN,
    00034   BUILD_VEHICLE_WIDGET_LIST,
    00035   BUILD_VEHICLE_WIDGET_SCROLLBAR,
    00036   BUILD_VEHICLE_WIDGET_PANEL,
    00037   BUILD_VEHICLE_WIDGET_BUILD,
    00038   BUILD_VEHICLE_WIDGET_RENAME,
    00039   BUILD_VEHICLE_WIDGET_RESIZE,
    00040   BUILD_VEHICLE_WIDGET_END
    00041 };
    00042
    00043 static const Widget _build_vehicle_widgets[] = {
    00044   {   WWT_CLOSEBOX,   RESIZE_NONE,  COLOUR_GREY,     0,    10,     0,    13, STR_00C5,                STR_018B_CLOSE_WINDOW },
    00045   {    WWT_CAPTION,  RESIZE_RIGHT,  COLOUR_GREY,    11,   239,     0,    13, 0x0,                     STR_018C_WINDOW_TITLE_DRAG_THIS },
    00046   { WWT_PUSHTXTBTN,   RESIZE_NONE,  COLOUR_GREY,     0,    80,    14,    25, STR_SORT_BY,             STR_SORT_ORDER_TIP},
    00047   {   WWT_DROPDOWN,  RESIZE_RIGHT,  COLOUR_GREY,    81,   239,    14,    25, 0x0,                     STR_SORT_CRITERIA_TIP},
    00048   {     WWT_MATRIX,     RESIZE_RB,  COLOUR_GREY,     0,   227,    26,    39, 0x101,                   STR_NULL },
    00049   {  WWT_SCROLLBAR,    RESIZE_LRB,  COLOUR_GREY,   228,   239,    26,    39, 0x0,                     STR_0190_SCROLL_BAR_SCROLLS_LIST },
    00050   {      WWT_PANEL,    RESIZE_RTB,  COLOUR_GREY,     0,   239,    40,   161, 0x0,                     STR_NULL },
    00051
    00052   { WWT_PUSHTXTBTN,     RESIZE_TB,  COLOUR_GREY,     0,   114,   162,   173, 0x0,                     STR_NULL },
    00053   { WWT_PUSHTXTBTN,    RESIZE_RTB,  COLOUR_GREY,   115,   227,   162,   173, 0x0,                     STR_NULL },
    00054   {  WWT_RESIZEBOX,   RESIZE_LRTB,  COLOUR_GREY,   228,   239,   162,   173, 0x0,                     STR_RESIZE_BUTTON },
    00055   {   WIDGETS_END},
    00056 };
    00057
    00058
    00059 static bool _internal_sort_order; // descending/ascending
    00060 static byte _last_sort_criteria[]    = {0, 0, 0, 0};
    00061 static bool _last_sort_order[]       = {false, false, false, false};
    00062
    00063 static int CDECL EngineNumberSorter(const void *a, const void *b)
    00064 {
    00065   const EngineID va = *(const EngineID*)a;
    00066   const EngineID vb = *(const EngineID*)b;
    00067   int r = ListPositionOfEngine(va) - ListPositionOfEngine(vb);
    00068
    00069   return _internal_sort_order ? -r : r;
    00070 }
    00071
    00072 static int CDECL EngineIntroDateSorter(const void *a, const void *b)
    00073 {
    00074   const int va = GetEngine(*(const EngineID*)a)->intro_date;
    00075   const int vb = GetEngine(*(const EngineID*)b)->intro_date;
    00076   const int r = va - vb;
    00077
    00078   /* Use EngineID to sort instead since we want consistent sorting */
    00079   if (r == 0) return EngineNumberSorter(a, b);
    00080   return _internal_sort_order ? -r : r;
    00081 }
    00082
    00083 static int CDECL EngineNameSorter(const void *a, const void *b)
    00084 {
    00085   static EngineID last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
    00086   static char     last_name[2][64] = { "\0", "\0" };
    00087
    00088   const EngineID va = *(const EngineID*)a;
    00089   const EngineID vb = *(const EngineID*)b;
    00090
    00091   if (va != last_engine[0]) {
    00092     last_engine[0] = va;
    00093     SetDParam(0, va);
    00094     GetString(last_name[0], STR_ENGINE_NAME, lastof(last_name[0]));
    00095   }
    00096
    00097   if (vb != last_engine[1]) {
    00098     last_engine[1] = vb;
    00099     SetDParam(0, vb);
    00100     GetString(last_name[1], STR_ENGINE_NAME, lastof(last_name[1]));
    00101   }
    00102
    00103   int r = strcmp(last_name[0], last_name[1]); // sort by name
    00104
    00105   /* Use EngineID to sort instead since we want consistent sorting */
    00106   if (r == 0) return EngineNumberSorter(a, b);
    00107   return _internal_sort_order ? -r : r;
    00108 }
    00109
    00110 static int CDECL EngineReliabilitySorter(const void *a, const void *b)
    00111 {
    00112   const int va = GetEngine(*(const EngineID*)a)->reliability;
    00113   const int vb = GetEngine(*(const EngineID*)b)->reliability;
    00114   const int r = va - vb;
    00115
    00116   /* Use EngineID to sort instead since we want consistent sorting */
    00117   if (r == 0) return EngineNumberSorter(a, b);
    00118   return _internal_sort_order ? -r : r;
    00119 }
    00120
    00121 static int CDECL EngineCostSorter(const void *a, const void *b)
    00122 {
    00123   int va = GetEngine(*(const EngineID*)a)->GetRunningCost();
    00124   int vb = GetEngine(*(const EngineID*)b)->GetRunningCost();
    00125   int r = ClampToI32(va - vb);
    00126
    00127   /* Use EngineID to sort instead since we want consistent sorting */
    00128   if (r == 0) return EngineNumberSorter(a, b);
    00129   return _internal_sort_order ? -r : r;
    00130 }
    00131
    00132 static int CDECL EngineSpeedSorter(const void *a, const void *b)
    00133 {
    00134   int va = GetEngine(*(const EngineID*)a)->GetDisplayMaxSpeed();
    00135   int vb = GetEngine(*(const EngineID*)b)->GetDisplayMaxSpeed();
    00136   int r = va - vb;
    00137
    00138   /* Use EngineID to sort instead since we want consistent sorting */
    00139   if (r == 0) return EngineNumberSorter(a, b);
    00140   return _internal_sort_order ? -r : r;
    00141 }
    00142
    00143 static int CDECL EnginePowerSorter(const void *a, const void *b)
    00144 {
    00145   int va = GetEngine(*(const EngineID*)a)->GetPower();
    00146   int vb = GetEngine(*(const EngineID*)b)->GetPower();
    00147   int r = va - vb;
    00148
    00149   /* Use EngineID to sort instead since we want consistent sorting */
    00150   if (r == 0) return EngineNumberSorter(a, b);
    00151   return _internal_sort_order ? -r : r;
    00152 }
    00153
    00154 static int CDECL EngineRunningCostSorter(const void *a, const void *b)
    00155 {
    00156   Money va = GetEngine(*(const EngineID*)a)->GetRunningCost();
    00157   Money vb = GetEngine(*(const EngineID*)b)->GetRunningCost();
    00158   int r = ClampToI32(va - vb);
    00159
    00160   /* Use EngineID to sort instead since we want consistent sorting */
    00161   if (r == 0) return EngineNumberSorter(a, b);
    00162   return _internal_sort_order ? -r : r;
    00163 }
    00164
    00165 /* Train sorting functions */
    00166 static int CDECL TrainEnginePowerVsRunningCostSorter(const void *a, const void *b)
    00167 {
    00168   const Engine *e_a = GetEngine(*(const EngineID*)a);
    00169   const Engine *e_b = GetEngine(*(const EngineID*)b);
    00170
    00171   /* Here we are using a few tricks to get the right sort.
    00172    * We want power/running cost, but since we usually got higher running cost than power and we store the result in an int,
    00173    * we will actually calculate cunning cost/power (to make it more than 1).
    00174    * Because of this, the return value have to be reversed as well and we return b - a instead of a - b.
    00175    * Another thing is that both power and running costs should be doubled for multiheaded engines.
    00176    * Since it would be multipling with 2 in both numerator and denumerator, it will even themselves out and we skip checking for multiheaded. */
    00177   Money va = (e_a->GetRunningCost()) / max(1U, (uint)e_a->GetPower());
    00178   Money vb = (e_b->GetRunningCost()) / max(1U, (uint)e_b->GetPower());
    00179   int r = ClampToI32(vb - va);
    00180
    00181   /* Use EngineID to sort instead since we want consistent sorting */
    00182   if (r == 0) return EngineNumberSorter(a, b);
    00183   return _internal_sort_order ? -r : r;
    00184 }
    00185
    00186 static int CDECL TrainEngineCapacitySorter(const void *a, const void *b)
    00187 {
    00188   const RailVehicleInfo *rvi_a = RailVehInfo(*(const EngineID*)a);
    00189   const RailVehicleInfo *rvi_b = RailVehInfo(*(const EngineID*)b);
    00190
    00191   int va = GetTotalCapacityOfArticulatedParts(*(const EngineID*)a, VEH_TRAIN) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
    00192   int vb = GetTotalCapacityOfArticulatedParts(*(const EngineID*)b, VEH_TRAIN) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
    00193   int r = va - vb;
    00194
    00195   /* Use EngineID to sort instead since we want consistent sorting */
    00196   if (r == 0) return EngineNumberSorter(a, b);
    00197   return _internal_sort_order ? -r : r;
    00198 }
    00199
    00200 static int CDECL TrainEnginesThenWagonsSorter(const void *a, const void *b)
    00201 {
    00202   EngineID va = *(const EngineID*)a;
    00203   EngineID vb = *(const EngineID*)b;
    00204   int val_a = (RailVehInfo(va)->railveh_type == RAILVEH_WAGON ? 1 : 0);
    00205   int val_b = (RailVehInfo(vb)->railveh_type == RAILVEH_WAGON ? 1 : 0);
    00206   int r = val_a - val_b;
    00207
    00208   /* Use EngineID to sort instead since we want consistent sorting */
    00209   if (r == 0) return EngineNumberSorter(a, b);
    00210   return _internal_sort_order ? -r : r;
    00211 }
    00212
    00213 /* Road vehicle sorting functions */
    00214 static int CDECL RoadVehEngineCapacitySorter(const void *a, const void *b)
    00215 {
    00216   int va = GetTotalCapacityOfArticulatedParts(*(const EngineID*)a, VEH_ROAD);
    00217   int vb = GetTotalCapacityOfArticulatedParts(*(const EngineID*)b, VEH_ROAD);
    00218   int r = va - vb;
    00219
    00220   /* Use EngineID to sort instead since we want consistent sorting */
    00221   if (r == 0) return EngineNumberSorter(a, b);
    00222   return _internal_sort_order ? -r : r;
    00223 }
    00224
    00225 /* Ship vehicle sorting functions */
    00226 static int CDECL ShipEngineCapacitySorter(const void *a, const void *b)
    00227 {
    00228   int va = ShipVehInfo(*(const EngineID*)a)->capacity;
    00229   int vb = ShipVehInfo(*(const EngineID*)b)->capacity;
    00230   int r = va - vb;
    00231
    00232   /* Use EngineID to sort instead since we want consistent sorting */
    00233   if (r == 0) return EngineNumberSorter(a, b);
    00234   return _internal_sort_order ? -r : r;
    00235 }
    00236
    00237 /* Aircraft sorting functions */
    00238 static int CDECL AircraftEngineCargoSorter(const void *a, const void *b)
    00239 {
    00240   const Engine *e_a = GetEngine(*(const EngineID*)a);
    00241   const Engine *e_b = GetEngine(*(const EngineID*)b);
    00242
    00243   int va = AircraftDefaultCargoCapacity(e_a->GetDefaultCargoType(), &e_a->u.air);
    00244   int vb = AircraftDefaultCargoCapacity(e_b->GetDefaultCargoType(), &e_b->u.air);
    00245   int r = va - vb;
    00246
    00247   if (r == 0) {
    00248     /* The planes has the same passenger capacity. Check mail capacity instead */
    00249     va = AircraftVehInfo(*(const EngineID*)a)->mail_capacity;
    00250     vb = AircraftVehInfo(*(const EngineID*)b)->mail_capacity;
    00251     r = va - vb;
    00252
    00253     if (r == 0) {
    00254       /* Use EngineID to sort instead since we want consistent sorting */
    00255       return EngineNumberSorter(a, b);
    00256     }
    00257   }
    00258   return _internal_sort_order ? -r : r;
    00259 }
    00260
    00261 static EngList_SortTypeFunction * const _sorter[][10] = {{
    00262   /* Trains */
    00263   &EngineNumberSorter,
    00264   &EngineCostSorter,
    00265   &EngineSpeedSorter,
    00266   &EnginePowerSorter,
    00267   &EngineIntroDateSorter,
    00268   &EngineNameSorter,
    00269   &EngineRunningCostSorter,
    00270   &TrainEnginePowerVsRunningCostSorter,
    00271   &EngineReliabilitySorter,
    00272   &TrainEngineCapacitySorter,
    00273 }, {
    00274   /* Road vehicles */
    00275   &EngineNumberSorter,
    00276   &EngineCostSorter,
    00277   &EngineSpeedSorter,
    00278   &EngineIntroDateSorter,
    00279   &EngineNameSorter,
    00280   &EngineRunningCostSorter,
    00281   &EngineReliabilitySorter,
    00282   &RoadVehEngineCapacitySorter,
    00283 }, {
    00284   /* Ships */
    00285   &EngineNumberSorter,
    00286   &EngineCostSorter,
    00287   &EngineSpeedSorter,
    00288   &EngineIntroDateSorter,
    00289   &EngineNameSorter,
    00290   &EngineRunningCostSorter,
    00291   &EngineReliabilitySorter,
    00292   &ShipEngineCapacitySorter,
    00293 }, {
    00294   /* Aircraft */
    00295   &EngineNumberSorter,
    00296   &EngineCostSorter,
    00297   &EngineSpeedSorter,
    00298   &EngineIntroDateSorter,
    00299   &EngineNameSorter,
    00300   &EngineRunningCostSorter,
    00301   &EngineReliabilitySorter,
    00302   &AircraftEngineCargoSorter,
    00303 }};
    00304
    00305 static const StringID _sort_listing[][11] = {{
    00306   /* Trains */
    00307   STR_ENGINE_SORT_ENGINE_ID,
    00308   STR_ENGINE_SORT_COST,
    00309   STR_SORT_BY_MAX_SPEED,
    00310   STR_ENGINE_SORT_POWER,
    00311   STR_ENGINE_SORT_INTRO_DATE,
    00312   STR_SORT_BY_DROPDOWN_NAME,
    00313   STR_ENGINE_SORT_RUNNING_COST,
    00314   STR_ENGINE_SORT_POWER_VS_RUNNING_COST,
    00315   STR_SORT_BY_RELIABILITY,
    00316   STR_ENGINE_SORT_CARGO_CAPACITY,
    00317   INVALID_STRING_ID
    00318 }, {
    00319   /* Road vehicles */
    00320   STR_ENGINE_SORT_ENGINE_ID,
    00321   STR_ENGINE_SORT_COST,
    00322   STR_SORT_BY_MAX_SPEED,
    00323   STR_ENGINE_SORT_INTRO_DATE,
    00324   STR_SORT_BY_DROPDOWN_NAME,
    00325   STR_ENGINE_SORT_RUNNING_COST,
    00326   STR_SORT_BY_RELIABILITY,
    00327   STR_ENGINE_SORT_CARGO_CAPACITY,
    00328   INVALID_STRING_ID
    00329 }, {
    00330   /* Ships */
    00331   STR_ENGINE_SORT_ENGINE_ID,
    00332   STR_ENGINE_SORT_COST,
    00333   STR_SORT_BY_MAX_SPEED,
    00334   STR_ENGINE_SORT_INTRO_DATE,
    00335   STR_SORT_BY_DROPDOWN_NAME,
    00336   STR_ENGINE_SORT_RUNNING_COST,
    00337   STR_SORT_BY_RELIABILITY,
    00338   STR_ENGINE_SORT_CARGO_CAPACITY,
    00339   INVALID_STRING_ID
    00340 }, {
    00341   /* Aircraft */
    00342   STR_ENGINE_SORT_ENGINE_ID,
    00343   STR_ENGINE_SORT_COST,
    00344   STR_SORT_BY_MAX_SPEED,
    00345   STR_ENGINE_SORT_INTRO_DATE,
    00346   STR_SORT_BY_DROPDOWN_NAME,
    00347   STR_ENGINE_SORT_RUNNING_COST,
    00348   STR_SORT_BY_RELIABILITY,
    00349   STR_ENGINE_SORT_CARGO_CAPACITY,
    00350   INVALID_STRING_ID
    00351 }};
    00352
    00353 static int DrawCargoCapacityInfo(int x, int y, EngineID engine, VehicleType type, bool refittable)
    00354 {
    00355   uint16 *cap = GetCapacityOfArticulatedParts(engine, type);
    00356
    00357   for (uint c = 0; c < NUM_CARGO; c++) {
    00358     if (cap[c] == 0) continue;
    00359
    00360     SetDParam(0, c);
    00361     SetDParam(1, cap[c]);
    00362     SetDParam(2, refittable ? STR_9842_REFITTABLE : STR_EMPTY);
    00363     DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING);
    00364     y += 10;
    00365
    00366     /* Only show as refittable once */
    00367     refittable = false;
    00368   }
    00369
    00370   return y;
    00371 }
    00372
    00373 /* Draw rail wagon specific details */
    00374 static int DrawRailWagonPurchaseInfo(int x, int y, EngineID engine_number, const RailVehicleInfo *rvi)
    00375 {
    00376   const Engine *e = GetEngine(engine_number);
    00377
    00378   /* Purchase cost */
    00379   SetDParam(0, e->GetCost());
    00380   DrawString(x, y, STR_PURCHASE_INFO_COST, TC_FROMSTRING);
    00381   y += 10;
    00382
    00383   /* Wagon weight - (including cargo) */
    00384   uint weight = e->GetDisplayWeight();
    00385   SetDParam(0, weight);
    00386   uint cargo_weight = (e->CanCarryCargo() ? GetCargo(e->GetDefaultCargoType())->weight * GetEngineProperty(engine_number, 0x14, rvi->capacity) >> 4 : 0);
    00387   SetDParam(1, cargo_weight + weight);
    00388   DrawString(x, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT, TC_FROMSTRING);
    00389   y += 10;
    00390
    00391   /* Wagon speed limit, displayed if above zero */
    00392   if (_settings_game.vehicle.wagon_speed_limits) {
    00393     uint max_speed = e->GetDisplayMaxSpeed();
    00394     if (max_speed > 0) {
    00395       SetDParam(0, max_speed);
    00396       DrawString(x, y, STR_PURCHASE_INFO_SPEED, TC_FROMSTRING);
    00397       y += 10;
    00398     }
    00399   }
    00400
    00401   /* Running cost */
    00402   if (rvi->running_cost_class != 0xFF) {
    00403     SetDParam(0, e->GetRunningCost());
    00404     DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING);
    00405     y += 10;
    00406   }
    00407
    00408   return y;
    00409 }
    00410
    00411 /* Draw locomotive specific details */
    00412 static int DrawRailEnginePurchaseInfo(int x, int y, EngineID engine_number, const RailVehicleInfo *rvi)
    00413 {
    00414   const Engine *e = GetEngine(engine_number);
    00415
    00416   /* Purchase Cost - Engine weight */
    00417   SetDParam(0, e->GetCost());
    00418   SetDParam(1, e->GetDisplayWeight());
    00419   DrawString(x, y, STR_PURCHASE_INFO_COST_WEIGHT, TC_FROMSTRING);
    00420   y += 10;
    00421
    00422   /* Max speed - Engine power */
    00423   SetDParam(0, e->GetDisplayMaxSpeed());
    00424   SetDParam(1, e->GetPower());
    00425   DrawString(x, y, STR_PURCHASE_INFO_SPEED_POWER, TC_FROMSTRING);
    00426   y += 10;
    00427
    00428   /* Max tractive effort - not applicable if old acceleration or maglev */
    00429   if (_settings_game.vehicle.train_acceleration_model != TAM_ORIGINAL && rvi->railtype != RAILTYPE_MAGLEV) {
    00430     SetDParam(0, e->GetDisplayMaxTractiveEffort());
    00431     DrawString(x, y, STR_PURCHASE_INFO_MAX_TE, TC_FROMSTRING);
    00432     y += 10;
    00433   }
    00434
    00435   /* Running cost */
    00436   if (rvi->running_cost_class != 0xFF) {
    00437     SetDParam(0, e->GetRunningCost());
    00438     DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING);
    00439     y += 10;
    00440   }
    00441
    00442   /* Powered wagons power - Powered wagons extra weight */
    00443   if (rvi->pow_wag_power != 0) {
    00444     SetDParam(0, rvi->pow_wag_power);
    00445     SetDParam(1, rvi->pow_wag_weight);
    00446     DrawString(x, y, STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT, TC_FROMSTRING);
    00447     y += 10;
    00448   };
    00449
    00450   return y;
    00451 }
    00452
    00453 /* Draw road vehicle specific details */
    00454 static int DrawRoadVehPurchaseInfo(int x, int y, EngineID engine_number)
    00455 {
    00456   const Engine *e = GetEngine(engine_number);
    00457
    00458   /* Purchase cost - Max speed */
    00459   SetDParam(0, e->GetCost());
    00460   SetDParam(1, e->GetDisplayMaxSpeed());
    00461   DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, TC_FROMSTRING);
    00462   y += 10;
    00463
    00464   /* Running cost */
    00465   SetDParam(0, e->GetRunningCost());
    00466   DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING);
    00467   y += 10;
    00468
    00469   return y;
    00470 }
    00471
    00472 /* Draw ship specific details */
    00473 static int DrawShipPurchaseInfo(int x, int y, EngineID engine_number, const ShipVehicleInfo *svi, bool refittable)
    00474 {
    00475   const Engine *e = GetEngine(engine_number);
    00476
    00477   /* Purchase cost - Max speed */
    00478   SetDParam(0, e->GetCost());
    00479   SetDParam(1, e->GetDisplayMaxSpeed());
    00480   DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, TC_FROMSTRING);
    00481   y += 10;
    00482
    00483   /* Cargo type + capacity */
    00484   SetDParam(0, e->GetDefaultCargoType());
    00485   SetDParam(1, GetEngineProperty(engine_number, 0x0D, svi->capacity));
    00486   SetDParam(2, refittable ? STR_9842_REFITTABLE : STR_EMPTY);
    00487   DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING);
    00488   y += 10;
    00489
    00490   /* Running cost */
    00491   SetDParam(0, e->GetRunningCost());
    00492   DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING);
    00493   y += 10;
    00494
    00495   return y;
    00496 }
    00497
    00498 /* Draw aircraft specific details */
    00499 static int DrawAircraftPurchaseInfo(int x, int y, EngineID engine_number, const AircraftVehicleInfo *avi, bool refittable)
    00500 {
    00501   const Engine *e = GetEngine(engine_number);
    00502   CargoID cargo = e->GetDefaultCargoType();
    00503
    00504   /* Purchase cost - Max speed */
    00505   SetDParam(0, e->GetCost());
    00506   SetDParam(1, e->GetDisplayMaxSpeed());
    00507   DrawString(x, y, STR_PURCHASE_INFO_COST_SPEED, TC_FROMSTRING);
    00508   y += 10;
    00509
    00510   /* Cargo capacity */
    00511   if (cargo == CT_INVALID || cargo == CT_PASSENGERS) {
    00512     SetDParam(0, avi->passenger_capacity);
    00513     SetDParam(1, avi->mail_capacity);
    00514     DrawString(x, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY, TC_FROMSTRING);
    00515   } else {
    00516     /* Note, if the default capacity is selected by the refit capacity
    00517      * callback, then the capacity shown is likely to be incorrect. */
    00518     SetDParam(0, cargo);
    00519     SetDParam(1, AircraftDefaultCargoCapacity(cargo, avi));
    00520     SetDParam(2, refittable ? STR_9842_REFITTABLE : STR_EMPTY);
    00521     DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING);
    00522   }
    00523   y += 10;
    00524
    00525   /* Running cost */
    00526   SetDParam(0, e->GetRunningCost());
    00527   DrawString(x, y, STR_PURCHASE_INFO_RUNNINGCOST, TC_FROMSTRING);
    00528   y += 10;
    00529
    00530   return y;
    00531 }
    00532
    00540 int DrawVehiclePurchaseInfo(int x, int y, uint w, EngineID engine_number)
    00541 {
    00542   const Engine *e = GetEngine(engine_number);
    00543   YearMonthDay ymd;
    00544   ConvertDateToYMD(e->intro_date, &ymd);
    00545   bool refittable = IsArticulatedVehicleRefittable(engine_number);
    00546
    00547   switch (e->type) {
    00548     default: NOT_REACHED();
    00549     case VEH_TRAIN: {
    00550       const RailVehicleInfo *rvi = RailVehInfo(engine_number);
    00551       if (rvi->railveh_type == RAILVEH_WAGON) {
    00552         y = DrawRailWagonPurchaseInfo(x, y, engine_number, rvi);
    00553       } else {
    00554         y = DrawRailEnginePurchaseInfo(x, y, engine_number, rvi);
    00555       }
    00556
    00557       /* Cargo type + capacity, or N/A */
    00558       int new_y = DrawCargoCapacityInfo(x, y, engine_number, VEH_TRAIN, refittable);
    00559
    00560       if (new_y == y) {
    00561         SetDParam(0, CT_INVALID);
    00562         SetDParam(2, STR_EMPTY);
    00563         DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING);
    00564         y += 10;
    00565       } else {
    00566         y = new_y;
    00567       }
    00568       break;
    00569     }
    00570     case VEH_ROAD: {
    00571       y = DrawRoadVehPurchaseInfo(x, y, engine_number);
    00572
    00573       /* Cargo type + capacity, or N/A */
    00574       int new_y = DrawCargoCapacityInfo(x, y, engine_number, VEH_ROAD, refittable);
    00575
    00576       if (new_y == y) {
    00577         SetDParam(0, CT_INVALID);
    00578         SetDParam(2, STR_EMPTY);
    00579         DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING);
    00580         y += 10;
    00581       } else {
    00582         y = new_y;
    00583       }
    00584       break;
    00585     }
    00586     case VEH_SHIP:
    00587       y = DrawShipPurchaseInfo(x, y, engine_number, ShipVehInfo(engine_number), refittable);
    00588       break;
    00589     case VEH_AIRCRAFT:
    00590       y = DrawAircraftPurchaseInfo(x, y, engine_number, AircraftVehInfo(engine_number), refittable);
    00591       break;
    00592   }
    00593
    00594   /* Draw details, that applies to all types except rail wagons */
    00595   if (e->type != VEH_TRAIN || RailVehInfo(engine_number)->railveh_type != RAILVEH_WAGON) {
    00596     /* Design date - Life length */
    00597     SetDParam(0, ymd.year);
    00598     SetDParam(1, e->lifelength);
    00599     DrawString(x, y, STR_PURCHASE_INFO_DESIGNED_LIFE, TC_FROMSTRING);
    00600     y += 10;
    00601
    00602     /* Reliability */
    00603     SetDParam(0, e->reliability * 100 >> 16);
    00604     DrawString(x, y, STR_PURCHASE_INFO_RELIABILITY, TC_FROMSTRING);
    00605     y += 10;
    00606   }
    00607
    00608   /* Additional text from NewGRF */
    00609   y += ShowAdditionalText(x, y, w, engine_number);
    00610   if (refittable) y += ShowRefitOptionsList(x, y, w, engine_number);
    00611
    00612   return y;
    00613 }
    00614
    00615 static void DrawVehicleEngine(VehicleType type, int x, int y, EngineID engine, SpriteID pal)
    00616 {
    00617   switch (type) {
    00618     case VEH_TRAIN:    DrawTrainEngine(   x, y, engine, pal); break;
    00619     case VEH_ROAD:     DrawRoadVehEngine( x, y, engine, pal); break;
    00620     case VEH_SHIP:     DrawShipEngine(    x, y, engine, pal); break;
    00621     case VEH_AIRCRAFT: DrawAircraftEngine(x, y, engine, pal); break;
    00622     default: NOT_REACHED();
    00623   }
    00624 }
    00625
    00635 void DrawEngineList(VehicleType type, int x, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, int count_location, GroupID selected_group)
    00636 {
    00637   byte step_size = GetVehicleListHeight(type);
    00638   byte x_offset = 0;
    00639   byte y_offset = 0;
    00640
    00641   assert(max <= eng_list->Length());
    00642
    00643   switch (type) {
    00644     case VEH_TRAIN:
    00645       x++; // train and road vehicles use the same offset, except trains are one more pixel to the right
    00646       /* Fallthough */
    00647     case VEH_ROAD:
    00648       x += 26;
    00649       x_offset = 30;
    00650       y += 2;
    00651       y_offset = 4;
    00652       break;
    00653     case VEH_SHIP:
    00654       x += 35;
    00655       x_offset = 40;
    00656       y += 7;
    00657       y_offset = 3;
    00658       break;
    00659     case VEH_AIRCRAFT:
    00660       x += 27;
    00661       x_offset = 33;
    00662       y += 7;
    00663       y_offset = 3;
    00664       break;
    00665     default: NOT_REACHED();
    00666   }
    00667
    00668   uint maxw = r - x - x_offset;
    00669
    00670   for (; min < max; min++, y += step_size) {
    00671     const EngineID engine = (*eng_list)[min];
    00672     /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
    00673     const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine);
    00674
    00675     SetDParam(0, engine);
    00676     DrawStringTruncated(x + x_offset, y, STR_ENGINE_NAME, engine == selected_id ? TC_WHITE : TC_BLACK, maxw);
    00677     DrawVehicleEngine(type, x, y + y_offset, engine, (count_location != 0 && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company));
    00678     if (count_location != 0) {
    00679       SetDParam(0, num_engines);
    00680       DrawStringRightAligned(count_location, y + (GetVehicleListHeight(type) == 14 ? 3 : 8), STR_TINY_BLACK, TC_FROMSTRING);
    00681     }
    00682   }
    00683 }
    00684
    00685
    00686 struct BuildVehicleWindow : Window {
    00687   VehicleType vehicle_type;
    00688   union {
    00689     RailTypeByte railtype;
    00690     AirportFTAClass::Flags flags;
    00691     RoadTypes roadtypes;
    00692   } filter;
    00693   bool descending_sort_order;
    00694   byte sort_criteria;
    00695   bool regenerate_list;
    00696   bool listview_mode;
    00697   EngineID sel_engine;
    00698   EngineID rename_engine;
    00699   GUIEngineList eng_list;
    00700
    00701   BuildVehicleWindow(const WindowDesc *desc, TileIndex tile, VehicleType type) : Window(desc, tile == INVALID_TILE ? (int)type : tile)
    00702   {
    00703     this->vehicle_type = type;
    00704     int vlh = GetVehicleListHeight(this->vehicle_type);
    00705
    00706     ResizeWindow(this, 0, vlh - 14);
    00707     this->resize.step_height = vlh;
    00708     this->vscroll.cap = 1;
    00709     this->widget[BUILD_VEHICLE_WIDGET_LIST].data = 0x101;
    00710
    00711     this->resize.width  = this->width;
    00712     this->resize.height = this->height;
    00713
    00714     this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
    00715
    00716     this->sel_engine      = INVALID_ENGINE;
    00717     this->regenerate_list = false;
    00718
    00719     this->sort_criteria         = _last_sort_criteria[type];
    00720     this->descending_sort_order = _last_sort_order[type];
    00721
    00722     switch (type) {
    00723       default: NOT_REACHED();
    00724       case VEH_TRAIN:
    00725         this->filter.railtype = (tile == INVALID_TILE) ? RAILTYPE_END : GetRailType(tile);
    00726         break;
    00727       case VEH_ROAD:
    00728         this->filter.roadtypes = (tile == INVALID_TILE) ? ROADTYPES_ALL : GetRoadTypes(tile);
    00729       case VEH_SHIP:
    00730         break;
    00731       case VEH_AIRCRAFT:
    00732         this->filter.flags =
    00733           tile == INVALID_TILE ? AirportFTAClass::ALL : GetStationByTile(tile)->Airport()->flags;
    00734         break;
    00735     }
    00736     this->SetupWindowStrings(type);
    00737
    00738     this->listview_mode = (this->window_number <= VEH_END);
    00739     /* If we are just viewing the list of vehicles, we do not need the Build button.
    00740      * So we just hide it, and enlarge the Rename buton by the now vacant place. */
    00741     if (this->listview_mode) {
    00742       this->HideWidget(BUILD_VEHICLE_WIDGET_BUILD);
    00743       this->widget[BUILD_VEHICLE_WIDGET_RENAME].left = this->widget[BUILD_VEHICLE_WIDGET_BUILD].left;
    00744     } else {
    00745       /* Both are visible, adjust the size of each */
    00746       ResizeButtons(this, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME);
    00747     }
    00748
    00749     this->GenerateBuildList(); // generate the list, since we need it in the next line
    00750     /* Select the first engine in the list as default when opening the window */
    00751     if (this->eng_list.Length() > 0) this->sel_engine = this->eng_list[0];
    00752
    00753     this->FindWindowPlacementAndResize(desc);
    00754   }
    00755
    00756   /* Setup widget strings to fit the different types of vehicles */
    00757   void SetupWindowStrings(VehicleType type)
    00758   {
    00759     switch (type) {
    00760       default: NOT_REACHED();
    00761
    00762       case VEH_TRAIN:
    00763         this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data    = this->listview_mode ? STR_AVAILABLE_TRAINS : STR_JUST_STRING;
    00764         this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips   = STR_8843_TRAIN_VEHICLE_SELECTION;
    00765         this->widget[BUILD_VEHICLE_WIDGET_BUILD].data      = STR_881F_BUILD_VEHICLE;
    00766         this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips  = STR_8844_BUILD_THE_HIGHLIGHTED_TRAIN;
    00767         this->widget[BUILD_VEHICLE_WIDGET_RENAME].data     = STR_8820_RENAME;
    00768         this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_8845_RENAME_TRAIN_VEHICLE_TYPE;
    00769         break;
    00770
    00771       case VEH_ROAD:
    00772         this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data    = this->listview_mode ? STR_AVAILABLE_ROAD_VEHICLES : STR_9006_NEW_ROAD_VEHICLES;
    00773         this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips   = STR_9026_ROAD_VEHICLE_SELECTION;
    00774         this->widget[BUILD_VEHICLE_WIDGET_BUILD].data      = STR_9007_BUILD_VEHICLE;
    00775         this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips  = STR_9027_BUILD_THE_HIGHLIGHTED_ROAD;
    00776         this->widget[BUILD_VEHICLE_WIDGET_RENAME].data     = STR_9034_RENAME;
    00777         this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9035_RENAME_ROAD_VEHICLE_TYPE;
    00778         break;
    00779
    00780       case VEH_SHIP:
    00781         this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data    = this->listview_mode ? STR_AVAILABLE_SHIPS : STR_9808_NEW_SHIPS;
    00782         this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips   = STR_9825_SHIP_SELECTION_LIST_CLICK;
    00783         this->widget[BUILD_VEHICLE_WIDGET_BUILD].data      = STR_9809_BUILD_SHIP;
    00784         this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips  = STR_9826_BUILD_THE_HIGHLIGHTED_SHIP;
    00785         this->widget[BUILD_VEHICLE_WIDGET_RENAME].data     = STR_9836_RENAME;
    00786         this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_9837_RENAME_SHIP_TYPE;
    00787         break;
    00788
    00789       case VEH_AIRCRAFT:
    00790         this->widget[BUILD_VEHICLE_WIDGET_CAPTION].data    = this->listview_mode ? STR_AVAILABLE_AIRCRAFT : STR_A005_NEW_AIRCRAFT;
    00791         this->widget[BUILD_VEHICLE_WIDGET_LIST].tooltips   = STR_A025_AIRCRAFT_SELECTION_LIST;
    00792         this->widget[BUILD_VEHICLE_WIDGET_BUILD].data      = STR_A006_BUILD_AIRCRAFT;
    00793         this->widget[BUILD_VEHICLE_WIDGET_BUILD].tooltips  = STR_A026_BUILD_THE_HIGHLIGHTED_AIRCRAFT;
    00794         this->widget[BUILD_VEHICLE_WIDGET_RENAME].data     = STR_A037_RENAME;
    00795         this->widget[BUILD_VEHICLE_WIDGET_RENAME].tooltips = STR_A038_RENAME_AIRCRAFT_TYPE;
    00796         break;
    00797     }
    00798   }
    00799
    00800   /* Figure out what train EngineIDs to put in the list */
    00801   void GenerateBuildTrainList()
    00802   {
    00803     EngineID sel_id = INVALID_ENGINE;
    00804     int num_engines = 0;
    00805     int num_wagons  = 0;
    00806
    00807     this->filter.railtype = (this->listview_mode) ? RAILTYPE_END : GetRailType(this->window_number);
    00808
    00809     this->eng_list.Clear();
    00810
    00811     /* Make list of all available train engines and wagons.
    00812      * Also check to see if the previously selected engine is still available,
    00813      * and if not, reset selection to INVALID_ENGINE. This could be the case
    00814      * when engines become obsolete and are removed */
    00815     const Engine *e;
    00816     FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
    00817       EngineID eid = e->index;
    00818       const RailVehicleInfo *rvi = &e->u.rail;
    00819
    00820       if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
    00821       if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
    00822
    00823       *this->eng_list.Append() = eid;
    00824
    00825       if (rvi->railveh_type != RAILVEH_WAGON) {
    00826         num_engines++;
    00827       } else {
    00828         num_wagons++;
    00829       }
    00830
    00831       if (eid == this->sel_engine) sel_id = eid;
    00832     }
    00833
    00834     this->sel_engine = sel_id;
    00835
    00836     /* make engines first, and then wagons, sorted by ListPositionOfEngine() */
    00837     _internal_sort_order = false;
    00838     EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter);
    00839
    00840     /* and then sort engines */
    00841     _internal_sort_order = this->descending_sort_order;
    00842     EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], 0, num_engines);
    00843
    00844     /* and finally sort wagons */
    00845     EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], num_engines, num_wagons);
    00846   }
    00847
    00848   /* Figure out what road vehicle EngineIDs to put in the list */
    00849   void GenerateBuildRoadVehList()
    00850   {
    00851     EngineID sel_id = INVALID_ENGINE;
    00852
    00853     this->eng_list.Clear();
    00854
    00855     const Engine *e;
    00856     FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
    00857       EngineID eid = e->index;
    00858       if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
    00859       if (!HasBit(this->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue;
    00860       *this->eng_list.Append() = eid;
    00861
    00862       if (eid == this->sel_engine) sel_id = eid;
    00863     }
    00864     this->sel_engine = sel_id;
    00865   }
    00866
    00867   /* Figure out what ship EngineIDs to put in the list */
    00868   void GenerateBuildShipList()
    00869   {
    00870     EngineID sel_id = INVALID_ENGINE;
    00871     this->eng_list.Clear();
    00872
    00873     const Engine *e;
    00874     FOR_ALL_ENGINES_OF_TYPE(e, VEH_SHIP) {
    00875       EngineID eid = e->index;
    00876       if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
    00877       *this->eng_list.Append() = eid;
    00878
    00879       if (eid == this->sel_engine) sel_id = eid;
    00880     }
    00881     this->sel_engine = sel_id;
    00882   }
    00883
    00884   /* Figure out what aircraft EngineIDs to put in the list */
    00885   void GenerateBuildAircraftList()
    00886   {
    00887     EngineID sel_id = INVALID_ENGINE;
    00888
    00889     this->eng_list.Clear();
    00890
    00891     const Station *st = this->listview_mode ? NULL : GetStationByTile(this->window_number);
    00892
    00893     /* Make list of all available planes.
    00894      * Also check to see if the previously selected plane is still available,
    00895      * and if not, reset selection to INVALID_ENGINE. This could be the case
    00896      * when planes become obsolete and are removed */
    00897     const Engine *e;
    00898     FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) {
    00899       EngineID eid = e->index;
    00900       if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_company)) continue;
    00901       /* First VEH_END window_numbers are fake to allow a window open for all different types at once */
    00902       if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue;
    00903
    00904       *this->eng_list.Append() = eid;
    00905       if (eid == this->sel_engine) sel_id = eid;
    00906     }
    00907
    00908     this->sel_engine = sel_id;
    00909   }
    00910
    00911   /* Generate the list of vehicles */
    00912   void GenerateBuildList()
    00913   {
    00914     switch (this->vehicle_type) {
    00915       default: NOT_REACHED();
    00916       case VEH_TRAIN:
    00917         this->GenerateBuildTrainList();
    00918         return; // trains should not reach the last sorting
    00919       case VEH_ROAD:
    00920         this->GenerateBuildRoadVehList();
    00921         break;
    00922       case VEH_SHIP:
    00923         this->GenerateBuildShipList();
    00924         break;
    00925       case VEH_AIRCRAFT:
    00926         this->GenerateBuildAircraftList();
    00927         break;
    00928     }
    00929     _internal_sort_order = this->descending_sort_order;
    00930     EngList_Sort(&this->eng_list, _sorter[this->vehicle_type][this->sort_criteria]);
    00931   }
    00932
    00933   void OnClick(Point pt, int widget)
    00934   {
    00935     switch (widget) {
    00936       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
    00937         this->descending_sort_order ^= true;
    00938         _last_sort_order[this->vehicle_type] = this->descending_sort_order;
    00939         this->regenerate_list = true;
    00940         this->SetDirty();
    00941         break;
    00942
    00943       case BUILD_VEHICLE_WIDGET_LIST: {
    00944         uint i = (pt.y - this->widget[BUILD_VEHICLE_WIDGET_LIST].top) / GetVehicleListHeight(this->vehicle_type) + this->vscroll.pos;
    00945         size_t num_items = this->eng_list.Length();
    00946         this->sel_engine = (i < num_items) ? this->eng_list[i] : INVALID_ENGINE;
    00947         this->SetDirty();
    00948         break;
    00949       }
    00950
    00951       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN: // Select sorting criteria dropdown menu
    00952         ShowDropDownMenu(this, _sort_listing[this->vehicle_type], this->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, 0);
    00953         break;
    00954
    00955       case BUILD_VEHICLE_WIDGET_BUILD: {
    00956         EngineID sel_eng = this->sel_engine;
    00957         if (sel_eng != INVALID_ENGINE) {
    00958           switch (this->vehicle_type) {
    00959             default: NOT_REACHED();
    00960             case VEH_TRAIN:
    00961               DoCommandP(this->window_number, sel_eng, 0,
    00962                   CMD_BUILD_RAIL_VEHICLE | CMD_MSG(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE),
    00963                   (RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildLoco);
    00964               break;
    00965             case VEH_ROAD:
    00966               DoCommandP(this->window_number, sel_eng, 0, CMD_BUILD_ROAD_VEH | CMD_MSG(STR_9009_CAN_T_BUILD_ROAD_VEHICLE), CcBuildRoadVeh);
    00967               break;
    00968             case VEH_SHIP:
    00969               DoCommandP(this->window_number, sel_eng, 0, CMD_BUILD_SHIP | CMD_MSG(STR_980D_CAN_T_BUILD_SHIP), CcBuildShip);
    00970               break;
    00971             case VEH_AIRCRAFT:
    00972               DoCommandP(this->window_number, sel_eng, 0, CMD_BUILD_AIRCRAFT | CMD_MSG(STR_A008_CAN_T_BUILD_AIRCRAFT), CcBuildAircraft);
    00973               break;
    00974           }
    00975         }
    00976         break;
    00977       }
    00978
    00979       case BUILD_VEHICLE_WIDGET_RENAME: {
    00980         EngineID sel_eng = this->sel_engine;
    00981         if (sel_eng != INVALID_ENGINE) {
    00982           StringID str = STR_NULL;
    00983
    00984           this->rename_engine = sel_eng;
    00985           switch (this->vehicle_type) {
    00986             default: NOT_REACHED();
    00987             case VEH_TRAIN:    str = STR_886A_RENAME_TRAIN_VEHICLE_TYPE; break;
    00988             case VEH_ROAD:     str = STR_9036_RENAME_ROAD_VEHICLE_TYPE;  break;
    00989             case VEH_SHIP:     str = STR_9838_RENAME_SHIP_TYPE;          break;
    00990             case VEH_AIRCRAFT: str = STR_A039_RENAME_AIRCRAFT_TYPE;      break;
    00991           }
    00992           SetDParam(0, sel_eng);
    00993           ShowQueryString(STR_ENGINE_NAME, str, MAX_LENGTH_ENGINE_NAME_BYTES, MAX_LENGTH_ENGINE_NAME_PIXELS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT);
    00994         }
    00995         break;
    00996       }
    00997     }
    00998   }
    00999
    01000   virtual void OnInvalidateData(int data)
    01001   {
    01002     this->regenerate_list = true;
    01003   }
    01004
    01005   virtual void OnPaint()
    01006   {
    01007     if (this->regenerate_list) {
    01008       this->regenerate_list = false;
    01009       this->GenerateBuildList();
    01010     }
    01011
    01012     uint max = min(this->vscroll.pos + this->vscroll.cap, this->eng_list.Length());
    01013
    01014     SetVScrollCount(this, this->eng_list.Length());
    01015     if (this->vehicle_type == VEH_TRAIN) {
    01016       if (this->filter.railtype == RAILTYPE_END) {
    01017         SetDParam(0, STR_ALL_AVAIL_RAIL_VEHICLES);
    01018       } else {
    01019         const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
    01020         SetDParam(0, rti->strings.build_caption);
    01021       }
    01022     }
    01023
    01024     /* Set text of sort by dropdown */
    01025     this->widget[BUILD_VEHICLE_WIDGET_SORT_DROPDOWN].data = _sort_listing[this->vehicle_type][this->sort_criteria];
    01026
    01027     this->DrawWidgets();
    01028
    01029     DrawEngineList(this->vehicle_type, this->widget[BUILD_VEHICLE_WIDGET_LIST].left + 2, this->widget[BUILD_VEHICLE_WIDGET_LIST].right, this->widget[BUILD_VEHICLE_WIDGET_LIST].top + 1, &this->eng_list, this->vscroll.pos, max, this->sel_engine, 0, DEFAULT_GROUP);
    01030
    01031     if (this->sel_engine != INVALID_ENGINE) {
    01032       const Widget *wi = &this->widget[BUILD_VEHICLE_WIDGET_PANEL];
    01033       int text_end = DrawVehiclePurchaseInfo(2, wi->top + 1, wi->right - wi->left - 2, this->sel_engine);
    01034
    01035       if (text_end > wi->bottom) {
    01036         this->SetDirty();
    01037         ResizeWindowForWidget(this, BUILD_VEHICLE_WIDGET_PANEL, 0, text_end - wi->bottom);
    01038         this->SetDirty();
    01039       }
    01040     }
    01041
    01042     this->DrawSortButtonState(BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
    01043   }
    01044
    01045   virtual void OnDoubleClick(Point pt, int widget)
    01046   {
    01047     if (widget == BUILD_VEHICLE_WIDGET_LIST) {
    01048       /* When double clicking, we want to buy a vehicle */
    01049       this->OnClick(pt, BUILD_VEHICLE_WIDGET_BUILD);
    01050     }
    01051   }
    01052
    01053   virtual void OnQueryTextFinished(char *str)
    01054   {
    01055     if (str == NULL) return;
    01056
    01057     StringID err_str = STR_NULL;
    01058     switch (this->vehicle_type) {
    01059       default: NOT_REACHED();
    01060       case VEH_TRAIN:    err_str = STR_886B_CAN_T_RENAME_TRAIN_VEHICLE; break;
    01061       case VEH_ROAD:     err_str = STR_9037_CAN_T_RENAME_ROAD_VEHICLE;  break;
    01062       case VEH_SHIP:     err_str = STR_9839_CAN_T_RENAME_SHIP_TYPE;     break;
    01063       case VEH_AIRCRAFT: err_str = STR_A03A_CAN_T_RENAME_AIRCRAFT_TYPE; break;
    01064     }
    01065     DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(err_str), NULL, str);
    01066   }
    01067
    01068   virtual void OnDropdownSelect(int widget, int index)
    01069   {
    01070     if (this->sort_criteria != index) {
    01071       this->sort_criteria = index;
    01072       _last_sort_criteria[this->vehicle_type] = this->sort_criteria;
    01073       this->regenerate_list = true;
    01074     }
    01075     this->SetDirty();
    01076   }
    01077
    01078   virtual void OnResize(Point new_size, Point delta)
    01079   {
    01080     if (delta.x != 0 && !this->listview_mode) {
    01081       ResizeButtons(this, BUILD_VEHICLE_WIDGET_BUILD, BUILD_VEHICLE_WIDGET_RENAME);
    01082     }
    01083     if (delta.y == 0) return;
    01084
    01085     this->vscroll.cap += delta.y / (int)GetVehicleListHeight(this->vehicle_type);
    01086     this->widget[BUILD_VEHICLE_WIDGET_LIST].data = (this->vscroll.cap << 8) + 1;
    01087   }
    01088 };
    01089
    01090 static const WindowDesc _build_vehicle_desc(
    01091   WDP_AUTO, WDP_AUTO, 240, 174, 240, 256,
    01092   WC_BUILD_VEHICLE, WC_NONE,
    01093   WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESIZABLE | WDF_CONSTRUCTION,
    01094   _build_vehicle_widgets
    01095 );
    01096
    01097 void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
    01098 {
    01099   /* We want to be able to open both Available Train as Available Ships,
    01100    *  so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
    01101    *  As it always is a low value, it won't collide with any real tile
    01102    *  number. */
    01103   uint num = (tile == INVALID_TILE) ? (int)type : tile;
    01104
    01105   assert(IsCompanyBuildableVehicleType(type));
    01106
    01107   DeleteWindowById(WC_BUILD_VEHICLE, num);
    01108
    01109   new BuildVehicleWindow(&_build_vehicle_desc, tile, type);
    01110 }