OpenTTD
autoreplace_gui.cpp
Go to the documentation of this file.
1 /* $Id: autoreplace_gui.cpp 26960 2014-10-05 11:20:02Z peter1138 $ */
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 && show_engines) {
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  {
214  if (vehicletype == VEH_TRAIN) {
215  /* For rail vehicles find the most used vehicle type, which is usually
216  * better than 'just' the first/previous vehicle type. */
217  uint type_count[RAILTYPE_END];
218  memset(type_count, 0, sizeof(type_count));
219 
220  const Engine *e;
221  FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
222  if (e->u.rail.railveh_type == RAILVEH_WAGON) continue;
223  type_count[e->u.rail.railtype] += GetGroupNumEngines(_local_company, id_g, e->index);
224  }
225 
227  for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
228  if (type_count[this->sel_railtype] < type_count[rt]) this->sel_railtype = rt;
229  }
230  }
231 
232  this->replace_engines = true; // start with locomotives (all other vehicles will not read this bool)
233  this->engines[0].ForceRebuild();
234  this->engines[1].ForceRebuild();
235  this->reset_sel_engine = true;
236  this->details_height = ((vehicletype == VEH_TRAIN) ? 10 : 9) * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
237  this->sel_engine[0] = INVALID_ENGINE;
238  this->sel_engine[1] = INVALID_ENGINE;
240 
241  this->CreateNestedTree();
242  this->vscroll[0] = this->GetScrollbar(WID_RV_LEFT_SCROLLBAR);
243  this->vscroll[1] = this->GetScrollbar(WID_RV_RIGHT_SCROLLBAR);
244 
245  NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_RV_SHOW_HIDDEN_ENGINES);
246  widget->widget_data = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN + vehicletype;
247  widget->tool_tip = STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP + vehicletype;
248  widget->SetLowered(this->show_hidden_engines);
249  this->FinishInitNested(vehicletype);
250 
251  this->sort_criteria = _engine_sort_last_criteria[vehicletype];
252  this->descending_sort_order = _engine_sort_last_order[vehicletype];
253  this->owner = _local_company;
254  this->sel_group = id_g;
255  }
256 
257  virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
258  {
259  switch (widget) {
261  Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
262  d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
263  d.height += padding.height;
264  *size = maxdim(*size, d);
265  break;
266  }
267 
268  case WID_RV_LEFT_MATRIX:
269  case WID_RV_RIGHT_MATRIX:
270  resize->height = GetEngineListHeight((VehicleType)this->window_number);
271  size->height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize->height;
272  break;
273 
274  case WID_RV_LEFT_DETAILS:
276  size->height = this->details_height;
277  break;
278 
280  StringID str = this->GetWidget<NWidgetCore>(widget)->widget_data;
281  SetDParam(0, STR_CONFIG_SETTING_ON);
283  SetDParam(0, STR_CONFIG_SETTING_OFF);
284  d = maxdim(d, GetStringBoundingBox(str));
285  d.width += padding.width;
286  d.height += padding.height;
287  *size = maxdim(*size, d);
288  break;
289  }
290 
292  StringID str = this->GetWidget<NWidgetCore>(widget)->widget_data;
293  SetDParam(0, STR_REPLACE_ENGINES);
295  SetDParam(0, STR_REPLACE_WAGONS);
296  d = maxdim(d, GetStringBoundingBox(str));
297  d.width += padding.width;
298  d.height += padding.height;
299  *size = maxdim(*size, d);
300  break;
301  }
302 
303  case WID_RV_INFO_TAB: {
304  Dimension d = GetStringBoundingBox(STR_REPLACE_NOT_REPLACING);
305  d = maxdim(d, GetStringBoundingBox(STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED));
308  *size = maxdim(*size, d);
309  break;
310  }
311 
313  Dimension d = {0, 0};
314  for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
315  const RailtypeInfo *rti = GetRailTypeInfo(rt);
316  /* Skip rail type if it has no label */
317  if (rti->label == 0) continue;
319  }
320  d.width += padding.width;
321  d.height += padding.height;
322  *size = maxdim(*size, d);
323  break;
324  }
325 
326  case WID_RV_START_REPLACE: {
327  Dimension d = GetStringBoundingBox(STR_REPLACE_VEHICLES_START);
328  for (int i = 0; _start_replace_dropdown[i] != INVALID_STRING_ID; i++) {
329  d = maxdim(d, GetStringBoundingBox(_start_replace_dropdown[i]));
330  }
331  d.width += padding.width;
332  d.height += padding.height;
333  *size = maxdim(*size, d);
334  break;
335  }
336  }
337  }
338 
339  virtual void SetStringParameters(int widget) const
340  {
341  switch (widget) {
342  case WID_RV_CAPTION:
343  SetDParam(0, STR_REPLACE_VEHICLE_TRAIN + this->window_number);
344  switch (this->sel_group) {
345  case ALL_GROUP:
346  SetDParam(1, STR_GROUP_ALL_TRAINS + this->window_number);
347  break;
348 
349  case DEFAULT_GROUP:
350  SetDParam(1, STR_GROUP_DEFAULT_TRAINS + this->window_number);
351  break;
352 
353  default:
354  SetDParam(1, STR_GROUP_NAME);
355  SetDParam(2, sel_group);
356  break;
357  }
358  break;
359 
361  SetDParam(0, _engine_sort_listing[this->window_number][this->sort_criteria]);
362  break;
363 
365  const Company *c = Company::Get(_local_company);
366  SetDParam(0, c->settings.renew_keep_length ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
367  break;
368  }
369 
371  SetDParam(0, this->replace_engines ? STR_REPLACE_ENGINES : STR_REPLACE_WAGONS);
372  break;
373  }
374  }
375 
376  virtual void DrawWidget(const Rect &r, int widget) const
377  {
378  switch (widget) {
380  this->DrawSortButtonState(WID_RV_SORT_ASCENDING_DESCENDING, this->descending_sort_order ? SBS_DOWN : SBS_UP);
381  break;
382 
383  case WID_RV_INFO_TAB: {
384  const Company *c = Company::Get(_local_company);
385  StringID str;
386  if (this->sel_engine[0] != INVALID_ENGINE) {
387  if (!EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group)) {
388  str = STR_REPLACE_NOT_REPLACING;
389  } else {
390  bool when_old = false;
391  EngineID e = EngineReplacementForCompany(c, this->sel_engine[0], this->sel_group, &when_old);
392  str = when_old ? STR_REPLACE_REPLACING_WHEN_OLD : STR_ENGINE_NAME;
393  SetDParam(0, e);
394  }
395  } else {
396  str = STR_REPLACE_NOT_REPLACING_VEHICLE_SELECTED;
397  }
398 
399  DrawString(r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_BLACK, SA_HOR_CENTER);
400  break;
401  }
402 
403  case WID_RV_LEFT_MATRIX:
404  case WID_RV_RIGHT_MATRIX: {
405  int side = (widget == WID_RV_LEFT_MATRIX) ? 0 : 1;
406  EngineID start = this->vscroll[side]->GetPosition(); // what is the offset for the start (scrolling)
407  EngineID end = min(this->vscroll[side]->GetCapacity() + start, this->engines[side].Length());
408 
409  /* Do the actual drawing */
410  DrawEngineList((VehicleType)this->window_number, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP,
411  &this->engines[side], start, end, this->sel_engine[side], side == 0, this->sel_group);
412  break;
413  }
414  }
415  }
416 
417  virtual void OnPaint()
418  {
419  if (this->engines[0].NeedRebuild() || this->engines[1].NeedRebuild()) this->GenerateLists();
420 
422 
423  /* Disable the "Start Replacing" button if:
424  * Either engines list is empty
425  * or The selected replacement engine has a replacement (to prevent loops). */
426  this->SetWidgetDisabledState(WID_RV_START_REPLACE,
427  this->sel_engine[0] == INVALID_ENGINE || this->sel_engine[1] == INVALID_ENGINE || EngineReplacementForCompany(c, this->sel_engine[1], this->sel_group) != INVALID_ENGINE);
428 
429  /* Disable the "Stop Replacing" button if:
430  * The left engines list (existing vehicle) is empty
431  * or The selected vehicle has no replacement set up */
432  this->SetWidgetDisabledState(WID_RV_STOP_REPLACE, this->sel_engine[0] == INVALID_ENGINE || !EngineHasReplacementForCompany(c, this->sel_engine[0], this->sel_group));
433 
434  if (this->window_number == VEH_TRAIN) {
435  /* sets the colour of that art thing */
436  this->GetWidget<NWidgetCore>(WID_RV_TRAIN_FLUFF_LEFT)->colour = _company_colours[_local_company];
437  this->GetWidget<NWidgetCore>(WID_RV_TRAIN_FLUFF_RIGHT)->colour = _company_colours[_local_company];
438 
439  /* Show the selected railtype in the pulldown menu */
440  this->GetWidget<NWidgetCore>(WID_RV_TRAIN_RAILTYPE_DROPDOWN)->widget_data = GetRailTypeInfo(sel_railtype)->strings.replace_text;
441  }
442 
443  this->DrawWidgets();
444 
445  if (!this->IsShaded()) {
446  int needed_height = this->details_height;
447  /* Draw details panels. */
448  for (int side = 0; side < 2; side++) {
449  if (this->sel_engine[side] != INVALID_ENGINE) {
450  NWidgetBase *nwi = this->GetWidget<NWidgetBase>(side == 0 ? WID_RV_LEFT_DETAILS : WID_RV_RIGHT_DETAILS);
452  nwi->pos_y + WD_FRAMERECT_TOP, this->sel_engine[side]);
453  needed_height = max(needed_height, text_end - (int)nwi->pos_y + WD_FRAMERECT_BOTTOM);
454  }
455  }
456  if (needed_height != this->details_height) { // Details window are not high enough, enlarge them.
457  this->details_height = needed_height;
458  this->ReInit();
459  return;
460  }
461  }
462  }
463 
464  virtual void OnClick(Point pt, int widget, int click_count)
465  {
466  switch (widget) {
468  this->descending_sort_order ^= true;
469  _engine_sort_last_order[this->window_number] = this->descending_sort_order;
470  this->engines[1].ForceRebuild();
471  this->SetDirty();
472  break;
473 
475  this->show_hidden_engines ^= true;
476  _engine_sort_show_hidden_engines[this->window_number] = this->show_hidden_engines;
477  this->engines[1].ForceRebuild();
478  this->SetWidgetLoweredState(widget, this->show_hidden_engines);
479  this->SetDirty();
480  break;
481 
483  DisplayVehicleSortDropDown(this, static_cast<VehicleType>(this->window_number), this->sort_criteria, WID_RV_SORT_DROPDOWN);
484  break;
485 
487  this->replace_engines = !(this->replace_engines);
488  this->engines[0].ForceRebuild();
489  this->reset_sel_engine = true;
490  this->SetDirty();
491  break;
492 
493  case WID_RV_TRAIN_RAILTYPE_DROPDOWN: // Railtype selection dropdown menu
495  break;
496 
497  case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: // toggle renew_keep_length
498  DoCommandP(0, GetCompanySettingIndex("company.renew_keep_length"), Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1, CMD_CHANGE_COMPANY_SETTING);
499  break;
500 
501  case WID_RV_START_REPLACE: { // Start replacing
502  if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
503  this->HandleButtonClick(WID_RV_START_REPLACE);
504  ReplaceClick_StartReplace(false);
505  } else {
506  bool replacment_when_old = EngineHasReplacementWhenOldForCompany(Company::Get(_local_company), this->sel_engine[0], this->sel_group);
507  ShowDropDownMenu(this, _start_replace_dropdown, replacment_when_old ? 1 : 0, WID_RV_START_REPLACE, !this->replace_engines ? 1 << 1 : 0, 0);
508  }
509  break;
510  }
511 
512  case WID_RV_STOP_REPLACE: { // Stop replacing
513  EngineID veh_from = this->sel_engine[0];
514  DoCommandP(0, this->sel_group << 16, veh_from + (INVALID_ENGINE << 16), CMD_SET_AUTOREPLACE);
515  break;
516  }
517 
518  case WID_RV_LEFT_MATRIX:
519  case WID_RV_RIGHT_MATRIX: {
520  byte click_side;
521  if (widget == WID_RV_LEFT_MATRIX) {
522  click_side = 0;
523  } else {
524  click_side = 1;
525  }
526  uint i = this->vscroll[click_side]->GetScrolledRowFromWidget(pt.y, this, widget);
527  size_t engine_count = this->engines[click_side].Length();
528 
529  EngineID e = engine_count > i ? this->engines[click_side][i] : INVALID_ENGINE;
530  if (e == this->sel_engine[click_side]) break; // we clicked the one we already selected
531  this->sel_engine[click_side] = e;
532  if (click_side == 0) {
533  this->engines[1].ForceRebuild();
534  this->reset_sel_engine = true;
535  }
536  this->SetDirty();
537  break;
538  }
539  }
540  }
541 
542  virtual void OnDropdownSelect(int widget, int index)
543  {
544  switch (widget) {
546  if (this->sort_criteria != index) {
547  this->sort_criteria = index;
548  _engine_sort_last_criteria[this->window_number] = this->sort_criteria;
549  this->engines[1].ForceRebuild();
550  this->SetDirty();
551  }
552  break;
553 
555  RailType temp = (RailType)index;
556  if (temp == sel_railtype) return; // we didn't select a new one. No need to change anything
557  sel_railtype = temp;
558  /* Reset scrollbar positions */
559  this->vscroll[0]->SetPosition(0);
560  this->vscroll[1]->SetPosition(0);
561  /* Rebuild the lists */
562  this->engines[0].ForceRebuild();
563  this->engines[1].ForceRebuild();
564  this->reset_sel_engine = true;
565  this->SetDirty();
566  break;
567  }
568 
570  this->ReplaceClick_StartReplace(index != 0);
571  break;
572  }
573  }
574 
575  virtual void OnResize()
576  {
577  this->vscroll[0]->SetCapacityFromWidget(this, WID_RV_LEFT_MATRIX);
578  this->vscroll[1]->SetCapacityFromWidget(this, WID_RV_RIGHT_MATRIX);
579  }
580 
586  virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
587  {
588  if (data != 0) {
589  /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
590  this->engines[0].ForceRebuild();
591  } else {
592  this->engines[1].ForceRebuild();
593  }
594  }
595 };
596 
597 static const NWidgetPart _nested_replace_rail_vehicle_widgets[] = {
599  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
600  NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
601  NWidget(WWT_SHADEBOX, COLOUR_GREY),
602  NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
603  NWidget(WWT_STICKYBOX, COLOUR_GREY),
604  EndContainer(),
606  NWidget(WWT_PANEL, COLOUR_GREY),
607  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),
608  EndContainer(),
609  NWidget(WWT_PANEL, COLOUR_GREY),
610  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),
611  EndContainer(),
612  EndContainer(),
614  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
617  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER), SetFill(1, 1),
618  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
619  EndContainer(),
621  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP),
622  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
623  EndContainer(),
624  EndContainer(),
625  EndContainer(),
627  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),
629  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),
631  EndContainer(),
633  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
634  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
635  EndContainer(),
637  NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
638  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0),
639  EndContainer(),
640  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(150, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
641  EndContainer(),
643  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_ENGINEWAGON_TOGGLE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_ENGINE_WAGON_SELECT, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
645  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(136, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetResize(1, 0),
647  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_HELP),
648  NWidget(WWT_RESIZEBOX, COLOUR_GREY),
649  EndContainer(),
650 };
651 
652 static WindowDesc _replace_rail_vehicle_desc(
653  WDP_AUTO, "replace_vehicle_train", 500, 140,
656  _nested_replace_rail_vehicle_widgets, lengthof(_nested_replace_rail_vehicle_widgets)
657 );
658 
659 static const NWidgetPart _nested_replace_vehicle_widgets[] = {
661  NWidget(WWT_CLOSEBOX, COLOUR_GREY),
662  NWidget(WWT_CAPTION, COLOUR_GREY, WID_RV_CAPTION), SetMinimalSize(433, 14), SetDataTip(STR_REPLACE_VEHICLES_WHITE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
663  NWidget(WWT_SHADEBOX, COLOUR_GREY),
664  NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
665  NWidget(WWT_STICKYBOX, COLOUR_GREY),
666  EndContainer(),
668  NWidget(WWT_PANEL, COLOUR_GREY),
669  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),
670  EndContainer(),
671  NWidget(WWT_PANEL, COLOUR_GREY),
672  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),
673  EndContainer(),
674  EndContainer(),
676  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), EndContainer(),
679  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_SORT_ASCENDING_DESCENDING), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
680  NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_RV_SORT_DROPDOWN), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_SORT_CRITERIA),
681  EndContainer(),
683  NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_RV_SHOW_HIDDEN_ENGINES), SetDataTip(STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN, STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN_TOOLTIP),
684  NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(),
685  EndContainer(),
686  EndContainer(),
687  EndContainer(),
689  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),
691  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),
693  EndContainer(),
695  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(228, 92), SetResize(1, 0), EndContainer(),
696  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(228, 92), SetResize(1, 0), EndContainer(),
697  EndContainer(),
699  NWidget(NWID_PUSHBUTTON_DROPDOWN, COLOUR_GREY, WID_RV_START_REPLACE), SetMinimalSize(139, 12), SetDataTip(STR_REPLACE_VEHICLES_START, STR_REPLACE_HELP_START_BUTTON),
700  NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_INFO_TAB), SetMinimalSize(167, 12), SetDataTip(0x0, STR_REPLACE_HELP_REPLACE_INFO_TAB), SetResize(1, 0), EndContainer(),
701  NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_STOP_REPLACE), SetMinimalSize(138, 12), SetDataTip(STR_REPLACE_VEHICLES_STOP, STR_REPLACE_HELP_STOP_BUTTON),
702  NWidget(WWT_RESIZEBOX, COLOUR_GREY),
703  EndContainer(),
704 };
705 
706 static WindowDesc _replace_vehicle_desc(
707  WDP_AUTO, "replace_vehicle", 456, 118,
710  _nested_replace_vehicle_widgets, lengthof(_nested_replace_vehicle_widgets)
711 );
712 
719 {
720  DeleteWindowById(WC_REPLACE_VEHICLE, vehicletype);
721  new ReplaceVehicleWindow(vehicletype == VEH_TRAIN ? &_replace_rail_vehicle_desc : &_replace_vehicle_desc, vehicletype, id_g);
722 }