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 * e->GetDisplayDefaultCapacity() >> 4 : 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 = CargoSpec::Get(e->GetDefaultCargoType())->weight * GetTotalCapacityOfArticulatedParts(engine_number) / 16;
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   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   /* Cargo type + capacity */
00675   SetDParam(0, e->GetDefaultCargoType());
00676   SetDParam(1, e->GetDisplayDefaultCapacity());
00677   SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
00678   DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00679   y += FONT_HEIGHT_NORMAL;
00680 
00681   /* Running cost */
00682   SetDParam(0, e->GetRunningCost());
00683   DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00684   y += FONT_HEIGHT_NORMAL;
00685 
00686   return y;
00687 }
00688 
00689 /* Draw aircraft specific details */
00690 static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_number, bool refittable)
00691 {
00692   const Engine *e = Engine::Get(engine_number);
00693   CargoID cargo = e->GetDefaultCargoType();
00694 
00695   /* Purchase cost - Max speed */
00696   SetDParam(0, e->GetCost());
00697   SetDParam(1, e->GetDisplayMaxSpeed());
00698   DrawString(left, right, y, STR_PURCHASE_INFO_COST_SPEED);
00699   y += FONT_HEIGHT_NORMAL;
00700 
00701   /* Cargo capacity */
00702   uint16 mail_capacity;
00703   uint capacity = e->GetDisplayDefaultCapacity(&mail_capacity);
00704   if (mail_capacity > 0) {
00705     SetDParam(0, cargo);
00706     SetDParam(1, capacity);
00707     SetDParam(2, CT_MAIL);
00708     SetDParam(3, mail_capacity);
00709     DrawString(left, right, y, STR_PURCHASE_INFO_AIRCRAFT_CAPACITY);
00710   } else {
00711     /* Note, if the default capacity is selected by the refit capacity
00712      * callback, then the capacity shown is likely to be incorrect. */
00713     SetDParam(0, cargo);
00714     SetDParam(1, capacity);
00715     SetDParam(2, refittable ? STR_PURCHASE_INFO_REFITTABLE : STR_EMPTY);
00716     DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00717   }
00718   y += FONT_HEIGHT_NORMAL;
00719 
00720   /* Running cost */
00721   SetDParam(0, e->GetRunningCost());
00722   DrawString(left, right, y, STR_PURCHASE_INFO_RUNNINGCOST);
00723   y += FONT_HEIGHT_NORMAL;
00724 
00725   return y;
00726 }
00727 
00736 static uint ShowAdditionalText(int left, int right, int y, EngineID engine)
00737 {
00738   uint16 callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, NULL);
00739   if (callback == CALLBACK_FAILED) return y;
00740 
00741   /* STR_BLACK_STRING is used to start the string with {BLACK} */
00742   SetDParam(0, GetGRFStringID(GetEngineGRFID(engine), 0xD000 + callback));
00743   PrepareTextRefStackUsage(0);
00744   uint result = DrawStringMultiLine(left, right, y, INT32_MAX, STR_BLACK_STRING);
00745   StopTextRefStackUsage();
00746   return result;
00747 }
00748 
00755 int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number)
00756 {
00757   const Engine *e = Engine::Get(engine_number);
00758   YearMonthDay ymd;
00759   ConvertDateToYMD(e->intro_date, &ymd);
00760   bool refittable = IsArticulatedVehicleRefittable(engine_number);
00761   bool articulated_cargo = false;
00762 
00763   switch (e->type) {
00764     default: NOT_REACHED();
00765     case VEH_TRAIN:
00766       if (e->u.rail.railveh_type == RAILVEH_WAGON) {
00767         y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->u.rail);
00768       } else {
00769         y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->u.rail);
00770       }
00771       articulated_cargo = true;
00772       break;
00773 
00774     case VEH_ROAD:
00775       y = DrawRoadVehPurchaseInfo(left, right, y, engine_number);
00776       articulated_cargo = true;
00777       break;
00778 
00779     case VEH_SHIP:
00780       y = DrawShipPurchaseInfo(left, right, y, engine_number, refittable);
00781       break;
00782 
00783     case VEH_AIRCRAFT:
00784       y = DrawAircraftPurchaseInfo(left, right, y, engine_number, refittable);
00785       break;
00786   }
00787 
00788   if (articulated_cargo) {
00789     /* Cargo type + capacity, or N/A */
00790     int new_y = DrawCargoCapacityInfo(left, right, y, engine_number, refittable);
00791 
00792     if (new_y == y) {
00793       SetDParam(0, CT_INVALID);
00794       SetDParam(2, STR_EMPTY);
00795       DrawString(left, right, y, STR_PURCHASE_INFO_CAPACITY);
00796       y += FONT_HEIGHT_NORMAL;
00797     } else {
00798       y = new_y;
00799     }
00800   }
00801 
00802   /* Draw details that apply to all types except rail wagons. */
00803   if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) {
00804     /* Design date - Life length */
00805     SetDParam(0, ymd.year);
00806     SetDParam(1, e->GetLifeLengthInDays() / DAYS_IN_LEAP_YEAR);
00807     DrawString(left, right, y, STR_PURCHASE_INFO_DESIGNED_LIFE);
00808     y += FONT_HEIGHT_NORMAL;
00809 
00810     /* Reliability */
00811     SetDParam(0, ToPercent16(e->reliability));
00812     DrawString(left, right, y, STR_PURCHASE_INFO_RELIABILITY);
00813     y += FONT_HEIGHT_NORMAL;
00814   }
00815 
00816   /* Additional text from NewGRF */
00817   y = ShowAdditionalText(left, right, y, engine_number);
00818   if (refittable) y = ShowRefitOptionsList(left, right, y, engine_number);
00819 
00820   return y;
00821 }
00822 
00836 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)
00837 {
00838   static const int sprite_widths[]  = { 60, 60, 76, 67 };
00839   static const int sprite_y_offsets[] = { -1, -1, -2, -2 };
00840 
00841   /* Obligatory sanity checks! */
00842   assert((uint)type < lengthof(sprite_widths));
00843   assert_compile(lengthof(sprite_y_offsets) == lengthof(sprite_widths));
00844   assert(max <= eng_list->Length());
00845 
00846   bool rtl = _current_text_dir == TD_RTL;
00847   int step_size = GetEngineListHeight(type);
00848   int sprite_width = sprite_widths[type];
00849 
00850   int sprite_x        = (rtl ? r - sprite_width / 2 : l + sprite_width / 2) - 1;
00851   int sprite_y_offset = sprite_y_offsets[type] + step_size / 2;
00852 
00853   int text_left  = l + (rtl ? WD_FRAMERECT_LEFT : sprite_width);
00854   int text_right = r - (rtl ? sprite_width : WD_FRAMERECT_RIGHT);
00855 
00856   int normal_text_y_offset = (step_size - FONT_HEIGHT_NORMAL) / 2;
00857   int small_text_y_offset  = step_size - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - 1;
00858 
00859   for (; min < max; min++, y += step_size) {
00860     const EngineID engine = (*eng_list)[min];
00861     /* Note: num_engines is only used in the autoreplace GUI, so it is correct to use _local_company here. */
00862     const uint num_engines = GetGroupNumEngines(_local_company, selected_group, engine);
00863 
00864     SetDParam(0, engine);
00865     DrawString(text_left, text_right, y + normal_text_y_offset, STR_ENGINE_NAME, engine == selected_id ? TC_WHITE : TC_BLACK);
00866     DrawVehicleEngine(l, r, sprite_x, y + sprite_y_offset, engine, (show_count && num_engines == 0) ? PALETTE_CRASH : GetEnginePalette(engine, _local_company));
00867     if (show_count) {
00868       SetDParam(0, num_engines);
00869       DrawString(text_left, text_right, y + small_text_y_offset, STR_TINY_BLACK_COMA, TC_FROMSTRING, SA_RIGHT);
00870     }
00871   }
00872 }
00873 
00874 
00875 struct BuildVehicleWindow : Window {
00876   VehicleType vehicle_type;
00877   union {
00878     RailTypeByte railtype;
00879     RoadTypes roadtypes;
00880   } filter;
00881   bool descending_sort_order;
00882   byte sort_criteria;
00883   bool listview_mode;
00884   EngineID sel_engine;
00885   EngineID rename_engine;
00886   GUIEngineList eng_list;
00887   CargoID cargo_filter[NUM_CARGO + 2];        
00888   StringID cargo_filter_texts[NUM_CARGO + 3]; 
00889   byte cargo_filter_criteria;                 
00890   int details_height;                         
00891   Scrollbar *vscroll;
00892 
00893   BuildVehicleWindow(const WindowDesc *desc, TileIndex tile, VehicleType type) : Window()
00894   {
00895     this->vehicle_type = type;
00896     this->window_number = tile == INVALID_TILE ? (int)type : tile;
00897 
00898     this->sel_engine      = INVALID_ENGINE;
00899 
00900     this->sort_criteria         = _last_sort_criteria[type];
00901     this->descending_sort_order = _last_sort_order[type];
00902 
00903     switch (type) {
00904       default: NOT_REACHED();
00905       case VEH_TRAIN:
00906         this->filter.railtype = (tile == INVALID_TILE) ? RAILTYPE_END : GetRailType(tile);
00907         break;
00908       case VEH_ROAD:
00909         this->filter.roadtypes = (tile == INVALID_TILE) ? ROADTYPES_ALL : GetRoadTypes(tile);
00910       case VEH_SHIP:
00911       case VEH_AIRCRAFT:
00912         break;
00913     }
00914 
00915     this->listview_mode = (this->window_number <= VEH_END);
00916 
00917     this->CreateNestedTree(desc);
00918 
00919     this->vscroll = this->GetScrollbar(BUILD_VEHICLE_WIDGET_SCROLLBAR);
00920 
00921     /* If we are just viewing the list of vehicles, we do not need the Build button.
00922      * So we just hide it, and enlarge the Rename buton by the now vacant place. */
00923     if (this->listview_mode) this->GetWidget<NWidgetStacked>(BUILD_VEHICLE_WIDGET_BUILD_SEL)->SetDisplayedPlane(SZSP_NONE);
00924 
00925     NWidgetCore *widget = this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_LIST);
00926     widget->tool_tip = STR_BUY_VEHICLE_TRAIN_LIST_TOOLTIP + type;
00927 
00928     widget = this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_BUILD);
00929     widget->widget_data = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + type;
00930     widget->tool_tip    = STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_TOOLTIP + type;
00931 
00932     widget = this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_RENAME);
00933     widget->widget_data = STR_BUY_VEHICLE_TRAIN_RENAME_BUTTON + type;
00934     widget->tool_tip    = STR_BUY_VEHICLE_TRAIN_RENAME_TOOLTIP + type;
00935 
00936     this->details_height = ((this->vehicle_type == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
00937 
00938     this->FinishInitNested(desc, tile == INVALID_TILE ? (int)type : tile);
00939 
00940     this->owner = (tile != INVALID_TILE) ? GetTileOwner(tile) : _local_company;
00941 
00942     this->eng_list.ForceRebuild();
00943     this->GenerateBuildList(); // generate the list, since we need it in the next line
00944     /* Select the first engine in the list as default when opening the window */
00945     if (this->eng_list.Length() > 0) this->sel_engine = this->eng_list[0];
00946   }
00947 
00949   void SetCargoFilterArray()
00950   {
00951     uint filter_items = 0;
00952 
00953     /* Add item for disabling filtering. */
00954     this->cargo_filter[filter_items] = CF_ANY;
00955     this->cargo_filter_texts[filter_items] = STR_PURCHASE_INFO_ALL_TYPES;
00956     filter_items++;
00957 
00958     /* Add item for vehicles not carrying anything, e.g. train engines.
00959      * This could also be useful for eyecandy vehicles of other types, but is likely too confusing for joe, */
00960     if (this->vehicle_type == VEH_TRAIN) {
00961       this->cargo_filter[filter_items] = CF_NONE;
00962       this->cargo_filter_texts[filter_items] = STR_LAND_AREA_INFORMATION_LOCAL_AUTHORITY_NONE;
00963       filter_items++;
00964     }
00965 
00966     /* Collect available cargo types for filtering. */
00967     const CargoSpec *cs;
00968     FOR_ALL_SORTED_STANDARD_CARGOSPECS(cs) {
00969       this->cargo_filter[filter_items] = cs->Index();
00970       this->cargo_filter_texts[filter_items] = cs->name;
00971       filter_items++;
00972     }
00973 
00974     /* Terminate the filter list. */
00975     this->cargo_filter_texts[filter_items] = INVALID_STRING_ID;
00976 
00977     /* If not found, the cargo criteria will be set to all cargos. */
00978     this->cargo_filter_criteria = 0;
00979 
00980     /* Find the last cargo filter criteria. */
00981     for (uint i = 0; i < filter_items; i++) {
00982       if (this->cargo_filter[i] == _last_filter_criteria[this->vehicle_type]) {
00983         this->cargo_filter_criteria = i;
00984         break;
00985       }
00986     }
00987 
00988     this->eng_list.SetFilterFuncs(_filter_funcs);
00989     this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
00990   }
00991 
00992   void OnInit()
00993   {
00994     this->SetCargoFilterArray();
00995   }
00996 
00998   void FilterEngineList()
00999   {
01000     this->eng_list.Filter(this->cargo_filter[this->cargo_filter_criteria]);
01001     if (0 == this->eng_list.Length()) { // no engine passed through the filter, invalidate the previously selected engine
01002       this->sel_engine = INVALID_ENGINE;
01003     } else if (!this->eng_list.Contains(this->sel_engine)) { // previously selected engine didn't pass the filter, select the first engine of the list
01004       this->sel_engine = this->eng_list[0];
01005     }
01006   }
01007 
01009   bool FilterSingleEngine(EngineID eid)
01010   {
01011     CargoID filter_type = this->cargo_filter[this->cargo_filter_criteria];
01012     return (filter_type == CF_ANY || CargoFilter(&eid, filter_type));
01013   }
01014 
01015   /* Figure out what train EngineIDs to put in the list */
01016   void GenerateBuildTrainList()
01017   {
01018     EngineID sel_id = INVALID_ENGINE;
01019     int num_engines = 0;
01020     int num_wagons  = 0;
01021 
01022     this->filter.railtype = (this->listview_mode) ? RAILTYPE_END : GetRailType(this->window_number);
01023 
01024     this->eng_list.Clear();
01025 
01026     /* Make list of all available train engines and wagons.
01027      * Also check to see if the previously selected engine is still available,
01028      * and if not, reset selection to INVALID_ENGINE. This could be the case
01029      * when engines become obsolete and are removed */
01030     const Engine *e;
01031     FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
01032       EngineID eid = e->index;
01033       const RailVehicleInfo *rvi = &e->u.rail;
01034 
01035       if (this->filter.railtype != RAILTYPE_END && !HasPowerOnRail(rvi->railtype, this->filter.railtype)) continue;
01036       if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
01037 
01038       /* Filter now! So num_engines and num_wagons is valid */
01039       if (!FilterSingleEngine(eid)) continue;
01040 
01041       *this->eng_list.Append() = eid;
01042 
01043       if (rvi->railveh_type != RAILVEH_WAGON) {
01044         num_engines++;
01045       } else {
01046         num_wagons++;
01047       }
01048 
01049       if (eid == this->sel_engine) sel_id = eid;
01050     }
01051 
01052     this->sel_engine = sel_id;
01053 
01054     /* make engines first, and then wagons, sorted by ListPositionOfEngine() */
01055     _internal_sort_order = false;
01056     EngList_Sort(&this->eng_list, TrainEnginesThenWagonsSorter);
01057 
01058     /* and then sort engines */
01059     _internal_sort_order = this->descending_sort_order;
01060     EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], 0, num_engines);
01061 
01062     /* and finally sort wagons */
01063     EngList_SortPartial(&this->eng_list, _sorter[0][this->sort_criteria], num_engines, num_wagons);
01064   }
01065 
01066   /* Figure out what road vehicle EngineIDs to put in the list */
01067   void GenerateBuildRoadVehList()
01068   {
01069     EngineID sel_id = INVALID_ENGINE;
01070 
01071     this->eng_list.Clear();
01072 
01073     const Engine *e;
01074     FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
01075       EngineID eid = e->index;
01076       if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
01077       if (!HasBit(this->filter.roadtypes, HasBit(EngInfo(eid)->misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD)) continue;
01078       *this->eng_list.Append() = eid;
01079 
01080       if (eid == this->sel_engine) sel_id = eid;
01081     }
01082     this->sel_engine = sel_id;
01083   }
01084 
01085   /* Figure out what ship EngineIDs to put in the list */
01086   void GenerateBuildShipList()
01087   {
01088     EngineID sel_id = INVALID_ENGINE;
01089     this->eng_list.Clear();
01090 
01091     const Engine *e;
01092     FOR_ALL_ENGINES_OF_TYPE(e, VEH_SHIP) {
01093       EngineID eid = e->index;
01094       if (!IsEngineBuildable(eid, VEH_SHIP, _local_company)) continue;
01095       *this->eng_list.Append() = eid;
01096 
01097       if (eid == this->sel_engine) sel_id = eid;
01098     }
01099     this->sel_engine = sel_id;
01100   }
01101 
01102   /* Figure out what aircraft EngineIDs to put in the list */
01103   void GenerateBuildAircraftList()
01104   {
01105     EngineID sel_id = INVALID_ENGINE;
01106 
01107     this->eng_list.Clear();
01108 
01109     const Station *st = this->listview_mode ? NULL : Station::GetByTile(this->window_number);
01110 
01111     /* Make list of all available planes.
01112      * Also check to see if the previously selected plane is still available,
01113      * and if not, reset selection to INVALID_ENGINE. This could be the case
01114      * when planes become obsolete and are removed */
01115     const Engine *e;
01116     FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) {
01117       EngineID eid = e->index;
01118       if (!IsEngineBuildable(eid, VEH_AIRCRAFT, _local_company)) continue;
01119       /* First VEH_END window_numbers are fake to allow a window open for all different types at once */
01120       if (!this->listview_mode && !CanVehicleUseStation(eid, st)) continue;
01121 
01122       *this->eng_list.Append() = eid;
01123       if (eid == this->sel_engine) sel_id = eid;
01124     }
01125 
01126     this->sel_engine = sel_id;
01127   }
01128 
01129   /* Generate the list of vehicles */
01130   void GenerateBuildList()
01131   {
01132     if (!this->eng_list.NeedRebuild()) return;
01133     switch (this->vehicle_type) {
01134       default: NOT_REACHED();
01135       case VEH_TRAIN:
01136         this->GenerateBuildTrainList();
01137         this->eng_list.Compact();
01138         this->eng_list.RebuildDone();
01139         return; // trains should not reach the last sorting
01140       case VEH_ROAD:
01141         this->GenerateBuildRoadVehList();
01142         break;
01143       case VEH_SHIP:
01144         this->GenerateBuildShipList();
01145         break;
01146       case VEH_AIRCRAFT:
01147         this->GenerateBuildAircraftList();
01148         break;
01149     }
01150 
01151     this->FilterEngineList();
01152 
01153     _internal_sort_order = this->descending_sort_order;
01154     EngList_Sort(&this->eng_list, _sorter[this->vehicle_type][this->sort_criteria]);
01155 
01156     this->eng_list.Compact();
01157     this->eng_list.RebuildDone();
01158   }
01159 
01160   void OnClick(Point pt, int widget, int click_count)
01161   {
01162     switch (widget) {
01163       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
01164         this->descending_sort_order ^= true;
01165         _last_sort_order[this->vehicle_type] = this->descending_sort_order;
01166         this->eng_list.ForceRebuild();
01167         this->SetDirty();
01168         break;
01169 
01170       case BUILD_VEHICLE_WIDGET_LIST: {
01171         uint i = this->vscroll->GetScrolledRowFromWidget(pt.y, this, BUILD_VEHICLE_WIDGET_LIST);
01172         size_t num_items = this->eng_list.Length();
01173         this->sel_engine = (i < num_items) ? this->eng_list[i] : INVALID_ENGINE;
01174         this->SetDirty();
01175         if (click_count > 1 && !this->listview_mode) this->OnClick(pt, BUILD_VEHICLE_WIDGET_BUILD, 1);
01176         break;
01177       }
01178 
01179       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN: { // Select sorting criteria dropdown menu
01180         uint32 hidden_mask = 0;
01181         /* Disable sorting by power or tractive effort when the original acceleration model for road vehicles is being used. */
01182         if (this->vehicle_type == VEH_ROAD &&
01183             _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) {
01184           SetBit(hidden_mask, 3); // power
01185           SetBit(hidden_mask, 4); // tractive effort
01186           SetBit(hidden_mask, 8); // power by running costs
01187         }
01188         /* Disable sorting by tractive effort when the original acceleration model for trains is being used. */
01189         if (this->vehicle_type == VEH_TRAIN &&
01190             _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
01191           SetBit(hidden_mask, 4); // tractive effort
01192         }
01193         ShowDropDownMenu(this, _sort_listing[this->vehicle_type], this->sort_criteria, BUILD_VEHICLE_WIDGET_SORT_DROPDOWN, 0, hidden_mask);
01194         break;
01195       }
01196 
01197       case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN: // Select cargo filtering criteria dropdown menu
01198         ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN, 0, 0);
01199         break;
01200 
01201       case BUILD_VEHICLE_WIDGET_BUILD: {
01202         EngineID sel_eng = this->sel_engine;
01203         if (sel_eng != INVALID_ENGINE) {
01204           CommandCallback *callback = (this->vehicle_type == VEH_TRAIN && RailVehInfo(sel_eng)->railveh_type == RAILVEH_WAGON) ? CcBuildWagon : CcBuildPrimaryVehicle;
01205           DoCommandP(this->window_number, sel_eng, 0, GetCmdBuildVeh(this->vehicle_type), callback);
01206         }
01207         break;
01208       }
01209 
01210       case BUILD_VEHICLE_WIDGET_RENAME: {
01211         EngineID sel_eng = this->sel_engine;
01212         if (sel_eng != INVALID_ENGINE) {
01213           this->rename_engine = sel_eng;
01214           SetDParam(0, sel_eng);
01215           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);
01216         }
01217         break;
01218       }
01219     }
01220   }
01221 
01227   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
01228   {
01229     if (!gui_scope) return;
01230     /* When switching to original acceleration model for road vehicles, clear the selected sort criteria if it is not available now. */
01231     if (this->vehicle_type == VEH_ROAD &&
01232         _settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL &&
01233         this->sort_criteria > 7) {
01234       this->sort_criteria = 0;
01235       _last_sort_criteria[VEH_ROAD] = 0;
01236     }
01237     this->eng_list.ForceRebuild();
01238   }
01239 
01240   virtual void SetStringParameters(int widget) const
01241   {
01242     switch (widget) {
01243       case BUILD_VEHICLE_WIDGET_CAPTION:
01244         if (this->vehicle_type == VEH_TRAIN && !this->listview_mode) {
01245           const RailtypeInfo *rti = GetRailTypeInfo(this->filter.railtype);
01246           SetDParam(0, rti->strings.build_caption);
01247         } else {
01248           SetDParam(0, (this->listview_mode ? STR_VEHICLE_LIST_AVAILABLE_TRAINS : STR_BUY_VEHICLE_TRAIN_ALL_CAPTION) + this->vehicle_type);
01249         }
01250         break;
01251 
01252       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:
01253         SetDParam(0, _sort_listing[this->vehicle_type][this->sort_criteria]);
01254         break;
01255 
01256       case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN:
01257         SetDParam(0, this->cargo_filter_texts[this->cargo_filter_criteria]);
01258     }
01259   }
01260 
01261   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
01262   {
01263     switch (widget) {
01264       case BUILD_VEHICLE_WIDGET_LIST:
01265         resize->height = GetEngineListHeight(this->vehicle_type);
01266         size->height = 3 * resize->height;
01267         break;
01268 
01269       case BUILD_VEHICLE_WIDGET_PANEL:
01270         size->height = this->details_height;
01271         break;
01272 
01273       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING: {
01274         Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
01275         d.width += padding.width + WD_SORTBUTTON_ARROW_WIDTH * 2; // Doubled since the string is centred and it also looks better.
01276         d.height += padding.height;
01277         *size = maxdim(*size, d);
01278         break;
01279       }
01280     }
01281   }
01282 
01283   virtual void DrawWidget(const Rect &r, int widget) const
01284   {
01285     switch (widget) {
01286       case BUILD_VEHICLE_WIDGET_LIST:
01287         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);
01288         break;
01289 
01290       case BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING:
01291         this->DrawSortButtonState(BUILD_VEHICLE_WIDGET_SORT_ASSENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
01292         break;
01293     }
01294   }
01295 
01296   virtual void OnPaint()
01297   {
01298     this->GenerateBuildList();
01299     this->vscroll->SetCount(this->eng_list.Length());
01300 
01301     this->DrawWidgets();
01302 
01303     if (!this->IsShaded()) {
01304       int needed_height = this->details_height;
01305       /* Draw details panels. */
01306       if (this->sel_engine != INVALID_ENGINE) {
01307         NWidgetBase *nwi = this->GetWidget<NWidgetBase>(BUILD_VEHICLE_WIDGET_PANEL);
01308         int text_end = DrawVehiclePurchaseInfo(nwi->pos_x + WD_FRAMETEXT_LEFT, nwi->pos_x + nwi->current_x - WD_FRAMETEXT_RIGHT,
01309             nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine);
01310         needed_height = max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM);
01311       }
01312       if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
01313         int resize = needed_height - this->details_height;
01314         this->details_height = needed_height;
01315         this->ReInit(0, resize);
01316         return;
01317       }
01318     }
01319   }
01320 
01321   virtual void OnQueryTextFinished(char *str)
01322   {
01323     if (str == NULL) return;
01324 
01325     DoCommandP(0, this->rename_engine, 0, CMD_RENAME_ENGINE | CMD_MSG(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type), NULL, str);
01326   }
01327 
01328   virtual void OnDropdownSelect(int widget, int index)
01329   {
01330     switch (widget) {
01331       case BUILD_VEHICLE_WIDGET_SORT_DROPDOWN:
01332         if (this->sort_criteria != index) {
01333           this->sort_criteria = index;
01334           _last_sort_criteria[this->vehicle_type] = this->sort_criteria;
01335           this->eng_list.ForceRebuild();
01336         }
01337         break;
01338 
01339       case BUILD_VEHICLE_WIDGET_CARGO_FILTER_DROPDOWN: // Select a cargo filter criteria
01340         if (this->cargo_filter_criteria != index) {
01341           this->cargo_filter_criteria = index;
01342           _last_filter_criteria[this->vehicle_type] = this->cargo_filter[this->cargo_filter_criteria];
01343           /* deactivate filter if criteria is 'Show All', activate it otherwise */
01344           this->eng_list.SetFilterState(this->cargo_filter[this->cargo_filter_criteria] != CF_ANY);
01345           this->eng_list.ForceRebuild();
01346         }
01347         break;
01348     }
01349     this->SetDirty();
01350   }
01351 
01352   virtual void OnResize()
01353   {
01354     this->vscroll->SetCapacityFromWidget(this, BUILD_VEHICLE_WIDGET_LIST);
01355     this->GetWidget<NWidgetCore>(BUILD_VEHICLE_WIDGET_LIST)->widget_data = (this->vscroll->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
01356   }
01357 };
01358 
01359 static const WindowDesc _build_vehicle_desc(
01360   WDP_AUTO, 240, 268,
01361   WC_BUILD_VEHICLE, WC_NONE,
01362   WDF_UNCLICK_BUTTONS | WDF_CONSTRUCTION,
01363   _nested_build_vehicle_widgets, lengthof(_nested_build_vehicle_widgets)
01364 );
01365 
01366 void ShowBuildVehicleWindow(TileIndex tile, VehicleType type)
01367 {
01368   /* We want to be able to open both Available Train as Available Ships,
01369    *  so if tile == INVALID_TILE (Available XXX Window), use 'type' as unique number.
01370    *  As it always is a low value, it won't collide with any real tile
01371    *  number. */
01372   uint num = (tile == INVALID_TILE) ? (int)type : tile;
01373 
01374   assert(IsCompanyBuildableVehicleType(type));
01375 
01376   DeleteWindowById(WC_BUILD_VEHICLE, num);
01377 
01378   new BuildVehicleWindow(&_build_vehicle_desc, tile, type);
01379 }

Generated on Fri May 27 04:19:40 2011 for OpenTTD by  doxygen 1.6.1