Rework the way the ReinitSeparation command is called. The old way was way too danger...
[openttd-joker.git] / src / tracerestrict_gui.cpp
blob00af40f53f2fa4e0c166329659ef9e2e3536a977
1 /* $Id$ */
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 */
10 /** @file tracerestrict_gui.cpp GUI code for Trace Restrict
12 * This is largely based on the programmable signals patch's GUI
13 * */
15 #include "stdafx.h"
16 #include "tracerestrict.h"
17 #include "command_func.h"
18 #include "window_func.h"
19 #include "strings_func.h"
20 #include "string_func.h"
21 #include "viewport_func.h"
22 #include "textbuf_gui.h"
23 #include "company_func.h"
24 #include "tilehighlight_func.h"
25 #include "widgets/dropdown_func.h"
26 #include "widgets/dropdown_type.h"
27 #include "gui.h"
28 #include "gfx_func.h"
29 #include "rail_map.h"
30 #include "depot_map.h"
31 #include "tile_cmd.h"
32 #include "station_base.h"
33 #include "waypoint_base.h"
34 #include "depot_base.h"
35 #include "error.h"
36 #include "cargotype.h"
37 #include "group.h"
38 #include "company_base.h"
39 #include "vehicle_base.h"
40 #include "vehicle_gui.h"
41 #include "vehicle_gui_base.h"
42 #include "scope.h"
43 #include "sortlist_type.h"
44 #include "table/sprites.h"
45 #include "core/geometry_func.hpp"
47 #include "safeguards.h"
49 extern uint ConvertSpeedToDisplaySpeed(uint speed);
50 extern uint ConvertDisplaySpeedToSpeed(uint speed);
52 /** Widget IDs */
53 enum TraceRestrictWindowWidgets {
54 TR_WIDGET_CAPTION,
55 TR_WIDGET_INSTRUCTION_LIST,
56 TR_WIDGET_SCROLLBAR,
58 TR_WIDGET_SEL_TOP_LEFT_2,
59 TR_WIDGET_SEL_TOP_LEFT,
60 TR_WIDGET_SEL_TOP_LEFT_AUX,
61 TR_WIDGET_SEL_TOP_MIDDLE,
62 TR_WIDGET_SEL_TOP_RIGHT,
63 TR_WIDGET_SEL_SHARE,
64 TR_WIDGET_SEL_COPY,
66 TR_WIDGET_UP_BTN,
67 TR_WIDGET_DOWN_BTN,
69 TR_WIDGET_TYPE_COND,
70 TR_WIDGET_TYPE_NONCOND,
71 TR_WIDGET_CONDFLAGS,
72 TR_WIDGET_COMPARATOR,
73 TR_WIDGET_SLOT_OP,
74 TR_WIDGET_VALUE_INT,
75 TR_WIDGET_VALUE_DROPDOWN,
76 TR_WIDGET_VALUE_DEST,
77 TR_WIDGET_VALUE_SIGNAL,
78 TR_WIDGET_LEFT_AUX_DROPDOWN,
80 TR_WIDGET_BLANK_L2,
81 TR_WIDGET_BLANK_L,
82 TR_WIDGET_BLANK_M,
83 TR_WIDGET_BLANK_R,
85 TR_WIDGET_GOTO_SIGNAL,
86 TR_WIDGET_INSERT,
87 TR_WIDGET_REMOVE,
88 TR_WIDGET_RESET,
89 TR_WIDGET_COPY,
90 TR_WIDGET_COPY_APPEND,
91 TR_WIDGET_SHARE,
92 TR_WIDGET_UNSHARE,
95 /** Selection mappings for NWID_SELECTION selectors */
96 enum PanelWidgets {
97 // Left 2
98 DPL2_TYPE = 0,
99 DPL2_CONDFLAGS,
100 DPL2_BLANK,
102 // Left
103 DPL_TYPE = 0,
104 DPL_BLANK,
106 // Left aux
107 DPLA_DROPDOWN = 0,
109 // Middle
110 DPM_COMPARATOR = 0,
111 DPM_SLOT_OP,
112 DPM_BLANK,
114 // Right
115 DPR_VALUE_INT = 0,
116 DPR_VALUE_DROPDOWN,
117 DPR_VALUE_DEST,
118 DPR_VALUE_SIGNAL,
119 DPR_BLANK,
121 // Share
122 DPS_SHARE = 0,
123 DPS_UNSHARE,
125 // Copy
126 DPC_COPY = 0,
127 DPC_APPEND,
131 * drop down list string array, and corresponding integer values
133 * value_array *must* be at least as long as string_array,
134 * where the length of string_array is defined as the offset
135 * of the first INVALID_STRING_ID
137 struct TraceRestrictDropDownListSet {
138 const StringID *string_array;
139 const uint *value_array;
142 static const StringID _program_insert_str[] = {
143 STR_TRACE_RESTRICT_CONDITIONAL_IF,
144 STR_TRACE_RESTRICT_CONDITIONAL_ELIF,
145 STR_TRACE_RESTRICT_CONDITIONAL_ORIF,
146 STR_TRACE_RESTRICT_CONDITIONAL_ELSE,
147 STR_TRACE_RESTRICT_PF_DENY,
148 STR_TRACE_RESTRICT_PF_PENALTY,
149 STR_TRACE_RESTRICT_RESERVE_THROUGH,
150 STR_TRACE_RESTRICT_LONG_RESERVE,
151 STR_TRACE_RESTRICT_WAIT_AT_PBS,
152 STR_TRACE_RESTRICT_SLOT_OP,
153 INVALID_STRING_ID
155 static const uint32 _program_insert_else_hide_mask = 8; ///< disable bitmask for else
156 static const uint32 _program_insert_or_if_hide_mask = 4; ///< disable bitmask for orif
157 static const uint32 _program_insert_else_if_hide_mask = 2; ///< disable bitmask for elif
158 static const uint32 _program_wait_pbs_hide_mask = 0x100; ///< disable bitmask for wait at PBS
159 static const uint32 _program_slot_hide_mask = 0x200; ///< disable bitmask for slot
160 static const uint _program_insert_val[] = {
161 TRIT_COND_UNDEFINED, // if block
162 TRIT_COND_UNDEFINED | (TRCF_ELSE << 16), // elif block
163 TRIT_COND_UNDEFINED | (TRCF_OR << 16), // orif block
164 TRIT_COND_ENDIF | (TRCF_ELSE << 16), // else block
165 TRIT_PF_DENY, // deny
166 TRIT_PF_PENALTY, // penalty
167 TRIT_RESERVE_THROUGH, // reserve through
168 TRIT_LONG_RESERVE, // long reserve
169 TRIT_WAIT_AT_PBS, // wait at PBS signal
170 TRIT_SLOT, // slot operation
173 /** insert drop down list strings and values */
174 static const TraceRestrictDropDownListSet _program_insert = {
175 _program_insert_str, _program_insert_val,
178 static const StringID _deny_value_str[] = {
179 STR_TRACE_RESTRICT_PF_DENY,
180 STR_TRACE_RESTRICT_PF_ALLOW,
181 INVALID_STRING_ID
183 static const uint _deny_value_val[] = {
188 /** value drop down list for deny types strings and values */
189 static const TraceRestrictDropDownListSet _deny_value = {
190 _deny_value_str, _deny_value_val,
193 static const StringID _reserve_through_value_str[] = {
194 STR_TRACE_RESTRICT_RESERVE_THROUGH,
195 STR_TRACE_RESTRICT_RESERVE_THROUGH_CANCEL,
196 INVALID_STRING_ID
198 static const uint _reserve_through_value_val[] = {
203 static const StringID _long_reserve_value_str[] = {
204 STR_TRACE_RESTRICT_LONG_RESERVE,
205 STR_TRACE_RESTRICT_LONG_RESERVE_CANCEL,
206 INVALID_STRING_ID
208 static const uint _long_reserve_value_val[] = {
213 /** value drop down list for reserve through types strings and values */
214 static const TraceRestrictDropDownListSet _reserve_through_value = {
215 _reserve_through_value_str, _reserve_through_value_val,
218 /** value drop down list for long reserve types strings and values */
219 static const TraceRestrictDropDownListSet _long_reserve_value = {
220 _long_reserve_value_str, _long_reserve_value_val,
223 static const StringID _wait_at_pbs_value_str[] = {
224 STR_TRACE_RESTRICT_WAIT_AT_PBS,
225 STR_TRACE_RESTRICT_WAIT_AT_PBS_CANCEL,
226 INVALID_STRING_ID
228 static const uint _wait_at_pbs_value_val[] = {
233 /** value drop down list for wait at PBS types strings and values */
234 static const TraceRestrictDropDownListSet _wait_at_pbs_value = {
235 _wait_at_pbs_value_str, _wait_at_pbs_value_val,
238 static const StringID _direction_value_str[] = {
239 STR_TRACE_RESTRICT_DIRECTION_FRONT,
240 STR_TRACE_RESTRICT_DIRECTION_BACK,
241 STR_TRACE_RESTRICT_DIRECTION_NE,
242 STR_TRACE_RESTRICT_DIRECTION_SE,
243 STR_TRACE_RESTRICT_DIRECTION_SW,
244 STR_TRACE_RESTRICT_DIRECTION_NW,
245 INVALID_STRING_ID
247 static const uint _direction_value_val[] = {
248 TRDTSV_FRONT,
249 TRDTSV_BACK,
250 TRNTSV_NE,
251 TRNTSV_SE,
252 TRNTSV_SW,
253 TRNTSV_NW,
256 /** value drop down list for direction type strings and values */
257 static const TraceRestrictDropDownListSet _direction_value = {
258 _direction_value_str, _direction_value_val,
262 * Get index of @p value in @p list_set
263 * if @p value is not present, assert if @p missing_ok is false, otherwise return -1
265 static int GetDropDownListIndexByValue(const TraceRestrictDropDownListSet *list_set, uint value, bool missing_ok)
267 const StringID *string_array = list_set->string_array;
268 const uint *value_array = list_set->value_array;
270 for (; *string_array != INVALID_STRING_ID; string_array++, value_array++) {
271 if (*value_array == value) {
272 return value_array - list_set->value_array;
275 assert(missing_ok == true);
276 return -1;
280 * Get StringID correspoding to @p value, in @list_set
281 * @p value must be present
283 static StringID GetDropDownStringByValue(const TraceRestrictDropDownListSet *list_set, uint value)
285 return list_set->string_array[GetDropDownListIndexByValue(list_set, value, false)];
288 typedef uint TraceRestrictGuiItemType;
290 static TraceRestrictGuiItemType GetItemGuiType(TraceRestrictItem item)
292 TraceRestrictItemType type = GetTraceRestrictType(item);
293 if (IsTraceRestrictTypeAuxSubtype(type)) {
294 return type | (GetTraceRestrictAuxField(item) << 16);
296 else {
297 return type;
301 static TraceRestrictItemType ItemTypeFromGuiType(TraceRestrictGuiItemType type)
303 return static_cast<TraceRestrictItemType>(type & 0xFFFF);
307 * Return the appropriate type dropdown TraceRestrictDropDownListSet for the given item type @p type
309 static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictGuiItemType type, uint32 *hide_mask = nullptr)
311 static const StringID str_action[] = {
312 STR_TRACE_RESTRICT_PF_DENY,
313 STR_TRACE_RESTRICT_PF_PENALTY,
314 STR_TRACE_RESTRICT_RESERVE_THROUGH,
315 STR_TRACE_RESTRICT_LONG_RESERVE,
316 STR_TRACE_RESTRICT_WAIT_AT_PBS,
317 STR_TRACE_RESTRICT_SLOT_OP,
318 INVALID_STRING_ID,
320 static const uint val_action[] = {
321 TRIT_PF_DENY,
322 TRIT_PF_PENALTY,
323 TRIT_RESERVE_THROUGH,
324 TRIT_LONG_RESERVE,
325 TRIT_WAIT_AT_PBS,
326 TRIT_SLOT,
328 static const TraceRestrictDropDownListSet set_action = {
329 str_action, val_action,
332 static const StringID str_cond[] = {
333 STR_TRACE_RESTRICT_VARIABLE_TRAIN_LENGTH,
334 STR_TRACE_RESTRICT_VARIABLE_MAX_SPEED,
335 STR_TRACE_RESTRICT_VARIABLE_CURRENT_ORDER,
336 STR_TRACE_RESTRICT_VARIABLE_NEXT_ORDER,
337 STR_TRACE_RESTRICT_VARIABLE_LAST_VISITED_STATION,
338 STR_TRACE_RESTRICT_VARIABLE_CARGO,
339 STR_TRACE_RESTRICT_VARIABLE_ENTRY_DIRECTION,
340 STR_TRACE_RESTRICT_VARIABLE_PBS_ENTRY_SIGNAL,
341 STR_TRACE_RESTRICT_VARIABLE_TRAIN_GROUP,
342 STR_TRACE_RESTRICT_VARIABLE_TRAIN_SLOT,
343 STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY,
344 STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY_REMAINING,
345 STR_TRACE_RESTRICT_VARIABLE_UNDEFINED,
346 INVALID_STRING_ID,
348 static const uint val_cond[] = {
349 TRIT_COND_TRAIN_LENGTH,
350 TRIT_COND_MAX_SPEED,
351 TRIT_COND_CURRENT_ORDER,
352 TRIT_COND_NEXT_ORDER,
353 TRIT_COND_LAST_STATION,
354 TRIT_COND_CARGO,
355 TRIT_COND_ENTRY_DIRECTION,
356 TRIT_COND_PBS_ENTRY_SIGNAL,
357 TRIT_COND_TRAIN_GROUP,
358 TRIT_COND_TRAIN_IN_SLOT,
359 TRIT_COND_SLOT_OCCUPANCY | (TRSOCAF_OCCUPANTS << 16),
360 TRIT_COND_SLOT_OCCUPANCY | (TRSOCAF_REMAINING << 16),
361 TRIT_COND_UNDEFINED,
363 static const TraceRestrictDropDownListSet set_cond = {
364 str_cond, val_cond,
367 bool is_conditional = IsTraceRestrictTypeConditional(ItemTypeFromGuiType(type));
369 if (hide_mask != nullptr) {
370 *hide_mask = 0;
373 return is_conditional ? &set_cond : &set_action;
377 * Get a TraceRestrictDropDownListSet of the sorted cargo list
379 static const TraceRestrictDropDownListSet *GetSortedCargoTypeDropDownListSet()
381 static StringID cargo_list_str[NUM_CARGO + 1];
382 static uint cargo_list_id[NUM_CARGO];
383 static const TraceRestrictDropDownListSet cargo_list = {
384 cargo_list_str, cargo_list_id,
387 for (size_t i = 0; i < _sorted_standard_cargo_specs_size; ++i) {
388 const CargoSpec *cs = _sorted_cargo_specs[i];
389 cargo_list_str[i] = cs->name;
390 cargo_list_id[i] = cs->Index();
392 cargo_list_str[_sorted_standard_cargo_specs_size] = INVALID_STRING_ID;
394 return &cargo_list;
398 * Get a DropDownList of the group list
400 static DropDownList *GetGroupDropDownList(Owner owner, GroupID group_id, int &selected)
402 typedef GUIList<const Group*> GUIGroupList;
403 extern int CDECL GroupNameSorter(const Group * const *a, const Group * const *b);
405 GUIGroupList list;
407 const Group *g;
408 FOR_ALL_GROUPS(g) {
409 if (g->owner == owner && g->vehicle_type == VEH_TRAIN && g->statistics.num_vehicle != 0) {
410 *list.Append() = g;
414 list.ForceResort();
415 list.Sort(&GroupNameSorter);
417 DropDownList *dlist = new DropDownList();
418 selected = -1;
420 if (group_id == DEFAULT_GROUP) selected = DEFAULT_GROUP;
421 *dlist->Append() = new DropDownListStringItem(STR_GROUP_DEFAULT_TRAINS, DEFAULT_GROUP, false);
423 for (uint i = 0; i < list.Length(); ++i) {
424 const Group *g = list[i];
425 if (group_id == g->index) selected = group_id;
426 DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_GROUP_NAME, g->index, false);
427 item->SetParam(0, g->index);
428 *dlist->Append() = item;
431 return dlist;
434 /** Sort slots by their name */
435 static int CDECL SlotNameSorter(const TraceRestrictSlot * const *a, const TraceRestrictSlot * const *b)
437 int r = strnatcmp((*a)->name.c_str(), (*b)->name.c_str()); // Sort by name (natural sorting).
438 if (r == 0) return (*a)->index - (*b)->index;
439 return r;
443 * Get a DropDownList of the group list
445 DropDownList *GetSlotDropDownList(Owner owner, TraceRestrictSlotID slot_id, int &selected)
447 GUIList<const TraceRestrictSlot*> list;
449 const TraceRestrictSlot *slot;
450 FOR_ALL_TRACE_RESTRICT_SLOTS(slot) {
451 if (slot->owner == owner) {
452 *list.Append() = slot;
456 if (list.Length() == 0) return nullptr;
458 list.ForceResort();
459 list.Sort(&SlotNameSorter);
461 DropDownList *dlist = new DropDownList();
462 selected = -1;
464 for (uint i = 0; i < list.Length(); ++i) {
465 const TraceRestrictSlot *s = list[i];
466 if (slot_id == s->index) selected = slot_id;
467 DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_TRACE_RESTRICT_SLOT_NAME, s->index, false);
468 item->SetParam(0, s->index);
469 *dlist->Append() = item;
472 return dlist;
475 static const StringID _cargo_cond_ops_str[] = {
476 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_EQUALS,
477 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_NOT_EQUALS,
478 INVALID_STRING_ID,
480 static const uint _cargo_cond_ops_val[] = {
481 TRCO_IS,
482 TRCO_ISNOT,
484 /** cargo conditional operators dropdown list set */
485 static const TraceRestrictDropDownListSet _cargo_cond_ops = {
486 _cargo_cond_ops_str, _cargo_cond_ops_val,
489 static const StringID _slot_op_cond_ops_str[] = {
490 STR_TRACE_RESTRICT_SLOT_ACQUIRE_WAIT,
491 STR_TRACE_RESTRICT_SLOT_TRY_ACQUIRE,
492 STR_TRACE_RESTRICT_SLOT_RELEASE_FRONT,
493 STR_TRACE_RESTRICT_SLOT_RELEASE_BACK,
494 INVALID_STRING_ID,
496 static const uint _slot_op_cond_ops_val[] = {
497 TRSCOF_ACQUIRE_WAIT,
498 TRSCOF_ACQUIRE_TRY,
499 TRSCOF_RELEASE_FRONT,
500 TRSCOF_RELEASE_BACK,
502 /** cargo conditional operators dropdown list set */
503 static const TraceRestrictDropDownListSet _slot_op_cond_ops = {
504 _slot_op_cond_ops_str, _slot_op_cond_ops_val,
508 * Get the StringID for a given CargoID @p cargo, or STR_NEWGRF_INVALID_CARGO
510 static StringID GetCargoStringByID(CargoID cargo)
512 const CargoSpec *cs = CargoSpec::Get(cargo);
513 return cs->IsValid() ? cs->name : STR_NEWGRF_INVALID_CARGO;
517 * Get the StringID for a given item type @p type
519 static StringID GetTypeString(TraceRestrictItem item)
521 TraceRestrictGuiItemType type = GetItemGuiType(item);
522 return GetDropDownStringByValue(GetTypeDropDownListSet(type), type);
526 * Get the conditional operator field drop down list set for a given type property set @p properties
528 static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestrictTypePropertySet properties)
530 static const StringID str_long[] = {
531 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_EQUALS,
532 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_NOT_EQUALS,
533 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_LESS_THAN,
534 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_LESS_EQUALS,
535 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_MORE_THAN,
536 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_MORE_EQUALS,
537 INVALID_STRING_ID,
539 static const uint val_long[] = {
540 TRCO_IS,
541 TRCO_ISNOT,
542 TRCO_LT,
543 TRCO_LTE,
544 TRCO_GT,
545 TRCO_GTE,
547 static const TraceRestrictDropDownListSet set_long = {
548 str_long, val_long,
551 static const StringID str_short[] = {
552 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_EQUALS,
553 STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_NOT_EQUALS,
554 INVALID_STRING_ID,
556 static const uint val_short[] = {
557 TRCO_IS,
558 TRCO_ISNOT,
560 static const TraceRestrictDropDownListSet set_short = {
561 str_short, val_short,
564 if (properties.value_type == TRVT_CARGO_ID) return &_cargo_cond_ops;
566 switch (properties.cond_type) {
567 case TRCOT_NONE:
568 return nullptr;
570 case TRCOT_BINARY:
571 return &set_short;
573 case TRCOT_ALL:
574 return &set_long;
576 NOT_REACHED();
577 return nullptr;
581 * Return true if item type field @p type is an integer value type
583 static bool IsIntegerValueType(TraceRestrictValueType type)
585 switch (type) {
586 case TRVT_INT:
587 case TRVT_SPEED:
588 return true;
590 default:
591 return false;
596 * Convert integer values or custom penalty values between internal units and display units
598 static uint ConvertIntegerValue(TraceRestrictValueType type, uint in, bool to_display)
600 switch (type) {
601 case TRVT_INT:
602 return in;
604 case TRVT_SPEED:
605 return to_display
606 ? ConvertSpeedToDisplaySpeed(in) * 10 / 16
607 : ConvertDisplaySpeedToSpeed(in) * 16 / 10;
609 case TRVT_PF_PENALTY:
610 return in;
612 default:
613 NOT_REACHED();
614 return 0;
618 /** String values for TraceRestrictCondFlags, value gives offset into array */
619 static const StringID _program_cond_type[] = {
620 STR_TRACE_RESTRICT_CONDITIONAL_IF, // TRCF_DEFAULT
621 STR_TRACE_RESTRICT_CONDITIONAL_ELIF, // TRCF_ELSE
622 STR_TRACE_RESTRICT_CONDITIONAL_ORIF, // TRCF_OR
625 /** condition flags field drop down value types */
626 enum CondFlagsDropDownType {
627 CFDDT_ELSE = 0, ///< This is an else block
628 CFDDT_ELIF = TRCF_ELSE, ///< This is an else-if block
629 CFDDT_ORIF = TRCF_OR, ///< This is an or-if block
632 static const uint32 _condflags_dropdown_else_hide_mask = 1; ///< disable bitmask for CFDDT_ELSE
633 static const uint32 _condflags_dropdown_else_if_hide_mask = 6; ///< disable bitmask for CFDDT_ELIF and CFDDT_ORIF
635 static const StringID _condflags_dropdown_str[] = {
636 STR_TRACE_RESTRICT_CONDITIONAL_ELSE,
637 STR_TRACE_RESTRICT_CONDITIONAL_ELIF,
638 STR_TRACE_RESTRICT_CONDITIONAL_ORIF,
639 INVALID_STRING_ID,
641 static const uint _condflags_dropdown_val[] = {
642 CFDDT_ELSE,
643 CFDDT_ELIF,
644 CFDDT_ORIF,
646 /** condition flags dropdown list set */
647 static const TraceRestrictDropDownListSet _condflags_dropdown = {
648 _condflags_dropdown_str, _condflags_dropdown_val,
651 static const StringID _pf_penalty_dropdown_str[] = {
652 STR_TRACE_RESTRICT_PF_VALUE_SMALL,
653 STR_TRACE_RESTRICT_PF_VALUE_MEDIUM,
654 STR_TRACE_RESTRICT_PF_VALUE_LARGE,
655 STR_TRACE_RESTRICT_PF_VALUE_CUSTOM,
656 INVALID_STRING_ID,
658 static const uint _pf_penalty_dropdown_val[] = {
659 TRPPPI_SMALL,
660 TRPPPI_MEDIUM,
661 TRPPPI_LARGE,
662 TRPPPI_END, // this is a placeholder for "custom"
664 /** Pathfinder penalty dropdown set */
665 static const TraceRestrictDropDownListSet _pf_penalty_dropdown = {
666 _pf_penalty_dropdown_str, _pf_penalty_dropdown_val,
669 static uint GetPathfinderPenaltyDropdownIndex(TraceRestrictItem item)
671 switch (static_cast<TraceRestrictPathfinderPenaltyAuxField>(GetTraceRestrictAuxField(item))) {
672 case TRPPAF_VALUE:
673 return TRPPPI_END;
675 case TRPPAF_PRESET: {
676 uint16 index = GetTraceRestrictValue(item);
677 assert(index < TRPPPI_END);
678 return index;
681 default:
682 NOT_REACHED();
686 /** Common function for drawing an ordinary conditional instruction */
687 static void DrawInstructionStringConditionalCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties)
689 assert(GetTraceRestrictCondFlags(item) <= TRCF_OR);
690 SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]);
691 SetDParam(1, GetTypeString(GetTraceRestrictType(item)));
692 SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item)));
695 /** Common function for drawing an integer conditional instruction */
696 static void DrawInstructionStringConditionalIntegerCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties)
698 DrawInstructionStringConditionalCommon(item, properties);
699 SetDParam(3, GetTraceRestrictValue(item));
702 /** Common function for drawing an integer conditional instruction with an invalid value */
703 static void DrawInstructionStringConditionalInvalidValue(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties, StringID &instruction_string, bool selected)
705 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_UNDEFINED;
706 DrawInstructionStringConditionalCommon(item, properties);
707 SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
711 * Draws an instruction in the programming GUI
712 * @param prog The program (may be nullptr)
713 * @param item The instruction to draw
714 * @param index The instruction index
715 * @param y Y position for drawing
716 * @param selected True, if the order is selected
717 * @param indent How many levels the instruction is indented
718 * @param left Left border for text drawing
719 * @param right Right border for text drawing
721 static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestrictItem item, int index, int y, bool selected, int indent, int left, int right)
723 StringID instruction_string = INVALID_STRING_ID;
725 TraceRestrictTypePropertySet properties = GetTraceRestrictTypeProperties(item);
727 if (IsTraceRestrictConditional(item)) {
728 if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) {
729 if (GetTraceRestrictCondFlags(item) & TRCF_ELSE) {
730 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ELSE;
731 } else {
732 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ENDIF;
734 } else if (GetTraceRestrictType(item) == TRIT_COND_UNDEFINED) {
735 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_UNDEFINED;
736 SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]);
737 SetDParam(1, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
738 } else {
739 switch (properties.value_type) {
740 case TRVT_INT:
741 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER;
742 DrawInstructionStringConditionalIntegerCommon(item, properties);
743 break;
745 case TRVT_SPEED:
746 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED;
747 DrawInstructionStringConditionalIntegerCommon(item, properties);
748 break;
750 case TRVT_ORDER: {
751 switch (static_cast<TraceRestrictOrderCondAuxField>(GetTraceRestrictAuxField(item))) {
752 case TROCAF_STATION:
753 if (GetTraceRestrictValue(item) != INVALID_STATION) {
754 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ORDER_STATION;
755 DrawInstructionStringConditionalIntegerCommon(item, properties);
756 } else {
757 // this is an invalid station, use a seperate string
758 DrawInstructionStringConditionalInvalidValue(item, properties, instruction_string, selected);
760 break;
762 case TROCAF_WAYPOINT:
763 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ORDER_WAYPOINT;
764 DrawInstructionStringConditionalIntegerCommon(item, properties);
765 break;
767 case TROCAF_DEPOT:
768 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ORDER_DEPOT;
769 DrawInstructionStringConditionalCommon(item, properties);
770 SetDParam(3, VEH_TRAIN);
771 SetDParam(4, GetTraceRestrictValue(item));
772 break;
774 default:
775 NOT_REACHED();
776 break;
778 break;
781 case TRVT_CARGO_ID:
782 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_CARGO;
783 assert(GetTraceRestrictCondFlags(item) <= TRCF_OR);
784 SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]);
785 SetDParam(1, GetDropDownStringByValue(&_cargo_cond_ops, GetTraceRestrictCondOp(item)));
786 SetDParam(2, GetCargoStringByID(GetTraceRestrictValue(item)));
787 break;
789 case TRVT_DIRECTION:
790 if (GetTraceRestrictValue(item) >= TRDTSV_FRONT) {
791 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_SIGNAL_FACE;
792 } else {
793 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_DIRECTION;
795 SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]);
796 SetDParam(1, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item)));
797 SetDParam(2, GetDropDownStringByValue(&_direction_value, GetTraceRestrictValue(item)));
798 break;
800 case TRVT_TILE_INDEX: {
801 assert(prog != nullptr);
802 assert(GetTraceRestrictType(item) == TRIT_COND_PBS_ENTRY_SIGNAL);
803 TileIndex tile = *(TraceRestrictProgram::InstructionAt(prog->items, index - 1) + 1);
804 if (tile == INVALID_TILE) {
805 DrawInstructionStringConditionalInvalidValue(item, properties, instruction_string, selected);
806 } else {
807 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_TILE_INDEX;
808 SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]);
809 SetDParam(1, STR_TRACE_RESTRICT_VARIABLE_PBS_ENTRY_SIGNAL_LONG);
810 SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item)));
811 SetDParam(3, TileX(tile));
812 SetDParam(4, TileY(tile));
814 break;
817 case TRVT_GROUP_INDEX: {
818 assert(GetTraceRestrictCondFlags(item) <= TRCF_OR);
819 SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]);
820 SetDParam(1, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item)));
821 if (GetTraceRestrictValue(item) == INVALID_GROUP) {
822 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_GROUP_STR;
823 SetDParam(2, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED);
824 SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
825 } else if (GetTraceRestrictValue(item) == DEFAULT_GROUP) {
826 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_GROUP_STR;
827 SetDParam(2, STR_GROUP_DEFAULT_TRAINS);
828 SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
829 } else {
830 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_GROUP;
831 SetDParam(2, GetTraceRestrictValue(item));
833 break;
836 case TRVT_SLOT_INDEX:
837 SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]);
838 SetDParam(1, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item)));
839 if (GetTraceRestrictValue(item) == INVALID_TRACE_RESTRICT_SLOT_ID) {
840 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_SLOT_STR;
841 SetDParam(2, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED);
842 SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
843 } else {
844 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_SLOT;
845 SetDParam(2, GetTraceRestrictValue(item));
847 break;
849 case TRVT_SLOT_INDEX_INT: {
850 assert(prog != nullptr);
851 assert(GetTraceRestrictType(item) == TRIT_COND_SLOT_OCCUPANCY);
852 uint32 value = *(TraceRestrictProgram::InstructionAt(prog->items, index - 1) + 1);
853 SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]);
854 SetDParam(1, GetTraceRestrictAuxField(item) ? STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY_REMAINING_SHORT : STR_TRACE_RESTRICT_VARIABLE_SLOT_OCCUPANCY_SHORT);
855 if (GetTraceRestrictValue(item) == INVALID_TRACE_RESTRICT_SLOT_ID) {
856 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_SLOT_OCCUPANCY_STR;
857 SetDParam(2, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED);
858 SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
859 SetDParam(4, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item)));
860 SetDParam(5, value);
861 } else {
862 instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_SLOT_OCCUPANCY;
863 SetDParam(2, GetTraceRestrictValue(item));
864 SetDParam(3, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item)));
865 SetDParam(4, value);
867 break;
870 default:
871 NOT_REACHED();
872 break;
875 } else {
876 switch (GetTraceRestrictType(item)) {
877 case TRIT_NULL:
878 switch (GetTraceRestrictValue(item)) {
879 case TRNTSV_START:
880 instruction_string = STR_TRACE_RESTRICT_START;
881 break;
883 case TRNTSV_END:
884 instruction_string = STR_TRACE_RESTRICT_END;
885 break;
887 default:
888 NOT_REACHED();
889 break;
891 break;
893 case TRIT_PF_DENY:
894 instruction_string = GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_PF_ALLOW_LONG : STR_TRACE_RESTRICT_PF_DENY;
895 break;
897 case TRIT_PF_PENALTY:
898 switch (static_cast<TraceRestrictPathfinderPenaltyAuxField>(GetTraceRestrictAuxField(item))) {
899 case TRPPAF_VALUE:
900 instruction_string = STR_TRACE_RESTRICT_PF_PENALTY_ITEM;
901 SetDParam(0, GetTraceRestrictValue(item));
902 break;
904 case TRPPAF_PRESET: {
905 instruction_string = STR_TRACE_RESTRICT_PF_PENALTY_ITEM_PRESET;
906 uint16 index = GetTraceRestrictValue(item);
907 assert(index < TRPPPI_END);
908 SetDParam(0, _pf_penalty_dropdown_str[index]);
909 break;
912 default:
913 NOT_REACHED();
915 break;
917 case TRIT_RESERVE_THROUGH:
918 instruction_string = GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_RESERVE_THROUGH_CANCEL : STR_TRACE_RESTRICT_RESERVE_THROUGH;
919 break;
921 case TRIT_LONG_RESERVE:
922 instruction_string = GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_LONG_RESERVE_CANCEL : STR_TRACE_RESTRICT_LONG_RESERVE;
923 break;
925 case TRIT_WAIT_AT_PBS:
926 instruction_string = GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_WAIT_AT_PBS_CANCEL : STR_TRACE_RESTRICT_WAIT_AT_PBS;
927 break;
929 case TRIT_SLOT:
930 switch (static_cast<TraceRestrictSlotCondOpField>(GetTraceRestrictCondOp(item))) {
931 case TRSCOF_ACQUIRE_WAIT:
932 instruction_string = STR_TRACE_RESTRICT_SLOT_ACQUIRE_WAIT_ITEM;
933 break;
935 case TRSCOF_ACQUIRE_TRY:
936 instruction_string = STR_TRACE_RESTRICT_SLOT_TRY_ACQUIRE_ITEM;
937 break;
939 case TRSCOF_RELEASE_BACK:
940 instruction_string = STR_TRACE_RESTRICT_SLOT_RELEASE_BACK_ITEM;
941 break;
943 case TRSCOF_RELEASE_FRONT:
944 instruction_string = STR_TRACE_RESTRICT_SLOT_RELEASE_FRONT_ITEM;
945 break;
947 default:
948 NOT_REACHED();
949 break;
951 if (GetTraceRestrictValue(item) == INVALID_TRACE_RESTRICT_SLOT_ID) {
952 SetDParam(0, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED);
953 } else {
954 SetDParam(0, STR_TRACE_RESTRICT_SLOT_NAME);
955 SetDParam(1, GetTraceRestrictValue(item));
957 SetDParam(2, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY);
958 break;
960 default:
961 NOT_REACHED();
962 break;
966 DrawString(left + indent * 16, right, y, instruction_string, selected ? TC_WHITE : TC_BLACK);
969 /** Main GUI window class */
970 class TraceRestrictWindow: public Window {
971 TileIndex tile; ///< tile this window is for
972 Track track; ///< track this window is for
973 int selected_instruction; ///< selected instruction index, this is offset by one due to the display of the "start" item
974 Scrollbar *vscroll; ///< scrollbar widget
975 std::map<int, const TraceRestrictDropDownListSet *> drop_down_list_mapping; ///< mapping of widget IDs to drop down list sets
976 TraceRestrictItem expecting_inserted_item; ///< set to instruction when performing an instruction insertion, used to handle selection update on insertion
977 int current_placement_widget; ///< which widget has a SetObjectToPlaceWnd, if any
978 int current_left_aux_plane; ///< current plane for TR_WIDGET_SEL_TOP_LEFT_AUX widget
980 public:
981 TraceRestrictWindow(WindowDesc *desc, TileIndex tile, Track track)
982 : Window(desc)
984 this->tile = tile;
985 this->track = track;
986 this->selected_instruction = -1;
987 this->expecting_inserted_item = static_cast<TraceRestrictItem>(0);
988 this->current_placement_widget = -1;
990 this->CreateNestedTree();
991 this->vscroll = this->GetScrollbar(TR_WIDGET_SCROLLBAR);
992 this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_TOP_LEFT_AUX)->SetDisplayedPlane(SZSP_NONE);
993 this->current_left_aux_plane = SZSP_NONE;
994 this->FinishInitNested(MakeTraceRestrictRefId(tile, track));
996 this->ReloadProgramme();
999 virtual void OnClick(Point pt, int widget, int click_count)
1001 switch (widget) {
1002 case TR_WIDGET_INSTRUCTION_LIST: {
1003 int sel = this->GetItemIndexFromPt(pt.y);
1005 if (_ctrl_pressed) {
1006 // scroll to target (for stations, waypoints, depots)
1008 if (sel == -1) return;
1010 TraceRestrictItem item = this->GetItem(this->GetProgram(), sel);
1011 if (GetTraceRestrictTypeProperties(item).value_type == TRVT_ORDER) {
1012 switch (static_cast<TraceRestrictOrderCondAuxField>(GetTraceRestrictAuxField(item))) {
1013 case TROCAF_STATION:
1014 case TROCAF_WAYPOINT: {
1015 BaseStation *st = BaseStation::GetIfValid(GetTraceRestrictValue(item));
1016 if (st) {
1017 ScrollMainWindowToTile(st->xy);
1019 break;
1022 case TROCAF_DEPOT: {
1023 Depot *depot = Depot::GetIfValid(GetTraceRestrictValue(item));
1024 if (depot) {
1025 ScrollMainWindowToTile(depot->xy);
1027 break;
1030 } else if (GetTraceRestrictTypeProperties(item).value_type == TRVT_TILE_INDEX) {
1031 TileIndex tile = *(TraceRestrictProgram::InstructionAt(this->GetProgram()->items, sel - 1) + 1);
1032 if (tile != INVALID_TILE) {
1033 ScrollMainWindowToTile(tile);
1036 return;
1039 this->DeleteChildWindows();
1040 HideDropDownMenu(this);
1042 if (sel == -1 || this->GetOwner() != _local_company) {
1043 // Deselect
1044 this->selected_instruction = -1;
1045 } else {
1046 this->selected_instruction = sel;
1049 this->expecting_inserted_item = static_cast<TraceRestrictItem>(0);
1051 this->UpdateButtonState();
1052 break;
1055 case TR_WIDGET_INSERT: {
1056 if (this->GetOwner() != _local_company || this->selected_instruction < 1) {
1057 return;
1060 uint32 disabled = _program_insert_or_if_hide_mask;
1061 TraceRestrictItem item = this->GetSelected();
1062 if (GetTraceRestrictType(item) == TRIT_COND_ENDIF ||
1063 (IsTraceRestrictConditional(item) && GetTraceRestrictCondFlags(item) != 0)) {
1064 // this is either: an else/or if, an else, or an end if
1065 // try to include else if, else in insertion list
1066 if (!ElseInsertionDryRun(false)) disabled |= _program_insert_else_hide_mask;
1067 if (!ElseIfInsertionDryRun(false)) disabled |= _program_insert_else_if_hide_mask;
1068 } else {
1069 // can't insert else/end if here
1070 disabled |= _program_insert_else_hide_mask | _program_insert_else_if_hide_mask;
1072 if (this->selected_instruction > 1) {
1073 TraceRestrictItem prev_item = this->GetItem(this->GetProgram(), this->selected_instruction - 1);
1074 if (IsTraceRestrictConditional(prev_item) && GetTraceRestrictType(prev_item) != TRIT_COND_ENDIF) {
1075 // previous item is either: an if, or an else/or if
1077 // else if has same validation rules as or if, use it instead of creating another test function
1078 if (ElseIfInsertionDryRun(false)) disabled &= ~_program_insert_or_if_hide_mask;
1082 this->ShowDropDownListWithValue(&_program_insert, 0, true, TR_WIDGET_INSERT, disabled, 0, 0);
1083 break;
1086 case TR_WIDGET_REMOVE: {
1087 TraceRestrictItem item = this->GetSelected();
1088 if (this->GetOwner() != _local_company || item == 0) {
1089 return;
1092 TraceRestrictDoCommandP(tile, track, _ctrl_pressed ? TRDCT_SHALLOW_REMOVE_ITEM : TRDCT_REMOVE_ITEM,
1093 this->selected_instruction - 1, 0, STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ITEM);
1094 break;
1097 case TR_WIDGET_UP_BTN:
1098 case TR_WIDGET_DOWN_BTN: {
1099 TraceRestrictItem item = this->GetSelected();
1100 if (this->GetOwner() != _local_company || item == 0) {
1101 return;
1104 uint32 p2 = 0;
1105 if (widget == TR_WIDGET_UP_BTN) p2 |= 1;
1106 if (_ctrl_pressed) p2 |= 2;
1108 uint32 offset = this->selected_instruction - 1;
1110 this->IsUpDownBtnUsable(widget == TR_WIDGET_UP_BTN, true);
1112 TraceRestrictDoCommandP(tile, track, TRDCT_MOVE_ITEM,
1113 offset, p2, STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM);
1114 break;
1117 case TR_WIDGET_CONDFLAGS: {
1118 TraceRestrictItem item = this->GetSelected();
1119 if (this->GetOwner() != _local_company || item == 0) {
1120 return;
1123 CondFlagsDropDownType type;
1124 if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) {
1125 if (GetTraceRestrictCondFlags(item) == 0) return; // end if
1126 type = CFDDT_ELSE;
1127 } else if (IsTraceRestrictConditional(item) && GetTraceRestrictCondFlags(item) != 0) {
1128 type = static_cast<CondFlagsDropDownType>(GetTraceRestrictCondFlags(item));
1129 } else {
1130 return;
1133 uint32 disabled = 0;
1134 if (!ElseInsertionDryRun(true)) disabled |= _condflags_dropdown_else_hide_mask;
1135 if (!ElseIfInsertionDryRun(true)) disabled |= _condflags_dropdown_else_if_hide_mask;
1137 this->ShowDropDownListWithValue(&_condflags_dropdown, type, false, TR_WIDGET_CONDFLAGS, disabled, 0, 0);
1138 break;
1141 case TR_WIDGET_TYPE_COND:
1142 case TR_WIDGET_TYPE_NONCOND: {
1143 TraceRestrictItem item = this->GetSelected();
1144 TraceRestrictGuiItemType type = GetItemGuiType(item);
1146 if (type != TRIT_NULL) {
1147 uint32 hide_mask = 0;
1148 const TraceRestrictDropDownListSet *set = GetTypeDropDownListSet(type, &hide_mask);
1149 this->ShowDropDownListWithValue(set, type, false, widget, 0, hide_mask, 0);
1151 break;
1155 case TR_WIDGET_COMPARATOR: {
1156 TraceRestrictItem item = this->GetSelected();
1157 const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(GetTraceRestrictTypeProperties(item));
1158 if (list_set) {
1159 this->ShowDropDownListWithValue(list_set, GetTraceRestrictCondOp(item), false, TR_WIDGET_COMPARATOR, 0, 0, 0);
1161 break;
1164 case TR_WIDGET_SLOT_OP: {
1165 TraceRestrictItem item = this->GetSelected();
1166 this->ShowDropDownListWithValue(&_slot_op_cond_ops, GetTraceRestrictCondOp(item), false, TR_WIDGET_SLOT_OP, 0, 0, 0);
1167 break;
1170 case TR_WIDGET_VALUE_INT: {
1171 TraceRestrictItem item = this->GetSelected();
1172 TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type;
1173 if (IsIntegerValueType(type)) {
1174 SetDParam(0, ConvertIntegerValue(type, GetTraceRestrictValue(item), true));
1175 ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 10, this, CS_NUMERAL, QSF_NONE);
1177 else if (type == TRVT_SLOT_INDEX_INT) {
1178 SetDParam(0, *(TraceRestrictProgram::InstructionAt(this->GetProgram()->items, this->selected_instruction - 1) + 1));
1179 ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 10, this, CS_NUMERAL, QSF_NONE);
1181 break;
1184 case TR_WIDGET_VALUE_DROPDOWN: {
1185 TraceRestrictItem item = this->GetSelected();
1186 switch (GetTraceRestrictTypeProperties(item).value_type) {
1187 case TRVT_DENY:
1188 this->ShowDropDownListWithValue(&_deny_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0);
1189 break;
1191 case TRVT_CARGO_ID:
1192 this->ShowDropDownListWithValue(GetSortedCargoTypeDropDownListSet(), GetTraceRestrictValue(item), true, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); // current cargo is permitted to not be in list
1193 break;
1195 case TRVT_DIRECTION:
1196 this->ShowDropDownListWithValue(&_direction_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0);
1197 break;
1199 case TRVT_PF_PENALTY:
1200 this->ShowDropDownListWithValue(&_pf_penalty_dropdown, GetPathfinderPenaltyDropdownIndex(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0);
1201 break;
1203 case TRVT_RESERVE_THROUGH:
1204 this->ShowDropDownListWithValue(&_reserve_through_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0);
1205 break;
1207 case TRVT_LONG_RESERVE:
1208 this->ShowDropDownListWithValue(&_long_reserve_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0);
1209 break;
1211 case TRVT_WAIT_AT_PBS:
1212 this->ShowDropDownListWithValue(&_wait_at_pbs_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0);
1213 break;
1215 case TRVT_GROUP_INDEX: {
1216 int selected;
1217 DropDownList *dlist = GetGroupDropDownList(this->GetOwner(), GetTraceRestrictValue(item), selected);
1218 ShowDropDownList(this, dlist, selected, TR_WIDGET_VALUE_DROPDOWN);
1219 break;
1222 case TRVT_SLOT_INDEX: {
1223 int selected;
1224 DropDownList *dlist = GetSlotDropDownList(this->GetOwner(), GetTraceRestrictValue(item), selected);
1225 if (dlist != nullptr) ShowDropDownList(this, dlist, selected, TR_WIDGET_VALUE_DROPDOWN);
1226 break;
1229 default:
1230 break;
1232 break;
1235 case TR_WIDGET_LEFT_AUX_DROPDOWN: {
1236 TraceRestrictItem item = this->GetSelected();
1237 switch (GetTraceRestrictTypeProperties(item).value_type) {
1238 case TRVT_SLOT_INDEX_INT: {
1239 int selected;
1240 DropDownList *dlist = GetSlotDropDownList(this->GetOwner(),
1241 *(TraceRestrictProgram::InstructionAt(this->GetProgram()->items, this->selected_instruction - 1) + 1), selected);
1242 if (dlist != nullptr) ShowDropDownList(this, dlist, selected, TR_WIDGET_LEFT_AUX_DROPDOWN);
1243 break;
1246 default:
1247 break;
1251 case TR_WIDGET_VALUE_DEST: {
1252 SetObjectToPlaceAction(widget, ANIMCURSOR_PICKSTATION);
1253 break;
1256 case TR_WIDGET_VALUE_SIGNAL: {
1257 SetObjectToPlaceAction(widget, ANIMCURSOR_BUILDSIGNALS);
1258 break;
1261 case TR_WIDGET_GOTO_SIGNAL: {
1262 ScrollMainWindowToTile(this->tile);
1263 this->UpdateButtonState();
1264 break;
1267 case TR_WIDGET_RESET: {
1268 TraceRestrictProgMgmtDoCommandP(tile, track, TRDCT_PROG_RESET, STR_TRACE_RESTRICT_ERROR_CAN_T_RESET_SIGNAL);
1269 break;
1272 case TR_WIDGET_COPY:
1273 case TR_WIDGET_COPY_APPEND:
1274 case TR_WIDGET_SHARE:
1275 SetObjectToPlaceAction(widget, ANIMCURSOR_BUILDSIGNALS);
1276 break;
1278 case TR_WIDGET_UNSHARE: {
1279 TraceRestrictProgMgmtDoCommandP(tile, track, TRDCT_PROG_UNSHARE, STR_TRACE_RESTRICT_ERROR_CAN_T_UNSHARE_PROGRAM);
1280 break;
1285 virtual void OnQueryTextFinished(char *str)
1287 if (StrEmpty(str)) {
1288 return;
1291 TraceRestrictItem item = GetSelected();
1292 TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type;
1293 uint value;
1295 if (IsIntegerValueType(type) || type == TRVT_PF_PENALTY) {
1296 value = ConvertIntegerValue(type, atoi(str), false);
1297 if (value >= (1 << TRIFA_VALUE_COUNT)) {
1298 SetDParam(0, ConvertIntegerValue(type, (1 << TRIFA_VALUE_COUNT) - 1, true));
1299 ShowErrorMessage(STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE, STR_EMPTY, WL_INFO);
1300 return;
1303 if (type == TRVT_PF_PENALTY) {
1304 SetTraceRestrictAuxField(item, TRPPAF_VALUE);
1306 } else if (type == TRVT_SLOT_INDEX_INT) {
1307 value = atoi(str);
1308 TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_DUAL_ITEM, this->selected_instruction - 1, value, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1309 return;
1310 } else {
1311 return;
1314 SetTraceRestrictValue(item, value);
1315 TraceRestrictDoCommandP(tile, track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1318 virtual void OnDropdownSelect(int widget, int index)
1320 TraceRestrictItem item = GetSelected();
1321 if (item == 0 || index < 0 || this->selected_instruction < 1) {
1322 return;
1325 if (widget == TR_WIDGET_VALUE_DROPDOWN || widget == TR_WIDGET_LEFT_AUX_DROPDOWN) {
1326 TraceRestrictTypePropertySet type = GetTraceRestrictTypeProperties(item);
1327 if (type.value_type == TRVT_GROUP_INDEX || type.value_type == TRVT_SLOT_INDEX || type.value_type == TRVT_SLOT_INDEX_INT) {
1328 SetTraceRestrictValue(item, index);
1329 TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1330 return;
1334 const TraceRestrictDropDownListSet *list_set = this->drop_down_list_mapping[widget];
1335 if (!list_set) {
1336 return;
1339 uint value = list_set->value_array[index];
1341 switch (widget) {
1342 case TR_WIDGET_INSERT: {
1343 TraceRestrictItem insert_item = 0;
1345 TraceRestrictCondFlags cond_flags = static_cast<TraceRestrictCondFlags>(value >> 16);
1346 value &= 0xFFFF;
1347 SetTraceRestrictTypeAndNormalise(insert_item, static_cast<TraceRestrictItemType>(value));
1348 SetTraceRestrictCondFlags(insert_item, cond_flags); // this needs to happen after calling SetTraceRestrictTypeAndNormalise
1350 this->expecting_inserted_item = insert_item;
1351 TraceRestrictDoCommandP(this->tile, this->track, TRDCT_INSERT_ITEM, this->selected_instruction - 1, insert_item, STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM);
1352 break;
1355 case TR_WIDGET_CONDFLAGS: {
1356 CondFlagsDropDownType cond_type = static_cast<CondFlagsDropDownType>(value);
1357 if (cond_type == CFDDT_ELSE) {
1358 SetTraceRestrictTypeAndNormalise(item, TRIT_COND_ENDIF);
1359 SetTraceRestrictCondFlags(item, TRCF_ELSE);
1360 } else {
1361 if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) {
1362 // item is currently an else, convert to else/or if
1363 SetTraceRestrictTypeAndNormalise(item, TRIT_COND_UNDEFINED);
1366 SetTraceRestrictCondFlags(item, static_cast<TraceRestrictCondFlags>(cond_type));
1369 TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1370 break;
1373 case TR_WIDGET_TYPE_COND:
1374 case TR_WIDGET_TYPE_NONCOND: {
1375 SetTraceRestrictTypeAndNormalise(item, static_cast<TraceRestrictItemType>(value & 0xFFFF), value >> 16);
1376 TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1377 break;
1380 case TR_WIDGET_COMPARATOR:
1381 case TR_WIDGET_SLOT_OP: {
1382 SetTraceRestrictCondOp(item, static_cast<TraceRestrictCondOp>(value));
1383 TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1384 break;
1387 case TR_WIDGET_VALUE_DROPDOWN: {
1388 if (GetTraceRestrictTypeProperties(item).value_type == TRVT_PF_PENALTY) {
1389 if (value == TRPPPI_END) {
1390 uint16 penalty_value;
1391 if (GetTraceRestrictAuxField(item) == TRPPAF_PRESET) {
1392 penalty_value = _tracerestrict_pathfinder_penalty_preset_values[GetTraceRestrictValue(item)];
1393 } else {
1394 penalty_value = GetTraceRestrictValue(item);
1396 SetDParam(0, penalty_value);
1397 ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 10, this, CS_NUMERAL, QSF_NONE);
1398 return;
1399 } else {
1400 SetTraceRestrictValue(item, value);
1401 SetTraceRestrictAuxField(item, TRPPAF_PRESET);
1403 } else {
1404 SetTraceRestrictValue(item, value);
1406 TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1407 break;
1412 virtual void OnPlaceObject(Point pt, TileIndex tile)
1414 int widget = this->current_placement_widget;
1415 this->current_placement_widget = -1;
1417 this->RaiseButtons();
1418 ResetObjectToPlace();
1420 if (widget < 0) {
1421 return;
1424 switch (widget) {
1425 case TR_WIDGET_COPY:
1426 OnPlaceObjectSignal(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM);
1427 break;
1429 case TR_WIDGET_COPY_APPEND:
1430 OnPlaceObjectSignal(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_APPEND_PROGRAM);
1431 break;
1433 case TR_WIDGET_SHARE:
1434 OnPlaceObjectSignal(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM);
1435 break;
1437 case TR_WIDGET_VALUE_DEST:
1438 OnPlaceObjectDestination(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1439 break;
1441 case TR_WIDGET_VALUE_SIGNAL:
1442 OnPlaceObjectSignalTileValue(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1443 break;
1445 default:
1446 NOT_REACHED();
1447 break;
1452 * Common OnPlaceObject handler for program management actions which involve clicking on a signal
1454 void OnPlaceObjectSignal(Point pt, TileIndex source_tile, int widget, int error_message)
1456 if (!IsPlainRailTile(source_tile)) {
1457 ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO);
1458 return;
1461 TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(source_tile, TRANSPORT_RAIL, 0));
1462 if (trackbits & TRACK_BIT_VERT) { // N-S direction
1463 trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT;
1466 if (trackbits & TRACK_BIT_HORZ) { // E-W direction
1467 trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER;
1469 Track source_track = FindFirstTrack(trackbits);
1470 if(source_track == INVALID_TRACK) {
1471 ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO);
1472 return;
1475 if (!HasTrack(source_tile, source_track)) {
1476 ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO);
1477 return;
1480 if (!HasSignalOnTrack(source_tile, source_track)) {
1481 ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO);
1482 return;
1485 switch (widget) {
1486 case TR_WIDGET_COPY:
1487 TraceRestrictProgMgmtWithSourceDoCommandP(this->tile, this->track, TRDCT_PROG_COPY,
1488 source_tile, source_track, STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM);
1489 break;
1491 case TR_WIDGET_COPY_APPEND:
1492 TraceRestrictProgMgmtWithSourceDoCommandP(this->tile, this->track, TRDCT_PROG_COPY_APPEND,
1493 source_tile, source_track, STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_APPEND_PROGRAM);
1494 break;
1496 case TR_WIDGET_SHARE:
1497 TraceRestrictProgMgmtWithSourceDoCommandP(this->tile, this->track, TRDCT_PROG_SHARE,
1498 source_tile, source_track, STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM);
1499 break;
1501 default:
1502 NOT_REACHED();
1503 break;
1508 * Common OnPlaceObject handler for instruction value modification actions which involve selecting an order target
1510 void OnPlaceObjectDestination(Point pt, TileIndex tile, int widget, int error_message)
1512 TraceRestrictItem item = GetSelected();
1513 if (GetTraceRestrictTypeProperties(item).value_type != TRVT_ORDER) return;
1515 bool stations_only = (GetTraceRestrictType(item) == TRIT_COND_LAST_STATION);
1517 if (IsDepotTypeTile(tile, TRANSPORT_RAIL)) {
1518 if (stations_only) return;
1519 SetTraceRestrictValue(item, GetDepotIndex(tile));
1520 SetTraceRestrictAuxField(item, TROCAF_DEPOT);
1521 } else if (IsRailWaypointTile(tile)) {
1522 if (stations_only) return;
1523 SetTraceRestrictValue(item, GetStationIndex(tile));
1524 SetTraceRestrictAuxField(item, TROCAF_WAYPOINT);
1525 } else if (IsTileType(tile, MP_STATION)) {
1526 StationID st_index = GetStationIndex(tile);
1527 const Station *st = Station::Get(st_index);
1528 if (st->facilities & FACIL_TRAIN) {
1529 SetTraceRestrictValue(item, st_index);
1530 SetTraceRestrictAuxField(item, TROCAF_STATION);
1531 } else {
1532 return;
1534 } else {
1535 return;
1538 if (!IsTileOwner(tile, _local_company)) {
1539 ShowErrorMessage(error_message, STR_ERROR_AREA_IS_OWNED_BY_ANOTHER, WL_INFO);
1540 return;
1543 TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1547 * Common OnPlaceObject handler for instruction value modification actions which involve selecting a signal tile value
1549 void OnPlaceObjectSignalTileValue(Point pt, TileIndex tile, int widget, int error_message)
1551 TraceRestrictItem item = GetSelected();
1552 if (GetTraceRestrictTypeProperties(item).value_type != TRVT_TILE_INDEX) return;
1554 if (!IsTileOwner(tile, _local_company)) {
1555 ShowErrorMessage(error_message, STR_ERROR_AREA_IS_OWNED_BY_ANOTHER, WL_INFO);
1556 return;
1558 if (IsRailDepotTile(tile)) {
1559 // OK
1560 } else {
1561 if (!IsPlainRailTile(tile)) {
1562 ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO);
1563 return;
1565 if (GetPresentSignals(tile) == 0) {
1566 ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO);
1567 return;
1571 TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_DUAL_ITEM, this->selected_instruction - 1, tile, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM);
1574 virtual void OnPlaceObjectAbort()
1576 this->RaiseButtons();
1577 this->current_placement_widget = -1;
1580 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
1582 switch (widget) {
1583 case TR_WIDGET_INSTRUCTION_LIST:
1584 resize->height = FONT_HEIGHT_NORMAL;
1585 size->height = 6 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
1586 break;
1590 virtual void OnResize()
1592 /* Update the scroll bar */
1593 this->vscroll->SetCapacityFromWidget(this, TR_WIDGET_INSTRUCTION_LIST);
1596 virtual void OnPaint()
1598 this->DrawWidgets();
1601 virtual void DrawWidget(const Rect &r, int widget) const
1603 if (widget != TR_WIDGET_INSTRUCTION_LIST) return;
1605 int y = r.top + WD_FRAMERECT_TOP;
1606 int line_height = this->GetWidget<NWidgetBase>(TR_WIDGET_INSTRUCTION_LIST)->resize_y;
1607 int scroll_position = this->vscroll->GetPosition();
1609 // prog may be nullptr
1610 const TraceRestrictProgram *prog = this->GetProgram();
1612 int count = this->GetItemCount(prog);
1613 uint indent = 1;
1614 for(int i = 0; i < count; i++) {
1615 TraceRestrictItem item = this->GetItem(prog, i);
1616 uint this_indent = indent;
1617 if (IsTraceRestrictConditional(item)) {
1618 if (GetTraceRestrictCondFlags(item) & (TRCF_ELSE | TRCF_OR)) {
1619 this_indent--;
1620 } else if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) {
1621 indent--;
1622 this_indent--;
1623 } else {
1624 indent++;
1626 } else if (GetTraceRestrictType(item) == TRIT_NULL) {
1627 this_indent = 0;
1630 if (i >= scroll_position && this->vscroll->IsVisible(i)) {
1631 DrawInstructionString(prog, item, i, y, i == this->selected_instruction, this_indent, r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT);
1632 y += line_height;
1637 virtual void OnInvalidateData(int data, bool gui_scope)
1639 if (gui_scope) {
1640 this->ReloadProgramme();
1644 virtual void SetStringParameters(int widget) const
1646 switch (widget) {
1647 case TR_WIDGET_VALUE_INT: {
1648 SetDParam(0, 0);
1649 TraceRestrictItem item = this->GetSelected();
1650 TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type;
1651 if (IsIntegerValueType(type)) {
1652 SetDParam(0, ConvertIntegerValue(type, GetTraceRestrictValue(item), true));
1653 } else if (type == TRVT_SLOT_INDEX_INT) {
1654 SetDParam(0, *(TraceRestrictProgram::InstructionAt(this->GetProgram()->items, this->selected_instruction - 1) + 1));
1656 break;
1659 case TR_WIDGET_CAPTION: {
1660 const TraceRestrictProgram *prog = this->GetProgram();
1661 if (prog) {
1662 SetDParam(0, prog->refcount);
1663 } else {
1664 SetDParam(0, 1);
1666 break;
1669 case TR_WIDGET_VALUE_DROPDOWN: {
1670 TraceRestrictItem item = this->GetSelected();
1671 TraceRestrictTypePropertySet type = GetTraceRestrictTypeProperties(item);
1672 if ((type.value_type == TRVT_PF_PENALTY &&
1673 GetTraceRestrictAuxField(item) == TRPPAF_VALUE)
1674 || type.value_type == TRVT_GROUP_INDEX
1675 || type.value_type == TRVT_SLOT_INDEX) {
1676 SetDParam(0, GetTraceRestrictValue(item));
1678 break;
1681 case TR_WIDGET_LEFT_AUX_DROPDOWN: {
1682 TraceRestrictItem item = this->GetSelected();
1683 TraceRestrictTypePropertySet type = GetTraceRestrictTypeProperties(item);
1684 if (type.value_type == TRVT_SLOT_INDEX_INT) {
1685 SetDParam(0, GetTraceRestrictValue(item));
1687 break;
1692 virtual EventState OnCTRLStateChange() {
1693 this->UpdateButtonState();
1694 return ES_NOT_HANDLED;
1697 private:
1699 * Helper function to make start and end instructions (these are not stored in the actual program)
1701 TraceRestrictItem MakeSpecialItem(TraceRestrictNullTypeSpecialValue value) const
1703 TraceRestrictItem item = 0;
1704 SetTraceRestrictType(item, TRIT_NULL);
1705 SetTraceRestrictValue(item, value);
1706 return item;
1710 * Get item count of program, including start and end markers
1712 int GetItemCount(const TraceRestrictProgram *prog) const
1714 if (prog) {
1715 return 2 + (int)prog->GetInstructionCount();
1716 } else {
1717 return 2;
1722 * Get current program
1723 * This may return nullptr if no program currently exists
1725 const TraceRestrictProgram *GetProgram() const
1727 return GetTraceRestrictProgram(MakeTraceRestrictRefId(tile, track), false);
1731 * Get instruction at @p index in program @p prog
1732 * This correctly handles start/end markers, offsets, etc.
1733 * This returns a 0 instruction if out of bounds
1734 * @p prog may be nullptr
1736 TraceRestrictItem GetItem(const TraceRestrictProgram *prog, int index) const
1738 if (index < 0) {
1739 return 0;
1742 if (index == 0) {
1743 return MakeSpecialItem(TRNTSV_START);
1746 if (prog) {
1747 size_t instruction_count = prog->GetInstructionCount();
1749 if (static_cast<size_t>(index) == instruction_count + 1) {
1750 return MakeSpecialItem(TRNTSV_END);
1753 if (static_cast<size_t>(index) > instruction_count + 1) {
1754 return 0;
1757 return prog->items[prog->InstructionOffsetToArrayOffset(index - 1)];
1758 } else {
1759 // No program defined, this is equivalent to an empty program
1760 if (index == 1) {
1761 return MakeSpecialItem(TRNTSV_END);
1762 } else {
1763 return 0;
1769 * Get selected instruction, or a zero instruction
1771 TraceRestrictItem GetSelected() const
1773 return this->GetItem(this->GetProgram(), this->selected_instruction);
1777 * Get owner of the signal tile this window is pointing at
1779 Owner GetOwner()
1781 return GetTileOwner(this->tile);
1785 * Return item index from point in instruction list widget
1787 int GetItemIndexFromPt(int y)
1789 NWidgetBase *nwid = this->GetWidget<NWidgetBase>(TR_WIDGET_INSTRUCTION_LIST);
1790 int sel = (y - nwid->pos_y - WD_FRAMERECT_TOP) / nwid->resize_y; // Selected line
1792 if ((uint)sel >= this->vscroll->GetCapacity()) return -1;
1794 sel += this->vscroll->GetPosition();
1796 return (sel < this->GetItemCount(this->GetProgram()) && sel >= 0) ? sel : -1;
1800 * Reload details of program, and adjust length/selection position as necessary
1802 void ReloadProgramme()
1804 const TraceRestrictProgram *prog = this->GetProgram();
1806 if (this->vscroll->GetCount() != this->GetItemCount(prog)) {
1807 // program length has changed
1809 if (this->GetItemCount(prog) < this->vscroll->GetCount() ||
1810 this->GetItem(prog, this->selected_instruction) != this->expecting_inserted_item) {
1811 // length has shrunk or if we weren't expecting an insertion, deselect
1812 this->selected_instruction = -1;
1814 this->expecting_inserted_item = static_cast<TraceRestrictItem>(0);
1816 // update scrollbar size
1817 this->vscroll->SetCount(this->GetItemCount(prog));
1819 this->UpdateButtonState();
1822 bool IsUpDownBtnUsable(bool up, bool update_selection = false) {
1823 const TraceRestrictProgram *prog = this->GetProgram();
1824 if (!prog) return false;
1826 TraceRestrictItem item = this->GetSelected();
1827 if (GetTraceRestrictType(item) == TRIT_NULL) return false;
1829 std::vector<TraceRestrictItem> items = prog->items; // copy
1830 uint32 offset = this->selected_instruction - 1;
1831 if (TraceRestrictProgramMoveItemAt(items, offset, up, _ctrl_pressed).Succeeded()) {
1832 TraceRestrictProgramActionsUsedFlags actions_used_flags;
1833 if (TraceRestrictProgram::Validate(items, actions_used_flags).Succeeded()) {
1834 if (update_selection) this->selected_instruction = offset + 1;
1835 return true;
1839 return false;
1843 * Update button states, text values, etc.
1845 void UpdateButtonState()
1847 this->RaiseWidget(TR_WIDGET_INSERT);
1848 this->RaiseWidget(TR_WIDGET_REMOVE);
1849 this->RaiseWidget(TR_WIDGET_TYPE_COND);
1850 this->RaiseWidget(TR_WIDGET_TYPE_NONCOND);
1851 this->RaiseWidget(TR_WIDGET_CONDFLAGS);
1852 this->RaiseWidget(TR_WIDGET_COMPARATOR);
1853 this->RaiseWidget(TR_WIDGET_SLOT_OP);
1854 this->RaiseWidget(TR_WIDGET_VALUE_INT);
1855 this->RaiseWidget(TR_WIDGET_VALUE_DROPDOWN);
1856 this->RaiseWidget(TR_WIDGET_VALUE_DEST);
1857 this->RaiseWidget(TR_WIDGET_VALUE_SIGNAL);
1858 this->RaiseWidget(TR_WIDGET_LEFT_AUX_DROPDOWN);
1860 NWidgetStacked *left_2_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_TOP_LEFT_2);
1861 NWidgetStacked *left_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_TOP_LEFT);
1862 NWidgetStacked *left_aux_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_TOP_LEFT_AUX);
1863 NWidgetStacked *middle_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_TOP_MIDDLE);
1864 NWidgetStacked *right_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_TOP_RIGHT);
1865 NWidgetStacked *share_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_SHARE);
1866 NWidgetStacked *copy_sel = this->GetWidget<NWidgetStacked>(TR_WIDGET_SEL_COPY);
1868 this->DisableWidget(TR_WIDGET_TYPE_COND);
1869 this->DisableWidget(TR_WIDGET_TYPE_NONCOND);
1870 this->DisableWidget(TR_WIDGET_CONDFLAGS);
1871 this->DisableWidget(TR_WIDGET_COMPARATOR);
1872 this->DisableWidget(TR_WIDGET_SLOT_OP);
1873 this->DisableWidget(TR_WIDGET_VALUE_INT);
1874 this->DisableWidget(TR_WIDGET_VALUE_DROPDOWN);
1875 this->DisableWidget(TR_WIDGET_VALUE_DEST);
1876 this->DisableWidget(TR_WIDGET_VALUE_SIGNAL);
1877 this->DisableWidget(TR_WIDGET_LEFT_AUX_DROPDOWN);
1879 this->DisableWidget(TR_WIDGET_INSERT);
1880 this->DisableWidget(TR_WIDGET_REMOVE);
1881 this->DisableWidget(TR_WIDGET_RESET);
1882 this->DisableWidget(TR_WIDGET_COPY);
1883 this->DisableWidget(TR_WIDGET_SHARE);
1884 this->DisableWidget(TR_WIDGET_UNSHARE);
1886 this->DisableWidget(TR_WIDGET_BLANK_L2);
1887 this->DisableWidget(TR_WIDGET_BLANK_L);
1888 this->DisableWidget(TR_WIDGET_BLANK_M);
1889 this->DisableWidget(TR_WIDGET_BLANK_R);
1891 this->DisableWidget(TR_WIDGET_UP_BTN);
1892 this->DisableWidget(TR_WIDGET_DOWN_BTN);
1894 this->EnableWidget(TR_WIDGET_COPY_APPEND);
1896 left_2_sel->SetDisplayedPlane(DPL2_BLANK);
1897 left_sel->SetDisplayedPlane(DPL_BLANK);
1898 left_aux_sel->SetDisplayedPlane(SZSP_NONE);
1899 middle_sel->SetDisplayedPlane(DPM_BLANK);
1900 right_sel->SetDisplayedPlane(DPR_BLANK);
1901 share_sel->SetDisplayedPlane(DPS_SHARE);
1902 copy_sel->SetDisplayedPlane(_ctrl_pressed ? DPC_APPEND : DPC_COPY);
1904 const TraceRestrictProgram *prog = this->GetProgram();
1906 this->GetWidget<NWidgetCore>(TR_WIDGET_CAPTION)->widget_data =
1907 (prog && prog->refcount > 1) ? STR_TRACE_RESTRICT_CAPTION_SHARED : STR_TRACE_RESTRICT_CAPTION;
1909 auto left_aux_guard = scope_guard([&]() {
1910 if (this->current_left_aux_plane != left_aux_sel->shown_plane) {
1911 this->current_left_aux_plane = left_aux_sel->shown_plane;
1912 this->ReInit();
1916 // Don't allow modifications if don't own
1917 if (this->GetOwner() != _local_company) {
1918 this->SetDirty();
1919 return;
1922 if (prog && prog->refcount > 1) {
1923 // program is shared, show and enable unshare button, and reset button
1924 share_sel->SetDisplayedPlane(DPS_UNSHARE);
1925 this->EnableWidget(TR_WIDGET_UNSHARE);
1926 this->EnableWidget(TR_WIDGET_RESET);
1927 } else if (this->GetItemCount(prog) > 2) {
1928 // program is non-empty and not shared, enable reset button
1929 this->EnableWidget(TR_WIDGET_RESET);
1930 } else {
1931 // program is empty and not shared, show copy and share buttons
1932 this->EnableWidget(TR_WIDGET_COPY);
1933 this->EnableWidget(TR_WIDGET_SHARE);
1936 // haven't selected instruction
1937 if (this->selected_instruction < 1) {
1938 this->SetDirty();
1939 return;
1942 TraceRestrictItem item = this->GetItem(prog, this->selected_instruction);
1943 if (item != 0) {
1944 if (GetTraceRestrictType(item) == TRIT_NULL) {
1945 switch (GetTraceRestrictValue(item)) {
1946 case TRNTSV_START:
1947 break;
1949 case TRNTSV_END:
1950 this->EnableWidget(TR_WIDGET_INSERT);
1951 break;
1953 default:
1954 NOT_REACHED();
1955 break;
1957 } else if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) {
1958 this->EnableWidget(TR_WIDGET_INSERT);
1959 if (GetTraceRestrictCondFlags(item) != 0) {
1960 // this is not an end if, it must be an else, enable removing
1961 this->EnableWidget(TR_WIDGET_REMOVE);
1963 // setup condflags dropdown to show else
1964 left_2_sel->SetDisplayedPlane(DPL2_CONDFLAGS);
1965 this->EnableWidget(TR_WIDGET_CONDFLAGS);
1966 this->GetWidget<NWidgetCore>(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_ELSE;
1968 } else {
1969 TraceRestrictTypePropertySet properties = GetTraceRestrictTypeProperties(item);
1971 int type_widget;
1972 if (IsTraceRestrictConditional(item)) {
1973 // note that else and end if items are not handled here, they are handled above
1975 left_2_sel->SetDisplayedPlane(DPL2_CONDFLAGS);
1976 left_sel->SetDisplayedPlane(DPL_TYPE);
1977 type_widget = TR_WIDGET_TYPE_COND;
1979 // setup condflags dropdown box
1980 left_2_sel->SetDisplayedPlane(DPL2_CONDFLAGS);
1981 switch (GetTraceRestrictCondFlags(item)) {
1982 case TRCF_DEFAULT: // opening if, leave disabled
1983 this->GetWidget<NWidgetCore>(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_IF;
1984 break;
1986 case TRCF_ELSE: // else-if
1987 this->GetWidget<NWidgetCore>(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_ELIF;
1988 this->EnableWidget(TR_WIDGET_CONDFLAGS);
1989 break;
1991 case TRCF_OR: // or-if
1992 this->GetWidget<NWidgetCore>(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_ORIF;
1993 this->EnableWidget(TR_WIDGET_CONDFLAGS);
1994 break;
1996 default:
1997 NOT_REACHED();
1998 break;
2000 } else {
2001 left_2_sel->SetDisplayedPlane(DPL2_TYPE);
2002 type_widget = TR_WIDGET_TYPE_NONCOND;
2004 this->EnableWidget(type_widget);
2006 this->GetWidget<NWidgetCore>(type_widget)->widget_data =
2007 GetTypeString(item);
2009 if (properties.cond_type == TRCOT_BINARY || properties.cond_type == TRCOT_ALL) {
2010 middle_sel->SetDisplayedPlane(DPM_COMPARATOR);
2011 this->EnableWidget(TR_WIDGET_COMPARATOR);
2013 const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(properties);
2015 if (list_set) {
2016 this->GetWidget<NWidgetCore>(TR_WIDGET_COMPARATOR)->widget_data =
2017 GetDropDownStringByValue(list_set, GetTraceRestrictCondOp(item));
2021 if (IsIntegerValueType(properties.value_type)) {
2022 right_sel->SetDisplayedPlane(DPR_VALUE_INT);
2023 this->EnableWidget(TR_WIDGET_VALUE_INT);
2024 } else {
2025 switch (properties.value_type) {
2026 case TRVT_DENY:
2027 right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN);
2028 this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);
2029 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data =
2030 GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_PF_ALLOW : STR_TRACE_RESTRICT_PF_DENY;
2031 break;
2033 case TRVT_ORDER:
2034 right_sel->SetDisplayedPlane(DPR_VALUE_DEST);
2035 this->EnableWidget(TR_WIDGET_VALUE_DEST);
2036 break;
2038 case TRVT_CARGO_ID:
2039 right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN);
2040 this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);
2041 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data =
2042 GetCargoStringByID(GetTraceRestrictValue(item));
2043 break;
2045 case TRVT_DIRECTION:
2046 right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN);
2047 this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);
2048 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data =
2049 GetDropDownStringByValue(&_direction_value, GetTraceRestrictValue(item));
2050 break;
2052 case TRVT_TILE_INDEX:
2053 right_sel->SetDisplayedPlane(DPR_VALUE_SIGNAL);
2054 this->EnableWidget(TR_WIDGET_VALUE_SIGNAL);
2055 break;
2057 case TRVT_PF_PENALTY:
2058 right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN);
2059 this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);
2060 if (GetTraceRestrictAuxField(item) == TRPPAF_VALUE) {
2061 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_BLACK_COMMA;
2062 } else {
2063 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data =
2064 GetDropDownStringByValue(&_pf_penalty_dropdown, GetPathfinderPenaltyDropdownIndex(item));
2066 break;
2068 case TRVT_RESERVE_THROUGH:
2069 right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN);
2070 this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);
2071 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data =
2072 GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_RESERVE_THROUGH_CANCEL : STR_TRACE_RESTRICT_RESERVE_THROUGH;
2073 break;
2075 case TRVT_LONG_RESERVE:
2076 right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN);
2077 this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);
2078 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data =
2079 GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_LONG_RESERVE_CANCEL : STR_TRACE_RESTRICT_LONG_RESERVE;
2080 break;
2082 case TRVT_WAIT_AT_PBS:
2083 right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN);
2084 this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);
2085 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data =
2086 GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_WAIT_AT_PBS_CANCEL : STR_TRACE_RESTRICT_WAIT_AT_PBS;
2087 break;
2089 case TRVT_GROUP_INDEX:
2090 right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN);
2091 this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);
2092 switch (GetTraceRestrictValue(item)) {
2093 case INVALID_GROUP:
2094 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_TRACE_RESTRICT_VARIABLE_UNDEFINED;
2095 break;
2097 case DEFAULT_GROUP:
2098 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_GROUP_DEFAULT_TRAINS;
2099 break;
2101 default:
2102 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_GROUP_NAME;
2103 break;
2105 break;
2107 case TRVT_SLOT_INDEX: {
2108 right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN);
2109 if (!IsTraceRestrictConditional(item)) {
2110 middle_sel->SetDisplayedPlane(DPM_SLOT_OP);
2111 this->EnableWidget(TR_WIDGET_SLOT_OP);
2114 const TraceRestrictSlot *slot;
2115 FOR_ALL_TRACE_RESTRICT_SLOTS(slot) {
2116 if (slot->owner == this->GetOwner()) {
2117 this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN);
2118 break;
2122 this->GetWidget<NWidgetCore>(TR_WIDGET_SLOT_OP)->widget_data =
2123 GetDropDownStringByValue(&_slot_op_cond_ops, GetTraceRestrictCondOp(item));
2124 switch (GetTraceRestrictValue(item)) {
2125 case INVALID_TRACE_RESTRICT_SLOT_ID:
2126 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_TRACE_RESTRICT_VARIABLE_UNDEFINED;
2127 break;
2129 default:
2130 this->GetWidget<NWidgetCore>(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_TRACE_RESTRICT_SLOT_NAME;
2131 break;
2133 break;
2136 case TRVT_SLOT_INDEX_INT: {
2137 right_sel->SetDisplayedPlane(DPR_VALUE_INT);
2138 left_aux_sel->SetDisplayedPlane(DPLA_DROPDOWN);
2139 this->EnableWidget(TR_WIDGET_VALUE_INT);
2141 const TraceRestrictSlot *slot;
2142 FOR_ALL_TRACE_RESTRICT_SLOTS(slot) {
2143 if (slot->owner == this->GetOwner()) {
2144 this->EnableWidget(TR_WIDGET_LEFT_AUX_DROPDOWN);
2145 break;
2149 switch (GetTraceRestrictValue(item)) {
2150 case INVALID_TRACE_RESTRICT_SLOT_ID:
2151 this->GetWidget<NWidgetCore>(TR_WIDGET_LEFT_AUX_DROPDOWN)->widget_data = STR_TRACE_RESTRICT_VARIABLE_UNDEFINED;
2152 break;
2154 default:
2155 this->GetWidget<NWidgetCore>(TR_WIDGET_LEFT_AUX_DROPDOWN)->widget_data = STR_TRACE_RESTRICT_SLOT_NAME;
2156 break;
2158 break;
2161 default:
2162 break;
2166 this->EnableWidget(TR_WIDGET_INSERT);
2167 this->EnableWidget(TR_WIDGET_REMOVE);
2170 if (this->IsUpDownBtnUsable(true)) this->EnableWidget(TR_WIDGET_UP_BTN);
2171 if (this->IsUpDownBtnUsable(false)) this->EnableWidget(TR_WIDGET_DOWN_BTN);
2174 this->SetDirty();
2178 * Show a drop down list using @p list_set, setting the pre-selected item to the one corresponding to @p value
2179 * This asserts if @p value is not in @p list_set, and @p missing_ok is false
2181 void ShowDropDownListWithValue(const TraceRestrictDropDownListSet *list_set, uint value, bool missing_ok,
2182 int button, uint32 disabled_mask, uint32 hidden_mask, uint width)
2184 this->drop_down_list_mapping[button] = list_set;
2185 int selected = GetDropDownListIndexByValue(list_set, value, missing_ok);
2186 ShowDropDownMenu(this, list_set->string_array, selected, button, disabled_mask, hidden_mask, width);
2190 * Helper function to set or unset a SetObjectToPlaceWnd, for the given widget and cursor type
2192 void SetObjectToPlaceAction(int widget, CursorID cursor)
2194 if (this->current_placement_widget != -1 && widget != this->current_placement_widget) {
2195 ResetObjectToPlace();
2198 this->ToggleWidgetLoweredState(widget);
2199 this->SetWidgetDirty(widget);
2200 if (this->IsWidgetLowered(widget)) {
2201 SetObjectToPlaceWnd(cursor, PAL_NONE, HT_RECT, this);
2202 this->current_placement_widget = widget;
2203 } else {
2204 ResetObjectToPlace();
2205 this->current_placement_widget = -1;
2210 * This used for testing whether else or else-if blocks could be inserted, or replace the selection
2211 * If @p replace is true, replace selection with @p item, else insert @p item before selection
2212 * Returns true if resulting instruction list passes validation
2214 bool GenericElseInsertionDryRun(TraceRestrictItem item, bool replace)
2216 if (this->selected_instruction < 1) return false;
2217 uint offset = this->selected_instruction - 1;
2219 const TraceRestrictProgram *prog = this->GetProgram();
2220 if (!prog) return false;
2222 std::vector<TraceRestrictItem> items = prog->items; // copy
2224 if (offset >= (TraceRestrictProgram::GetInstructionCount(items) + (replace ? 0 : 1))) return false; // off the end of the program
2226 uint array_offset = (uint)TraceRestrictProgram::InstructionOffsetToArrayOffset(items, offset);
2227 if (replace) {
2228 items[array_offset] = item;
2229 } else {
2230 items.insert(items.begin() + array_offset, item);
2233 TraceRestrictProgramActionsUsedFlags actions_used_flags;
2234 return TraceRestrictProgram::Validate(items, actions_used_flags).Succeeded();
2238 * Run GenericElseInsertionDryRun with an else instruction
2240 bool ElseInsertionDryRun(bool replace)
2242 TraceRestrictItem item = 0;
2243 SetTraceRestrictType(item, TRIT_COND_ENDIF);
2244 SetTraceRestrictCondFlags(item, TRCF_ELSE);
2245 return GenericElseInsertionDryRun(item, replace);
2249 * Run GenericElseInsertionDryRun with an elif instruction
2251 bool ElseIfInsertionDryRun(bool replace)
2253 TraceRestrictItem item = 0;
2254 SetTraceRestrictType(item, TRIT_COND_UNDEFINED);
2255 SetTraceRestrictCondFlags(item, TRCF_ELSE);
2256 return GenericElseInsertionDryRun(item, replace);
2260 static const NWidgetPart _nested_program_widgets[] = {
2261 // Title bar
2262 NWidget(NWID_HORIZONTAL),
2263 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2264 NWidget(WWT_CAPTION, COLOUR_GREY, TR_WIDGET_CAPTION), SetDataTip(STR_TRACE_RESTRICT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
2265 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2266 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2267 EndContainer(),
2269 // Program display
2270 NWidget(NWID_HORIZONTAL),
2271 NWidget(WWT_PANEL, COLOUR_GREY, TR_WIDGET_INSTRUCTION_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_TRACE_RESTRICT_INSTRUCTION_LIST_TOOLTIP),
2272 SetResize(1, 1), SetScrollbar(TR_WIDGET_SCROLLBAR), EndContainer(),
2273 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TR_WIDGET_SCROLLBAR),
2274 EndContainer(),
2276 // Button Bar
2277 NWidget(NWID_HORIZONTAL),
2278 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, TR_WIDGET_UP_BTN), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_UP, STR_TRACE_RESTRICT_UP_BTN_TOOLTIP),
2279 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, TR_WIDGET_DOWN_BTN), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_DOWN, STR_TRACE_RESTRICT_DOWN_BTN_TOOLTIP),
2280 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2281 NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT_2),
2282 NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE_NONCOND), SetMinimalSize(124, 12), SetFill(1, 0),
2283 SetDataTip(STR_NULL, STR_TRACE_RESTRICT_TYPE_TOOLTIP), SetResize(1, 0),
2284 NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_CONDFLAGS), SetMinimalSize(124, 12), SetFill(1, 0),
2285 SetDataTip(STR_NULL, STR_TRACE_RESTRICT_CONDFLAGS_TOOLTIP), SetResize(1, 0),
2286 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_L2), SetMinimalSize(124, 12), SetFill(1, 0),
2287 SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0),
2288 EndContainer(),
2289 NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT),
2290 NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE_COND), SetMinimalSize(124, 12), SetFill(1, 0),
2291 SetDataTip(STR_NULL, STR_TRACE_RESTRICT_TYPE_TOOLTIP), SetResize(1, 0),
2292 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_L), SetMinimalSize(124, 12), SetFill(1, 0),
2293 SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0),
2294 EndContainer(),
2295 NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT_AUX),
2296 NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_LEFT_AUX_DROPDOWN), SetMinimalSize(124, 12), SetFill(1, 0),
2297 SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0),
2298 EndContainer(),
2299 NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_MIDDLE),
2300 NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0),
2301 SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_COMPARATOR_TOOLTIP), SetResize(1, 0),
2302 NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_SLOT_OP), SetMinimalSize(124, 12), SetFill(1, 0),
2303 SetDataTip(STR_NULL, STR_TRACE_RESTRICT_SLOT_OP_TOOLTIP), SetResize(1, 0),
2304 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_M), SetMinimalSize(124, 12), SetFill(1, 0),
2305 SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0),
2306 EndContainer(),
2307 NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_RIGHT),
2308 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_INT), SetMinimalSize(124, 12), SetFill(1, 0),
2309 SetDataTip(STR_BLACK_COMMA, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0),
2310 NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_VALUE_DROPDOWN), SetMinimalSize(124, 12), SetFill(1, 0),
2311 SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0),
2312 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_DEST), SetMinimalSize(124, 12), SetFill(1, 0),
2313 SetDataTip(STR_TRACE_RESTRICT_SELECT_TARGET, STR_TRACE_RESTRICT_SELECT_TARGET), SetResize(1, 0),
2314 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_SIGNAL), SetMinimalSize(124, 12), SetFill(1, 0),
2315 SetDataTip(STR_TRACE_RESTRICT_SELECT_SIGNAL, STR_TRACE_RESTRICT_SELECT_SIGNAL), SetResize(1, 0),
2316 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_R), SetMinimalSize(124, 12), SetFill(1, 0),
2317 SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0),
2318 EndContainer(),
2319 EndContainer(),
2320 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, TR_WIDGET_GOTO_SIGNAL), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_RIGHT, STR_TRACE_RESTRICT_GOTO_SIGNAL_TOOLTIP),
2321 EndContainer(),
2323 /* Second button row. */
2324 NWidget(NWID_HORIZONTAL),
2325 NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
2326 NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_INSERT), SetMinimalSize(124, 12), SetFill(1, 0),
2327 SetDataTip(STR_TRACE_RESTRICT_INSERT, STR_TRACE_RESTRICT_INSERT_TOOLTIP), SetResize(1, 0),
2328 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_REMOVE), SetMinimalSize(124, 12), SetFill(1, 0),
2329 SetDataTip(STR_TRACE_RESTRICT_REMOVE, STR_TRACE_RESTRICT_REMOVE_TOOLTIP), SetResize(1, 0),
2330 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_RESET), SetMinimalSize(124, 12), SetFill(1, 0),
2331 SetDataTip(STR_TRACE_RESTRICT_RESET, STR_TRACE_RESTRICT_RESET_TOOLTIP), SetResize(1, 0),
2332 NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_COPY),
2333 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_COPY), SetMinimalSize(124, 12), SetFill(1, 0),
2334 SetDataTip(STR_TRACE_RESTRICT_COPY, STR_TRACE_RESTRICT_COPY_TOOLTIP), SetResize(1, 0),
2335 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_COPY_APPEND), SetMinimalSize(124, 12), SetFill(1, 0),
2336 SetDataTip(STR_TRACE_RESTRICT_APPEND, STR_TRACE_RESTRICT_COPY_TOOLTIP), SetResize(1, 0),
2337 EndContainer(),
2338 NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_SHARE),
2339 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_SHARE), SetMinimalSize(124, 12), SetFill(1, 0),
2340 SetDataTip(STR_TRACE_RESTRICT_SHARE, STR_TRACE_RESTRICT_SHARE_TOOLTIP), SetResize(1, 0),
2341 NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_UNSHARE), SetMinimalSize(124, 12), SetFill(1, 0),
2342 SetDataTip(STR_TRACE_RESTRICT_UNSHARE, STR_TRACE_RESTRICT_UNSHARE_TOOLTIP), SetResize(1, 0),
2343 EndContainer(),
2344 EndContainer(),
2345 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2346 EndContainer(),
2349 static WindowDesc _program_desc(
2350 WDP_AUTO, "trace_restrict_gui", 384, 100,
2351 WC_TRACE_RESTRICT, WC_BUILD_SIGNAL,
2352 WDF_CONSTRUCTION,
2353 _nested_program_widgets, lengthof(_nested_program_widgets)
2357 * Show or create program window for given @p tile and @p track
2359 void ShowTraceRestrictProgramWindow(TileIndex tile, Track track)
2361 if (BringWindowToFrontById(WC_TRACE_RESTRICT, MakeTraceRestrictRefId(tile, track)) != nullptr) {
2362 return;
2365 new TraceRestrictWindow(&_program_desc, tile, track);
2368 /** Slot GUI widget IDs */
2369 enum TraceRestrictSlotWindowWidgets {
2370 WID_TRSL_CAPTION,
2371 WID_TRSL_ALL_VEHICLES,
2372 WID_TRSL_LIST_SLOTS,
2373 WID_TRSL_LIST_SLOTS_SCROLLBAR,
2374 WID_TRSL_CREATE_SLOT,
2375 WID_TRSL_DELETE_SLOT,
2376 WID_TRSL_RENAME_SLOT,
2377 WID_TRSL_SET_SLOT_MAX_OCCUPANCY,
2378 WID_TRSL_SORT_BY_ORDER,
2379 WID_TRSL_SORT_BY_DROPDOWN,
2380 WID_TRSL_FILTER_BY_CARGO,
2381 WID_TRSL_LIST_VEHICLE,
2382 WID_TRSL_LIST_VEHICLE_SCROLLBAR,
2386 static const NWidgetPart _nested_slot_widgets[] = {
2387 NWidget(NWID_HORIZONTAL), // Window header
2388 NWidget(WWT_CLOSEBOX, COLOUR_GREY),
2389 NWidget(WWT_CAPTION, COLOUR_GREY, WID_TRSL_CAPTION), SetDataTip(STR_TRACE_RESTRICT_SLOT_CAPTION, STR_NULL),
2390 NWidget(WWT_SHADEBOX, COLOUR_GREY),
2391 NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
2392 NWidget(WWT_STICKYBOX, COLOUR_GREY),
2393 EndContainer(),
2394 NWidget(NWID_HORIZONTAL),
2395 /* left part */
2396 NWidget(NWID_VERTICAL),
2397 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalTextLines(1, WD_DROPDOWNTEXT_TOP + WD_DROPDOWNTEXT_BOTTOM), SetFill(1, 0), EndContainer(),
2398 NWidget(WWT_PANEL, COLOUR_GREY, WID_TRSL_ALL_VEHICLES), SetFill(1, 0), EndContainer(),
2399 NWidget(NWID_HORIZONTAL),
2400 NWidget(WWT_MATRIX, COLOUR_GREY, WID_TRSL_LIST_SLOTS), SetMatrixDataTip(1, 0, STR_TRACE_RESTRICT_SLOT_GUI_LIST_TOOLTIP),
2401 SetFill(1, 0), SetResize(0, 1), SetScrollbar(WID_TRSL_LIST_SLOTS_SCROLLBAR),
2402 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_TRSL_LIST_SLOTS_SCROLLBAR),
2403 EndContainer(),
2404 NWidget(NWID_HORIZONTAL),
2405 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TRSL_CREATE_SLOT), SetFill(0, 1),
2406 SetDataTip(SPR_GROUP_CREATE_TRAIN, STR_TRACE_RESTRICT_SLOT_CREATE_TOOLTIP),
2407 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TRSL_DELETE_SLOT), SetFill(0, 1),
2408 SetDataTip(SPR_GROUP_DELETE_TRAIN, STR_TRACE_RESTRICT_SLOT_DELETE_TOOLTIP),
2409 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TRSL_RENAME_SLOT), SetFill(0, 1),
2410 SetDataTip(SPR_GROUP_RENAME_TRAIN, STR_TRACE_RESTRICT_SLOT_RENAME_TOOLTIP),
2411 NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), EndContainer(),
2412 NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_TRSL_SET_SLOT_MAX_OCCUPANCY), SetFill(0, 1),
2413 SetDataTip(SPR_IMG_SETTINGS, STR_TRACE_RESTRICT_SLOT_SET_MAX_OCCUPANCY_TOOLTIP),
2414 EndContainer(),
2415 EndContainer(),
2416 /* right part */
2417 NWidget(NWID_VERTICAL),
2418 NWidget(NWID_HORIZONTAL),
2419 NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_TRSL_SORT_BY_ORDER), SetMinimalSize(81, 12), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
2420 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_TRSL_SORT_BY_DROPDOWN), SetMinimalSize(167, 12), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIA),
2421 NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_TRSL_FILTER_BY_CARGO), SetMinimalSize(167, 12), SetDataTip(STR_JUST_STRING, STR_TOOLTIP_FILTER_CRITERIA),
2422 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(12, 12), SetResize(1, 0), EndContainer(),
2423 EndContainer(),
2424 NWidget(NWID_HORIZONTAL),
2425 NWidget(WWT_MATRIX, COLOUR_GREY, WID_TRSL_LIST_VEHICLE), SetMinimalSize(248, 0), SetMatrixDataTip(1, 0, STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP), SetResize(1, 1), SetFill(1, 0), SetScrollbar(WID_TRSL_LIST_VEHICLE_SCROLLBAR),
2426 NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_TRSL_LIST_VEHICLE_SCROLLBAR),
2427 EndContainer(),
2428 NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(1, 0), SetFill(1, 1), SetResize(1, 0), EndContainer(),
2429 NWidget(NWID_HORIZONTAL),
2430 NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), EndContainer(),
2431 NWidget(WWT_RESIZEBOX, COLOUR_GREY),
2432 EndContainer(),
2433 EndContainer(),
2434 EndContainer(),
2437 class TraceRestrictSlotWindow : public BaseVehicleListWindow {
2438 private:
2439 /* Columns in the group list */
2440 enum ListColumns {
2441 VGC_NAME, ///< Group name.
2442 VGC_NUMBER, ///< Number of vehicles in the group.
2444 VGC_END
2447 TraceRestrictSlotID slot_sel; ///< Selected slot (for drag/drop)
2448 bool slot_set_max_occupancy; ///< True if slot max occupancy is being changed, instead of renaming
2449 TraceRestrictSlotID slot_rename; ///< Slot being renamed or max occupancy changed, INVALID_TRACE_RESTRICT_SLOT_ID if none
2450 TraceRestrictSlotID slot_over; ///< Slot over which a vehicle is dragged, INVALID_TRACE_RESTRICT_SLOT_ID if none
2451 TraceRestrictSlotID slot_confirm; ///< Slot awaiting delete confirmation
2452 GUIList<const TraceRestrictSlot*> slots; ///< List of slots
2453 uint tiny_step_height; ///< Step height for the slot list
2454 Scrollbar *slot_sb;
2456 Dimension column_size[VGC_END]; ///< Size of the columns in the group list.
2459 * (Re)Build the slot list.
2461 * @param owner The owner of the window
2463 void BuildSlotList(Owner owner)
2465 if (!this->slots.NeedRebuild()) return;
2467 this->slots.Clear();
2469 const TraceRestrictSlot *slot;
2470 FOR_ALL_TRACE_RESTRICT_SLOTS(slot) {
2471 if (slot->owner == owner) {
2472 *(this->slots.Append()) = slot;
2476 this->slots.ForceResort();
2477 this->slots.Sort(&SlotNameSorter);
2478 this->slots.Compact();
2479 this->slots.RebuildDone();
2483 * Compute tiny_step_height and column_size
2484 * @return Total width required for the group list.
2486 uint ComputeSlotInfoSize()
2488 this->column_size[VGC_NAME] = GetStringBoundingBox(STR_GROUP_ALL_TRAINS);
2489 this->column_size[VGC_NAME].width = max(170u, this->column_size[VGC_NAME].width);
2490 this->tiny_step_height = this->column_size[VGC_NAME].height;
2492 SetDParamMaxValue(0, 9999, 3, FS_SMALL);
2493 SetDParamMaxValue(1, 9999, 3, FS_SMALL);
2494 this->column_size[VGC_NUMBER] = GetStringBoundingBox(STR_TRACE_RESTRICT_SLOT_MAX_OCCUPANCY);
2495 this->tiny_step_height = max(this->tiny_step_height, this->column_size[VGC_NUMBER].height);
2497 this->tiny_step_height += WD_MATRIX_TOP;
2499 return WD_FRAMERECT_LEFT + 8 +
2500 this->column_size[VGC_NAME].width + 8 +
2501 this->column_size[VGC_NUMBER].width + 2 +
2502 WD_FRAMERECT_RIGHT;
2506 * Draw a row in the slot list.
2507 * @param y Top of the row.
2508 * @param left Left of the row.
2509 * @param right Right of the row.
2510 * @param g_id Group to list.
2512 void DrawSlotInfo(int y, int left, int right, TraceRestrictSlotID slot_id) const
2514 /* Highlight the group if a vehicle is dragged over it */
2515 if (slot_id == this->slot_over) {
2516 GfxFillRect(left + WD_FRAMERECT_LEFT, y + WD_FRAMERECT_TOP, right - WD_FRAMERECT_RIGHT, y + this->tiny_step_height - WD_FRAMERECT_BOTTOM - WD_MATRIX_TOP, _colour_gradient[COLOUR_GREY][7]);
2519 /* draw the selected group in white, else we draw it in black */
2520 TextColour colour = slot_id == this->vli.index ? TC_WHITE : TC_BLACK;
2521 bool rtl = _current_text_dir == TD_RTL;
2523 /* draw group name */
2524 StringID str;
2525 if (slot_id == ALL_TRAINS_TRACE_RESTRICT_SLOT_ID) {
2526 str = STR_GROUP_ALL_TRAINS;
2527 } else {
2528 SetDParam(0, slot_id);
2529 str = STR_TRACE_RESTRICT_SLOT_NAME;
2531 int x = rtl ? right - WD_FRAMERECT_RIGHT - 8 - this->column_size[VGC_NAME].width + 1 : left + WD_FRAMERECT_LEFT + 8;
2532 DrawString(x, x + this->column_size[VGC_NAME].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NAME].height) / 2, str, colour);
2534 if (slot_id == ALL_TRAINS_TRACE_RESTRICT_SLOT_ID) return;
2536 const TraceRestrictSlot *slot = TraceRestrictSlot::Get(slot_id);
2538 /* draw the number of vehicles of the group */
2539 x = rtl ? x - 2 - this->column_size[VGC_NUMBER].width : x + 2 + this->column_size[VGC_NAME].width;
2540 SetDParam(0, slot->occupants.size());
2541 SetDParam(1, slot->max_occupancy);
2542 DrawString(x, x + this->column_size[VGC_NUMBER].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NUMBER].height) / 2, STR_TRACE_RESTRICT_SLOT_MAX_OCCUPANCY, colour, SA_RIGHT | SA_FORCE);
2546 * Mark the widget containing the currently highlighted slot as dirty.
2548 void DirtyHighlightedSlotWidget()
2550 if (this->slot_over == INVALID_TRACE_RESTRICT_SLOT_ID) return;
2552 if (this->slot_over == ALL_TRAINS_TRACE_RESTRICT_SLOT_ID) {
2553 this->SetWidgetDirty(WID_TRSL_ALL_VEHICLES);
2554 } else {
2555 this->SetWidgetDirty(WID_TRSL_LIST_SLOTS);
2559 public:
2560 TraceRestrictSlotWindow(WindowDesc *desc, WindowNumber window_number) : BaseVehicleListWindow(desc, window_number)
2562 this->CreateNestedTree();
2564 this->vscroll = this->GetScrollbar(WID_TRSL_LIST_VEHICLE_SCROLLBAR);
2565 this->slot_sb = this->GetScrollbar(WID_TRSL_LIST_SLOTS_SCROLLBAR);
2566 this->sorting = &_sorting.train;
2568 this->vli.index = ALL_TRAINS_TRACE_RESTRICT_SLOT_ID;
2569 this->slot_sel = INVALID_TRACE_RESTRICT_SLOT_ID;
2570 this->slot_rename = INVALID_TRACE_RESTRICT_SLOT_ID;
2571 this->slot_set_max_occupancy = false;
2572 this->slot_over = INVALID_TRACE_RESTRICT_SLOT_ID;
2574 this->vehicles.SetListing(*this->sorting);
2575 this->vehicles.ForceRebuild();
2576 this->vehicles.NeedResort();
2578 this->BuildVehicleList();
2579 this->SortVehicleList();
2581 this->slots.ForceRebuild();
2582 this->slots.NeedResort();
2583 this->BuildSlotList(vli.company);
2585 this->FinishInitNested(window_number);
2586 this->owner = vli.company;
2589 ~TraceRestrictSlotWindow()
2591 *this->sorting = this->vehicles.GetListing();
2594 virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
2596 switch (widget) {
2597 case WID_TRSL_LIST_SLOTS: {
2598 size->width = this->ComputeSlotInfoSize();
2599 resize->height = this->tiny_step_height;
2601 /* Minimum height is the height of the list widget minus all vehicles... */
2602 size->height = 4 * GetVehicleListHeight(this->vli.vtype, this->tiny_step_height) - this->tiny_step_height;
2604 /* ... minus the buttons at the bottom ... */
2605 uint max_icon_height = GetSpriteSize(this->GetWidget<NWidgetCore>(WID_TRSL_CREATE_SLOT)->widget_data).height;
2606 max_icon_height = max(max_icon_height, GetSpriteSize(this->GetWidget<NWidgetCore>(WID_TRSL_DELETE_SLOT)->widget_data).height);
2607 max_icon_height = max(max_icon_height, GetSpriteSize(this->GetWidget<NWidgetCore>(WID_TRSL_RENAME_SLOT)->widget_data).height);
2608 max_icon_height = max(max_icon_height, GetSpriteSize(this->GetWidget<NWidgetCore>(WID_TRSL_SET_SLOT_MAX_OCCUPANCY)->widget_data).height);
2610 /* Get a multiple of tiny_step_height of that amount */
2611 size->height = Ceil(size->height - max_icon_height, tiny_step_height);
2612 break;
2615 case WID_TRSL_ALL_VEHICLES:
2616 size->width = this->ComputeSlotInfoSize();
2617 size->height = this->tiny_step_height;
2618 break;
2620 case WID_TRSL_SORT_BY_ORDER: {
2621 Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
2622 d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
2623 d.height += padding.height;
2624 *size = maxdim(*size, d);
2625 break;
2628 case WID_TRSL_LIST_VEHICLE:
2629 this->ComputeSlotInfoSize();
2630 resize->height = GetVehicleListHeight(this->vli.vtype, this->tiny_step_height);
2631 size->height = 4 * resize->height;
2632 break;
2637 * Some data on this window has become invalid.
2638 * @param data Information about the changed data.
2639 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
2641 virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
2643 if (data == 0) {
2644 /* This needs to be done in command-scope to enforce rebuilding before resorting invalid data */
2645 this->vehicles.ForceRebuild();
2646 this->slots.ForceRebuild();
2647 } else {
2648 this->vehicles.ForceResort();
2649 this->slots.ForceResort();
2652 /* Process ID-invalidation in command-scope as well */
2653 if (this->slot_rename != INVALID_TRACE_RESTRICT_SLOT_ID && this->slot_rename != NEW_TRACE_RESTRICT_SLOT_ID &&
2654 !TraceRestrictSlot::IsValidID(this->slot_rename)) {
2655 DeleteWindowByClass(WC_QUERY_STRING);
2656 this->slot_rename = INVALID_TRACE_RESTRICT_SLOT_ID;
2659 if (this->vli.index != ALL_TRAINS_TRACE_RESTRICT_SLOT_ID && !TraceRestrictSlot::IsValidID(this->vli.index)) {
2660 this->vli.index = ALL_TRAINS_TRACE_RESTRICT_SLOT_ID;
2662 this->SetDirty();
2665 virtual void SetStringParameters(int widget) const
2667 switch (widget) {
2668 case WID_TRSL_FILTER_BY_CARGO:
2669 SetDParam(0, this->cargo_filter_texts[this->cargo_filter_criteria]);
2670 break;
2674 virtual void OnPaint()
2676 /* If we select the all vehicles, this->list will contain all vehicles of the owner
2677 * else this->list will contain all vehicles which belong to the selected group */
2678 this->BuildVehicleList();
2679 this->SortVehicleList();
2681 this->BuildSlotList(this->owner);
2683 this->slot_sb->SetCount(this->slots.Length());
2684 this->vscroll->SetCount(this->vehicles.Length());
2686 /* Disable the slot specific function when we select all vehicles */
2687 this->SetWidgetsDisabledState(this->vli.index == ALL_TRAINS_TRACE_RESTRICT_SLOT_ID || _local_company != this->vli.company,
2688 WID_TRSL_DELETE_SLOT,
2689 WID_TRSL_RENAME_SLOT,
2690 WID_TRSL_SET_SLOT_MAX_OCCUPANCY,
2691 WIDGET_LIST_END);
2693 /* Disable remaining buttons for non-local companies
2694 * Needed while changing _local_company, eg. by cheats
2695 * All procedures (eg. move vehicle to a slot)
2696 * verify, whether you are the owner of the vehicle,
2697 * so it doesn't have to be disabled
2699 this->SetWidgetsDisabledState(_local_company != this->vli.company,
2700 WID_TRSL_CREATE_SLOT,
2701 WIDGET_LIST_END);
2703 /* Set text of sort by dropdown */
2704 this->GetWidget<NWidgetCore>(WID_TRSL_SORT_BY_DROPDOWN)->widget_data = this->vehicle_sorter_names[this->vehicles.SortType()];
2706 this->GetWidget<NWidgetCore>(WID_TRSL_FILTER_BY_CARGO)->widget_data = this->cargo_filter_texts[this->cargo_filter_criteria];
2708 this->DrawWidgets();
2711 virtual void DrawWidget(const Rect &r, int widget) const
2713 switch (widget) {
2714 case WID_TRSL_ALL_VEHICLES:
2715 DrawSlotInfo(r.top + WD_FRAMERECT_TOP, r.left, r.right, ALL_TRAINS_TRACE_RESTRICT_SLOT_ID);
2716 break;
2718 case WID_TRSL_LIST_SLOTS: {
2719 int y1 = r.top + WD_FRAMERECT_TOP;
2720 int max = min(this->slot_sb->GetPosition() + this->slot_sb->GetCapacity(), this->slots.Length());
2721 for (int i = this->slot_sb->GetPosition(); i < max; ++i) {
2722 const TraceRestrictSlot *slot = this->slots[i];
2724 assert(slot->owner == this->owner);
2726 DrawSlotInfo(y1, r.left, r.right, slot->index);
2728 y1 += this->tiny_step_height;
2730 break;
2733 case WID_TRSL_SORT_BY_ORDER:
2734 this->DrawSortButtonState(WID_TRSL_SORT_BY_ORDER, this->vehicles.IsDescSortOrder() ? SBS_DOWN : SBS_UP);
2735 break;
2737 case WID_TRSL_LIST_VEHICLE:
2738 this->DrawVehicleListItems(this->vehicle_sel, this->resize.step_height, r);
2739 break;
2743 static void DeleteSlotCallback(Window *win, bool confirmed)
2745 if (confirmed) {
2746 TraceRestrictSlotWindow *w = (TraceRestrictSlotWindow*)win;
2747 w->vli.index = ALL_TRAINS_TRACE_RESTRICT_SLOT_ID;
2748 DoCommandP(0, w->slot_confirm, 0, CMD_DELETE_TRACERESTRICT_SLOT | CMD_MSG(STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_DELETE));
2752 virtual void OnClick(Point pt, int widget, int click_count)
2754 switch (widget) {
2755 case WID_TRSL_SORT_BY_ORDER: // Flip sorting method ascending/descending
2756 this->vehicles.ToggleSortOrder();
2757 this->SetDirty();
2758 break;
2760 case WID_TRSL_SORT_BY_DROPDOWN: // Select sorting criteria dropdown menu
2761 ShowDropDownMenu(this, this->vehicle_sorter_names, this->vehicles.SortType(), WID_TRSL_SORT_BY_DROPDOWN, 0, 0);
2762 return;
2764 case WID_TRSL_FILTER_BY_CARGO: // Cargo filter dropdown
2765 ShowDropDownMenu(this, this->cargo_filter_texts, this->cargo_filter_criteria, WID_TRSL_FILTER_BY_CARGO, 0, 0);
2766 break;
2768 case WID_TRSL_ALL_VEHICLES: // All vehicles button
2769 if (this->vli.index != ALL_TRAINS_TRACE_RESTRICT_SLOT_ID) {
2770 this->vli.index = ALL_TRAINS_TRACE_RESTRICT_SLOT_ID;
2771 this->slot_sel = INVALID_TRACE_RESTRICT_SLOT_ID;
2772 this->vehicles.ForceRebuild();
2773 this->SetDirty();
2775 break;
2777 case WID_TRSL_LIST_SLOTS: { // Matrix Slot
2778 uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0, this->tiny_step_height);
2779 if (id_s >= this->slots.Length()) return;
2781 this->slot_sel = this->vli.index = this->slots[id_s]->index;
2783 this->vehicles.ForceRebuild();
2784 this->SetDirty();
2785 break;
2788 case WID_TRSL_LIST_VEHICLE: { // Matrix Vehicle
2789 uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_VEHICLE);
2790 if (id_v >= this->vehicles.Length()) return; // click out of list bound
2792 const Vehicle *v = this->vehicles[id_v];
2793 if (VehicleClicked(v)) break;
2795 this->vehicle_sel = v->index;
2797 SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this);
2798 SetMouseCursorVehicle(v, EIT_IN_LIST);
2799 _cursor.vehchain = true;
2801 this->SetDirty();
2802 break;
2805 case WID_TRSL_CREATE_SLOT: { // Create a new slot
2806 this->ShowCreateSlotWindow();
2807 break;
2810 case WID_TRSL_DELETE_SLOT: { // Delete the selected slot
2811 this->slot_confirm = this->vli.index;
2812 ShowQuery(STR_TRACE_RESTRICT_SLOT_QUERY_DELETE_CAPTION, STR_TRACE_RESTRICT_SLOT_DELETE_QUERY_TEXT, this, DeleteSlotCallback);
2813 break;
2816 case WID_TRSL_RENAME_SLOT: // Rename the selected slot
2817 this->ShowRenameSlotWindow(this->vli.index);
2818 break;
2820 case WID_TRSL_SET_SLOT_MAX_OCCUPANCY: // Set max occupancy of the selected slot
2821 this->ShowSetSlotMaxOccupancyWindow(this->vli.index);
2822 break;
2826 void OnDragDrop_Vehicle(Point pt, int widget)
2828 switch (widget) {
2829 case WID_TRSL_ALL_VEHICLES: // All vehicles
2830 if (this->slot_sel != INVALID_TRACE_RESTRICT_SLOT_ID) {
2831 DoCommandP(0, this->slot_sel, this->vehicle_sel, CMD_REMOVE_VEHICLE_TRACERESTRICT_SLOT | CMD_MSG(STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_REMOVE_VEHICLE));
2833 this->vehicle_sel = INVALID_VEHICLE;
2834 this->slot_over = INVALID_GROUP;
2836 this->SetDirty();
2838 break;
2840 case WID_TRSL_LIST_SLOTS: { // Matrix slot
2841 const VehicleID vindex = this->vehicle_sel;
2842 this->vehicle_sel = INVALID_VEHICLE;
2843 this->slot_over = INVALID_GROUP;
2844 this->SetDirty();
2846 uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0, this->tiny_step_height);
2847 if (id_s >= this->slots.Length()) return; // click out of list bound
2849 if (_ctrl_pressed) {
2850 // remove from old group
2851 DoCommandP(0, this->slot_sel, vindex, CMD_REMOVE_VEHICLE_TRACERESTRICT_SLOT | CMD_MSG(STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_REMOVE_VEHICLE));
2853 DoCommandP(0, this->slots[id_s]->index, vindex, CMD_ADD_VEHICLE_TRACERESTRICT_SLOT | CMD_MSG(STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_ADD_VEHICLE));
2854 break;
2857 case WID_TRSL_LIST_VEHICLE: { // Matrix vehicle
2858 const VehicleID vindex = this->vehicle_sel;
2859 this->vehicle_sel = INVALID_VEHICLE;
2860 this->slot_over = INVALID_GROUP;
2861 this->SetDirty();
2863 uint id_v = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_VEHICLE);
2864 if (id_v >= this->vehicles.Length()) return; // click out of list bound
2866 const Vehicle *v = this->vehicles[id_v];
2867 if (!VehicleClicked(v) && vindex == v->index) {
2868 ShowVehicleViewWindow(v);
2870 break;
2875 virtual void OnDragDrop(Point pt, int widget)
2877 if (this->vehicle_sel != INVALID_VEHICLE) OnDragDrop_Vehicle(pt, widget);
2879 _cursor.vehchain = false;
2882 virtual void OnQueryTextFinished(char *str)
2884 if (str != nullptr) {
2885 if (this->slot_set_max_occupancy) {
2886 if (!StrEmpty(str)) DoCommandP(0, this->slot_rename | (1 << 16), atoi(str), CMD_ALTER_TRACERESTRICT_SLOT | CMD_MSG(STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_SET_MAX_OCCUPANCY));
2887 } else if (this->slot_rename == NEW_TRACE_RESTRICT_SLOT_ID) {
2888 DoCommandP(0, 0, 0, CMD_CREATE_TRACERESTRICT_SLOT | CMD_MSG(STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_CREATE), nullptr, str);
2889 } else {
2890 DoCommandP(0, this->slot_rename, 0, CMD_ALTER_TRACERESTRICT_SLOT | CMD_MSG(STR_TRACE_RESTRICT_ERROR_SLOT_CAN_T_RENAME), nullptr, str);
2893 this->slot_rename = INVALID_TRACE_RESTRICT_SLOT_ID;
2896 virtual void OnResize()
2898 this->slot_sb->SetCapacityFromWidget(this, WID_TRSL_LIST_SLOTS);
2899 this->vscroll->SetCapacityFromWidget(this, WID_TRSL_LIST_VEHICLE);
2902 virtual void OnDropdownSelect(int widget, int index)
2904 switch (widget) {
2905 case WID_TRSL_SORT_BY_DROPDOWN:
2906 this->vehicles.SetSortType(index);
2907 break;
2909 case WID_TRSL_FILTER_BY_CARGO: // Select a cargo filter criteria
2910 this->SetCargoFilterIndex(index);
2911 break;
2913 default: NOT_REACHED();
2916 this->SetDirty();
2919 virtual void OnTick()
2921 if (_pause_mode != PM_UNPAUSED) return;
2922 if (this->slots.NeedResort() || this->vehicles.NeedResort()) {
2923 this->SetDirty();
2927 virtual void OnPlaceObjectAbort()
2929 /* abort drag & drop */
2930 this->vehicle_sel = INVALID_VEHICLE;
2931 this->DirtyHighlightedSlotWidget();
2932 this->slot_over = INVALID_GROUP;
2933 this->SetWidgetDirty(WID_TRSL_LIST_VEHICLE);
2936 virtual void OnMouseDrag(Point pt, int widget)
2938 if (this->vehicle_sel == INVALID_VEHICLE) return;
2940 /* A vehicle is dragged over... */
2941 TraceRestrictSlotID new_slot_over = INVALID_TRACE_RESTRICT_SLOT_ID;
2942 switch (widget) {
2943 case WID_TRSL_ALL_VEHICLES: // ... all trains.
2944 new_slot_over = ALL_TRAINS_TRACE_RESTRICT_SLOT_ID;
2945 break;
2947 case WID_TRSL_LIST_SLOTS: { // ... the list of slots.
2948 uint id_s = this->slot_sb->GetScrolledRowFromWidget(pt.y, this, WID_TRSL_LIST_SLOTS, 0, this->tiny_step_height);
2949 if (id_s < this->slots.Length()) new_slot_over = this->slots[id_s]->index;
2950 break;
2954 /* Do not highlight when dragging over the current group */
2955 if (this->slot_sel == new_slot_over) new_slot_over = INVALID_TRACE_RESTRICT_SLOT_ID;
2957 /* Mark widgets as dirty if the group changed. */
2958 if (new_slot_over != this->slot_over) {
2959 this->DirtyHighlightedSlotWidget();
2960 this->slot_over = new_slot_over;
2961 this->DirtyHighlightedSlotWidget();
2965 void ShowRenameSlotWindow(TraceRestrictSlotID slot_id)
2967 assert(TraceRestrictSlot::IsValidID(slot_id));
2968 this->slot_set_max_occupancy = false;
2969 this->slot_rename = slot_id;
2970 SetDParam(0, slot_id);
2971 ShowQueryString(STR_TRACE_RESTRICT_SLOT_NAME, STR_TRACE_RESTRICT_SLOT_RENAME_CAPTION, MAX_LENGTH_TRACE_RESTRICT_SLOT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
2974 void ShowSetSlotMaxOccupancyWindow(TraceRestrictSlotID slot_id)
2976 this->slot_set_max_occupancy = true;
2977 this->slot_rename = slot_id;
2978 SetDParam(0, TraceRestrictSlot::Get(slot_id)->max_occupancy);
2979 ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_SLOT_SET_MAX_OCCUPANCY_CAPTION, 5, this, CS_NUMERAL, QSF_ENABLE_DEFAULT);
2982 void ShowCreateSlotWindow()
2984 this->slot_set_max_occupancy = false;
2985 this->slot_rename = NEW_TRACE_RESTRICT_SLOT_ID;
2986 ShowQueryString(STR_EMPTY, STR_TRACE_RESTRICT_SLOT_CREATE_CAPTION, MAX_LENGTH_TRACE_RESTRICT_SLOT_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS);
2990 * Tests whether a given vehicle is selected in the window, and unselects it if necessary.
2991 * Called when the vehicle is deleted.
2992 * @param vehicle Vehicle that is going to be deleted
2994 void UnselectVehicle(VehicleID vehicle)
2996 if (this->vehicle_sel == vehicle) ResetObjectToPlace();
3000 static WindowDesc _slot_window_desc(
3001 WDP_AUTO, "list_groups_train", 525, 246,
3002 WC_TRACE_RESTRICT_SLOTS, WC_NONE,
3004 _nested_slot_widgets, lengthof(_nested_slot_widgets)
3008 * Show the trace restrict slot window for the given company.
3009 * @param company The company to show the window for.
3011 void ShowTraceRestrictSlotWindow(CompanyID company)
3013 if (!Company::IsValidID(company)) return;
3015 WindowNumber num = VehicleListIdentifier(VL_SLOT_LIST, VEH_TRAIN, company).Pack();
3016 AllocateWindowDescFront<TraceRestrictSlotWindow>(&_slot_window_desc, num);
3020 * Finds a group list window determined by vehicle type and owner
3021 * @param vt vehicle type
3022 * @param owner owner of groups
3023 * @return pointer to VehicleGroupWindow, nullptr if not found
3025 static inline TraceRestrictSlotWindow *FindTraceRestrictSlotWindow(Owner owner)
3027 return (TraceRestrictSlotWindow *)FindWindowById(GetWindowClassForVehicleType(VEH_TRAIN), VehicleListIdentifier(VL_SLOT_LIST, VEH_TRAIN, owner).Pack());
3031 * Removes the highlight of a vehicle in a group window
3032 * @param *v Vehicle to remove all highlights from
3034 void DeleteTraceRestrictSlotHighlightOfVehicle(const Vehicle *v)
3036 /* If we haven't got any vehicles on the mouse pointer, we haven't got any highlighted in any group windows either
3037 * If that is the case, we can skip looping though the windows and save time
3039 if (_special_mouse_mode != WSM_DRAGDROP) return;
3041 TraceRestrictSlotWindow *w = FindTraceRestrictSlotWindow(v->owner);
3042 if (w != nullptr) w->UnselectVehicle(v->index);