build_vehicle_gui.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "engine_base.h"
00014 #include "engine_func.h"
00015 #include "station_base.h"
00016 #include "articulated_vehicles.h"
00017 #include "textbuf_gui.h"
00018 #include "command_func.h"
00019 #include "company_func.h"
00020 #include "vehicle_gui.h"
00021 #include "newgrf_engine.h"
00022 #include "newgrf_text.h"
00023 #include "group.h"
00024 #include "string_func.h"
00025 #include "strings_func.h"
00026 #include "window_func.h"
00027 #include "date_func.h"
00028 #include "vehicle_func.h"
00029 #include "widgets/dropdown_func.h"
00030 #include "engine_gui.h"
00031 #include "cargotype.h"
00032 #include "core/geometry_func.hpp"
00033 
00034 #include "table/strings.h"
00035 
00041 uint GetEngineListHeight(VehicleType type)
00042 {
00043   return max<uint>(FONT_HEIGHT_NORMAL + WD_MATRIX_TOP + WD_MATRIX_BOTTOM, GetVehicleHeight(type));
00044 }
00045 
00046 enum BuildVehicleWidgets {
00047   BUILD_VEHICLE_WIDGET_CAPTION,
00048   BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING,
00049   BUILD_VEHICLE_WIDGET_SORT_DROPDOWN,
00050   BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN,
00051   BUILD_VEHICLE_WIDGET_LIST,
00052   BUILD_VEHICLE_WIDGET_SCROLLBAR,
00053   BUILD_VEHICLE_WIDGET_PANEL,
00054   BUILD_VEHICLE_WIDGET_BUILD,
00055   BUILD_VEHICLE_WIDGET_BUILD_SEL,
00056   BUILD_VEHICLE_WIDGET_RENAME,
00057   BUILD_VEHICLE_WIDGET_END
00058 };
00059 
00060 static const NWidgetPart _nested_build_vehicle_widgets[] = {
00061   NWidget(NWID_HORIZONTAL),
00062     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
00063     NWidget(WWT_CAPTION, COLOUR_GREY, BUILD_VEHICLE_WIDGET_CAPTION), SetDataTip(STR_WHITE_STRING, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
00064     NWidget(WWT_SHADEBOX, COLOUR_GREY),
00065     NWidget(WWT_STICKYBOX, COLOUR_GREY),
00066   EndContainer(),
00067   NWidget(WWT_PANEL, COLOUR_GREY),
00068     NWidget(NWID_HORIZONTAL),
00069       NWidget(NWID_VERTICAL),
00070         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0),
00071         NWidget(NWID_SPACER), SetFill(1, 1),
00072       EndContainer(),
00073       NWidget(NWID_VERTICAL),
00074         NWidget(WWT_DROPDOWN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
00075         NWidget(WWT_DROPDOWN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
00076       EndContainer(),
00077     EndContainer(),
00078   EndContainer(),
00079   /* Vehicle list. */
00080   NWidget(NWID_HORIZONTAL),
00081     NWidget(WWT_MATRIX, COLOUR_GREY, BUILD_VEHICLE_WIDGET_LIST), SetResize(1, 1), SetFill(1, 0), SetDataTip(0x101, STR_NULL), SetScrollbar(BUILD_VEHICLE_WIDGET_SCROLLBAR),
00082     NWidget(NWID_VSCROLLBAR, COLOUR_GREY, BUILD_VEHICLE_WIDGET_SCROLLBAR),
00083   EndContainer(),
00084   /* Panel with details. */
00085   NWidget(WWT_PANEL, COLOUR_GREY, BUILD_VEHICLE_WIDGET_PANEL), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
00086   /* Build/rename buttons, resize button. */
00087   NWidget(NWID_HORIZONTAL),
00088     NWidget(NWID_SELECTION, INVALID_COLOUR, BUILD_VEHICLE_WIDGET_BUILD_SEL),
00089       NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_BUILD), SetResize(1, 0), SetFill(1, 0),
00090     EndContainer(),
00091     NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, BUILD_VEHICLE_WIDGET_RENAME), SetResize(1, 0), SetFill(1, 0),
00092     NWidget(WWT_RESIZEBOX, COLOUR_GREY),
00093   EndContainer(),
00094 };
00095 
00097 static const CargoID CF_ANY  = CT_NO_REFIT; 
00098 static const CargoID CF_NONE = CT_INVALID;  
00099 
00100 static bool _internal_sort_order;           
00101 static byte _last_sort_criteria[]      = {0, 0, 0, 0};
00102 static bool _last_sort_order[]         = {false, false, false, false};
00103 static CargoID _last_filter_criteria[] = {CF_ANY, CF_ANY, CF_ANY, CF_ANY};
00104 
00111 static int CDECL EngineNumberSorter(const EngineID *a, const EngineID *b)
00112 {
00113   int r = ListPositionOfEngine(*a) - ListPositionOfEngine(*b);
00114 
00115   return _internal_sort_order ? -r : r;
00116 }
00117 
00124 static int CDECL EngineIntroDateSorter(const EngineID *a, const EngineID *b)
00125 {
00126   const int va = Engine::Get(*a)->intro_date;
00127   const int vb = Engine::Get(*b)->intro_date;
00128   const int r = va - vb;
00129 
00130   /* Use EngineID to sort instead since we want consistent sorting */
00131   if (r == 0) return EngineNumberSorter(a, b);
00132   return _internal_sort_order ? -r : r;
00133 }
00134 
00141 static int CDECL EngineNameSorter(const EngineID *a, const EngineID *b)
00142 {
00143   static EngineID last_engine[2] = { INVALID_ENGINE, INVALID_ENGINE };
00144   static char     last_name[2][64] = { "\0", "\0" };
00145 
00146   const EngineID va = *a;
00147   const EngineID vb = *b;
00148 
00149   if (va != last_engine[0]) {
00150     last_engine[0] = va;
00151     SetDParam(0, va);
00152     GetString(last_name[0], STR_ENGINE_NAME, lastof(last_name[0]));
00153   }
00154 
00155   if (vb != last_engine[1]) {
00156     last_engine[1] = vb;
00157     SetDParam(0, vb);
00158     GetString(last_name[1], STR_ENGINE_NAME, lastof(last_name[1]));
00159   }
00160 
00161   int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
00162 
00163   /* Use EngineID to sort instead since we want consistent sorting */
00164   if (r == 0) return EngineNumberSorter(a, b);
00165   return _internal_sort_order ? -r : r;
00166 }
00167 
00174 static int CDECL EngineReliabilitySorter(const EngineID *a, const EngineID *b)
00175 {
00176   const int va = Engine::Get(*a)->reliability;
00177   const int vb = Engine::Get(*b)->reliability;
00178   const int r = va - vb;
00179 
00180   /* Use EngineID to sort instead since we want consistent sorting */
00181   if (r == 0) return EngineNumberSorter(a, b);
00182   return _internal_sort_order ? -r : r;
00183 }
00184 
00191 static int CDECL EngineCostSorter(const EngineID *a, const EngineID *b)
00192 {
00193   Money va = Engine::Get(*a)->GetCost();
00194   Money vb = Engine::Get(*b)->GetCost();
00195   int r = ClampToI32(va - vb);
00196 
00197   /* Use EngineID to sort instead since we want consistent sorting */
00198   if (r == 0) return EngineNumberSorter(a, b);
00199   return _internal_sort_order ? -r : r;
00200 }
00201 
00208 static int CDECL EngineSpeedSorter(const EngineID *a, const EngineID *b)
00209 {
00210   int va = Engine::Get(*a)->GetDisplayMaxSpeed();
00211   int vb = Engine::Get(*b)->GetDisplayMaxSpeed();
00212   int r = va - vb;
00213 
00214   /* Use EngineID to sort instead since we want consistent sorting */
00215   if (r == 0) return EngineNumberSorter(a, b);
00216   return _internal_sort_order ? -r : r;
00217 }
00218 
00225 static int CDECL EnginePowerSorter(const EngineID *a, const EngineID *b)
00226 {
00227   int va = Engine::Get(*a)->GetPower();
00228   int vb = Engine::Get(*b)->GetPower();
00229   int r = va - vb;
00230 
00231   /* Use EngineID to sort instead since we want consistent sorting */
00232   if (r == 0) return EngineNumberSorter(a, b);
00233   return _internal_sort_order ? -r : r;
00234 }
00235 
00242 static int CDECL EngineTractiveEffortSorter(const EngineID *a, const EngineID *b)
00243 {
00244   int va = Engine::Get(*a)->GetDisplayMaxTractiveEffort();
00245   int vb = Engine::Get(*b)->GetDisplayMaxTractiveEffort();
00246   int r = va - vb;
00247 
00248   /* Use EngineID to sort instead since we want consistent sorting */
00249   if (r == 0) return EngineNumberSorter(a, b);
00250   return _internal_sort_order ? -r : r;
00251 }
00252 
00259 static int CDECL EngineRunningCostSorter(const EngineID *a, const EngineID *b)
00260 {
00261   Money va = Engine::Get(*a)->GetRunningCost();
00262   Money vb = Engine::Get(*b)->GetRunningCost();
00263   int r = ClampToI32(va - vb);
00264 
00265   /* Use EngineID to sort instead since we want consistent sorting */
00266   if (r == 0) return EngineNumberSorter(a, b);
00267   return _internal_sort_order ? -r : r;
00268 }
00269 
00276 static int CDECL EnginePowerVsRunningCostSorter(const EngineID *a, const EngineID *b)
00277 {
00278   const Engine *e_a = Engine::Get(*a);
00279   const Engine *e_b = Engine::Get(*b);
00280 
00281   /* Here we are using a few tricks to get the right sort.
00282    * We want power/running cost, but since we usually got higher running cost than power and we store the result in an int,
00283    * we will actually calculate cunning cost/power (to make it more than 1).
00284    * Because of this, the return value have to be reversed as well and we return b - a instead of a - b.
00285    * Another thing is that both power and running costs should be doubled for multiheaded engines.
00286    * Since it would be multipling with 2 in both numerator and denumerator, it will even themselves out and we skip checking for multiheaded. */
00287   Money va = (e_a->GetRunningCost()) / max(1U, (uint)e_a->GetPower());
00288   Money vb = (e_b->GetRunningCost()) / max(1U, (uint)e_b->GetPower());
00289   int r = ClampToI32(vb - va);
00290 
00291   /* Use EngineID to sort instead since we want consistent sorting */
00292   if (r == 0) return EngineNumberSorter(a, b);
00293   return _internal_sort_order ? -r : r;
00294 }
00295 
00296 /* Train sorting functions */
00297 
00304 static int CDECL TrainEngineCapacitySorter(const EngineID *a, const EngineID *b)
00305 {
00306   const RailVehicleInfo *rvi_a = RailVehInfo(*a);
00307   const RailVehicleInfo *rvi_b = RailVehInfo(*b);
00308 
00309   int va = GetTotalCapacityOfArticulatedParts(*a) * (rvi_a->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
00310   int vb = GetTotalCapacityOfArticulatedParts(*b) * (rvi_b->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1);
00311   int r = va - vb;
00312 
00313   /* Use EngineID to sort instead since we want consistent sorting */
00314   if (r == 0) return EngineNumberSorter(a, b);
00315   return _internal_sort_order ? -r : r;
00316 }
00317 
00324 static int CDECL TrainEnginesThenWagonsSorter(const EngineID *a, const EngineID *b)
00325 {
00326   int val_a = (RailVehInfo(*a)->railveh_type == RAILVEH_WAGON ? 1 : 0);
00327   int val_b = (RailVehInfo(*b)->railveh_type == RAILVEH_WAGON ? 1 : 0);
00328   int r = val_a - val_b;
00329 
00330   /* Use EngineID to sort instead since we want consistent sorting */
00331   if (r == 0) return EngineNumberSorter(a, b);
00332   return _internal_sort_order ? -r : r;
00333 }
00334 
00335 /* Road vehicle sorting functions */
00336 
00343 static int CDECL RoadVehEngineCapacitySorter(const EngineID *a, const EngineID *b)
00344 {
00345   int va = GetTotalCapacityOfArticulatedParts(*a);
00346   int vb = GetTotalCapacityOfArticulatedParts(*b);
00347   int r = va - vb;
00348 
00349   /* Use EngineID to sort instead since we want consistent sorting */
00350   if (r == 0) return EngineNumberSorter(a, b);
00351   return _internal_sort_order ? -r : r;
00352 }
00353 
00354 /* Ship vehicle sorting functions */
00355 
00362 static int CDECL ShipEngineCapacitySorter(const EngineID *a, const EngineID *b)
00363 {
00364   const Engine *e_a = Engine::Get(*a);
00365   const Engine *e_b = Engine::Get(*b);
00366 
00367   int va = e_a->GetDisplayDefaultCapacity();
00368   int vb = e_b->GetDisplayDefaultCapacity();
00369   int r = va - vb;
00370 
00371   /* Use EngineID to sort instead since we want consistent sorting */
00372   if (r == 0) return EngineNumberSorter(a, b);
00373   return _internal_sort_order ? -r : r;
00374 }
00375 
00376 /* Aircraft sorting functions */
00377 
00384 static int CDECL AircraftEngineCargoSorter(const EngineID *a, const EngineID *b)
00385 {
00386   const Engine *e_a = Engine::Get(*a);
00387   const Engine *e_b = Engine::Get(*b);
00388 
00389   uint16 mail_a, mail_b;
00390   int va = e_a->GetDisplayDefaultCapacity(&mail_a);
00391   int vb = e_b->GetDisplayDefaultCapacity(&mail_b);
00392   int r = va - vb;
00393 
00394   if (r == 0) {
00395     /* The planes have the same passenger capacity. Check mail capacity instead */
00396     r = mail_a - mail_b;
00397 
00398     if (r == 0) {
00399       /* Use EngineID to sort instead since we want consistent sorting */
00400       return EngineNumberSorter(a, b);
00401     }
00402   }
00403   return _internal_sort_order ? -r : r;
00404 }
00405 
00406 static EngList_SortTypeFunction * const _sorter[][11] = {{
00407   /* Trains */
00408   &EngineNumberSorter,
00409   &EngineCostSorter,
00410   &EngineSpeedSorter,
00411   &EnginePowerSorter,
00412   &EngineTractiveEffortSorter,
00413   &EngineIntroDateSorter,
00414   &EngineNameSorter,
00415   &EngineRunningCostSorter,
00416   &EnginePowerVsRunningCostSorter,
00417   &EngineReliabilitySorter,
00418   &TrainEngineCapacitySorter,
00419 }, {
00420   /* Road vehicles */
00421   &EngineNumberSorter,
00422   &EngineCostSorter,
00423   &EngineSpeedSorter,
00424   &EnginePowerSorter,
00425   &EngineTractiveEffortSorter,
00426   &EngineIntroDateSorter,
00427   &EngineNameSorter,
00428   &EngineRunningCostSorter,
00429   &EnginePowerVsRunningCostSorter,
00430   &EngineReliabilitySorter,
00431   &RoadVehEngineCapacitySorter,
00432 }, {
00433   /* Ships */
00434   &EngineNumberSorter,
00435   &EngineCostSorter,
00436   &EngineSpeedSorter,
00437   &EngineIntroDateSorter,
00438   &EngineNameSorter,
00439   &EngineRunningCostSorter,
00440   &EngineReliabilitySorter,
00441   &ShipEngineCapacitySorter,
00442 }, {
00443   /* Aircraft */
00444   &EngineNumberSorter,
00445   &EngineCostSorter,
00446   &EngineSpeedSorter,
00447   &EngineIntroDateSorter,
00448   &EngineNameSorter,
00449   &EngineRunningCostSorter,
00450   &EngineReliabilitySorter,
00451   &AircraftEngineCargoSorter,
00452 }};
00453 
00454 static const StringID _sort_listing[][12] = {{
00455   /* Trains */
00456   STR_SORT_BY_ENGINE_ID,
00457   STR_SORT_BY_COST,
00458   STR_SORT_BY_MAX_SPEED,
00459   STR_SORT_BY_POWER,
00460   STR_SORT_BY_TRACTIVE_EFFORT,
00461   STR_SORT_BY_INTRO_DATE,
00462   STR_SORT_BY_NAME,
00463   STR_SORT_BY_RUNNING_COST,
00464   STR_SORT_BY_POWER_VS_RUNNING_COST,
00465   STR_SORT_BY_RELIABILITY,
00466   STR_SORT_BY_CARGO_CAPACITY,
00467   INVALID_STRING_ID
00468 }, {
00469   /* Road vehicles */
00470   STR_SORT_BY_ENGINE_ID,
00471   STR_SORT_BY_COST,
00472   STR_SORT_BY_MAX_SPEED,
00473   STR_SORT_BY_POWER,
00474   STR_SORT_BY_TRACTIVE_EFFORT,
00475   STR_SORT_BY_INTRO_DATE,
00476   STR_SORT_BY_NAME,
00477   STR_SORT_BY_RUNNING_COST,
00478   STR_SORT_BY_POWER_VS_RUNNING_COST,
00479   STR_SORT_BY_RELIABILITY,
00480   STR_SORT_BY_CARGO_CAPACITY,
00481   INVALID_STRING_ID
00482 }, {
00483   /* Ships */
00484   STR_SORT_BY_ENGINE_ID,
00485   STR_SORT_BY_COST,
00486   STR_SORT_BY_MAX_SPEED,
00487   STR_SORT_BY_INTRO_DATE,
00488   STR_SORT_BY_NAME,
00489   STR_SORT_BY_RUNNING_COST,
00490   STR_SORT_BY_RELIABILITY,
00491   STR_SORT_BY_CARGO_CAPACITY,
00492   INVALID_STRING_ID
00493 }, {
00494   /* Aircraft */
00495   STR_SORT_BY_ENGINE_ID,
00496   STR_SORT_BY_COST,
00497   STR_SORT_BY_MAX_SPEED,
00498   STR_SORT_BY_INTRO_DATE,
00499   STR_SORT_BY_NAME,
00500   STR_SORT_BY_RUNNING_COST,
00501   STR_SORT_BY_RELIABILITY,
00502   STR_SORT_BY_CARGO_CAPACITY,
00503   INVALID_STRING_ID
00504 }};
00505 
00507 static bool CDECL CargoFilter(const EngineID *eid, const CargoID cid)
00508 {
00509   if (cid == CF_ANY) return true;
00510   uint32 refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true);
00511   return (cid == CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
00512 }
00513 
00514 static GUIEngineList::FilterFunction * const _filter_funcs[] = {
00515   &CargoFilter,
00516 };
00517 
00518 static int DrawCargoCapacityInfo(int left, int right, int y, EngineID engine, bool refittable)
00519 {
00520   CargoArray cap = GetCapacityOfArticulatedParts(engine);
00521 
00522   for (CargoID c = 0; c < NUM_CARGO; c++) {
00523     if (cap[c] == 0) continue;
00524 
00525     SetDParam(0, c);
00526     SetDParam(1, cap[c]);
00527     SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
00528     DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00529     y += FONT_HEIGHT_NORMAL;
00530 
00531     /* Only show as refittable once */
00532     refittable = false;
00533   }
00534 
00535   return y;
00536 }
00537 
00538 /* Draw rail wagon specific details */
00539 static int DrawRailWagonPurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi)
00540 {
00541   const Engine *e = Engine::Get(engine_number);
00542 
00543   /* Purchase cost */
00544   SetDParam(0, e->GetCost());
00545   DrawString(left, right, y, STR_PURCHASE_INFO_COST);
00546   y += FONT_HEIGHT_NORMAL;
00547 
00548   /* Wagon weight - (including cargo) */
00549   uint weight = e->GetDisplayWeight();
00550   SetDParam(0, weight);
00551   uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(e->GetDefaultCargoType())->weight * GetTotalCapacityOfArticulatedParts(engine_number) / 16 : 0);
00552   SetDParam(1, cargo_weight + weight);
00553   DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
00554   y += FONT_HEIGHT_NORMAL;
00555 
00556   /* Wagon speed limit, displayed if above zero */
00557   if (_settings_game.vehicle.wagon_speed_limits) {
00558     uint max_speed = e->GetDisplayMaxSpeed();
00559     if (max_speed > 0) {
00560       SetDParam(0, max_speed);
00561       DrawString(left, right, y, STR_PURCHASE_INFO_SPEED);
00562       y += FONT_HEIGHT_NORMAL;
00563     }
00564   }
00565 
00566   /* Running cost */
00567   if (rvi->running_cost_class != INVALID_PRICE) {
00568     SetDParam(0, e->GetRunningCost());
00569     DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00570     y += FONT_HEIGHT_NORMAL;
00571   }
00572 
00573   return y;
00574 }
00575 
00576 /* Draw locomotive specific details */
00577 static int DrawRailEnginePurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi)
00578 {
00579   const Engine *e = Engine::Get(engine_number);
00580 
00581   /* Purchase Cost - Engine weight */
00582   SetDParam(0, e->GetCost());
00583   SetDParam(1, e->GetDisplayWeight());
00584   DrawString(left, right, y, STR_PURCHASE_INFO_COST_WEIGHT);
00585   y += FONT_HEIGHT_NORMAL;
00586 
00587   /* Max speed - Engine power */
00588   SetDParam(0, e->GetDisplayMaxSpeed());
00589   SetDParam(1, e->GetPower());
00590   DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_POWER);
00591   y += FONT_HEIGHT_NORMAL;
00592 
00593   /* Max tractive effort - not applicable if old acceleration or maglev */
00594   if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL && GetRailTypeInfo(rvi->railtype)->acceleration_type != 2) {
00595     SetDParam(0, e->GetDisplayMaxTractiveEffort());
00596     DrawString(left, right, y, STR_PURCHASE_INFO_MAX_TE);
00597     y += FONT_HEIGHT_NORMAL;
00598   }
00599 
00600   /* Running cost */
00601   if (rvi->running_cost_class != INVALID_PRICE) {
00602     SetDParam(0, e->GetRunningCost());
00603     DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00604     y += FONT_HEIGHT_NORMAL;
00605   }
00606 
00607   /* Powered wagons power - Powered wagons extra weight */
00608   if (rvi->pow_wag_power != 0) {
00609     SetDParam(0, rvi->pow_wag_power);
00610     SetDParam(1, rvi->pow_wag_weight);
00611     DrawString(left, right, y, STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT);
00612     y += FONT_HEIGHT_NORMAL;
00613   }
00614 
00615   return y;
00616 }
00617 
00618 /* Draw road vehicle specific details */
00619 static int DrawRoadVehPurchaseInfo(int left, int right, int y, EngineID engine_number)
00620 {
00621   const Engine *e = Engine::Get(engine_number);
00622 
00623   if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) {
00624     /* Purchase Cost */
00625     SetDParam(0, e->GetCost());
00626     DrawString(left, right, y, STR_PURCHASE_INFO_COST);
00627     y += FONT_HEIGHT_NORMAL;
00628 
00629     /* Road vehicle weight - (including cargo) */
00630     int16 weight = e->GetDisplayWeight();
00631     SetDParam(0, weight);
00632     uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(e->GetDefaultCargoType())->weight * GetTotalCapacityOfArticulatedParts(engine_number) / 16 : 0);
00633     SetDParam(1, cargo_weight + weight);
00634     DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
00635     y += FONT_HEIGHT_NORMAL;
00636 
00637     /* Max speed - Engine power */
00638     SetDParam(0, e->GetDisplayMaxSpeed());
00639     SetDParam(1, e->GetPower());
00640     DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_POWER);
00641     y += FONT_HEIGHT_NORMAL;
00642 
00643     /* Max tractive effort */
00644     SetDParam(0, e->GetDisplayMaxTractiveEffort());
00645     DrawString(left, right, y, STR_PURCHASE_INFO_MAX_TE);
00646     y += FONT_HEIGHT_NORMAL;
00647   } else {
00648     /* Purchase cost - Max speed */
00649     SetDParam(0, e->GetCost());
00650     SetDParam(1, e->GetDisplayMaxSpeed());
00651     DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
00652     y += FONT_HEIGHT_NORMAL;
00653   }
00654 
00655   /* Running cost */
00656   SetDParam(0, e->GetRunningCost());
00657   DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00658   y += FONT_HEIGHT_NORMAL;
00659 
00660   return y;
00661 }
00662 
00663 /* Draw ship specific details */
00664 static int DrawShipPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable)
00665 {
00666   const Engine *e = Engine::Get(engine_number);
00667 
00668   /* Purchase cost - Max speed */
00669   uint raw_speed = e->GetDisplayMaxSpeed();
00670   uint ocean_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, true);
00671   uint canal_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, false);
00672 
00673   SetDParam(0, e->GetCost());
00674   if (ocean_speed == canal_speed) {
00675     SetDParam(1, ocean_speed);
00676     DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
00677     y += FONT_HEIGHT_NORMAL;
00678   } else {
00679     DrawString(left, right, y, STR_PURCHASE_INFO_COST);
00680     y += FONT_HEIGHT_NORMAL;
00681 
00682     SetDParam(0, ocean_speed);
00683     DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_OCEAN);
00684     y += FONT_HEIGHT_NORMAL;
00685 
00686     SetDParam(0, canal_speed);
00687     DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_CANAL);
00688     y += FONT_HEIGHT_NORMAL;
00689   }
00690 
00691   /* Cargo type + capacity */
00692   SetDParam(0, e->GetDefaultCargoType());
00693   SetDParam(1, e->GetDisplayDefaultCapacity());
00694   SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
00695   DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00696   y += FONT_HEIGHT_NORMAL;
00697 
00698   /* Running cost */
00699   SetDParam(0, e->GetRunningCost());
00700   DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00701   y += FONT_HEIGHT_NORMAL;
00702 
00703   return y;
00704 }
00705 
00706 /* Draw aircraft specific details */
00707 static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable)
00708 {
00709   const Engine *e = Engine::Get(engine_number);
00710   CargoID cargo = e->GetDefaultCargoType();
00711 
00712   /* Purchase cost - Max speed */
00713   SetDParam(0, e->GetCost());
00714   SetDParam(1, e->GetDisplayMaxSpeed());
00715   DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
00716   y += FONT_HEIGHT_NORMAL;
00717 
00718   /* Cargo capacity */
00719   uint16 mail_capacity;
00720   uint capacity = e->GetDisplayDefaultCapacity(&mail_capacity);
00721   if (mail_capacity > 0) {
00722     SetDParam(0, cargo);
00723     SetDParam(1, capacity);
00724     SetDParam(2, CT_MAIL);
00725     SetDParam(3, mail_capacity);
00726     DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY);
00727   } else {
00728     /* Note, if the default capacity is selected by the refit capacity
00729      * callback, then the capacity shown is likely to be incorrect. */
00730     SetDParam(0, cargo);
00731     SetDParam(1, capacity);
00732     SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
00733     DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00734   }
00735   y += FONT_HEIGHT_NORMAL;
00736 
00737   /* Running cost */
00738   SetDParam(0, e->GetRunningCost());
00739   DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00740   y += FONT_HEIGHT_NORMAL;
00741 
00742   return y;
00743 }
00744 
00753 static uint ShowAdditionalText(int left, int right, int y, EngineID engine)
00754 {
00755   uint16 callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, NULL);
00756   if (callback == CALLBACK_FAILED || callback == 0x400) return y;
00757   if (callback > 0x400) {
00758     ErrorUnknownCallbackResult(Engine::Get(engine)->GetGRFID(), CBID_VEHICLE_ADDITIONAL_TEXT, callback);
00759     return y;
00760   }
00761 
00762   StartTextRefStackUsage(6);
00763   uint result = DrawStringMultiLine(left, right, y, INT32_MAX, GetGRFStringID(Engine::Get(engine)->GetGRFID(), 0xD000 + callback), TC_BLACK);
00764   StopTextRefStackUsage();
00765   return result;
00766 }
00767 
00774 int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number)
00775 {
00776   const Engine *e = Engine::Get(engine_number);
00777   YearMonthDay ymd;
00778   ConvertDateToYMD(e->intro_date, &ymd);
00779   bool refittable = IsArticulatedVehicleRefittable(engine_number);
00780   bool articulated_cargo = false;
00781 
00782   switch (e->type) {
00783     default: NOT_REACHED();
00784     case VEH_TRAIN:
00785       if (e->u.rail.railveh_type == RAILVEH_WAGON) {
00786         y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->u.rail);
00787       } else {
00788         y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->u.rail);
00789       }
00790       articulated_cargo = true;
00791       break;
00792 
00793     case VEH_ROAD:
00794       y = DrawRoadVehPurchaseInfo(left, right, y, engine_number);
00795       articulated_cargo = true;
00796       break;
00797 
00798     case VEH_SHIP:
00799       y = DrawShipPurchaseInfo(left, right, y, engine_number, refittable);
00800       break;
00801 
00802     case VEH_AIRCRAFT:
00803       y = DrawAircraftPurchaseInfo(left, right, y, engine_number, refittable);
00804       break;
00805   }
00806 
00807   if (articulated_cargo) {
00808     /* Cargo type + capacity, or N/A */
00809     int new_y = DrawCargoCapacityInfo(left, right, y, engine_number, refittable);
00810 
00811     if (new_y == y) {
00812       SetDParam(0, CT_INVALID);
00813       SetDParam(2, STR_EMPTY);
00814       DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00815       y += FONT_HEIGHT_NORMAL;
00816     } else {
00817       y = new_y;
00818     }
00819   }
00820 
00821   /* Draw details that apply to all types except rail wagons. */
00822   if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) {
00823     /* Design date - Life length */
00824     SetDParam(0, ymd.year);
00825     SetDParam(1, e->GetLifeLengthInDays() / DAYS_IN_LEAP_YEAR);
00826     DrawString(left, right, y, STR_PURCHASE_INFO_DESIGNED_LIFE);
00827     y += FONT_HEIGHT_NORMAL;
00828 
00829     /* Reliability */
00830     SetDParam(0, ToPercent16(e->reliability));
00831     DrawString(left, right, y, STR_PURCHASE_INFO_RELIABILITY);
00832     y += FONT_HEIGHT_NORMAL;
00833   }
00834 
00835   /* Additional text from NewGRF */
00836   y = ShowAdditionalText(left, right, y, engine_number);
00837   if (refittable) y = ShowRefitOptionsList(left, right, y, engine_number);
00838 
00839   return y;
00840 }
00841 
00855 void DrawEngineList(VehicleType type, int l, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group)
00856 {
00857   static const int sprite_widths[]  = { 60, 60, 76, 67 };
00858   static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
00859 
00860   /* Obligatory sanity checks! */
00861   assert((uint)type < lengthof(sprite_widths));
00862   assert_compile(lengthof(sprite_y_offsets) == lengthof(sprite_widths));
00863   assert(max <= eng_list->Length());
00864 
00865   bool rtl = _current_text_dir == TD_RTL;
00866   int step_size = GetEngineListHeight(type);
00867   int sprite_width = sprite_widths[type];
00868 
00869   int sprite_x        = (rtl ? r - sprite_width / 2 : l + sprite_width / 2) - 1;
00870   int sprite_y_offset = sprite_y_offsets[type] + step_size / 2;
00871 
00872   int text_left  = l + (rtl ? WD_FRAMERECT_LEFT : sprite_width);
00873   int text_right = r - (rtl ? sprite_width : WD_FRAMERECT_RIGHT);
00874 
00875   int normal_text_y_offset = (step_size - FONT_HEIGHT_NORMAL) / 2;
00876   int small_text_y_offset  = step_size - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 1;
00877 
00878   for (; min < max; min++, y += step_size) {
00879     const EngineID engine = (*eng_list)[min];
00880     /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
00881     const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine);
00882 
00883     SetDParam(0, engine);
00884     DrawString(text_left, text_right, y + normal_text_y_offset, STR_ENGINE_NAME, engine == selected_id ? TC_WHITE : TC_BLACK);
00885     DrawVehicleEngine(l, r, sprite_x, y + sprite_y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company), EIT_PURCHASE);
00886     if (show_count) {
00887       SetDParam(0, num_engines);
00888       DrawString(text_left, text_right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT);
00889     }
00890   }
00891 }
00892 
00893 
00894 struct BuildVehicleWindow : Window {
00895   VehicleType vehicle_type;
00896   union {
00897     RailTypeByte railtype;
00898     RoadTypes roadtypes;
00899   } filter;
00900   bool descending_sort_order;
00901   byte sort_criteria;
00902   bool listview_mode;
00903   EngineID sel_engine;
00904   EngineID rename_engine;
00905   GUIEngineList eng_list;
00906   CargoID cargo_filter[NUM_CARGO + 2];        
00907   StringID cargo_filter_texts[NUM_CARGO + 3]; 
00908   byte cargo_filter_criteria;                 
00909   int details_height;                         
00910   Scrollbar *vscroll;
00911 
00912   BuildVehicleWindow(const WindowDesc *desc, TileIndex tile, VehicleType type) : Window()
00913   {
00914     this->vehicle_type = type;
00915     this->window_number = tile == INVALID_TILE ? (int)type : tile;
00916 
00917     this->sel_engine      = INVALID_ENGINE;
00918 
00919     this->sort_criteria         = _last_sort_criteria[type];
00920     this->descending_sort_order = _last_sort_order[type];
00921 
00922     switch (type) {
00923       default: NOT_REACHED();
00924       case VEH_TRAIN:
00925         this->filter.railtype = (tile == INVALID_TILE) ? RAILTYPE_END : GetRailType(tile);
00926         break;
00927       case VEH_ROAD:
00928         this->filter.roadtypes = (tile == INVALID_TILE) ? ROADTYPES_ALL : GetRoadTypes(tile);
00929       case VEH_SHIP:
00930       case VEH_AIRCRAFT:
00931         break;
00932     }
00933 
00934     this->listview_mode = (this->window_number <= VEH_END);
00935 
00936     this->CreateNestedTree(desc);
00937 
00938     this->vscroll = this->GetScrollbar(BUILD_VEHICLE_WIDGET_SCROLLBAR);
00939 
00940     /* If we are just viewing the list of vehicles, we do not need the Build button.
00941      * So we just hide it, and enlarge the Rename buton by the now vacant place. */
00942     if (this->listview_mode) this->GetWidget<NWidgetStacked>(BUILD_VEHICLE_WIDGET_BUILD_SEL)->SetDisplayedPlane(SZSP_NONE);
00943 
00944     NWidgetCore *widget = this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_LIST);
00945     widget->tool_tip = STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type;
00946 
00947     widget = this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_BUILD);
00948     widget->widget_data = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + type;
00949     widget->tool_tip    = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP + type;
00950 
00951     widget = this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_RENAME);
00952     widget->widget_data = STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + type;
00953     widget->tool_tip    = STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + type;
00954 
00955     this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
00956 
00957     this->FinishInitNested(desc, tile == INVALID_TILE ? (int)type : tile);
00958 
00959     this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
00960 
00961     this->eng_list.ForceRebuild();
00962     this->GenerateBuildList(); // generate the list, since we need it in the next line
00963     /* Select the first engine in the list as default when opening the window */
00964     if (this->eng_list.Length() > 0) this->sel_engine = this->eng_list[0];
00965   }
00966 
00968   void SetCargoFilterArray()
00969   {
00970     uint filter_items = 0;
00971 
00972     /* Add item for disabling filtering. */
00973     this->cargo_filter[filter_items] = CF_ANY;
00974     this->cargo_filter_texts[filter_items] = STR_PURCHASE_INFO_ALL_TYPES;
00975     filter_items++;
00976 
00977     /* Add item for vehicles not carrying anything, e.g. train engines.
00978      * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
00979     if (this->vehicle_type == VEH_TRAIN) {
00980       this->cargo_filter[filter_items] = CF_NONE;
00981       this->cargo_filter_texts[filter_items] = STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE;
00982       filter_items++;
00983     }
00984 
00985     /* Collect available cargo types for filtering. */
00986     const CargoSpec *cs;
00987     FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
00988       this->cargo_filter[filter_items] = cs->Index();
00989       this->cargo_filter_texts[filter_items] = cs->name;
00990       filter_items++;
00991     }
00992 
00993     /* Terminate the filter list. */
00994     this->cargo_filter_texts[filter_items] = INVALID_STRING_ID;
00995 
00996     /* If not found, the cargo criteria will be set to all cargos. */
00997     this->cargo_filter_criteria = 0;
00998 
00999     /* Find the last cargo filter criteria. */
01000     for (uint i = 0; i < filter_items; i++) {
01001       if (this->cargo_filter[i] == _last_filter_criteria[this->vehicle_type]) {
01002         this->cargo_filter_criteria = i;
01003         break;
01004       }
01005     }
01006 
01007     this->eng_list.SetFilterFuncs(_filter_funcs);
01008     this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
01009   }
01010 
01011   void OnInit()
01012   {
01013     this->SetCargoFilterArray();
01014   }
01015 
01017   void FilterEngineList()
01018   {
01019     this->eng_list.Filter(this->cargo_filter[this->cargo_filter_criteria]);
01020     if (0 == this->eng_list.Length()) { // no engine passed through the filter, invalidate the previously selected engine
01021       this->sel_engine = INVALID_ENGINE;
01022     } else if (!this->eng_list.Contains(this->sel_engine)) { // previously selected engine didn't pass the filter, select the first engine of the list
01023       this->sel_engine = this->eng_list[0];
01024     }
01025   }
01026 
01028   bool FilterSingleEngine(EngineID eid)
01029   {
01030     CargoID filter_type = this->cargo_filter[this->cargo_filter_criteria];
01031     return (filter_type == CF_ANY || CargoFilter(&eid, filter_type));
01032   }
01033 
01034   /* Figure out what train EngineIDs to put in the list */
01035   void GenerateBuildTrainList()
01036   {
01037     EngineID sel_id = INVALID_ENGINE;
01038     int num_engines = 0;
01039     int num_wagons  = 0;
01040 
01041     this->filter.railtype = (this->listview_mode) ? RAILTYPE_END : GetRailType(this->window_number);
01042 
01043     this->eng_list.Clear();
01044 
01045     /* Make list of all available train engines and wagons.
01046      * Also check to see if the previously selected engine is still available,
01047      * and if not, reset selection to INVALID_ENGINE. This could be the case
01048      * when engines become obsolete and are removed */
01049     const Engine *e;
01050     FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
01051       EngineID eid = e->index;
01052       const RailVehicleInfo *rvi = &e->u.rail;
01053 
01054       if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
01055       if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
01056 
01057       /* Filter now! So num_engines and num_wagons is valid */
01058       if (!FilterSingleEngine(eid)) continue;
01059 
01060       *this->eng_list.Append() = eid;
01061 
01062       if (rvi->railveh_type != RAILVEH_WAGON) {
01063         num_engines++;
01064       } else {
01065         num_wagons++;
01066       }
01067 
01068       if (eid == this->sel_engine) sel_id = eid;
01069     }
01070 
01071     this->sel_engine = sel_id;
01072 
01073     /* make engines first, and then wagons, sorted by ListPositionOfEngine() */
01074     _internal_sort_order = false;
01075     EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter);
01076 
01077     /* and then sort engines */
01078     _internal_sort_order = this->descending_sort_order;
01079     EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], 0, num_engines);
01080 
01081     /* and finally sort wagons */
01082     EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], num_engines, num_wagons);
01083   }
01084 
01085   /* Figure out what road vehicle EngineIDs to put in the list */
01086   void GenerateBuildRoadVehList()
01087   {
01088     EngineID sel_id = INVALID_ENGINE;
01089 
01090     this->eng_list.Clear();
01091 
01092     const Engine *e;
01093     FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
01094       EngineID eid = e->index;
01095       if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
01096       if (!HasBit(this->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue;
01097       *this->eng_list.Append() = eid;
01098 
01099       if (eid == this->sel_engine) sel_id = eid;
01100     }
01101     this->sel_engine = sel_id;
01102   }
01103 
01104   /* Figure out what ship EngineIDs to put in the list */
01105   void GenerateBuildShipList()
01106   {
01107     EngineID sel_id = INVALID_ENGINE;
01108     this->eng_list.Clear();
01109 
01110     const Engine *e;
01111     FOR_ALL_ENGINES_OF_TYPE(e, VEH_SHIP) {
01112       EngineID eid = e->index;
01113       if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
01114       *this->eng_list.Append() = eid;
01115 
01116       if (eid == this->sel_engine) sel_id = eid;
01117     }
01118     this->sel_engine = sel_id;
01119   }
01120 
01121   /* Figure out what aircraft EngineIDs to put in the list */
01122   void GenerateBuildAircraftList()
01123   {
01124     EngineID sel_id = INVALID_ENGINE;
01125 
01126     this->eng_list.Clear();
01127 
01128     const Station *st = this->listview_mode ? NULL : Station::GetByTile(this->window_number);
01129 
01130     /* Make list of all available planes.
01131      * Also check to see if the previously selected plane is still available,
01132      * and if not, reset selection to INVALID_ENGINE. This could be the case
01133      * when planes become obsolete and are removed */
01134     const Engine *e;
01135     FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) {
01136       EngineID eid = e->index;
01137       if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_company)) continue;
01138       /* First VEH_END window_numbers are fake to allow a window open for all different types at once */
01139       if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue;
01140 
01141       *this->eng_list.Append() = eid;
01142       if (eid == this->sel_engine) sel_id = eid;
01143     }
01144 
01145     this->sel_engine = sel_id;
01146   }
01147 
01148   /* Generate the list of vehicles */
01149   void GenerateBuildList()
01150   {
01151     if (!this->eng_list.NeedRebuild()) return;
01152     switch (this->vehicle_type) {
01153       default: NOT_REACHED();
01154       case VEH_TRAIN:
01155         this->GenerateBuildTrainList();
01156         this->eng_list.Compact();
01157         this->eng_list.RebuildDone();
01158         return; // trains should not reach the last sorting
01159       case VEH_ROAD:
01160         this->GenerateBuildRoadVehList();
01161         break;
01162       case VEH_SHIP:
01163         this->GenerateBuildShipList();
01164         break;
01165       case VEH_AIRCRAFT:
01166         this->GenerateBuildAircraftList();
01167         break;
01168     }
01169 
01170     this->FilterEngineList();
01171 
01172     _internal_sort_order = this->descending_sort_order;
01173     EngList_Sort(&this->eng_list, _sorter[this->vehicle_type][this->sort_criteria]);
01174 
01175     this->eng_list.Compact();
01176     this->eng_list.RebuildDone();
01177   }
01178 
01179   void OnClick(Point pt, int widget, int click_count)
01180   {
01181     switch (widget) {
01182       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
01183         this->descending_sort_order ^= true;
01184         _last_sort_order[this->vehicle_type] = this->descending_sort_order;
01185         this->eng_list.ForceRebuild();
01186         this->SetDirty();
01187         break;
01188 
01189       case BUILD_VEHICLE_WIDGET_LIST: {
01190         uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, BUILD_VEHICLE_WIDGET_LIST);
01191         size_t num_items = this->eng_list.Length();
01192         this->sel_engine = (i < num_items) ? this->eng_list[i] : INVALID_ENGINE;
01193         this->SetDirty();
01194         if (click_count > 1 && !this->listview_mode) this->OnClick(pt, BUILD_VEHICLE_WIDGET_BUILD, 1);
01195         break;
01196       }
01197 
01198       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN: { // Select sorting criteria dropdown menu
01199         uint32 hidden_mask = 0;
01200         /* Disable sorting by power or tractive effort when the original acceleration model for road vehicles is being used. */
01201         if (this->vehicle_type == VEH_ROAD &&
01202             _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) {
01203           SetBit(hidden_mask, 3); // power
01204           SetBit(hidden_mask, 4); // tractive effort
01205           SetBit(hidden_mask, 8); // power by running costs
01206         }
01207         /* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
01208         if (this->vehicle_type == VEH_TRAIN &&
01209             _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
01210           SetBit(hidden_mask, 4); // tractive effort
01211         }
01212         ShowDropDownMenu(this, _sort_listing[this->vehicle_type], this->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, hidden_mask);
01213         break;
01214       }
01215 
01216       case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
01217         ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN, 0, 0);
01218         break;
01219 
01220       case BUILD_VEHICLE_WIDGET_BUILD: {
01221         EngineID sel_eng = this->sel_engine;
01222         if (sel_eng != INVALID_ENGINE) {
01223           CommandCallback *callback = (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildPrimaryVehicle;
01224           DoCommandP(this->window_number, sel_eng, 0, GetCmdBuildVeh(this->vehicle_type), callback);
01225         }
01226         break;
01227       }
01228 
01229       case BUILD_VEHICLE_WIDGET_RENAME: {
01230         EngineID sel_eng = this->sel_engine;
01231         if (sel_eng != INVALID_ENGINE) {
01232           this->rename_engine = sel_eng;
01233           SetDParam(0, sel_eng);
01234           ShowQueryString(STR_ENGINE_NAME, STR_QUERY_RENAME_TRAIN_TYPE_CAPTION + this->vehicle_type, MAX_LENGTH_ENGINE_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
01235         }
01236         break;
01237       }
01238     }
01239   }
01240 
01246   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01247   {
01248     if (!gui_scope) return;
01249     /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
01250     if (this->vehicle_type == VEH_ROAD &&
01251         _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL &&
01252         this->sort_criteria > 7) {
01253       this->sort_criteria = 0;
01254       _last_sort_criteria[VEH_ROAD] = 0;
01255     }
01256     this->eng_list.ForceRebuild();
01257   }
01258 
01259   virtual void SetStringParameters(int widget) const
01260   {
01261     switch (widget) {
01262       case BUILD_VEHICLE_WIDGET_CAPTION:
01263         if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
01264           const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
01265           SetDParam(0, rti->strings.build_caption);
01266         } else {
01267           SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
01268         }
01269         break;
01270 
01271       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:
01272         SetDParam(0, _sort_listing[this->vehicle_type][this->sort_criteria]);
01273         break;
01274 
01275       case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN:
01276         SetDParam(0, this->cargo_filter_texts[this->cargo_filter_criteria]);
01277     }
01278   }
01279 
01280   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01281   {
01282     switch (widget) {
01283       case BUILD_VEHICLE_WIDGET_LIST:
01284         resize->height = GetEngineListHeight(this->vehicle_type);
01285         size->height = 3 * resize->height;
01286         break;
01287 
01288       case BUILD_VEHICLE_WIDGET_PANEL:
01289         size->height = this->details_height;
01290         break;
01291 
01292       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING: {
01293         Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
01294         d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better.
01295         d.height += padding.height;
01296         *size = maxdim(*size, d);
01297         break;
01298       }
01299     }
01300   }
01301 
01302   virtual void DrawWidget(const Rect &r, int widget) const
01303   {
01304     switch (widget) {
01305       case BUILD_VEHICLE_WIDGET_LIST:
01306         DrawEngineList(this->vehicle_type, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, &this->eng_list, this->vscroll->GetPosition(), min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->eng_list.Length()), this->sel_engine, false, DEFAULT_GROUP);
01307         break;
01308 
01309       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
01310         this->DrawSortButtonState(BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
01311         break;
01312     }
01313   }
01314 
01315   virtual void OnPaint()
01316   {
01317     this->GenerateBuildList();
01318     this->vscroll->SetCount(this->eng_list.Length());
01319 
01320     this->DrawWidgets();
01321 
01322     if (!this->IsShaded()) {
01323       int needed_height = this->details_height;
01324       /* Draw details panels. */
01325       if (this->sel_engine != INVALID_ENGINE) {
01326         NWidgetBase *nwi = this->GetWidget<NWidgetBase>(BUILD_VEHICLE_WIDGET_PANEL);
01327         int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT,
01328             nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine);
01329         needed_height = max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM);
01330       }
01331       if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
01332         int resize = needed_height - this->details_height;
01333         this->details_height = needed_height;
01334         this->ReInit(0, resize);
01335         return;
01336       }
01337     }
01338   }
01339 
01340   virtual void OnQueryTextFinished(char *str)
01341   {
01342     if (str == NULL) return;
01343 
01344     DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type), NULL, str);
01345   }
01346 
01347   virtual void OnDropdownSelect(int widget, int index)
01348   {
01349     switch (widget) {
01350       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:
01351         if (this->sort_criteria != index) {
01352           this->sort_criteria = index;
01353           _last_sort_criteria[this->vehicle_type] = this->sort_criteria;
01354           this->eng_list.ForceRebuild();
01355         }
01356         break;
01357 
01358       case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
01359         if (this->cargo_filter_criteria != index) {
01360           this->cargo_filter_criteria = index;
01361           _last_filter_criteria[this->vehicle_type] = this->cargo_filter[this->cargo_filter_criteria];
01362           /* deactivate filter if criteria is 'Show All', activate it otherwise */
01363           this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
01364           this->eng_list.ForceRebuild();
01365         }
01366         break;
01367     }
01368     this->SetDirty();
01369   }
01370 
01371   virtual void OnResize()
01372   {
01373     this->vscroll->SetCapacityFromWidget(this, BUILD_VEHICLE_WIDGET_LIST);
01374     this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_LIST)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
01375   }
01376 };
01377 
01378 static const WindowDesc _build_vehicle_desc(
01379   WDP_AUTO, 240, 268,
01380   WC_BUILD_VEHICLE, WC_NONE,
01381   WDF_UNCLICK_BUTTONS | WDF_CONSTRUCTION,
01382   _nested_build_vehicle_widgets, lengthof(_nested_build_vehicle_widgets)
01383 );
01384 
01385 void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
01386 {
01387   /* We want to be able to open both Available Train as Available Ships,
01388    *  so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
01389    *  As it always is a low value, it won't collide with any real tile
01390    *  number. */
01391   uint num = (tile == INVALID_TILE) ? (int)type : tile;
01392 
01393   assert(IsCompanyBuildableVehicleType(type));
01394 
01395   DeleteWindowById(WC_BUILD_VEHICLE, num);
01396 
01397   new BuildVehicleWindow(&_build_vehicle_desc, tile, type);
01398 }