Update readme and changelog for v1.26.0
[openttd-joker.git] / src / tbtr_template_gui_main.cpp
blobaef3c39f6a63fe80b2e17a464f989cb20a834dd8
1 // mygui.c
4 //#include "tbtr_mygui.h"
5 #include <iostream>
6 #include <stdio.h>
9 #include "stdafx.h"
10 #include "command_func.h"
11 #include "vehicle_gui.h"
12 #include "newgrf_engine.h"
13 #include "group.h"
14 #include "rail.h"
15 #include "strings_func.h"
16 #include "window_func.h"
17 #include "autoreplace_func.h"
18 #include "company_func.h"
19 #include "engine_base.h"
20 #include "window_gui.h"
21 #include "viewport_func.h"
22 #include "tilehighlight_func.h"
23 #include "engine_gui.h"
24 #include "settings_func.h"
25 #include "core/geometry_func.hpp"
26 #include "rail_gui.h"
27 #include "network/network.h"
28 #include "zoom_func.h"
30 #include "table/sprites.h"
31 #include "table/strings.h"
33 // test creating pool -> creating vehicles
34 #include "core/pool_func.hpp"
36 #include "vehicle_gui_base.h"
37 #include "vehicle_base.h"
38 #include "train.h"
39 #include "vehicle_func.h"
41 #include "gfx_type.h"
43 #include "engine_func.h"
45 // drawing the vehicle length based on occupied tiles
46 #include "spritecache.h"
48 #include "tbtr_template_gui_main.h"
49 #include "tbtr_template_gui_create.h"
50 #include "tbtr_template_vehicle.h"
51 //#include "tbtr_template_vehicle_func.h"
54 typedef GUIList<const Group*> GUIGroupList;
57 /* template creation */
58 void ShowTemplateCreationWindow(TileIndex);
59 void ShowTestWindow();
61 enum TemplateReplaceWindowWidgets {
62 TRW_CAPTION,
64 TRW_WIDGET_INSET_GROUPS,
65 TRW_WIDGET_TOP_MATRIX,
66 TRW_WIDGET_TOP_SCROLLBAR,
68 TRW_WIDGET_INSET_TEMPLATES,
69 TRW_WIDGET_BOTTOM_MATRIX,
70 TRW_WIDGET_MIDDLE_SCROLLBAR,
71 TRW_WIDGET_BOTTOM_SCROLLBAR,
73 TRW_WIDGET_TMPL_INFO_INSET,
74 TRW_WIDGET_TMPL_INFO_PANEL,
76 TRW_WIDGET_TMPL_PRE_BUTTON_FLUFF,
78 TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE,
79 TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP,
80 TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT,
81 TRW_WIDGET_TMPL_BUTTONS_CONFIG_RIGHTPANEL,
83 TRW_WIDGET_TMPL_BUTTONS_DEFINE,
84 TRW_WIDGET_TMPL_BUTTONS_EDIT,
85 TRW_WIDGET_TMPL_BUTTONS_CLONE,
86 TRW_WIDGET_TMPL_BUTTONS_DELETE,
87 TRW_WIDGET_TMPL_BUTTONS_EDIT_RIGHTPANEL,
89 TRW_WIDGET_TITLE_INFO_GROUP,
90 TRW_WIDGET_TITLE_INFO_TEMPLATE,
92 TRW_WIDGET_INFO_GROUP,
93 TRW_WIDGET_INFO_TEMPLATE,
95 TRW_WIDGET_TMPL_BUTTONS_SPACER,
97 TRW_WIDGET_START,
98 TRW_WIDGET_TRAIN_FLUFF_LEFT,
99 TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN,
100 TRW_WIDGET_TRAIN_FLUFF_RIGHT,
101 TRW_WIDGET_STOP,
103 TRW_WIDGET_SEL_TMPL_DISPLAY_CREATE,
107 static const NWidgetPart _widgets[] = {
108 // Title bar
109 NWidget(NWID_HORIZONTAL),
110 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
111 NWidget(WWT_CAPTION, COLOUR_GREY, TRW_CAPTION), SetDataTip(STR_TMPL_RPL_TITLE, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
112 NWidget(WWT_SHADEBOX, COLOUR_GREY),
113 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
114 NWidget(WWT_STICKYBOX, COLOUR_GREY),
115 EndContainer(),
116 //Top Matrix
117 NWidget(NWID_VERTICAL),
118 NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_INSET_GROUPS), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetResize(1, 0), EndContainer(),
119 NWidget(NWID_HORIZONTAL),
120 NWidget(WWT_MATRIX, COLOUR_GREY, TRW_WIDGET_TOP_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetDataTip(0x1, STR_REPLACE_HELP_LEFT_ARRAY), SetResize(1, 0), SetScrollbar(TRW_WIDGET_TOP_SCROLLBAR),
121 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_TOP_SCROLLBAR),
122 EndContainer(),
123 EndContainer(),
124 // Template Display
125 NWidget(NWID_VERTICAL),
126 NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_INSET_TEMPLATES), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetResize(1, 0), EndContainer(),
127 NWidget(NWID_HORIZONTAL),
128 NWidget(WWT_MATRIX, COLOUR_GREY, TRW_WIDGET_BOTTOM_MATRIX), SetMinimalSize(216, 0), SetFill(1, 1), SetDataTip(0x1, STR_REPLACE_HELP_RIGHT_ARRAY), SetResize(1, 1), SetScrollbar(TRW_WIDGET_MIDDLE_SCROLLBAR),
129 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_MIDDLE_SCROLLBAR),
130 EndContainer(),
131 EndContainer(),
132 // Info Area
133 NWidget(NWID_VERTICAL),
134 NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_INFO_INSET), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetResize(1,0), EndContainer(),
135 NWidget(NWID_HORIZONTAL),
136 NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_INFO_PANEL), SetMinimalSize(216,120), SetResize(1,0), SetScrollbar(TRW_WIDGET_BOTTOM_SCROLLBAR), EndContainer(),
137 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TRW_WIDGET_BOTTOM_SCROLLBAR),
138 EndContainer(),
139 EndContainer(),
140 // Control Area
141 NWidget(NWID_VERTICAL),
142 // Spacing
143 NWidget(WWT_INSET, COLOUR_GREY, TRW_WIDGET_TMPL_PRE_BUTTON_FLUFF), SetMinimalSize(139, 12), SetResize(1,0), EndContainer(),
144 // Config buttons
145 NWidget(NWID_HORIZONTAL),
146 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_SET_USEDEPOT, STR_TMPL_SET_USEDEPOT_TIP),
147 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_SET_KEEPREMAINDERS, STR_TMPL_SET_KEEPREMAINDERS_TIP),
148 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_SET_REFIT, STR_TMPL_SET_REFIT_TIP),
149 NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CONFIG_RIGHTPANEL), SetMinimalSize(12,12), SetResize(1,0), EndContainer(),
150 EndContainer(),
151 // Edit buttons
152 NWidget(NWID_HORIZONTAL),
153 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_DEFINE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_DEFINE_TEMPLATE, STR_TMPL_DEFINE_TEMPLATE),
154 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_EDIT), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_EDIT_TEMPLATE, STR_TMPL_EDIT_TEMPLATE),
155 NWidget(WWT_TEXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_CLONE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_CREATE_CLONE_VEH, STR_TMPL_CREATE_CLONE_VEH),
156 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_DELETE), SetMinimalSize(75,12), SetResize(0,0), SetDataTip(STR_TMPL_DELETE_TEMPLATE, STR_TMPL_DELETE_TEMPLATE),
157 /*NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_RPLALL), SetMinimalSize(150,12), SetResize(0,0), SetDataTip(STR_TMPL_RPL_ALL_TMPL, STR_TMPL_RPL_ALL_TMPL),*/
158 NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TMPL_BUTTONS_EDIT_RIGHTPANEL), SetMinimalSize(50,12), SetResize(1,0), EndContainer(),
159 EndContainer(),
160 EndContainer(),
161 // Start/Stop buttons
162 NWidget(NWID_HORIZONTAL),
163 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_START), SetMinimalSize(150, 12), SetDataTip(STR_TMPL_RPL_START, STR_REPLACE_ENGINE_WAGON_SELECT_HELP),
164 NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TRAIN_FLUFF_LEFT), SetMinimalSize(15, 12), EndContainer(),
165 NWidget(WWT_DROPDOWN, COLOUR_GREY, TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN), SetMinimalSize(150, 12), SetDataTip(0x0, STR_REPLACE_HELP_RAILTYPE), SetResize(1, 0),
166 NWidget(WWT_PANEL, COLOUR_GREY, TRW_WIDGET_TRAIN_FLUFF_RIGHT), SetMinimalSize(16, 12), EndContainer(),
167 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, TRW_WIDGET_STOP), SetMinimalSize(150, 12), SetDataTip(STR_TMPL_RPL_STOP, STR_REPLACE_REMOVE_WAGON_HELP),
168 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
169 EndContainer(),
172 static WindowDesc _replace_rail_vehicle_desc(
173 WDP_AUTO,
174 "template replace window",
175 456, 156,
176 WC_TEMPLATEGUI_MAIN,
177 WC_NONE, // parent window class
178 WDF_CONSTRUCTION,
179 _widgets, lengthof(_widgets)
182 class TemplateReplaceWindow : public Window {
183 private:
185 GUIGroupList groups; ///< List of groups
186 byte unitnumber_digits;
188 SmallVector<int, 16> indents; ///< Indentation levels
190 short line_height;
191 short matrixContentLeftMargin;
193 int details_height; ///< Minimal needed height of the details panels (found so far).
194 RailType sel_railtype; ///< Type of rail tracks selected.
195 Scrollbar *vscroll[3];
196 // listing/sorting continued
197 GUITemplateList templates;
198 GUITemplateList::SortFunction **template_sorter_funcs;
200 short selected_template_index;
201 short selected_group_index;
203 bool editInProgress;
205 public:
206 TemplateReplaceWindow(WindowDesc *wdesc, byte dig, int step_h) : Window(wdesc)
208 // listing/sorting
209 templates.SetSortFuncs(this->template_sorter_funcs);
211 // From BaseVehicleListWindow
212 this->unitnumber_digits = dig;
214 /* Find the most used vehicle type, which is usually
215 * better than 'just' the first/previous vehicle type. */
216 uint type_count[RAILTYPE_END];
217 memset(type_count, 0, sizeof(type_count));
219 const Engine *e;
220 FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
221 if (e->u.rail.railveh_type == RAILVEH_WAGON) continue;
222 type_count[e->u.rail.railtype] += GetGroupNumEngines(_local_company, ALL_GROUP, e->index);
225 this->sel_railtype = INVALID_RAILTYPE;
227 this->details_height = 10 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
229 this->line_height = step_h;
231 this->CreateNestedTree(wdesc != nullptr);
232 this->vscroll[0] = this->GetScrollbar(TRW_WIDGET_TOP_SCROLLBAR);
233 this->vscroll[1] = this->GetScrollbar(TRW_WIDGET_MIDDLE_SCROLLBAR);
234 this->vscroll[2] = this->GetScrollbar(TRW_WIDGET_BOTTOM_SCROLLBAR);
235 this->FinishInitNested(VEH_TRAIN);
237 this->owner = _local_company;
239 this->groups.ForceRebuild();
240 this->groups.NeedResort();
241 this->BuildGroupList(_local_company);
243 this->matrixContentLeftMargin = 40;
244 this->selected_template_index = -1;
245 this->selected_group_index = -1;
247 this->UpdateButtonState();
249 this->editInProgress = false;
251 //this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_RPLALL, _networking);
253 this->templates.ForceRebuild();
255 BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
258 ~TemplateReplaceWindow() {
259 DeleteWindowById(WC_CREATE_TEMPLATE, this->window_number);
262 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
264 switch (widget) {
265 case TRW_WIDGET_TOP_MATRIX:
266 resize->height = GetVehicleListHeight(VEH_TRAIN, FONT_HEIGHT_NORMAL + WD_MATRIX_TOP) / 2;
267 size->height = 8 * resize->height;
268 break;
269 case TRW_WIDGET_BOTTOM_MATRIX:
270 resize->height = GetVehicleListHeight(VEH_TRAIN, FONT_HEIGHT_NORMAL + WD_MATRIX_TOP);
271 size->height = 4 * resize->height;
272 break;
273 case TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: {
274 Dimension d = {0, 0};
275 for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
276 const RailtypeInfo *rti = GetRailTypeInfo(rt);
277 // Skip rail type if it has no label
278 if (rti->label == 0) continue;
279 d = maxdim(d, GetStringBoundingBox(rti->strings.replace_text));
281 d.width += padding.width;
282 d.height += padding.height;
283 *size = maxdim(*size, d);
284 break;
286 default:
287 size->width = ScaleGUITrad(size->width);
288 break;
292 virtual void SetStringParameters(int widget) const
294 switch (widget) {
295 case TRW_CAPTION:
296 SetDParam(0, STR_TMPL_RPL_TITLE);
297 break;
301 virtual void DrawWidget(const Rect &r, int widget) const
303 switch (widget) {
304 case TRW_WIDGET_TOP_MATRIX: {
305 DrawAllGroupsFunction(this->line_height, r);
306 break;
308 case TRW_WIDGET_BOTTOM_MATRIX: {
309 DrawTemplateList(this->line_height, r);
310 break;
312 case TRW_WIDGET_TMPL_INFO_PANEL: {
313 DrawTemplateInfo(this->line_height, r);
314 break;
316 case TRW_WIDGET_INSET_GROUPS: {
317 DrawString(r.left + 2, r.right - 2, r.top + 2, STR_TMPL_MAINGUI_DEFINEDGROUPS);
318 break;
320 case TRW_WIDGET_INSET_TEMPLATES: {
321 DrawString(r.left + 2, r.right - 2, r.top + 2, STR_TMPL_AVAILABLE_TEMPLATES);
322 break;
324 case TRW_WIDGET_TMPL_INFO_INSET: {
325 DrawString(r.left + 2, r.right - 2, r.top + 2, STR_TMPL_TEMPLATE_INFO);
326 break;
331 virtual void OnPaint()
333 BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
335 this->BuildGroupList(_local_company);
337 /* sets the colour of that art thing */
338 this->GetWidget<NWidgetCore>(TRW_WIDGET_TRAIN_FLUFF_LEFT)->colour = _company_colours[_local_company];
339 this->GetWidget<NWidgetCore>(TRW_WIDGET_TRAIN_FLUFF_RIGHT)->colour = _company_colours[_local_company];
341 /* Show the selected railtype in the pulldown menu */
342 this->GetWidget<NWidgetCore>(TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN)->widget_data =
343 (sel_railtype == INVALID_RAILTYPE) ? STR_REPLACE_ALL_RAILTYPE : GetRailTypeInfo(sel_railtype)->strings.replace_text;
346 if ((this->selected_template_index < 0) || (this->selected_template_index >= (short)this->templates.Length())) {
347 this->vscroll[2]->SetCount(24);
348 } else {
349 const TemplateVehicle *tmp = this->templates[this->selected_template_index];
351 uint min_height = 30;
352 uint height = 30;
353 CargoArray cargo_caps;
354 short count_columns = 0;
355 short max_columns = 2;
357 for ( ; tmp; tmp=tmp->Next()) {
358 cargo_caps[tmp->cargo_type] += tmp->cargo_cap;
361 for (CargoID i = 0; i < NUM_CARGO; ++i) {
362 if ( cargo_caps[i] > 0 ) {
363 if (count_columns % max_columns == 0) {
364 height += this->line_height/3;
367 ++count_columns;
371 min_height = max(min_height, height);
372 this->vscroll[2]->SetCount(min_height);
375 this->DrawWidgets();
378 virtual void OnClick(Point pt, int widget, int click_count)
380 if ( this->editInProgress ) return;
382 switch (widget) {
383 case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE: {
384 if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length())) {
385 uint32 template_index = ((this->templates)[selected_template_index])->index;
387 DoCommandP(0, template_index, 0, CMD_TOGGLE_REUSE_DEPOT_VEHICLES, nullptr);
389 break;
391 case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP: {
392 if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length())) {
393 uint32 template_index = ((this->templates)[selected_template_index])->index;
395 DoCommandP(0, template_index, 0, CMD_TOGGLE_KEEP_REMAINING_VEHICLES, nullptr);
397 break;
399 case TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT: {
400 if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length())) {
401 uint32 template_index = ((this->templates)[selected_template_index])->index;
403 DoCommandP(0, template_index, 0, CMD_TOGGLE_REFIT_AS_TEMPLATE, nullptr);
405 break;
407 case TRW_WIDGET_TMPL_BUTTONS_DEFINE: {
408 editInProgress = true;
409 ShowTemplateCreateWindow(0, &editInProgress, this->line_height);
410 UpdateButtonState();
411 break;
413 case TRW_WIDGET_TMPL_BUTTONS_EDIT: {
414 if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length())) {
415 editInProgress = true;
416 TemplateVehicle *sel = TemplateVehicle::Get(((this->templates)[selected_template_index])->index);
417 ShowTemplateCreateWindow(sel, &editInProgress, this->line_height);
418 UpdateButtonState();
420 break;
422 case TRW_WIDGET_TMPL_BUTTONS_CLONE: {
423 this->SetWidgetDirty(TRW_WIDGET_TMPL_BUTTONS_CLONE);
424 this->ToggleWidgetLoweredState(TRW_WIDGET_TMPL_BUTTONS_CLONE);
426 if (this->IsWidgetLowered(TRW_WIDGET_TMPL_BUTTONS_CLONE)) {
427 static const CursorID clone_icon = SPR_CURSOR_CLONE_TRAIN;
428 SetObjectToPlaceWnd(clone_icon, PAL_NONE, HT_VEHICLE, this);
429 } else {
430 ResetObjectToPlace();
432 break;
434 case TRW_WIDGET_TMPL_BUTTONS_DELETE:
435 if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length()) && !editInProgress) {
437 uint32 template_index = ((this->templates)[selected_template_index])->index;
439 bool succeeded = DoCommandP(0, template_index, 0, CMD_DELETE_TEMPLATE_VEHICLE, nullptr);
441 if (succeeded) {
442 BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
443 selected_template_index = -1;
446 break;
447 //case TRW_WIDGET_TMPL_BUTTONS_RPLALL: {
448 // ShowTemplateReplaceAllGui();
449 // break;
451 case TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN: // Railtype selection dropdown menu
452 ShowDropDownList(this, GetRailTypeDropDownList(true, true), sel_railtype, TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN);
453 break;
455 case TRW_WIDGET_TOP_MATRIX: {
456 uint16 newindex = (uint16)((pt.y - this->nested_array[TRW_WIDGET_TOP_MATRIX]->pos_y) / (this->line_height/2) ) + this->vscroll[0]->GetPosition();
457 if ( newindex == this->selected_group_index || newindex >= this->groups.Length() ) {
458 this->selected_group_index = -1;
460 else if ((newindex >= 0) && (newindex < this->groups.Length())) {
461 this->selected_group_index = newindex;
463 this->UpdateButtonState();
464 break;
466 case TRW_WIDGET_BOTTOM_MATRIX: {
467 uint16 newindex = (uint16)((pt.y - this->nested_array[TRW_WIDGET_BOTTOM_MATRIX]->pos_y) / this->line_height) + this->vscroll[1]->GetPosition();
468 if ( newindex == this->selected_template_index || newindex >= templates.Length() ) {
469 this->selected_template_index = -1;
471 else if ((newindex >= 0) && (newindex < templates.Length())) {
472 this->selected_template_index = newindex;
474 this->UpdateButtonState();
475 break;
477 case TRW_WIDGET_START: {
478 if ((this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length()) &&
479 (this->selected_group_index >= 0) && (this->selected_group_index < (short)this->groups.Length())) {
480 uint32 tv_index = ((this->templates)[selected_template_index])->index;
481 int current_group_index = (this->groups)[this->selected_group_index]->index;
483 DoCommandP(0, current_group_index, tv_index, CMD_ISSUE_TEMPLATE_REPLACEMENT, nullptr);
484 this->UpdateButtonState();
486 break;
488 case TRW_WIDGET_STOP:
489 if ((this->selected_group_index < 0) || (this->selected_group_index >= (short)this->groups.Length()))
490 return;
492 int current_group_index = (this->groups)[this->selected_group_index]->index;
494 DoCommandP(0, current_group_index, 0, CMD_DELETE_TEMPLATE_REPLACEMENT, nullptr);
495 this->UpdateButtonState();
496 break;
498 this->SetDirty();
501 virtual bool OnVehicleSelect(const Vehicle *v)
503 bool succeeded = DoCommandP(0, v->index, 0, CMD_CLONE_TEMPLATE_VEHICLE_FROM_TRAIN | CMD_MSG(STR_TMPL_CANT_CREATE), nullptr);
505 if (!succeeded) return false;
507 BuildTemplateGuiList(&this->templates, vscroll[1], _local_company, this->sel_railtype);
508 this->ToggleWidgetLoweredState(TRW_WIDGET_TMPL_BUTTONS_CLONE);
509 ResetObjectToPlace();
510 this->SetDirty();
512 return true;
515 virtual void OnPlaceObjectAbort()
517 this->RaiseButtons();
520 virtual void OnDropdownSelect(int widget, int index)
522 RailType temp = (RailType)index;
523 if (temp == this->sel_railtype) return; // we didn't select a new one. No need to change anything
524 this->sel_railtype = temp;
525 /* Reset scrollbar positions */
526 this->vscroll[0]->SetPosition(0);
527 this->vscroll[1]->SetPosition(0);
528 BuildTemplateGuiList(&this->templates, this->vscroll[1], this->owner, this->sel_railtype);
529 this->SetDirty();
532 virtual void OnResize()
534 /* Top Matrix */
535 NWidgetCore *nwi = this->GetWidget<NWidgetCore>(TRW_WIDGET_TOP_MATRIX);
536 this->vscroll[0]->SetCapacityFromWidget(this, TRW_WIDGET_TOP_MATRIX);
537 nwi->widget_data = (this->vscroll[0]->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
538 /* Bottom Matrix */
539 NWidgetCore *nwi2 = this->GetWidget<NWidgetCore>(TRW_WIDGET_BOTTOM_MATRIX);
540 this->vscroll[1]->SetCapacityFromWidget(this, TRW_WIDGET_BOTTOM_MATRIX);
541 nwi2->widget_data = (this->vscroll[1]->GetCapacity() << MAT_ROW_START) + (1 << MAT_COL_START);
542 /* Info panel */
543 NWidgetCore *nwi3 = this->GetWidget<NWidgetCore>(TRW_WIDGET_TMPL_INFO_PANEL);
544 this->vscroll[2]->SetCapacity(nwi3->current_y);
547 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
549 this->groups.ForceRebuild();
550 this->templates.ForceRebuild();
551 this->UpdateButtonState();
552 this->SetDirty();
555 /** For a given group (id) find the template that is issued for template replacement for this group and return this template's index
556 * from the gui list */
557 short FindTemplateIndexForGroup(short gid) const
559 TemplateReplacement *tr = GetTemplateReplacementByGroupID(gid);
560 if ( !tr )
561 return -1;
563 for ( uint32 i=0; i<this->templates.Length(); ++i )
564 if ( templates[i]->index == tr->sel_template )
565 return i;
566 return -1;
569 void AddParents(GUIGroupList *source, GroupID parent, int indent)
571 for (const Group **g = source->Begin(); g != source->End(); g++) {
572 if ((*g)->parent == parent) {
573 *this->groups.Append() = *g;
574 *this->indents.Append() = indent;
575 AddParents(source, (*g)->index, indent + 1);
580 /** Sort the groups by their name */
581 static int CDECL GroupNameSorter(const Group * const *a, const Group * const *b)
583 static const Group *last_group[2] = { nullptr, nullptr };
584 static char last_name[2][64] = { "", "" };
586 if (*a != last_group[0]) {
587 last_group[0] = *a;
588 SetDParam(0, (*a)->index);
589 GetString(last_name[0], STR_GROUP_NAME, lastof(last_name[0]));
592 if (*b != last_group[1]) {
593 last_group[1] = *b;
594 SetDParam(0, (*b)->index);
595 GetString(last_name[1], STR_GROUP_NAME, lastof(last_name[1]));
598 int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting).
599 if (r == 0) return (*a)->index - (*b)->index;
600 return r;
603 void BuildGroupList(Owner owner)
605 if (!this->groups.NeedRebuild()) return;
607 this->groups.Clear();
608 this->indents.Clear();
610 GUIGroupList list;
612 const Group *g;
613 FOR_ALL_GROUPS(g) {
614 if (g->owner == owner && g->vehicle_type == VEH_TRAIN) {
615 *list.Append() = g;
619 list.ForceResort();
620 list.Sort(&GroupNameSorter);
622 AddParents(&list, INVALID_GROUP, 0);
624 this->groups.Compact();
625 this->groups.RebuildDone();
626 this->vscroll[0]->SetCount(groups.Length());
629 void DrawAllGroupsFunction(int line_height, const Rect &r) const
631 int left = r.left + WD_MATRIX_LEFT;
632 int right = r.right - WD_MATRIX_RIGHT;
633 int y = r.top;
634 int max = min(this->vscroll[0]->GetPosition() + this->vscroll[0]->GetCapacity(), this->groups.Length());
636 /* Then treat all groups defined by/for the current company */
637 for ( int i=this->vscroll[0]->GetPosition(); i<max; ++i ) {
638 const Group *g = (this->groups)[i];
639 short g_id = g->index;
641 /* Fill the background of the current cell in a darker tone for the currently selected template */
642 if ( this->selected_group_index == i ) {
643 GfxFillRect(r.left + 1, y, r.right, y + (this->line_height) / 2, _colour_gradient[COLOUR_GREY][3]);
646 int text_y = y + ScaleGUITrad(3);
648 SetDParam(0, g_id);
649 StringID str = STR_GROUP_NAME;
650 DrawString(left + ScaleGUITrad(30 + this->indents[i] * 10), right, text_y, str, TC_BLACK);
652 /* Draw the template in use for this group, if there is one */
653 short template_in_use = FindTemplateIndexForGroup(g_id);
654 if ( template_in_use >= 0 ) {
655 SetDParam(0, template_in_use);
656 DrawString(right - ScaleGUITrad(600), right, text_y, STR_TMPL_GROUP_USES_TEMPLATE, TC_BLACK, SA_HOR_CENTER);
658 /* If there isn't a template applied from the current group, check if there is one for another rail type */
659 else if ( GetTemplateReplacementByGroupID(g_id) ) {
660 DrawString(right - ScaleGUITrad(600), right, text_y, STR_TMPL_TMPLRPL_EX_DIFF_RAILTYPE, TC_SILVER, SA_HOR_CENTER);
663 /* Draw the number of trains that still need to be treated by the currently selected template replacement */
664 TemplateReplacement *tr = GetTemplateReplacementByGroupID(g_id);
665 if ( tr ) {
666 TemplateVehicle *tv = TemplateVehicle::Get(tr->sel_template);
667 int num_trains = NumTrainsNeedTemplateReplacement(g_id, tv);
668 // Draw text
669 TextColour color = TC_GREY;
670 if ( num_trains ) color = TC_BLACK;
671 DrawString(left, right - ScaleGUITrad(16), text_y, STR_TMPL_NUM_TRAINS_NEED_RPL, color, SA_RIGHT);
672 // Draw number
673 if ( num_trains ) color = TC_ORANGE;
674 else color = TC_GREY;
675 SetDParam(0, num_trains);
676 DrawString(left, right - ScaleGUITrad(4), text_y, STR_JUST_INT, color, SA_RIGHT);
679 y+=line_height / 2;
683 void DrawTemplateList(int line_height, const Rect &r) const
685 int left = r.left;
686 int right = r.right;
687 int y = r.top;
689 Scrollbar *draw_vscroll = vscroll[1];
690 uint max = min(draw_vscroll->GetPosition() + draw_vscroll->GetCapacity(), this->templates.Length());
692 const TemplateVehicle *v;
693 for ( uint i = draw_vscroll->GetPosition(); i < max; ++i) {
695 v = (this->templates)[i];
697 /* Fill the background of the current cell in a darker tone for the currently selected template */
698 if ( this->selected_template_index == (int32)i ) {
699 GfxFillRect(left + 1, y, right, y + this->line_height, _colour_gradient[COLOUR_GREY][3]);
702 /* Draw the template */
703 DrawTemplate(v, left + ScaleGUITrad(36), right - ScaleGUITrad(24), y);
705 /* Draw a notification string for chains that are not runnable */
706 if ( v->IsFreeWagonChain() ) {
707 DrawString(left, right - ScaleGUITrad(24), y + ScaleGUITrad(2), STR_TMPL_WARNING_FREE_WAGON, TC_RED, SA_RIGHT);
710 /* Draw the template's length in tile-units */
711 SetDParam(0, v->GetRealLength());
712 SetDParam(1, 1);
713 DrawString(left, right - ScaleGUITrad(4), y + ScaleGUITrad(2), STR_TINY_BLACK_DECIMAL, TC_BLACK, SA_RIGHT);
715 int bottom_edge = y + line_height - FONT_HEIGHT_SMALL - WD_FRAMERECT_BOTTOM - ScaleGUITrad(4);
717 /* Buying cost */
718 SetDParam(0, CalculateOverallTemplateCost(v));
719 DrawString(left + ScaleGUITrad(35), right, bottom_edge, STR_TMPL_TEMPLATE_OVR_VALUE_notinyfont, TC_BLUE, SA_LEFT);
721 /* Index of current template vehicle in the list of all templates for its company */
722 SetDParam(0, i);
723 DrawString(left + ScaleGUITrad(5), left + ScaleGUITrad(25), y + ScaleGUITrad(2), STR_BLACK_INT, TC_BLACK, SA_RIGHT);
725 /* Draw whether the current template is in use by any group */
726 if ( v->NumGroupsUsingTemplate() > 0 ) {
727 DrawString(left + ScaleGUITrad(35), right, bottom_edge - FONT_HEIGHT_SMALL - ScaleGUITrad(3),
728 STR_TMP_TEMPLATE_IN_USE, TC_GREEN, SA_LEFT);
731 /* Draw information about template configuration settings */
732 TextColour color;
733 if ( v->IsSetReuseDepotVehicles() ) color = TC_LIGHT_BLUE;
734 else color = TC_GREY;
735 DrawString(right - ScaleGUITrad(275), right, bottom_edge, STR_TMPL_CONFIG_USEDEPOT, color, SA_LEFT);
737 if (v->IsSetKeepRemainingVehicles()) color = TC_LIGHT_BLUE;
738 else color = TC_GREY;
739 DrawString(right - ScaleGUITrad(175), right, bottom_edge, STR_TMPL_CONFIG_KEEPREMAINDERS, color, SA_LEFT);
741 if (v->IsSetRefitAsTemplate()) color = TC_LIGHT_BLUE;
742 else color = TC_GREY;
743 DrawString(right - ScaleGUITrad(75), right, bottom_edge, STR_TMPL_CONFIG_REFIT, color, SA_LEFT);
745 y += line_height;
749 void DrawTemplateInfo(int line_height, const Rect &r) const
751 if ((this->selected_template_index < 0) || (this->selected_template_index >= (short)this->templates.Length()))
752 return;
755 DrawPixelInfo tmp_dpi, *old_dpi;
757 if (!FillDrawPixelInfo(&tmp_dpi, r.left, r.top, r.right - r.left, r.bottom - r.top))
758 return;
760 old_dpi = _cur_dpi;
761 _cur_dpi = &tmp_dpi;
763 const TemplateVehicle *tmp = this->templates[this->selected_template_index];
765 double tractive_effort_loaded = tmp->weight * 17;
766 double power = tmp->power * 746ll;
767 double max_te = tmp->max_te;
768 uint16 loaded_max_speed = min(tmp->max_speed, (tractive_effort_loaded == 0 || tractive_effort_loaded > max_te) ? 0 : (3.6 * (power / tractive_effort_loaded)));
770 /* Draw vehicle performance info */
771 SetDParam(0, tmp->weight);
772 SetDParam(1, tmp->power);
773 SetDParam(2, loaded_max_speed);
774 SetDParam(3, tmp->max_te / 1000);
775 DrawString(8, r.right, ScaleGUITrad(4) - this->vscroll[2]->GetPosition(), STR_VEHICLE_INFO_LOADED_WEIGHT_POWER_MAX_SPEED_MAX_TE);
777 /* Draw cargo summary */
778 short top = ScaleGUITrad(30) - this->vscroll[2]->GetPosition();
779 short left = ScaleGUITrad(8);
780 short count_columns = 0;
781 short max_columns = 2;
783 CargoArray cargo_caps;
784 for ( ; tmp; tmp=tmp->Next() )
785 cargo_caps[tmp->cargo_type] += tmp->cargo_cap;
786 int x = left;
787 for (CargoID i = 0; i < NUM_CARGO; ++i) {
788 if ( cargo_caps[i] > 0 ) {
789 count_columns++;
790 SetDParam(0, i);
791 SetDParam(1, cargo_caps[i]);
792 SetDParam(2, _settings_game.vehicle.freight_trains);
793 DrawString(x, r.right, top, FreightWagonMult(i) > 1 ? STR_TMPL_CARGO_SUMMARY_MULTI : STR_TMPL_CARGO_SUMMARY, TC_LIGHT_BLUE, SA_LEFT);
794 x += ScaleGUITrad(250);
795 if ( count_columns % max_columns == 0 ) {
796 x = left;
797 top += this->line_height/3;
802 _cur_dpi = old_dpi;
805 void UpdateButtonState()
807 bool selected_ok = (this->selected_template_index >= 0) && (this->selected_template_index < (short)this->templates.Length());
808 bool group_ok = (this->selected_group_index >= 0) && (this->selected_group_index < (short)this->groups.Length());
810 short g_id = -1;
811 if (group_ok) {
812 const Group *g = (this->groups)[this->selected_group_index];
813 g_id = g->index;
816 this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_EDIT, this->editInProgress || !selected_ok);
817 this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_DELETE, this->editInProgress || !selected_ok);
818 this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REUSE, this->editInProgress || !selected_ok);
819 this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_KEEP, this->editInProgress || !selected_ok);
820 this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_CONFIGTMPL_REFIT, this->editInProgress || !selected_ok);
822 this->SetWidgetDisabledState(TRW_WIDGET_START, this->editInProgress || !(selected_ok && group_ok && FindTemplateIndexForGroup(g_id) != this->selected_template_index));
823 this->SetWidgetDisabledState(TRW_WIDGET_STOP, this->editInProgress || !(group_ok && GetTemplateReplacementByGroupID(g_id) != nullptr));
825 this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_DEFINE, this->editInProgress);
826 this->SetWidgetDisabledState(TRW_WIDGET_TMPL_BUTTONS_CLONE, this->editInProgress);
827 this->SetWidgetDisabledState(TRW_WIDGET_TRAIN_RAILTYPE_DROPDOWN, this->editInProgress);
831 void ShowTemplateReplaceWindow(byte dig, int step_h)
834 new TemplateReplaceWindow(&_replace_rail_vehicle_desc, dig, step_h);