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 
00412 static int CDECL AircraftRangeSorter(const EngineID *a, const EngineID *b)
00413 {
00414   uint16 r_a = Engine::Get(*a)->GetRange();
00415   uint16 r_b = Engine::Get(*b)->GetRange();
00416 
00417   int r = r_a - r_b;
00418 
00419   /* Use EngineID to sort instead since we want consistent sorting */
00420   if (r == 0) return EngineNumberSorter(a, b);
00421   return _internal_sort_order ? -r : r;
00422 }
00423 
00424 static EngList_SortTypeFunction * const _sorter[][11] = {{
00425   /* Trains */
00426   &EngineNumberSorter,
00427   &EngineCostSorter,
00428   &EngineSpeedSorter,
00429   &EnginePowerSorter,
00430   &EngineTractiveEffortSorter,
00431   &EngineIntroDateSorter,
00432   &EngineNameSorter,
00433   &EngineRunningCostSorter,
00434   &EnginePowerVsRunningCostSorter,
00435   &EngineReliabilitySorter,
00436   &TrainEngineCapacitySorter,
00437 }, {
00438   /* Road vehicles */
00439   &EngineNumberSorter,
00440   &EngineCostSorter,
00441   &EngineSpeedSorter,
00442   &EnginePowerSorter,
00443   &EngineTractiveEffortSorter,
00444   &EngineIntroDateSorter,
00445   &EngineNameSorter,
00446   &EngineRunningCostSorter,
00447   &EnginePowerVsRunningCostSorter,
00448   &EngineReliabilitySorter,
00449   &RoadVehEngineCapacitySorter,
00450 }, {
00451   /* Ships */
00452   &EngineNumberSorter,
00453   &EngineCostSorter,
00454   &EngineSpeedSorter,
00455   &EngineIntroDateSorter,
00456   &EngineNameSorter,
00457   &EngineRunningCostSorter,
00458   &EngineReliabilitySorter,
00459   &ShipEngineCapacitySorter,
00460 }, {
00461   /* Aircraft */
00462   &EngineNumberSorter,
00463   &EngineCostSorter,
00464   &EngineSpeedSorter,
00465   &EngineIntroDateSorter,
00466   &EngineNameSorter,
00467   &EngineRunningCostSorter,
00468   &EngineReliabilitySorter,
00469   &AircraftEngineCargoSorter,
00470   &AircraftRangeSorter,
00471 }};
00472 
00473 static const StringID _sort_listing[][12] = {{
00474   /* Trains */
00475   STR_SORT_BY_ENGINE_ID,
00476   STR_SORT_BY_COST,
00477   STR_SORT_BY_MAX_SPEED,
00478   STR_SORT_BY_POWER,
00479   STR_SORT_BY_TRACTIVE_EFFORT,
00480   STR_SORT_BY_INTRO_DATE,
00481   STR_SORT_BY_NAME,
00482   STR_SORT_BY_RUNNING_COST,
00483   STR_SORT_BY_POWER_VS_RUNNING_COST,
00484   STR_SORT_BY_RELIABILITY,
00485   STR_SORT_BY_CARGO_CAPACITY,
00486   INVALID_STRING_ID
00487 }, {
00488   /* Road vehicles */
00489   STR_SORT_BY_ENGINE_ID,
00490   STR_SORT_BY_COST,
00491   STR_SORT_BY_MAX_SPEED,
00492   STR_SORT_BY_POWER,
00493   STR_SORT_BY_TRACTIVE_EFFORT,
00494   STR_SORT_BY_INTRO_DATE,
00495   STR_SORT_BY_NAME,
00496   STR_SORT_BY_RUNNING_COST,
00497   STR_SORT_BY_POWER_VS_RUNNING_COST,
00498   STR_SORT_BY_RELIABILITY,
00499   STR_SORT_BY_CARGO_CAPACITY,
00500   INVALID_STRING_ID
00501 }, {
00502   /* Ships */
00503   STR_SORT_BY_ENGINE_ID,
00504   STR_SORT_BY_COST,
00505   STR_SORT_BY_MAX_SPEED,
00506   STR_SORT_BY_INTRO_DATE,
00507   STR_SORT_BY_NAME,
00508   STR_SORT_BY_RUNNING_COST,
00509   STR_SORT_BY_RELIABILITY,
00510   STR_SORT_BY_CARGO_CAPACITY,
00511   INVALID_STRING_ID
00512 }, {
00513   /* Aircraft */
00514   STR_SORT_BY_ENGINE_ID,
00515   STR_SORT_BY_COST,
00516   STR_SORT_BY_MAX_SPEED,
00517   STR_SORT_BY_INTRO_DATE,
00518   STR_SORT_BY_NAME,
00519   STR_SORT_BY_RUNNING_COST,
00520   STR_SORT_BY_RELIABILITY,
00521   STR_SORT_BY_CARGO_CAPACITY,
00522   STR_SORT_BY_RANGE,
00523   INVALID_STRING_ID
00524 }};
00525 
00527 static bool CDECL CargoFilter(const EngineID *eid, const CargoID cid)
00528 {
00529   if (cid == CF_ANY) return true;
00530   uint32 refit_mask = GetUnionOfArticulatedRefitMasks(*eid, true);
00531   return (cid == CF_NONE ? refit_mask == 0 : HasBit(refit_mask, cid));
00532 }
00533 
00534 static GUIEngineList::FilterFunction * const _filter_funcs[] = {
00535   &CargoFilter,
00536 };
00537 
00538 static int DrawCargoCapacityInfo(int left, int right, int y, EngineID engine, bool refittable)
00539 {
00540   CargoArray cap = GetCapacityOfArticulatedParts(engine);
00541 
00542   for (CargoID c = 0; c < NUM_CARGO; c++) {
00543     if (cap[c] == 0) continue;
00544 
00545     SetDParam(0, c);
00546     SetDParam(1, cap[c]);
00547     SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
00548     DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00549     y += FONT_HEIGHT_NORMAL;
00550 
00551     /* Only show as refittable once */
00552     refittable = false;
00553   }
00554 
00555   return y;
00556 }
00557 
00558 /* Draw rail wagon specific details */
00559 static int DrawRailWagonPurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi)
00560 {
00561   const Engine *e = Engine::Get(engine_number);
00562 
00563   /* Purchase cost */
00564   SetDParam(0, e->GetCost());
00565   DrawString(left, right, y, STR_PURCHASE_INFO_COST);
00566   y += FONT_HEIGHT_NORMAL;
00567 
00568   /* Wagon weight - (including cargo) */
00569   uint weight = e->GetDisplayWeight();
00570   SetDParam(0, weight);
00571   uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(e->GetDefaultCargoType())->weight * GetTotalCapacityOfArticulatedParts(engine_number) / 16 : 0);
00572   SetDParam(1, cargo_weight + weight);
00573   DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
00574   y += FONT_HEIGHT_NORMAL;
00575 
00576   /* Wagon speed limit, displayed if above zero */
00577   if (_settings_game.vehicle.wagon_speed_limits) {
00578     uint max_speed = e->GetDisplayMaxSpeed();
00579     if (max_speed > 0) {
00580       SetDParam(0, max_speed);
00581       DrawString(left, right, y, STR_PURCHASE_INFO_SPEED);
00582       y += FONT_HEIGHT_NORMAL;
00583     }
00584   }
00585 
00586   /* Running cost */
00587   if (rvi->running_cost_class != INVALID_PRICE) {
00588     SetDParam(0, e->GetRunningCost());
00589     DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00590     y += FONT_HEIGHT_NORMAL;
00591   }
00592 
00593   return y;
00594 }
00595 
00596 /* Draw locomotive specific details */
00597 static int DrawRailEnginePurchaseInfo(int left, int right, int y, EngineID engine_number, const RailVehicleInfo *rvi)
00598 {
00599   const Engine *e = Engine::Get(engine_number);
00600 
00601   /* Purchase Cost - Engine weight */
00602   SetDParam(0, e->GetCost());
00603   SetDParam(1, e->GetDisplayWeight());
00604   DrawString(left, right, y, STR_PURCHASE_INFO_COST_WEIGHT);
00605   y += FONT_HEIGHT_NORMAL;
00606 
00607   /* Max speed - Engine power */
00608   SetDParam(0, e->GetDisplayMaxSpeed());
00609   SetDParam(1, e->GetPower());
00610   DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_POWER);
00611   y += FONT_HEIGHT_NORMAL;
00612 
00613   /* Max tractive effort - not applicable if old acceleration or maglev */
00614   if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL && GetRailTypeInfo(rvi->railtype)->acceleration_type != 2) {
00615     SetDParam(0, e->GetDisplayMaxTractiveEffort());
00616     DrawString(left, right, y, STR_PURCHASE_INFO_MAX_TE);
00617     y += FONT_HEIGHT_NORMAL;
00618   }
00619 
00620   /* Running cost */
00621   if (rvi->running_cost_class != INVALID_PRICE) {
00622     SetDParam(0, e->GetRunningCost());
00623     DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00624     y += FONT_HEIGHT_NORMAL;
00625   }
00626 
00627   /* Powered wagons power - Powered wagons extra weight */
00628   if (rvi->pow_wag_power != 0) {
00629     SetDParam(0, rvi->pow_wag_power);
00630     SetDParam(1, rvi->pow_wag_weight);
00631     DrawString(left, right, y, STR_PURCHASE_INFO_PWAGPOWER_PWAGWEIGHT);
00632     y += FONT_HEIGHT_NORMAL;
00633   }
00634 
00635   return y;
00636 }
00637 
00638 /* Draw road vehicle specific details */
00639 static int DrawRoadVehPurchaseInfo(int left, int right, int y, EngineID engine_number)
00640 {
00641   const Engine *e = Engine::Get(engine_number);
00642 
00643   if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) {
00644     /* Purchase Cost */
00645     SetDParam(0, e->GetCost());
00646     DrawString(left, right, y, STR_PURCHASE_INFO_COST);
00647     y += FONT_HEIGHT_NORMAL;
00648 
00649     /* Road vehicle weight - (including cargo) */
00650     int16 weight = e->GetDisplayWeight();
00651     SetDParam(0, weight);
00652     uint cargo_weight = (e->CanCarryCargo() ? CargoSpec::Get(e->GetDefaultCargoType())->weight * GetTotalCapacityOfArticulatedParts(engine_number) / 16 : 0);
00653     SetDParam(1, cargo_weight + weight);
00654     DrawString(left, right, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT);
00655     y += FONT_HEIGHT_NORMAL;
00656 
00657     /* Max speed - Engine power */
00658     SetDParam(0, e->GetDisplayMaxSpeed());
00659     SetDParam(1, e->GetPower());
00660     DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_POWER);
00661     y += FONT_HEIGHT_NORMAL;
00662 
00663     /* Max tractive effort */
00664     SetDParam(0, e->GetDisplayMaxTractiveEffort());
00665     DrawString(left, right, y, STR_PURCHASE_INFO_MAX_TE);
00666     y += FONT_HEIGHT_NORMAL;
00667   } else {
00668     /* Purchase cost - Max speed */
00669     SetDParam(0, e->GetCost());
00670     SetDParam(1, e->GetDisplayMaxSpeed());
00671     DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
00672     y += FONT_HEIGHT_NORMAL;
00673   }
00674 
00675   /* Running cost */
00676   SetDParam(0, e->GetRunningCost());
00677   DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00678   y += FONT_HEIGHT_NORMAL;
00679 
00680   return y;
00681 }
00682 
00683 /* Draw ship specific details */
00684 static int DrawShipPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable)
00685 {
00686   const Engine *e = Engine::Get(engine_number);
00687 
00688   /* Purchase cost - Max speed */
00689   uint raw_speed = e->GetDisplayMaxSpeed();
00690   uint ocean_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, true);
00691   uint canal_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, false);
00692 
00693   SetDParam(0, e->GetCost());
00694   if (ocean_speed == canal_speed) {
00695     SetDParam(1, ocean_speed);
00696     DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
00697     y += FONT_HEIGHT_NORMAL;
00698   } else {
00699     DrawString(left, right, y, STR_PURCHASE_INFO_COST);
00700     y += FONT_HEIGHT_NORMAL;
00701 
00702     SetDParam(0, ocean_speed);
00703     DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_OCEAN);
00704     y += FONT_HEIGHT_NORMAL;
00705 
00706     SetDParam(0, canal_speed);
00707     DrawString(left, right, y, STR_PURCHASE_INFO_SPEED_CANAL);
00708     y += FONT_HEIGHT_NORMAL;
00709   }
00710 
00711   /* Cargo type + capacity */
00712   SetDParam(0, e->GetDefaultCargoType());
00713   SetDParam(1, e->GetDisplayDefaultCapacity());
00714   SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
00715   DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00716   y += FONT_HEIGHT_NORMAL;
00717 
00718   /* Running cost */
00719   SetDParam(0, e->GetRunningCost());
00720   DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00721   y += FONT_HEIGHT_NORMAL;
00722 
00723   return y;
00724 }
00725 
00726 /* Draw aircraft specific details */
00727 static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable)
00728 {
00729   const Engine *e = Engine::Get(engine_number);
00730   CargoID cargo = e->GetDefaultCargoType();
00731 
00732   /* Purchase cost - Max speed */
00733   SetDParam(0, e->GetCost());
00734   SetDParam(1, e->GetDisplayMaxSpeed());
00735   DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
00736   y += FONT_HEIGHT_NORMAL;
00737 
00738   /* Cargo capacity */
00739   uint16 mail_capacity;
00740   uint capacity = e->GetDisplayDefaultCapacity(&mail_capacity);
00741   if (mail_capacity > 0) {
00742     SetDParam(0, cargo);
00743     SetDParam(1, capacity);
00744     SetDParam(2, CT_MAIL);
00745     SetDParam(3, mail_capacity);
00746     DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY);
00747   } else {
00748     /* Note, if the default capacity is selected by the refit capacity
00749      * callback, then the capacity shown is likely to be incorrect. */
00750     SetDParam(0, cargo);
00751     SetDParam(1, capacity);
00752     SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
00753     DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00754   }
00755   y += FONT_HEIGHT_NORMAL;
00756 
00757   /* Running cost */
00758   SetDParam(0, e->GetRunningCost());
00759   DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00760   y += FONT_HEIGHT_NORMAL;
00761 
00762   uint16 range = e->GetRange();
00763   if (range != 0) {
00764     SetDParam(0, range);
00765     DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_RANGE);
00766     y += FONT_HEIGHT_NORMAL;
00767   }
00768 
00769   return y;
00770 }
00771 
00780 static uint ShowAdditionalText(int left, int right, int y, EngineID engine)
00781 {
00782   uint16 callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, NULL);
00783   if (callback == CALLBACK_FAILED || callback == 0x400) return y;
00784   if (callback > 0x400) {
00785     ErrorUnknownCallbackResult(Engine::Get(engine)->GetGRFID(), CBID_VEHICLE_ADDITIONAL_TEXT, callback);
00786     return y;
00787   }
00788 
00789   StartTextRefStackUsage(6);
00790   uint result = DrawStringMultiLine(left, right, y, INT32_MAX, GetGRFStringID(Engine::Get(engine)->GetGRFID(), 0xD000 + callback), TC_BLACK);
00791   StopTextRefStackUsage();
00792   return result;
00793 }
00794 
00801 int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number)
00802 {
00803   const Engine *e = Engine::Get(engine_number);
00804   YearMonthDay ymd;
00805   ConvertDateToYMD(e->intro_date, &ymd);
00806   bool refittable = IsArticulatedVehicleRefittable(engine_number);
00807   bool articulated_cargo = false;
00808 
00809   switch (e->type) {
00810     default: NOT_REACHED();
00811     case VEH_TRAIN:
00812       if (e->u.rail.railveh_type == RAILVEH_WAGON) {
00813         y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->u.rail);
00814       } else {
00815         y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->u.rail);
00816       }
00817       articulated_cargo = true;
00818       break;
00819 
00820     case VEH_ROAD:
00821       y = DrawRoadVehPurchaseInfo(left, right, y, engine_number);
00822       articulated_cargo = true;
00823       break;
00824 
00825     case VEH_SHIP:
00826       y = DrawShipPurchaseInfo(left, right, y, engine_number, refittable);
00827       break;
00828 
00829     case VEH_AIRCRAFT:
00830       y = DrawAircraftPurchaseInfo(left, right, y, engine_number, refittable);
00831       break;
00832   }
00833 
00834   if (articulated_cargo) {
00835     /* Cargo type + capacity, or N/A */
00836     int new_y = DrawCargoCapacityInfo(left, right, y, engine_number, refittable);
00837 
00838     if (new_y == y) {
00839       SetDParam(0, CT_INVALID);
00840       SetDParam(2, STR_EMPTY);
00841       DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00842       y += FONT_HEIGHT_NORMAL;
00843     } else {
00844       y = new_y;
00845     }
00846   }
00847 
00848   /* Draw details that apply to all types except rail wagons. */
00849   if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) {
00850     /* Design date - Life length */
00851     SetDParam(0, ymd.year);
00852     SetDParam(1, e->GetLifeLengthInDays() / DAYS_IN_LEAP_YEAR);
00853     DrawString(left, right, y, STR_PURCHASE_INFO_DESIGNED_LIFE);
00854     y += FONT_HEIGHT_NORMAL;
00855 
00856     /* Reliability */
00857     SetDParam(0, ToPercent16(e->reliability));
00858     DrawString(left, right, y, STR_PURCHASE_INFO_RELIABILITY);
00859     y += FONT_HEIGHT_NORMAL;
00860   }
00861 
00862   /* Additional text from NewGRF */
00863   y = ShowAdditionalText(left, right, y, engine_number);
00864   if (refittable) y = ShowRefitOptionsList(left, right, y, engine_number);
00865 
00866   return y;
00867 }
00868 
00882 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)
00883 {
00884   static const int sprite_widths[]  = { 60, 60, 76, 67 };
00885   static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
00886 
00887   /* Obligatory sanity checks! */
00888   assert((uint)type < lengthof(sprite_widths));
00889   assert_compile(lengthof(sprite_y_offsets) == lengthof(sprite_widths));
00890   assert(max <= eng_list->Length());
00891 
00892   bool rtl = _current_text_dir == TD_RTL;
00893   int step_size = GetEngineListHeight(type);
00894   int sprite_width = sprite_widths[type];
00895 
00896   int sprite_x        = (rtl ? r - sprite_width / 2 : l + sprite_width / 2) - 1;
00897   int sprite_y_offset = sprite_y_offsets[type] + step_size / 2;
00898 
00899   int text_left  = l + (rtl ? WD_FRAMERECT_LEFT : sprite_width);
00900   int text_right = r - (rtl ? sprite_width : WD_FRAMERECT_RIGHT);
00901 
00902   int normal_text_y_offset = (step_size - FONT_HEIGHT_NORMAL) / 2;
00903   int small_text_y_offset  = step_size - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 1;
00904 
00905   for (; min < max; min++, y += step_size) {
00906     const EngineID engine = (*eng_list)[min];
00907     /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
00908     const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine);
00909 
00910     SetDParam(0, engine);
00911     DrawString(text_left, text_right, y + normal_text_y_offset, STR_ENGINE_NAME, engine == selected_id ? TC_WHITE : TC_BLACK);
00912     DrawVehicleEngine(l, r, sprite_x, y + sprite_y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company), EIT_PURCHASE);
00913     if (show_count) {
00914       SetDParam(0, num_engines);
00915       DrawString(text_left, text_right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT);
00916     }
00917   }
00918 }
00919 
00920 
00921 struct BuildVehicleWindow : Window {
00922   VehicleType vehicle_type;
00923   union {
00924     RailTypeByte railtype;
00925     RoadTypes roadtypes;
00926   } filter;
00927   bool descending_sort_order;
00928   byte sort_criteria;
00929   bool listview_mode;
00930   EngineID sel_engine;
00931   EngineID rename_engine;
00932   GUIEngineList eng_list;
00933   CargoID cargo_filter[NUM_CARGO + 2];        
00934   StringID cargo_filter_texts[NUM_CARGO + 3]; 
00935   byte cargo_filter_criteria;                 
00936   int details_height;                         
00937   Scrollbar *vscroll;
00938 
00939   BuildVehicleWindow(const WindowDesc *desc, TileIndex tile, VehicleType type) : Window()
00940   {
00941     this->vehicle_type = type;
00942     this->window_number = tile == INVALID_TILE ? (int)type : tile;
00943 
00944     this->sel_engine      = INVALID_ENGINE;
00945 
00946     this->sort_criteria         = _last_sort_criteria[type];
00947     this->descending_sort_order = _last_sort_order[type];
00948 
00949     switch (type) {
00950       default: NOT_REACHED();
00951       case VEH_TRAIN:
00952         this->filter.railtype = (tile == INVALID_TILE) ? RAILTYPE_END : GetRailType(tile);
00953         break;
00954       case VEH_ROAD:
00955         this->filter.roadtypes = (tile == INVALID_TILE) ? ROADTYPES_ALL : GetRoadTypes(tile);
00956       case VEH_SHIP:
00957       case VEH_AIRCRAFT:
00958         break;
00959     }
00960 
00961     this->listview_mode = (this->window_number <= VEH_END);
00962 
00963     this->CreateNestedTree(desc);
00964 
00965     this->vscroll = this->GetScrollbar(BUILD_VEHICLE_WIDGET_SCROLLBAR);
00966 
00967     /* If we are just viewing the list of vehicles, we do not need the Build button.
00968      * So we just hide it, and enlarge the Rename buton by the now vacant place. */
00969     if (this->listview_mode) this->GetWidget<NWidgetStacked>(BUILD_VEHICLE_WIDGET_BUILD_SEL)->SetDisplayedPlane(SZSP_NONE);
00970 
00971     NWidgetCore *widget = this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_LIST);
00972     widget->tool_tip = STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type;
00973 
00974     widget = this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_BUILD);
00975     widget->widget_data = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + type;
00976     widget->tool_tip    = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP + type;
00977 
00978     widget = this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_RENAME);
00979     widget->widget_data = STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + type;
00980     widget->tool_tip    = STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + type;
00981 
00982     this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
00983 
00984     this->FinishInitNested(desc, tile == INVALID_TILE ? (int)type : tile);
00985 
00986     this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
00987 
00988     this->eng_list.ForceRebuild();
00989     this->GenerateBuildList(); // generate the list, since we need it in the next line
00990     /* Select the first engine in the list as default when opening the window */
00991     if (this->eng_list.Length() > 0) this->sel_engine = this->eng_list[0];
00992   }
00993 
00995   void SetCargoFilterArray()
00996   {
00997     uint filter_items = 0;
00998 
00999     /* Add item for disabling filtering. */
01000     this->cargo_filter[filter_items] = CF_ANY;
01001     this->cargo_filter_texts[filter_items] = STR_PURCHASE_INFO_ALL_TYPES;
01002     filter_items++;
01003 
01004     /* Add item for vehicles not carrying anything, e.g. train engines.
01005      * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
01006     if (this->vehicle_type == VEH_TRAIN) {
01007       this->cargo_filter[filter_items] = CF_NONE;
01008       this->cargo_filter_texts[filter_items] = STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE;
01009       filter_items++;
01010     }
01011 
01012     /* Collect available cargo types for filtering. */
01013     const CargoSpec *cs;
01014     FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
01015       this->cargo_filter[filter_items] = cs->Index();
01016       this->cargo_filter_texts[filter_items] = cs->name;
01017       filter_items++;
01018     }
01019 
01020     /* Terminate the filter list. */
01021     this->cargo_filter_texts[filter_items] = INVALID_STRING_ID;
01022 
01023     /* If not found, the cargo criteria will be set to all cargos. */
01024     this->cargo_filter_criteria = 0;
01025 
01026     /* Find the last cargo filter criteria. */
01027     for (uint i = 0; i < filter_items; i++) {
01028       if (this->cargo_filter[i] == _last_filter_criteria[this->vehicle_type]) {
01029         this->cargo_filter_criteria = i;
01030         break;
01031       }
01032     }
01033 
01034     this->eng_list.SetFilterFuncs(_filter_funcs);
01035     this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
01036   }
01037 
01038   void OnInit()
01039   {
01040     this->SetCargoFilterArray();
01041   }
01042 
01044   void FilterEngineList()
01045   {
01046     this->eng_list.Filter(this->cargo_filter[this->cargo_filter_criteria]);
01047     if (0 == this->eng_list.Length()) { // no engine passed through the filter, invalidate the previously selected engine
01048       this->sel_engine = INVALID_ENGINE;
01049     } else if (!this->eng_list.Contains(this->sel_engine)) { // previously selected engine didn't pass the filter, select the first engine of the list
01050       this->sel_engine = this->eng_list[0];
01051     }
01052   }
01053 
01055   bool FilterSingleEngine(EngineID eid)
01056   {
01057     CargoID filter_type = this->cargo_filter[this->cargo_filter_criteria];
01058     return (filter_type == CF_ANY || CargoFilter(&eid, filter_type));
01059   }
01060 
01061   /* Figure out what train EngineIDs to put in the list */
01062   void GenerateBuildTrainList()
01063   {
01064     EngineID sel_id = INVALID_ENGINE;
01065     int num_engines = 0;
01066     int num_wagons  = 0;
01067 
01068     this->filter.railtype = (this->listview_mode) ? RAILTYPE_END : GetRailType(this->window_number);
01069 
01070     this->eng_list.Clear();
01071 
01072     /* Make list of all available train engines and wagons.
01073      * Also check to see if the previously selected engine is still available,
01074      * and if not, reset selection to INVALID_ENGINE. This could be the case
01075      * when engines become obsolete and are removed */
01076     const Engine *e;
01077     FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
01078       EngineID eid = e->index;
01079       const RailVehicleInfo *rvi = &e->u.rail;
01080 
01081       if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
01082       if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
01083 
01084       /* Filter now! So num_engines and num_wagons is valid */
01085       if (!FilterSingleEngine(eid)) continue;
01086 
01087       *this->eng_list.Append() = eid;
01088 
01089       if (rvi->railveh_type != RAILVEH_WAGON) {
01090         num_engines++;
01091       } else {
01092         num_wagons++;
01093       }
01094 
01095       if (eid == this->sel_engine) sel_id = eid;
01096     }
01097 
01098     this->sel_engine = sel_id;
01099 
01100     /* make engines first, and then wagons, sorted by ListPositionOfEngine() */
01101     _internal_sort_order = false;
01102     EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter);
01103 
01104     /* and then sort engines */
01105     _internal_sort_order = this->descending_sort_order;
01106     EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], 0, num_engines);
01107 
01108     /* and finally sort wagons */
01109     EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], num_engines, num_wagons);
01110   }
01111 
01112   /* Figure out what road vehicle EngineIDs to put in the list */
01113   void GenerateBuildRoadVehList()
01114   {
01115     EngineID sel_id = INVALID_ENGINE;
01116 
01117     this->eng_list.Clear();
01118 
01119     const Engine *e;
01120     FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
01121       EngineID eid = e->index;
01122       if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
01123       if (!HasBit(this->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue;
01124       *this->eng_list.Append() = eid;
01125 
01126       if (eid == this->sel_engine) sel_id = eid;
01127     }
01128     this->sel_engine = sel_id;
01129   }
01130 
01131   /* Figure out what ship EngineIDs to put in the list */
01132   void GenerateBuildShipList()
01133   {
01134     EngineID sel_id = INVALID_ENGINE;
01135     this->eng_list.Clear();
01136 
01137     const Engine *e;
01138     FOR_ALL_ENGINES_OF_TYPE(e, VEH_SHIP) {
01139       EngineID eid = e->index;
01140       if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
01141       *this->eng_list.Append() = eid;
01142 
01143       if (eid == this->sel_engine) sel_id = eid;
01144     }
01145     this->sel_engine = sel_id;
01146   }
01147 
01148   /* Figure out what aircraft EngineIDs to put in the list */
01149   void GenerateBuildAircraftList()
01150   {
01151     EngineID sel_id = INVALID_ENGINE;
01152 
01153     this->eng_list.Clear();
01154 
01155     const Station *st = this->listview_mode ? NULL : Station::GetByTile(this->window_number);
01156 
01157     /* Make list of all available planes.
01158      * Also check to see if the previously selected plane is still available,
01159      * and if not, reset selection to INVALID_ENGINE. This could be the case
01160      * when planes become obsolete and are removed */
01161     const Engine *e;
01162     FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) {
01163       EngineID eid = e->index;
01164       if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_company)) continue;
01165       /* First VEH_END window_numbers are fake to allow a window open for all different types at once */
01166       if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue;
01167 
01168       *this->eng_list.Append() = eid;
01169       if (eid == this->sel_engine) sel_id = eid;
01170     }
01171 
01172     this->sel_engine = sel_id;
01173   }
01174 
01175   /* Generate the list of vehicles */
01176   void GenerateBuildList()
01177   {
01178     if (!this->eng_list.NeedRebuild()) return;
01179     switch (this->vehicle_type) {
01180       default: NOT_REACHED();
01181       case VEH_TRAIN:
01182         this->GenerateBuildTrainList();
01183         this->eng_list.Compact();
01184         this->eng_list.RebuildDone();
01185         return; // trains should not reach the last sorting
01186       case VEH_ROAD:
01187         this->GenerateBuildRoadVehList();
01188         break;
01189       case VEH_SHIP:
01190         this->GenerateBuildShipList();
01191         break;
01192       case VEH_AIRCRAFT:
01193         this->GenerateBuildAircraftList();
01194         break;
01195     }
01196 
01197     this->FilterEngineList();
01198 
01199     _internal_sort_order = this->descending_sort_order;
01200     EngList_Sort(&this->eng_list, _sorter[this->vehicle_type][this->sort_criteria]);
01201 
01202     this->eng_list.Compact();
01203     this->eng_list.RebuildDone();
01204   }
01205 
01206   void OnClick(Point pt, int widget, int click_count)
01207   {
01208     switch (widget) {
01209       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
01210         this->descending_sort_order ^= true;
01211         _last_sort_order[this->vehicle_type] = this->descending_sort_order;
01212         this->eng_list.ForceRebuild();
01213         this->SetDirty();
01214         break;
01215 
01216       case BUILD_VEHICLE_WIDGET_LIST: {
01217         uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, BUILD_VEHICLE_WIDGET_LIST);
01218         size_t num_items = this->eng_list.Length();
01219         this->sel_engine = (i < num_items) ? this->eng_list[i] : INVALID_ENGINE;
01220         this->SetDirty();
01221         if (click_count > 1 && !this->listview_mode) this->OnClick(pt, BUILD_VEHICLE_WIDGET_BUILD, 1);
01222         break;
01223       }
01224 
01225       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN: { // Select sorting criteria dropdown menu
01226         uint32 hidden_mask = 0;
01227         /* Disable sorting by power or tractive effort when the original acceleration model for road vehicles is being used. */
01228         if (this->vehicle_type == VEH_ROAD &&
01229             _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) {
01230           SetBit(hidden_mask, 3); // power
01231           SetBit(hidden_mask, 4); // tractive effort
01232           SetBit(hidden_mask, 8); // power by running costs
01233         }
01234         /* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
01235         if (this->vehicle_type == VEH_TRAIN &&
01236             _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
01237           SetBit(hidden_mask, 4); // tractive effort
01238         }
01239         ShowDropDownMenu(this, _sort_listing[this->vehicle_type], this->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, hidden_mask);
01240         break;
01241       }
01242 
01243       case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
01244         ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN, 0, 0);
01245         break;
01246 
01247       case BUILD_VEHICLE_WIDGET_BUILD: {
01248         EngineID sel_eng = this->sel_engine;
01249         if (sel_eng != INVALID_ENGINE) {
01250           CommandCallback *callback = (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildPrimaryVehicle;
01251           DoCommandP(this->window_number, sel_eng, 0, GetCmdBuildVeh(this->vehicle_type), callback);
01252         }
01253         break;
01254       }
01255 
01256       case BUILD_VEHICLE_WIDGET_RENAME: {
01257         EngineID sel_eng = this->sel_engine;
01258         if (sel_eng != INVALID_ENGINE) {
01259           this->rename_engine = sel_eng;
01260           SetDParam(0, sel_eng);
01261           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);
01262         }
01263         break;
01264       }
01265     }
01266   }
01267 
01273   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01274   {
01275     if (!gui_scope) return;
01276     /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
01277     if (this->vehicle_type == VEH_ROAD &&
01278         _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL &&
01279         this->sort_criteria > 7) {
01280       this->sort_criteria = 0;
01281       _last_sort_criteria[VEH_ROAD] = 0;
01282     }
01283     this->eng_list.ForceRebuild();
01284   }
01285 
01286   virtual void SetStringParameters(int widget) const
01287   {
01288     switch (widget) {
01289       case BUILD_VEHICLE_WIDGET_CAPTION:
01290         if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
01291           const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
01292           SetDParam(0, rti->strings.build_caption);
01293         } else {
01294           SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
01295         }
01296         break;
01297 
01298       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:
01299         SetDParam(0, _sort_listing[this->vehicle_type][this->sort_criteria]);
01300         break;
01301 
01302       case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN:
01303         SetDParam(0, this->cargo_filter_texts[this->cargo_filter_criteria]);
01304     }
01305   }
01306 
01307   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01308   {
01309     switch (widget) {
01310       case BUILD_VEHICLE_WIDGET_LIST:
01311         resize->height = GetEngineListHeight(this->vehicle_type);
01312         size->height = 3 * resize->height;
01313         break;
01314 
01315       case BUILD_VEHICLE_WIDGET_PANEL:
01316         size->height = this->details_height;
01317         break;
01318 
01319       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING: {
01320         Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
01321         d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better.
01322         d.height += padding.height;
01323         *size = maxdim(*size, d);
01324         break;
01325       }
01326     }
01327   }
01328 
01329   virtual void DrawWidget(const Rect &r, int widget) const
01330   {
01331     switch (widget) {
01332       case BUILD_VEHICLE_WIDGET_LIST:
01333         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);
01334         break;
01335 
01336       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
01337         this->DrawSortButtonState(BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
01338         break;
01339     }
01340   }
01341 
01342   virtual void OnPaint()
01343   {
01344     this->GenerateBuildList();
01345     this->vscroll->SetCount(this->eng_list.Length());
01346 
01347     this->DrawWidgets();
01348 
01349     if (!this->IsShaded()) {
01350       int needed_height = this->details_height;
01351       /* Draw details panels. */
01352       if (this->sel_engine != INVALID_ENGINE) {
01353         NWidgetBase *nwi = this->GetWidget<NWidgetBase>(BUILD_VEHICLE_WIDGET_PANEL);
01354         int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT,
01355             nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine);
01356         needed_height = max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM);
01357       }
01358       if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
01359         int resize = needed_height - this->details_height;
01360         this->details_height = needed_height;
01361         this->ReInit(0, resize);
01362         return;
01363       }
01364     }
01365   }
01366 
01367   virtual void OnQueryTextFinished(char *str)
01368   {
01369     if (str == NULL) return;
01370 
01371     DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type), NULL, str);
01372   }
01373 
01374   virtual void OnDropdownSelect(int widget, int index)
01375   {
01376     switch (widget) {
01377       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:
01378         if (this->sort_criteria != index) {
01379           this->sort_criteria = index;
01380           _last_sort_criteria[this->vehicle_type] = this->sort_criteria;
01381           this->eng_list.ForceRebuild();
01382         }
01383         break;
01384 
01385       case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
01386         if (this->cargo_filter_criteria != index) {
01387           this->cargo_filter_criteria = index;
01388           _last_filter_criteria[this->vehicle_type] = this->cargo_filter[this->cargo_filter_criteria];
01389           /* deactivate filter if criteria is 'Show All', activate it otherwise */
01390           this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
01391           this->eng_list.ForceRebuild();
01392         }
01393         break;
01394     }
01395     this->SetDirty();
01396   }
01397 
01398   virtual void OnResize()
01399   {
01400     this->vscroll->SetCapacityFromWidget(this, BUILD_VEHICLE_WIDGET_LIST);
01401     this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_LIST)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
01402   }
01403 };
01404 
01405 static const WindowDesc _build_vehicle_desc(
01406   WDP_AUTO, 240, 268,
01407   WC_BUILD_VEHICLE, WC_NONE,
01408   WDF_UNCLICK_BUTTONS | WDF_CONSTRUCTION,
01409   _nested_build_vehicle_widgets, lengthof(_nested_build_vehicle_widgets)
01410 );
01411 
01412 void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
01413 {
01414   /* We want to be able to open both Available Train as Available Ships,
01415    *  so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
01416    *  As it always is a low value, it won't collide with any real tile
01417    *  number. */
01418   uint num = (tile == INVALID_TILE) ? (int)type : tile;
01419 
01420   assert(IsCompanyBuildableVehicleType(type));
01421 
01422   DeleteWindowById(WC_BUILD_VEHICLE, num);
01423 
01424   new BuildVehicleWindow(&_build_vehicle_desc, tile, type);
01425 }