OpenTTD
autoreplace_gui.cpp
Go to the documentation of this file.
1 /* $Id: autoreplace_gui.cpp 27683 2016-12-08 20:21:39Z frosch $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #include "stdafx.h"
13 #include "command_func.h"
14 #include "vehicle_gui.h"
15 #include "newgrf_engine.h"
16 #include "rail.h"
17 #include "strings_func.h"
18 #include "window_func.h"
19 #include "autoreplace_func.h"
20 #include "company_func.h"
21 #include "engine_base.h"
22 #include "window_gui.h"
23 #include "engine_gui.h"
24 #include "settings_func.h"
25 #include "core/geometry_func.hpp"
26 #include "rail_gui.h"
27 #include "widgets/dropdown_func.h"
28 
30 
31 #include "safeguards.h"
32 
33 void DrawEngineList(VehicleType type, int x, int r, int y, const GUIEngineList *eng_list, uint16 min, uint16 max, EngineID selected_id, bool show_count, GroupID selected_group);
34 
35 static int CDECL EngineNumberSorter(const EngineID *a, const EngineID *b)
36 {
37  int r = Engine::Get(*a)->list_position - Engine::Get(*b)->list_position;
38 
39  return r;
40 }
41 
52 {
54  /* We don't have any of this engine type.
55  * Either we just sold the last one, we build a new one or we stopped replacing it.
56  * In all cases, we need to update the left list */
58  }
59 }
60 
66 {
67  InvalidateWindowData(WC_REPLACE_VEHICLE, type, 0); // Update the autoreplace window
68  InvalidateWindowClassesData(WC_BUILD_VEHICLE); // The build windows needs updating as well
69 }
70 
71 static const StringID _start_replace_dropdown[] = {
72  STR_REPLACE_VEHICLES_NOW,
73  STR_REPLACE_VEHICLES_WHEN_OLD,
75 };
76 
80 class ReplaceVehicleWindow : public Window {
91  Scrollbar *vscroll[2];
92 
100  bool GenerateReplaceRailList(EngineID e, bool draw_left, bool show_engines)
101  {
102  const RailVehicleInfo *rvi = RailVehInfo(e);
103 
104  /* Ensure that the wagon/engine selection fits the engine. */
105  if ((rvi->railveh_type == RAILVEH_WAGON) == show_engines) return false;
106 
107  if (draw_left && this->sel_railtype != INVALID_RAILTYPE) {
108  /* Ensure that the railtype is specific to the selected one */
109  if (rvi->railtype != this->sel_railtype) return false;
110  }
111  return true;
112  }
113 
114 
119  void GenerateReplaceVehList(bool draw_left)
120  {
121  EngineID selected_engine = INVALID_ENGINE;
122  VehicleType type = (VehicleType)this->window_number;
123  byte side = draw_left ? 0 : 1;
124 
125  GUIEngineList *list = &this->engines[side];
126  list->Clear();
127 
128  const Engine *e;
129  FOR_ALL_ENGINES_OF_TYPE(e, type) {
130  if (!draw_left && !this->show_hidden_engines && e->IsHidden(_local_company)) continue;
131  EngineID eid = e->index;
132  if (type == VEH_TRAIN && !this->GenerateReplaceRailList(eid, draw_left, this->replace_engines)) continue; // special rules for trains
133 
134  if (draw_left) {
135  const uint num_engines = GetGroupNumEngines(_local_company, this->sel_group, eid);
136 
137  /* Skip drawing the engines we don't have any of and haven't set for replacement */
138  if (num_engines == 0 && EngineReplacementForCompany(Company::Get(_local_company), eid, this->sel_group) == INVALID_ENGINE) continue;
139  } else {
140  if (!CheckAutoreplaceValidity(this->sel_engine[0], eid, _local_company)) continue;
141  }
142 
143  *list->Append() = eid;
144  if (eid == this->sel_engine[side]) selected_engine = eid; // The selected engine is still in the list
145  }
146  this->sel_engine[side] = selected_engine; // update which engine we selected (the same or none, if it's not in the list anymore)
147  if (draw_left) {
149  } else {
152  }
153  }
154 
157  {
158  EngineID e = this->sel_engine[0];
159 
160  if (this->engines[0].NeedRebuild()) {
161  /* We need to rebuild the left engines list */
162  this->GenerateReplaceVehList(true);
163  this->vscroll[0]->SetCount(this->engines[0].Length());
164  if (this->reset_sel_engine && this->sel_engine[0] == INVALID_ENGINE && this->engines[0].Length() != 0) {
165  this->sel_engine[0] = this->engines[0][0];
166  }
167  }
168 
169  if (this->engines[1].NeedRebuild() || e != this->sel_engine[0]) {
170  /* Either we got a request to rebuild the right engines list, or the left engines list selected a different engine */
171  if (this->sel_engine[0] == INVALID_ENGINE) {
172  /* Always empty the right engines list when nothing is selected in the left engines list */
173  this->engines[1].Clear();
174  this->sel_engine[1] = INVALID_ENGINE;
175  } else {
176  if (this->reset_sel_engine && this->sel_engine[0] != INVALID_ENGINE) {
177  /* Select the current replacement for sel_engine[0]. */
178  const Company *c = Company::Get(_local_company);
179  this->sel_engine[1] = EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group);
180  }
181  /* Regenerate the list on the right. Note: This resets sel_engine[1] to INVALID_ENGINE, if it is no longer available. */
182  this->GenerateReplaceVehList(false);
183  this->vscroll[1]->SetCount(this->engines[1].Length());
184  if (this->reset_sel_engine && this->sel_engine[1] != INVALID_ENGINE) {
185  int position = 0;
186  for (EngineID *it = this->engines[1].Begin(); it != this->engines[1].End(); ++it) {
187  if (*it == this->sel_engine[1]) break;
188  ++position;
189  }
190  this->vscroll[1]->ScrollTowards(position);
191  }
192  }
193  }
194  /* Reset the flags about needed updates */
195  this->engines[0].RebuildDone();
196  this->engines[1].RebuildDone();
197  this->reset_sel_engine = false;
198  }
199 
204  void ReplaceClick_StartReplace(bool replace_when_old)
205  {
206  EngineID veh_from = this->sel_engine[0];
207  EngineID veh_to = this->sel_engine[1];
208  DoCommandP(0, (replace_when_old ? 1 : 0) | (this->sel_group << 16), veh_from + (veh_to << 16), CMD_SET_AUTOREPLACE);
209  }
210 
211 public:
212  ReplaceVehicleWindow(WindowDesc *desc, VehicleType vehicletype, GroupID id_g) : Window(desc)
213  {
215  this->replace_engines = true; // start with locomotives (all other vehicles will not read this bool)
216  this->engines[0].ForceRebuild();
217  this->engines[1].ForceRebuild();
218  this->reset_sel_engine = true;
219  this->details_height = ((vehicletype == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
220  this->sel_engine[0] = INVALID_ENGINE;
221  this->sel_engine[1] = INVALID_ENGINE;
223 
224  this->CreateNestedTree();
225  this->vscroll[0] = this->GetScrollbar(WID_RV_LEFT_SCROLLBAR);
226  this->vscroll[1] = this->GetScrollbar(WID_RV_RIGHT_SCROLLBAR);
227 
228  NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_RV_SHOW_HIDDEN_ENGINES);
229  widget->widget_data = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + vehicletype;
230  widget->tool_tip = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + vehicletype;
231  widget->SetLowered(this->show_hidden_engines);
232  this->FinishInitNested(vehicletype);
233 
234  this->sort_criteria = _engine_sort_last_criteria[vehicletype];
235  this->descending_sort_order = _engine_sort_last_order[vehicletype];
236  this->owner = _local_company;
237  this->sel_group = id_g;
238  }
239 
240  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
241  {
242  switch (widget) {
244  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
245  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
246  d.height += padding.height;
247  *size = maxdim(*size, d);
248  break;
249  }
250 
251  case WID_RV_LEFT_MATRIX:
252  case WID_RV_RIGHT_MATRIX:
253  resize->height = GetEngineListHeight((VehicleType)this->window_number);
254  size->height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize->height;
255  break;
256 
257  case WID_RV_LEFT_DETAILS:
259  size->height = this->details_height;
260  break;
261 
263  StringID str = this->GetWidget<NWidgetCore>(widget)->widget_data;
264  SetDParam(0, STR_CONFIG_SETTING_ON);
266  SetDParam(0, STR_CONFIG_SETTING_OFF);
267  d = maxdim(d, GetStringBoundingBox(str));
268  d.width += padding.width;
269  d.height += padding.height;
270  *size = maxdim(*size, d);
271  break;
272  }
273 
275  Dimension d = GetStringBoundingBox(STR_REPLACE_ENGINES);
276  d = maxdim(d, GetStringBoundingBox(STR_REPLACE_WAGONS));
277  d.width += padding.width;
278  d.height += padding.height;
279  *size = maxdim(*size, d);
280  break;
281  }
282 
283  case WID_RV_INFO_TAB: {
284  Dimension d = GetStringBoundingBox(STR_REPLACE_NOT_REPLACING);
285  d = maxdim(d, GetStringBoundingBox(STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED));
288  *size = maxdim(*size, d);
289  break;
290  }
291 
293  Dimension d = {0, 0};
294  for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
295  const RailtypeInfo *rti = GetRailTypeInfo(rt);
296  /* Skip rail type if it has no label */
297  if (rti->label == 0) continue;
299  }
300  d.width += padding.width;
301  d.height += padding.height;
302  *size = maxdim(*size, d);
303  break;
304  }
305 
306  case WID_RV_START_REPLACE: {
307  Dimension d = GetStringBoundingBox(STR_REPLACE_VEHICLES_START);
308  for (int i = 0; _start_replace_dropdown[i] != INVALID_STRING_ID; i++) {
309  d = maxdim(d, GetStringBoundingBox(_start_replace_dropdown[i]));
310  }
311  d.width += padding.width;
312  d.height += padding.height;
313  *size = maxdim(*size, d);
314  break;
315  }
316  }
317  }
318 
319  virtual void SetStringParameters(int widget) const
320  {
321  switch (widget) {
322  case WID_RV_CAPTION:
323  SetDParam(0, STR_REPLACE_VEHICLE_TRAIN + this->window_number);
324  switch (this->sel_group) {
325  case ALL_GROUP:
326  SetDParam(1, STR_GROUP_ALL_TRAINS + this->window_number);
327  break;
328 
329  case DEFAULT_GROUP:
330  SetDParam(1, STR_GROUP_DEFAULT_TRAINS + this->window_number);
331  break;
332 
333  default:
334  SetDParam(1, STR_GROUP_NAME);
335  SetDParam(2, sel_group);
336  break;
337  }
338  break;
339 
341  SetDParam(0, _engine_sort_listing[this->window_number][this->sort_criteria]);
342  break;
343 
345  const Company *c = Company::Get(_local_company);
346  SetDParam(0, c->settings.renew_keep_length ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
347  break;
348  }
349 
351  SetDParam(0, this->replace_engines ? STR_REPLACE_ENGINES : STR_REPLACE_WAGONS);
352  break;
353  }
354  }
355 
356  virtual void DrawWidget(const Rect &r, int widget) const
357  {
358  switch (widget) {
360  this->DrawSortButtonState(WID_RV_SORT_ASCENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
361  break;
362 
363  case WID_RV_INFO_TAB: {
364  const Company *c = Company::Get(_local_company);
365  StringID str;
366  if (this->sel_engine[0] != INVALID_ENGINE) {
367  if (!EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group)) {
368  str = STR_REPLACE_NOT_REPLACING;
369  } else {
370  bool when_old = false;
371  EngineID e = EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group, &when_old);
372  str = when_old ? STR_REPLACE_REPLACING_WHEN_OLD : STR_ENGINE_NAME;
373  SetDParam(0, e);
374  }
375  } else {
376  str = STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED;
377  }
378 
379  DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_BLACK, SA_HOR_CENTER);
380  break;
381  }
382 
383  case WID_RV_LEFT_MATRIX:
384  case WID_RV_RIGHT_MATRIX: {
385  int side = (widget == WID_RV_LEFT_MATRIX) ? 0 : 1;
386  EngineID start = this->vscroll[side]->GetPosition(); // what is the offset for the start (scrolling)
387  EngineID end = min(this->vscroll[side]->GetCapacity() + start, this->engines[side].Length());
388 
389  /* Do the actual drawing */
390  DrawEngineList((VehicleType)this->window_number, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP,
391  &this->engines[side], start, end, this->sel_engine[side], side == 0, this->sel_group);
392  break;
393  }
394  }
395  }
396 
397  virtual void OnPaint()
398  {
399  if (this->engines[0].NeedRebuild() || this->engines[1].NeedRebuild()) this->GenerateLists();
400 
402 
403  /* Disable the "Start Replacing" button if:
404  * Either engines list is empty
405  * or The selected replacement engine has a replacement (to prevent loops). */
406  this->SetWidgetDisabledState(WID_RV_START_REPLACE,
407  this->sel_engine[0] == INVALID_ENGINE || this->sel_engine[1] == INVALID_ENGINE || EngineReplacementForCompany(c, this->sel_engine[1], this->sel_group) != INVALID_ENGINE);
408 
409  /* Disable the "Stop Replacing" button if:
410  * The left engines list (existing vehicle) is empty
411  * or The selected vehicle has no replacement set up */
412  this->SetWidgetDisabledState(WID_RV_STOP_REPLACE, this->sel_engine[0] == INVALID_ENGINE || !EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group));
413 
414  if (this->window_number == VEH_TRAIN) {
415  /* Show the selected railtype in the pulldown menu */
416  this->GetWidget<NWidgetCore>(WID_RV_TRAIN_RAILTYPE_DROPDOWN)->widget_data = sel_railtype == INVALID_RAILTYPE ? STR_REPLACE_ALL_RAILTYPE : GetRailTypeInfo(sel_railtype)->strings.replace_text;
417  }
418 
419  this->DrawWidgets();
420 
421  if (!this->IsShaded()) {
422  int needed_height = this->details_height;
423  /* Draw details panels. */
424  for (int side = 0; side < 2; side++) {
425  if (this->sel_engine[side] != INVALID_ENGINE) {
426  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(side == 0 ? WID_RV_LEFT_DETAILS : WID_RV_RIGHT_DETAILS);
428  nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine[side]);
429  needed_height = max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM);
430  }
431  }
432  if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
433  this->details_height = needed_height;
434  this->ReInit();
435  return;
436  }
437  }
438  }
439 
440  virtual void OnClick(Point pt, int widget, int click_count)
441  {
442  switch (widget) {
444  this->descending_sort_order ^= true;
445  _engine_sort_last_order[this->window_number] = this->descending_sort_order;
446  this->engines[1].ForceRebuild();
447  this->SetDirty();
448  break;
449 
451  this->show_hidden_engines ^= true;
452  _engine_sort_show_hidden_engines[this->window_number] = this->show_hidden_engines;
453  this->engines[1].ForceRebuild();
454  this->SetWidgetLoweredState(widget, this->show_hidden_engines);
455  this->SetDirty();
456  break;
457 
459  DisplayVehicleSortDropDown(this, static_cast<VehicleType>(this->window_number), this->sort_criteria, WID_RV_SORT_DROPDOWN);
460  break;
461 
463  DropDownList *list = new DropDownList();
464  *list->Append() = new DropDownListStringItem(STR_REPLACE_ENGINES, 1, false);
465  *list->Append() = new DropDownListStringItem(STR_REPLACE_WAGONS, 0, false);
466  ShowDropDownList(this, list, this->replace_engines ? 1 : 0, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN);
467  break;
468  }
469 
470  case WID_RV_TRAIN_RAILTYPE_DROPDOWN: // Railtype selection dropdown menu
472  break;
473 
474  case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // toggle renew_keep_length
475  DoCommandP(0, GetCompanySettingIndex("company.renew_keep_length"), Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING);
476  break;
477 
478  case WID_RV_START_REPLACE: { // Start replacing
479  if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
480  this->HandleButtonClick(WID_RV_START_REPLACE);
481  ReplaceClick_StartReplace(false);
482  } else {
483  bool replacment_when_old = EngineHasReplacementWhenOldForCompany(Company::Get(_local_company), this->sel_engine[0], this->sel_group);
484  ShowDropDownMenu(this, _start_replace_dropdown, replacment_when_old ? 1 : 0, WID_RV_START_REPLACE, !this->replace_engines ? 1 << 1 : 0, 0);
485  }
486  break;
487  }
488 
489  case WID_RV_STOP_REPLACE: { // Stop replacing
490  EngineID veh_from = this->sel_engine[0];
491  DoCommandP(0, this->sel_group << 16, veh_from + (INVALID_ENGINE << 16), CMD_SET_AUTOREPLACE);
492  break;
493  }
494 
495  case WID_RV_LEFT_MATRIX:
496  case WID_RV_RIGHT_MATRIX: {
497  byte click_side;
498  if (widget == WID_RV_LEFT_MATRIX) {
499  click_side = 0;
500  } else {
501  click_side = 1;
502  }
503  uint i = this->vscroll[click_side]->GetScrolledRowFromWidget(pt.y, this, widget);
504  size_t engine_count = this->engines[click_side].Length();
505 
506  EngineID e = engine_count > i ? this->engines[click_side][i] : INVALID_ENGINE;
507  if (e == this->sel_engine[click_side]) break; // we clicked the one we already selected
508  this->sel_engine[click_side] = e;
509  if (click_side == 0) {
510  this->engines[1].ForceRebuild();
511  this->reset_sel_engine = true;
512  }
513  this->SetDirty();
514  break;
515  }
516  }
517  }
518 
519  virtual void OnDropdownSelect(int widget, int index)
520  {
521  switch (widget) {
523  if (this->sort_criteria != index) {
524  this->sort_criteria = index;
525  _engine_sort_last_criteria[this->window_number] = this->sort_criteria;
526  this->engines[1].ForceRebuild();
527  this->SetDirty();
528  }
529  break;
530 
532  RailType temp = (RailType)index;
533  if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything
534  sel_railtype = temp;
535  /* Reset scrollbar positions */
536  this->vscroll[0]->SetPosition(0);
537  this->vscroll[1]->SetPosition(0);
538  /* Rebuild the lists */
539  this->engines[0].ForceRebuild();
540  this->engines[1].ForceRebuild();
541  this->reset_sel_engine = true;
542  this->SetDirty();
543  break;
544  }
545 
547  this->replace_engines = index != 0;
548  this->engines[0].ForceRebuild();
549  this->reset_sel_engine = true;
550  this->SetDirty();
551  break;
552  }
553 
555  this->ReplaceClick_StartReplace(index != 0);
556  break;
557  }
558  }
559 
560  virtual void OnResize()
561  {
562  this->vscroll[0]->SetCapacityFromWidget(this, WID_RV_LEFT_MATRIX);
563  this->vscroll[1]->SetCapacityFromWidget(this, WID_RV_RIGHT_MATRIX);
564  }
565 
571  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
572  {
573  if (data != 0) {
574  /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
575  this->engines[0].ForceRebuild();
576  } else {
577  this->engines[1].ForceRebuild();
578  }
579  }
580 };
581 
582 static const NWidgetPart _nested_replace_rail_vehicle_widgets[] = {
584  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
585  NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
586  NWidget(WWT_SHADEBOX, COLOUR_GREY),
587  NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
588  NWidget(WWT_STICKYBOX, COLOUR_GREY),
589  EndContainer(),
591  NWidget(WWT_PANEL, COLOUR_GREY),
592  NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
593  EndContainer(),
594  NWidget(WWT_PANEL, COLOUR_GREY),
595  NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
596  EndContainer(),
597  EndContainer(),
601  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetFill(1, 0), SetResize(1, 0),
602  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_ENGINEWAGON_DROPDOWN), SetDataTip(STR_BLACK_STRING, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
603  EndContainer(),
604  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
605  EndContainer(),
608  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1),
609  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
610  EndContainer(),
612  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP),
613  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
614  EndContainer(),
615  EndContainer(),
616  EndContainer(),
618  NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR),
620  NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR),
622  EndContainer(),
624  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
626  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
627  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_HELP), SetFill(1, 0), SetResize(1, 0),
628  EndContainer(),
629  EndContainer(),
631  NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
632  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0),
633  EndContainer(),
634  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
635  NWidget(WWT_RESIZEBOX, COLOUR_GREY),
636  EndContainer(),
637 };
638 
639 static WindowDesc _replace_rail_vehicle_desc(
640  WDP_AUTO, "replace_vehicle_train", 500, 140,
643  _nested_replace_rail_vehicle_widgets, lengthof(_nested_replace_rail_vehicle_widgets)
644 );
645 
646 static const NWidgetPart _nested_replace_vehicle_widgets[] = {
648  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
649  NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetMinimalSize(433, 14), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
650  NWidget(WWT_SHADEBOX, COLOUR_GREY),
651  NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
652  NWidget(WWT_STICKYBOX, COLOUR_GREY),
653  EndContainer(),
655  NWidget(WWT_PANEL, COLOUR_GREY),
656  NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_VEHICLES_IN_USE, STR_REPLACE_VEHICLE_VEHICLES_IN_USE_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
657  EndContainer(),
658  NWidget(WWT_PANEL, COLOUR_GREY),
659  NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES, STR_REPLACE_VEHICLE_AVAILABLE_VEHICLES_TOOLTIP), SetFill(1, 1), SetMinimalSize(0, 12), SetResize(1, 0),
660  EndContainer(),
661  EndContainer(),
663  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
666  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
667  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
668  EndContainer(),
670  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP),
671  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
672  EndContainer(),
673  EndContainer(),
674  EndContainer(),
676  NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_LEFT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_LEFT_SCROLLBAR),
678  NWidget(WWT_MATRIX, COLOUR_GREY, WID_RV_RIGHT_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetMatrixDataTip(1, 0, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(WID_RV_RIGHT_SCROLLBAR),
680  EndContainer(),
682  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(228, 92), SetResize(1, 0), EndContainer(),
683  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(228, 92), SetResize(1, 0), EndContainer(),
684  EndContainer(),
686  NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
687  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0), EndContainer(),
688  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
689  NWidget(WWT_RESIZEBOX, COLOUR_GREY),
690  EndContainer(),
691 };
692 
693 static WindowDesc _replace_vehicle_desc(
694  WDP_AUTO, "replace_vehicle", 456, 118,
697  _nested_replace_vehicle_widgets, lengthof(_nested_replace_vehicle_widgets)
698 );
699 
706 {
707  DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
708  new ReplaceVehicleWindow(vehicletype == VEH_TRAIN ? &_replace_rail_vehicle_desc : &_replace_vehicle_desc, vehicletype, id_g);
709 }