/* $Id$ */ /* * This file is part of OpenTTD. * 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. * 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. * 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 . */ /** @file tracerestrict_gui.cpp GUI code for Trace Restrict * * This is largely based on the programmable signals patch's GUI * */ #include "stdafx.h" #include "tracerestrict.h" #include "command_func.h" #include "window_func.h" #include "strings_func.h" #include "string_func.h" #include "viewport_func.h" #include "textbuf_gui.h" #include "company_func.h" #include "tilehighlight_func.h" #include "widgets/dropdown_func.h" #include "gui.h" #include "gfx_func.h" #include "rail_map.h" #include "depot_map.h" #include "tile_cmd.h" #include "station_base.h" #include "waypoint_base.h" #include "depot_base.h" #include "error.h" #include "cargotype.h" #include "table/sprites.h" extern uint ConvertSpeedToDisplaySpeed(uint speed); extern uint ConvertDisplaySpeedToSpeed(uint speed); /** Widget IDs */ enum TraceRestrictWindowWidgets { TR_WIDGET_CAPTION, TR_WIDGET_INSTRUCTION_LIST, TR_WIDGET_SCROLLBAR, TR_WIDGET_SEL_TOP_LEFT_2, TR_WIDGET_SEL_TOP_LEFT, TR_WIDGET_SEL_TOP_MIDDLE, TR_WIDGET_SEL_TOP_RIGHT, TR_WIDGET_SEL_SHARE, TR_WIDGET_TYPE_COND, TR_WIDGET_TYPE_NONCOND, TR_WIDGET_CONDFLAGS, TR_WIDGET_COMPARATOR, TR_WIDGET_VALUE_INT, TR_WIDGET_VALUE_DROPDOWN, TR_WIDGET_VALUE_DEST, TR_WIDGET_VALUE_SIGNAL, TR_WIDGET_BLANK_L2, TR_WIDGET_BLANK_L, TR_WIDGET_BLANK_M, TR_WIDGET_BLANK_R, TR_WIDGET_GOTO_SIGNAL, TR_WIDGET_INSERT, TR_WIDGET_REMOVE, TR_WIDGET_RESET, TR_WIDGET_COPY, TR_WIDGET_SHARE, TR_WIDGET_UNSHARE, }; /** Selection mappings for NWID_SELECTION selectors */ enum PanelWidgets { // Left 2 DPL2_TYPE = 0, DPL2_CONDFLAGS, DPL2_BLANK, // Left DPL_TYPE = 0, DPL_BLANK, // Middle DPM_COMPARATOR = 0, DPM_BLANK, // Right DPR_VALUE_INT = 0, DPR_VALUE_DROPDOWN, DPR_VALUE_DEST, DPR_VALUE_SIGNAL, DPR_BLANK, // Share DPS_SHARE = 0, DPS_UNSHARE, }; /** * drop down list string array, and corresponding integer values * * value_array *must* be at least as long as string_array, * where the length of string_array is defined as the offset * of the first INVALID_STRING_ID */ struct TraceRestrictDropDownListSet { const StringID *string_array; const uint *value_array; }; static const StringID _program_insert_str[] = { STR_TRACE_RESTRICT_CONDITIONAL_IF, STR_TRACE_RESTRICT_CONDITIONAL_ELIF, STR_TRACE_RESTRICT_CONDITIONAL_ORIF, STR_TRACE_RESTRICT_CONDITIONAL_ELSE, STR_TRACE_RESTRICT_PF_DENY, STR_TRACE_RESTRICT_PF_PENALTY, STR_TRACE_RESTRICT_RESERVE_THROUGH, INVALID_STRING_ID }; static const uint32 _program_insert_else_hide_mask = 8; ///< disable bitmask for else static const uint32 _program_insert_or_if_hide_mask = 4; ///< disable bitmask for orif static const uint32 _program_insert_else_if_hide_mask = 2; ///< disable bitmask for elif static const uint _program_insert_val[] = { TRIT_COND_UNDEFINED, // if block TRIT_COND_UNDEFINED | (TRCF_ELSE << 16), // elif block TRIT_COND_UNDEFINED | (TRCF_OR << 16), // orif block TRIT_COND_ENDIF | (TRCF_ELSE << 16), // else block TRIT_PF_DENY, // deny TRIT_PF_PENALTY, // penalty TRIT_RESERVE_THROUGH, // reserve through }; /** insert drop down list strings and values */ static const TraceRestrictDropDownListSet _program_insert = { _program_insert_str, _program_insert_val, }; static const StringID _deny_value_str[] = { STR_TRACE_RESTRICT_PF_DENY, STR_TRACE_RESTRICT_PF_ALLOW, INVALID_STRING_ID }; static const uint _deny_value_val[] = { 0, 1, }; /** value drop down list for deny types strings and values */ static const TraceRestrictDropDownListSet _deny_value = { _deny_value_str, _deny_value_val, }; static const StringID _reserve_through_value_str[] = { STR_TRACE_RESTRICT_RESERVE_THROUGH, STR_TRACE_RESTRICT_RESERVE_THROUGH_CANCEL, INVALID_STRING_ID }; static const uint _reserve_through_value_val[] = { 0, 1, }; /** value drop down list for deny types strings and values */ static const TraceRestrictDropDownListSet _reserve_through_value = { _reserve_through_value_str, _reserve_through_value_val, }; static const StringID _direction_value_str[] = { STR_TRACE_RESTRICT_DIRECTION_FRONT, STR_TRACE_RESTRICT_DIRECTION_BACK, STR_TRACE_RESTRICT_DIRECTION_NE, STR_TRACE_RESTRICT_DIRECTION_SE, STR_TRACE_RESTRICT_DIRECTION_SW, STR_TRACE_RESTRICT_DIRECTION_NW, INVALID_STRING_ID }; static const uint _direction_value_val[] = { TRDTSV_FRONT, TRDTSV_BACK, TRNTSV_NE, TRNTSV_SE, TRNTSV_SW, TRNTSV_NW, }; /** value drop down list for direction type strings and values */ static const TraceRestrictDropDownListSet _direction_value = { _direction_value_str, _direction_value_val, }; /** * Get index of @p value in @p list_set * if @p value is not present, assert if @p missing_ok is false, otherwise return -1 */ static int GetDropDownListIndexByValue(const TraceRestrictDropDownListSet *list_set, uint value, bool missing_ok) { const StringID *string_array = list_set->string_array; const uint *value_array = list_set->value_array; for (; *string_array != INVALID_STRING_ID; string_array++, value_array++) { if (*value_array == value) { return value_array - list_set->value_array; } } assert(missing_ok == true); return -1; } /** * Get StringID correspoding to @p value, in @list_set * @p value must be present */ static StringID GetDropDownStringByValue(const TraceRestrictDropDownListSet *list_set, uint value) { return list_set->string_array[GetDropDownListIndexByValue(list_set, value, false)]; } /** * Return the appropriate type dropdown TraceRestrictDropDownListSet for the given item type @p type */ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictItemType type) { static const StringID str_action[] = { STR_TRACE_RESTRICT_PF_DENY, STR_TRACE_RESTRICT_PF_PENALTY, STR_TRACE_RESTRICT_RESERVE_THROUGH, INVALID_STRING_ID, }; static const uint val_action[] = { TRIT_PF_DENY, TRIT_PF_PENALTY, TRIT_RESERVE_THROUGH, }; static const TraceRestrictDropDownListSet set_action = { str_action, val_action, }; static const StringID str_cond[] = { STR_TRACE_RESTRICT_VARIABLE_TRAIN_LENGTH, STR_TRACE_RESTRICT_VARIABLE_MAX_SPEED, STR_TRACE_RESTRICT_VARIABLE_CURRENT_ORDER, STR_TRACE_RESTRICT_VARIABLE_NEXT_ORDER, STR_TRACE_RESTRICT_VARIABLE_LAST_VISITED_STATION, STR_TRACE_RESTRICT_VARIABLE_CARGO, STR_TRACE_RESTRICT_VARIABLE_ENTRY_DIRECTION, STR_TRACE_RESTRICT_VARIABLE_PBS_ENTRY_SIGNAL, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, }; static const uint val_cond[] = { TRIT_COND_TRAIN_LENGTH, TRIT_COND_MAX_SPEED, TRIT_COND_CURRENT_ORDER, TRIT_COND_NEXT_ORDER, TRIT_COND_LAST_STATION, TRIT_COND_CARGO, TRIT_COND_ENTRY_DIRECTION, TRIT_COND_PBS_ENTRY_SIGNAL, TRIT_COND_UNDEFINED, }; static const TraceRestrictDropDownListSet set_cond = { str_cond, val_cond, }; return IsTraceRestrictTypeConditional(type) ? &set_cond : &set_action; } /** * Get a TraceRestrictDropDownListSet of the sorted cargo list */ static const TraceRestrictDropDownListSet *GetSortedCargoTypeDropDownListSet() { static StringID cargo_list_str[NUM_CARGO + 1]; static uint cargo_list_id[NUM_CARGO]; static const TraceRestrictDropDownListSet cargo_list = { cargo_list_str, cargo_list_id, }; for (size_t i = 0; i < _sorted_standard_cargo_specs_size; ++i) { const CargoSpec *cs = _sorted_cargo_specs[i]; cargo_list_str[i] = cs->name; cargo_list_id[i] = cs->Index(); } cargo_list_str[_sorted_standard_cargo_specs_size] = INVALID_STRING_ID; return &cargo_list; } static const StringID _cargo_cond_ops_str[] = { STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_EQUALS, STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_NOT_EQUALS, INVALID_STRING_ID, }; static const uint _cargo_cond_ops_val[] = { TRCO_IS, TRCO_ISNOT, }; /** cargo conditional operators dropdown list set */ static const TraceRestrictDropDownListSet _cargo_cond_ops = { _cargo_cond_ops_str, _cargo_cond_ops_val, }; /** * Get the StringID for a given CargoID @p cargo, or STR_NEWGRF_INVALID_CARGO */ static StringID GetCargoStringByID(CargoID cargo) { const CargoSpec *cs = CargoSpec::Get(cargo); return cs->IsValid() ? cs->name : STR_NEWGRF_INVALID_CARGO; } /** * Get the StringID for a given item type @p type */ static StringID GetTypeString(TraceRestrictItemType type) { return GetDropDownStringByValue(GetTypeDropDownListSet(type), type); } /** * Get the conditional operator field drop down list set for a given type property set @p properties */ static const TraceRestrictDropDownListSet *GetCondOpDropDownListSet(TraceRestrictTypePropertySet properties) { static const StringID str_long[] = { STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_EQUALS, STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_NOT_EQUALS, STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_LESS_THAN, STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_LESS_EQUALS, STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_MORE_THAN, STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_MORE_EQUALS, INVALID_STRING_ID, }; static const uint val_long[] = { TRCO_IS, TRCO_ISNOT, TRCO_LT, TRCO_LTE, TRCO_GT, TRCO_GTE, }; static const TraceRestrictDropDownListSet set_long = { str_long, val_long, }; static const StringID str_short[] = { STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_EQUALS, STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_NOT_EQUALS, INVALID_STRING_ID, }; static const uint val_short[] = { TRCO_IS, TRCO_ISNOT, }; static const TraceRestrictDropDownListSet set_short = { str_short, val_short, }; if (properties.value_type == TRVT_CARGO_ID) return &_cargo_cond_ops; switch (properties.cond_type) { case TRCOT_NONE: return NULL; case TRCOT_BINARY: return &set_short; case TRCOT_ALL: return &set_long; } NOT_REACHED(); return NULL; } /** * Return true if item type field @p type is an integer value type */ static bool IsIntegerValueType(TraceRestrictValueType type) { switch (type) { case TRVT_INT: case TRVT_SPEED: return true; default: return false; } } /** * Convert integer values or custom penalty values between internal units and display units */ static uint ConvertIntegerValue(TraceRestrictValueType type, uint in, bool to_display) { switch (type) { case TRVT_INT: return in; case TRVT_SPEED: return to_display ? ConvertSpeedToDisplaySpeed(in) * 10 / 16 : ConvertDisplaySpeedToSpeed(in) * 16 / 10; case TRVT_PF_PENALTY: return in; default: NOT_REACHED(); return 0; } } /** String values for TraceRestrictCondFlags, value gives offset into array */ static const StringID _program_cond_type[] = { STR_TRACE_RESTRICT_CONDITIONAL_IF, // TRCF_DEFAULT STR_TRACE_RESTRICT_CONDITIONAL_ELIF, // TRCF_ELSE STR_TRACE_RESTRICT_CONDITIONAL_ORIF, // TRCF_OR }; /** condition flags field drop down value types */ enum CondFlagsDropDownType { CFDDT_ELSE = 0, ///< This is an else block CFDDT_ELIF = TRCF_ELSE, ///< This is an else-if block CFDDT_ORIF = TRCF_OR, ///< This is an or-if block }; static const uint32 _condflags_dropdown_else_hide_mask = 1; ///< disable bitmask for CFDDT_ELSE static const uint32 _condflags_dropdown_else_if_hide_mask = 6; ///< disable bitmask for CFDDT_ELIF and CFDDT_ORIF static const StringID _condflags_dropdown_str[] = { STR_TRACE_RESTRICT_CONDITIONAL_ELSE, STR_TRACE_RESTRICT_CONDITIONAL_ELIF, STR_TRACE_RESTRICT_CONDITIONAL_ORIF, INVALID_STRING_ID, }; static const uint _condflags_dropdown_val[] = { CFDDT_ELSE, CFDDT_ELIF, CFDDT_ORIF, }; /** condition flags dropdown list set */ static const TraceRestrictDropDownListSet _condflags_dropdown = { _condflags_dropdown_str, _condflags_dropdown_val, }; static const StringID _pf_penalty_dropdown_str[] = { STR_TRACE_RESTRICT_PF_VALUE_SMALL, STR_TRACE_RESTRICT_PF_VALUE_MEDIUM, STR_TRACE_RESTRICT_PF_VALUE_LARGE, STR_TRACE_RESTRICT_PF_VALUE_CUSTOM, INVALID_STRING_ID, }; static const uint _pf_penalty_dropdown_val[] = { TRPPPI_SMALL, TRPPPI_MEDIUM, TRPPPI_LARGE, TRPPPI_END, // this is a placeholder for "custom" }; /** Pathfinder penalty dropdown set */ static const TraceRestrictDropDownListSet _pf_penalty_dropdown = { _pf_penalty_dropdown_str, _pf_penalty_dropdown_val, }; static uint GetPathfinderPenaltyDropdownIndex(TraceRestrictItem item) { switch (static_cast(GetTraceRestrictAuxField(item))) { case TRPPAF_VALUE: return TRPPPI_END; case TRPPAF_PRESET: { uint16 index = GetTraceRestrictValue(item); assert(index < TRPPPI_END); return index; } default: NOT_REACHED(); } } /** Common function for drawing an ordinary conditional instruction */ static void DrawInstructionStringConditionalCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) { assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); SetDParam(1, GetTypeString(GetTraceRestrictType(item))); SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item))); } /** Common function for drawing an integer conditional instruction */ static void DrawInstructionStringConditionalIntegerCommon(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties) { DrawInstructionStringConditionalCommon(item, properties); SetDParam(3, GetTraceRestrictValue(item)); } /** Common function for drawing an integer conditional instruction with an invalid value */ static void DrawInstructionStringConditionalInvalidValue(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties, StringID &instruction_string, bool selected) { instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_UNDEFINED; DrawInstructionStringConditionalCommon(item, properties); SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); } /** * Draws an instruction in the programming GUI * @param prog The program (may be NULL) * @param item The instruction to draw * @param index The instruction index * @param y Y position for drawing * @param selected True, if the order is selected * @param indent How many levels the instruction is indented * @param left Left border for text drawing * @param right Right border for text drawing */ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestrictItem item, int index, int y, bool selected, int indent, int left, int right) { StringID instruction_string = INVALID_STRING_ID; TraceRestrictTypePropertySet properties = GetTraceRestrictTypeProperties(item); if (IsTraceRestrictConditional(item)) { if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { if (GetTraceRestrictCondFlags(item) & TRCF_ELSE) { instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ELSE; } else { instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ENDIF; } } else if (GetTraceRestrictType(item) == TRIT_COND_UNDEFINED) { instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_UNDEFINED; SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); SetDParam(1, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); } else { switch (properties.value_type) { case TRVT_INT: instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER; DrawInstructionStringConditionalIntegerCommon(item, properties); break; case TRVT_SPEED: instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED; DrawInstructionStringConditionalIntegerCommon(item, properties); break; case TRVT_ORDER: { switch (static_cast(GetTraceRestrictAuxField(item))) { case TROCAF_STATION: if (GetTraceRestrictValue(item) != INVALID_STATION) { instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ORDER_STATION; DrawInstructionStringConditionalIntegerCommon(item, properties); } else { // this is an invalid station, use a seperate string DrawInstructionStringConditionalInvalidValue(item, properties, instruction_string, selected); } break; case TROCAF_WAYPOINT: instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ORDER_WAYPOINT; DrawInstructionStringConditionalIntegerCommon(item, properties); break; case TROCAF_DEPOT: instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ORDER_DEPOT; DrawInstructionStringConditionalCommon(item, properties); SetDParam(3, VEH_TRAIN); SetDParam(4, GetTraceRestrictValue(item)); break; default: NOT_REACHED(); break; } break; } case TRVT_CARGO_ID: instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_CARGO; assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); SetDParam(1, GetDropDownStringByValue(&_cargo_cond_ops, GetTraceRestrictCondOp(item))); SetDParam(2, GetCargoStringByID(GetTraceRestrictValue(item))); break; case TRVT_DIRECTION: if (GetTraceRestrictValue(item) >= TRDTSV_FRONT) { instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_SIGNAL_FACE; } else { instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_DIRECTION; } SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); SetDParam(1, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item))); SetDParam(2, GetDropDownStringByValue(&_direction_value, GetTraceRestrictValue(item))); break; case TRVT_TILE_INDEX: { assert(prog != NULL); assert(GetTraceRestrictType(item) == TRIT_COND_PBS_ENTRY_SIGNAL); TileIndex tile = *(TraceRestrictProgram::InstructionAt(prog->items, index - 1) + 1); if (tile == INVALID_TILE) { DrawInstructionStringConditionalInvalidValue(item, properties, instruction_string, selected); } else { instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_TILE_INDEX; SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); SetDParam(1, STR_TRACE_RESTRICT_VARIABLE_PBS_ENTRY_SIGNAL_LONG); SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item))); SetDParam(3, TileX(tile)); SetDParam(4, TileY(tile)); } break; } default: NOT_REACHED(); break; } } } else { switch (GetTraceRestrictType(item)) { case TRIT_NULL: switch (GetTraceRestrictValue(item)) { case TRNTSV_START: instruction_string = STR_TRACE_RESTRICT_START; break; case TRNTSV_END: instruction_string = STR_TRACE_RESTRICT_END; break; default: NOT_REACHED(); break; } break; case TRIT_PF_DENY: instruction_string = GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_PF_ALLOW_LONG : STR_TRACE_RESTRICT_PF_DENY; break; case TRIT_PF_PENALTY: switch (static_cast(GetTraceRestrictAuxField(item))) { case TRPPAF_VALUE: instruction_string = STR_TRACE_RESTRICT_PF_PENALTY_ITEM; SetDParam(0, GetTraceRestrictValue(item)); break; case TRPPAF_PRESET: { instruction_string = STR_TRACE_RESTRICT_PF_PENALTY_ITEM_PRESET; uint16 index = GetTraceRestrictValue(item); assert(index < TRPPPI_END); SetDParam(0, _pf_penalty_dropdown_str[index]); break; } default: NOT_REACHED(); } break; case TRIT_RESERVE_THROUGH: instruction_string = GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_RESERVE_THROUGH_CANCEL : STR_TRACE_RESTRICT_RESERVE_THROUGH; break; default: NOT_REACHED(); break; } } DrawString(left + indent * 16, right, y, instruction_string, selected ? TC_WHITE : TC_BLACK); } /** Main GUI window class */ class TraceRestrictWindow: public Window { TileIndex tile; ///< tile this window is for Track track; ///< track this window is for int selected_instruction; ///< selected instruction index, this is offset by one due to the display of the "start" item Scrollbar *vscroll; ///< scrollbar widget std::map drop_down_list_mapping; ///< mapping of widget IDs to drop down list sets TraceRestrictItem expecting_inserted_item; ///< set to instruction when performing an instruction insertion, used to handle selection update on insertion int current_placement_widget; ///< which widget has a SetObjectToPlaceWnd, if any public: TraceRestrictWindow(WindowDesc *desc, TileIndex tile, Track track) : Window(desc) { this->tile = tile; this->track = track; this->selected_instruction = -1; this->expecting_inserted_item = static_cast(0); this->current_placement_widget = -1; this->CreateNestedTree(); this->vscroll = this->GetScrollbar(TR_WIDGET_SCROLLBAR); this->FinishInitNested(MakeTraceRestrictRefId(tile, track)); this->ReloadProgramme(); } virtual void OnClick(Point pt, int widget, int click_count) { switch (widget) { case TR_WIDGET_INSTRUCTION_LIST: { int sel = this->GetItemIndexFromPt(pt.y); if (_ctrl_pressed) { // scroll to target (for stations, waypoints, depots) if (sel == -1) return; TraceRestrictItem item = this->GetItem(this->GetProgram(), sel); if (GetTraceRestrictTypeProperties(item).value_type == TRVT_ORDER) { switch (static_cast(GetTraceRestrictAuxField(item))) { case TROCAF_STATION: case TROCAF_WAYPOINT: { BaseStation *st = BaseStation::GetIfValid(GetTraceRestrictValue(item)); if (st) { ScrollMainWindowToTile(st->xy); } break; } case TROCAF_DEPOT: { Depot *depot = Depot::GetIfValid(GetTraceRestrictValue(item)); if (depot) { ScrollMainWindowToTile(depot->xy); } break; } } } return; } this->DeleteChildWindows(); HideDropDownMenu(this); if (sel == -1 || this->GetOwner() != _local_company) { // Deselect this->selected_instruction = -1; } else { this->selected_instruction = sel; } this->expecting_inserted_item = static_cast(0); this->UpdateButtonState(); break; } case TR_WIDGET_INSERT: { if (this->GetOwner() != _local_company || this->selected_instruction < 1) { return; } uint32 disabled = _program_insert_or_if_hide_mask; TraceRestrictItem item = this->GetSelected(); if (GetTraceRestrictType(item) == TRIT_COND_ENDIF || (IsTraceRestrictConditional(item) && GetTraceRestrictCondFlags(item) != 0)) { // this is either: an else/or if, an else, or an end if // try to include else if, else in insertion list if (!ElseInsertionDryRun(false)) disabled |= _program_insert_else_hide_mask; if (!ElseIfInsertionDryRun(false)) disabled |= _program_insert_else_if_hide_mask; } else { // can't insert else/end if here disabled |= _program_insert_else_hide_mask | _program_insert_else_if_hide_mask; } if (this->selected_instruction > 1) { TraceRestrictItem prev_item = this->GetItem(this->GetProgram(), this->selected_instruction - 1); if (IsTraceRestrictConditional(prev_item) && GetTraceRestrictType(prev_item) != TRIT_COND_ENDIF) { // previous item is either: an if, or an else/or if // else if has same validation rules as or if, use it instead of creating another test function if (ElseIfInsertionDryRun(false)) disabled &= ~_program_insert_or_if_hide_mask; } } this->ShowDropDownListWithValue(&_program_insert, 0, true, TR_WIDGET_INSERT, disabled, 0, 0); break; } case TR_WIDGET_REMOVE: { TraceRestrictItem item = this->GetSelected(); if (this->GetOwner() != _local_company || item == 0) { return; } TraceRestrictDoCommandP(tile, track, TRDCT_REMOVE_ITEM, this->selected_instruction - 1, 0, STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ITEM); break; } case TR_WIDGET_CONDFLAGS: { TraceRestrictItem item = this->GetSelected(); if (this->GetOwner() != _local_company || item == 0) { return; } CondFlagsDropDownType type; if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { if (GetTraceRestrictCondFlags(item) == 0) return; // end if type = CFDDT_ELSE; } else if (IsTraceRestrictConditional(item) && GetTraceRestrictCondFlags(item) != 0) { type = static_cast(GetTraceRestrictCondFlags(item)); } else { return; } uint32 disabled = 0; if (!ElseInsertionDryRun(true)) disabled |= _condflags_dropdown_else_hide_mask; if (!ElseIfInsertionDryRun(true)) disabled |= _condflags_dropdown_else_if_hide_mask; this->ShowDropDownListWithValue(&_condflags_dropdown, type, false, TR_WIDGET_CONDFLAGS, disabled, 0, 0); break; } case TR_WIDGET_TYPE_COND: case TR_WIDGET_TYPE_NONCOND: { TraceRestrictItem item = this->GetSelected(); TraceRestrictItemType type = GetTraceRestrictType(item); if (type != TRIT_NULL) { this->ShowDropDownListWithValue(GetTypeDropDownListSet(type), type, false, widget, 0, 0, 0); } break; } case TR_WIDGET_COMPARATOR: { TraceRestrictItem item = this->GetSelected(); const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(GetTraceRestrictTypeProperties(item)); if (list_set) { this->ShowDropDownListWithValue(list_set, GetTraceRestrictCondOp(item), false, TR_WIDGET_COMPARATOR, 0, 0, 0); } break; } case TR_WIDGET_VALUE_INT: { TraceRestrictItem item = this->GetSelected(); TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type; if (IsIntegerValueType(type)) { SetDParam(0, ConvertIntegerValue(type, GetTraceRestrictValue(item), true)); ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 10, this, CS_NUMERAL, QSF_NONE); } break; } case TR_WIDGET_VALUE_DROPDOWN: { TraceRestrictItem item = this->GetSelected(); switch (GetTraceRestrictTypeProperties(item).value_type) { case TRVT_DENY: this->ShowDropDownListWithValue(&_deny_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); break; case TRVT_CARGO_ID: this->ShowDropDownListWithValue(GetSortedCargoTypeDropDownListSet(), GetTraceRestrictValue(item), true, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); // current cargo is permitted to not be in list break; case TRVT_DIRECTION: this->ShowDropDownListWithValue(&_direction_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); break; case TRVT_PF_PENALTY: this->ShowDropDownListWithValue(&_pf_penalty_dropdown, GetPathfinderPenaltyDropdownIndex(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); break; case TRVT_RESERVE_THROUGH: this->ShowDropDownListWithValue(&_reserve_through_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); break; default: break; } break; } case TR_WIDGET_VALUE_DEST: { SetObjectToPlaceAction(widget, ANIMCURSOR_PICKSTATION); break; } case TR_WIDGET_VALUE_SIGNAL: { SetObjectToPlaceAction(widget, ANIMCURSOR_BUILDSIGNALS); break; } case TR_WIDGET_GOTO_SIGNAL: ScrollMainWindowToTile(this->tile); break; case TR_WIDGET_RESET: { TraceRestrictProgMgmtDoCommandP(tile, track, TRDCT_PROG_RESET, STR_TRACE_RESTRICT_ERROR_CAN_T_RESET_SIGNAL); break; } case TR_WIDGET_COPY: case TR_WIDGET_SHARE: SetObjectToPlaceAction(widget, ANIMCURSOR_BUILDSIGNALS); break; case TR_WIDGET_UNSHARE: { TraceRestrictProgMgmtDoCommandP(tile, track, TRDCT_PROG_UNSHARE, STR_TRACE_RESTRICT_ERROR_CAN_T_UNSHARE_PROGRAM); break; } } } virtual void OnQueryTextFinished(char *str) { if (StrEmpty(str)) { return; } TraceRestrictItem item = GetSelected(); TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type; if (!IsIntegerValueType(type) && type != TRVT_PF_PENALTY) { return; } uint value = ConvertIntegerValue(type, atoi(str), false); if (value >= (1 << TRIFA_VALUE_COUNT)) { SetDParam(0, ConvertIntegerValue(type, (1 << TRIFA_VALUE_COUNT) - 1, true)); ShowErrorMessage(STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE, STR_EMPTY, WL_INFO); return; } if (type == TRVT_PF_PENALTY) { SetTraceRestrictAuxField(item, TRPPAF_VALUE); } SetTraceRestrictValue(item, value); TraceRestrictDoCommandP(tile, track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); } virtual void OnDropdownSelect(int widget, int index) { TraceRestrictItem item = GetSelected(); if (item == 0 || index < 0 || this->selected_instruction < 1) { return; } const TraceRestrictDropDownListSet *list_set = this->drop_down_list_mapping[widget]; if (!list_set) { return; } uint value = list_set->value_array[index]; switch (widget) { case TR_WIDGET_INSERT: { TraceRestrictItem insert_item = 0; TraceRestrictCondFlags cond_flags = static_cast(value >> 16); value &= 0xFFFF; SetTraceRestrictTypeAndNormalise(insert_item, static_cast(value)); SetTraceRestrictCondFlags(insert_item, cond_flags); // this needs to happen after calling SetTraceRestrictTypeAndNormalise this->expecting_inserted_item = insert_item; TraceRestrictDoCommandP(this->tile, this->track, TRDCT_INSERT_ITEM, this->selected_instruction - 1, insert_item, STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM); break; } case TR_WIDGET_CONDFLAGS: { CondFlagsDropDownType cond_type = static_cast(value); if (cond_type == CFDDT_ELSE) { SetTraceRestrictTypeAndNormalise(item, TRIT_COND_ENDIF); SetTraceRestrictCondFlags(item, TRCF_ELSE); } else { if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { // item is currently an else, convert to else/or if SetTraceRestrictTypeAndNormalise(item, TRIT_COND_UNDEFINED); } SetTraceRestrictCondFlags(item, static_cast(cond_type)); } TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); break; } case TR_WIDGET_TYPE_COND: case TR_WIDGET_TYPE_NONCOND: { SetTraceRestrictTypeAndNormalise(item, static_cast(value)); if (GetTraceRestrictType(item) == TRIT_COND_LAST_STATION && GetTraceRestrictAuxField(item) != TROCAF_STATION) { // if changing type from another order type to last visited station, reset value if not currently a station SetTraceRestrictValueDefault(item, TRVT_ORDER); } TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); break; } case TR_WIDGET_COMPARATOR: { SetTraceRestrictCondOp(item, static_cast(value)); TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); break; } case TR_WIDGET_VALUE_DROPDOWN: { if (GetTraceRestrictTypeProperties(item).value_type == TRVT_PF_PENALTY) { if (value == TRPPPI_END) { uint16 penalty_value; if (GetTraceRestrictAuxField(item) == TRPPAF_PRESET) { penalty_value = _tracerestrict_pathfinder_penalty_preset_values[GetTraceRestrictValue(item)]; } else { penalty_value = GetTraceRestrictValue(item); } SetDParam(0, penalty_value); ShowQueryString(STR_JUST_INT, STR_TRACE_RESTRICT_VALUE_CAPTION, 10, this, CS_NUMERAL, QSF_NONE); return; } else { SetTraceRestrictValue(item, value); SetTraceRestrictAuxField(item, TRPPAF_PRESET); } } else { SetTraceRestrictValue(item, value); } TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); break; } } } virtual void OnPlaceObject(Point pt, TileIndex tile) { int widget = this->current_placement_widget; this->current_placement_widget = -1; this->RaiseButtons(); ResetObjectToPlace(); if (widget < 0) { return; } switch (widget) { case TR_WIDGET_COPY: OnPlaceObjectSignal(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM); break; case TR_WIDGET_SHARE: OnPlaceObjectSignal(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM); break; case TR_WIDGET_VALUE_DEST: OnPlaceObjectDestination(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); break; case TR_WIDGET_VALUE_SIGNAL: OnPlaceObjectSignalTileValue(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); break; default: NOT_REACHED(); break; } } /** * Common OnPlaceObject handler for program management actions which involve clicking on a signal */ void OnPlaceObjectSignal(Point pt, TileIndex source_tile, int widget, int error_message) { if (!IsPlainRailTile(source_tile)) { ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); return; } TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(source_tile, TRANSPORT_RAIL, 0)); if (trackbits & TRACK_BIT_VERT) { // N-S direction trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT; } if (trackbits & TRACK_BIT_HORZ) { // E-W direction trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER; } Track source_track = FindFirstTrack(trackbits); if(source_track == INVALID_TRACK) { ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); return; } if (!HasTrack(source_tile, source_track)) { ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); return; } if (!HasSignalOnTrack(source_tile, source_track)) { ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO); return; } switch (widget) { case TR_WIDGET_COPY: TraceRestrictProgMgmtWithSourceDoCommandP(this->tile, this->track, TRDCT_PROG_COPY, source_tile, source_track, STR_TRACE_RESTRICT_ERROR_CAN_T_COPY_PROGRAM); break; case TR_WIDGET_SHARE: TraceRestrictProgMgmtWithSourceDoCommandP(this->tile, this->track, TRDCT_PROG_SHARE, source_tile, source_track, STR_TRACE_RESTRICT_ERROR_CAN_T_SHARE_PROGRAM); break; default: NOT_REACHED(); break; } } /** * Common OnPlaceObject handler for instruction value modification actions which involve selecting an order target */ void OnPlaceObjectDestination(Point pt, TileIndex tile, int widget, int error_message) { TraceRestrictItem item = GetSelected(); if (GetTraceRestrictTypeProperties(item).value_type != TRVT_ORDER) return; bool stations_only = (GetTraceRestrictType(item) == TRIT_COND_LAST_STATION); if (IsDepotTypeTile(tile, TRANSPORT_RAIL)) { if (stations_only) return; SetTraceRestrictValue(item, GetDepotIndex(tile)); SetTraceRestrictAuxField(item, TROCAF_DEPOT); } else if (IsRailWaypointTile(tile)) { if (stations_only) return; SetTraceRestrictValue(item, GetStationIndex(tile)); SetTraceRestrictAuxField(item, TROCAF_WAYPOINT); } else if (IsTileType(tile, MP_STATION)) { StationID st_index = GetStationIndex(tile); const Station *st = Station::Get(st_index); if (st->facilities & FACIL_TRAIN) { SetTraceRestrictValue(item, st_index); SetTraceRestrictAuxField(item, TROCAF_STATION); } else { return; } } else { return; } if (!IsTileOwner(tile, _local_company)) { ShowErrorMessage(error_message, STR_ERROR_AREA_IS_OWNED_BY_ANOTHER, WL_INFO); return; } TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); } /** * Common OnPlaceObject handler for instruction value modification actions which involve selecting a signal tile value */ void OnPlaceObjectSignalTileValue(Point pt, TileIndex tile, int widget, int error_message) { TraceRestrictItem item = GetSelected(); if (GetTraceRestrictTypeProperties(item).value_type != TRVT_TILE_INDEX) return; if (!IsPlainRailTile(tile)) { ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); return; } if (GetPresentSignals(tile) == 0) { ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO); return; } if (!IsTileOwner(tile, _local_company)) { ShowErrorMessage(error_message, STR_ERROR_AREA_IS_OWNED_BY_ANOTHER, WL_INFO); return; } TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_DUAL_ITEM, this->selected_instruction - 1, tile, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); } virtual void OnPlaceObjectAbort() { this->RaiseButtons(); this->current_placement_widget = -1; } virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) { switch (widget) { case TR_WIDGET_INSTRUCTION_LIST: resize->height = FONT_HEIGHT_NORMAL; size->height = 6 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM; break; } } virtual void OnResize() { /* Update the scroll bar */ this->vscroll->SetCapacityFromWidget(this, TR_WIDGET_INSTRUCTION_LIST); } virtual void OnPaint() { this->DrawWidgets(); } virtual void DrawWidget(const Rect &r, int widget) const { if (widget != TR_WIDGET_INSTRUCTION_LIST) return; int y = r.top + WD_FRAMERECT_TOP; int line_height = this->GetWidget(TR_WIDGET_INSTRUCTION_LIST)->resize_y; int scroll_position = this->vscroll->GetPosition(); // prog may be NULL const TraceRestrictProgram *prog = this->GetProgram(); int count = this->GetItemCount(prog); uint indent = 1; for(int i = 0; i < count; i++) { TraceRestrictItem item = this->GetItem(prog, i); uint this_indent = indent; if (IsTraceRestrictConditional(item)) { if (GetTraceRestrictCondFlags(item) & (TRCF_ELSE | TRCF_OR)) { this_indent--; } else if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { indent--; this_indent--; } else { indent++; } } else if (GetTraceRestrictType(item) == TRIT_NULL) { this_indent = 0; } if (i >= scroll_position && this->vscroll->IsVisible(i)) { DrawInstructionString(prog, item, i, y, i == this->selected_instruction, this_indent, r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT); y += line_height; } } } virtual void OnInvalidateData(int data, bool gui_scope) { if (gui_scope) { this->ReloadProgramme(); } } virtual void SetStringParameters(int widget) const { switch (widget) { case TR_WIDGET_VALUE_INT: { SetDParam(0, 0); TraceRestrictItem item = this->GetSelected(); TraceRestrictValueType type = GetTraceRestrictTypeProperties(item).value_type; if (IsIntegerValueType(type)) { SetDParam(0, ConvertIntegerValue(type, GetTraceRestrictValue(item), true)); } break; } case TR_WIDGET_CAPTION: { const TraceRestrictProgram *prog = this->GetProgram(); if (prog) { SetDParam(0, prog->refcount); } else { SetDParam(0, 1); } break; } case TR_WIDGET_VALUE_DROPDOWN: { TraceRestrictItem item = this->GetSelected(); if (GetTraceRestrictTypeProperties(item).value_type == TRVT_PF_PENALTY && GetTraceRestrictAuxField(item) == TRPPAF_VALUE) { SetDParam(0, GetTraceRestrictValue(item)); } break; } } } private: /** * Helper function to make start and end instructions (these are not stored in the actual program) */ TraceRestrictItem MakeSpecialItem(TraceRestrictNullTypeSpecialValue value) const { TraceRestrictItem item = 0; SetTraceRestrictType(item, TRIT_NULL); SetTraceRestrictValue(item, value); return item; } /** * Get item count of program, including start and end markers */ int GetItemCount(const TraceRestrictProgram *prog) const { if (prog) { return 2 + prog->GetInstructionCount(); } else { return 2; } } /** * Get current program * This may return NULL if no program currently exists */ const TraceRestrictProgram *GetProgram() const { return GetTraceRestrictProgram(MakeTraceRestrictRefId(tile, track), false); } /** * Get instruction at @p index in program @p prog * This correctly handles start/end markers, offsets, etc. * This returns a 0 instruction if out of bounds * @p prog may be NULL */ TraceRestrictItem GetItem(const TraceRestrictProgram *prog, int index) const { if (index < 0) { return 0; } if (index == 0) { return MakeSpecialItem(TRNTSV_START); } if (prog) { size_t instruction_count = prog->GetInstructionCount(); if (static_cast(index) == instruction_count + 1) { return MakeSpecialItem(TRNTSV_END); } if (static_cast(index) > instruction_count + 1) { return 0; } return prog->items[prog->InstructionOffsetToArrayOffset(index - 1)]; } else { // No program defined, this is equivalent to an empty program if (index == 1) { return MakeSpecialItem(TRNTSV_END); } else { return 0; } } } /** * Get selected instruction, or a zero instruction */ TraceRestrictItem GetSelected() const { return this->GetItem(this->GetProgram(), this->selected_instruction); } /** * Get owner of the signal tile this window is pointing at */ Owner GetOwner() { return GetTileOwner(this->tile); } /** * Return item index from point in instruction list widget */ int GetItemIndexFromPt(int y) { NWidgetBase *nwid = this->GetWidget(TR_WIDGET_INSTRUCTION_LIST); int sel = (y - nwid->pos_y - WD_FRAMERECT_TOP) / nwid->resize_y; // Selected line if ((uint)sel >= this->vscroll->GetCapacity()) return -1; sel += this->vscroll->GetPosition(); return (sel < this->GetItemCount(this->GetProgram()) && sel >= 0) ? sel : -1; } /** * Reload details of program, and adjust length/selection position as necessary */ void ReloadProgramme() { const TraceRestrictProgram *prog = this->GetProgram(); if (this->vscroll->GetCount() != this->GetItemCount(prog)) { // program length has changed if (this->GetItemCount(prog) < this->vscroll->GetCount() || this->GetItem(prog, this->selected_instruction) != this->expecting_inserted_item) { // length has shrunk or if we weren't expecting an insertion, deselect this->selected_instruction = -1; } this->expecting_inserted_item = static_cast(0); // update scrollbar size this->vscroll->SetCount(this->GetItemCount(prog)); } this->UpdateButtonState(); } /** * Update button states, text values, etc. */ void UpdateButtonState() { this->RaiseWidget(TR_WIDGET_INSERT); this->RaiseWidget(TR_WIDGET_REMOVE); this->RaiseWidget(TR_WIDGET_TYPE_COND); this->RaiseWidget(TR_WIDGET_TYPE_NONCOND); this->RaiseWidget(TR_WIDGET_CONDFLAGS); this->RaiseWidget(TR_WIDGET_COMPARATOR); this->RaiseWidget(TR_WIDGET_VALUE_INT); this->RaiseWidget(TR_WIDGET_VALUE_DROPDOWN); this->RaiseWidget(TR_WIDGET_VALUE_DEST); this->RaiseWidget(TR_WIDGET_VALUE_SIGNAL); NWidgetStacked *left_2_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT_2); NWidgetStacked *left_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT); NWidgetStacked *middle_sel = this->GetWidget(TR_WIDGET_SEL_TOP_MIDDLE); NWidgetStacked *right_sel = this->GetWidget(TR_WIDGET_SEL_TOP_RIGHT); NWidgetStacked *share_sel = this->GetWidget(TR_WIDGET_SEL_SHARE); this->DisableWidget(TR_WIDGET_TYPE_COND); this->DisableWidget(TR_WIDGET_TYPE_NONCOND); this->DisableWidget(TR_WIDGET_CONDFLAGS); this->DisableWidget(TR_WIDGET_COMPARATOR); this->DisableWidget(TR_WIDGET_VALUE_INT); this->DisableWidget(TR_WIDGET_VALUE_DROPDOWN); this->DisableWidget(TR_WIDGET_VALUE_DEST); this->DisableWidget(TR_WIDGET_VALUE_SIGNAL); this->DisableWidget(TR_WIDGET_INSERT); this->DisableWidget(TR_WIDGET_REMOVE); this->DisableWidget(TR_WIDGET_RESET); this->DisableWidget(TR_WIDGET_COPY); this->DisableWidget(TR_WIDGET_SHARE); this->DisableWidget(TR_WIDGET_UNSHARE); this->DisableWidget(TR_WIDGET_BLANK_L2); this->DisableWidget(TR_WIDGET_BLANK_L); this->DisableWidget(TR_WIDGET_BLANK_M); this->DisableWidget(TR_WIDGET_BLANK_R); left_2_sel->SetDisplayedPlane(DPL2_BLANK); left_sel->SetDisplayedPlane(DPL_BLANK); middle_sel->SetDisplayedPlane(DPM_BLANK); right_sel->SetDisplayedPlane(DPR_BLANK); share_sel->SetDisplayedPlane(DPS_SHARE); const TraceRestrictProgram *prog = this->GetProgram(); this->GetWidget(TR_WIDGET_CAPTION)->widget_data = (prog && prog->refcount > 1) ? STR_TRACE_RESTRICT_CAPTION_SHARED : STR_TRACE_RESTRICT_CAPTION; // Don't allow modifications if don't own if (this->GetOwner() != _local_company) { this->SetDirty(); return; } if (prog && prog->refcount > 1) { // program is shared, show and enable unshare button, and reset button share_sel->SetDisplayedPlane(DPS_UNSHARE); this->EnableWidget(TR_WIDGET_UNSHARE); this->EnableWidget(TR_WIDGET_RESET); } else if (this->GetItemCount(prog) > 2) { // program is non-empty and not shared, enable reset button this->EnableWidget(TR_WIDGET_RESET); } else { // program is empty and not shared, show copy and share buttons this->EnableWidget(TR_WIDGET_COPY); this->EnableWidget(TR_WIDGET_SHARE); } // haven't selected instruction if (this->selected_instruction < 1) { this->SetDirty(); return; } TraceRestrictItem item = this->GetItem(prog, this->selected_instruction); if (item != 0) { if (GetTraceRestrictType(item) == TRIT_NULL) { switch (GetTraceRestrictValue(item)) { case TRNTSV_START: break; case TRNTSV_END: this->EnableWidget(TR_WIDGET_INSERT); break; default: NOT_REACHED(); break; } } else if (GetTraceRestrictType(item) == TRIT_COND_ENDIF) { this->EnableWidget(TR_WIDGET_INSERT); if (GetTraceRestrictCondFlags(item) != 0) { // this is not an end if, it must be an else, enable removing this->EnableWidget(TR_WIDGET_REMOVE); // setup condflags dropdown to show else left_2_sel->SetDisplayedPlane(DPL2_CONDFLAGS); this->EnableWidget(TR_WIDGET_CONDFLAGS); this->GetWidget(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_ELSE; } } else { TraceRestrictTypePropertySet properties = GetTraceRestrictTypeProperties(item); int type_widget; if (IsTraceRestrictConditional(item)) { // note that else and end if items are not handled here, they are handled above left_2_sel->SetDisplayedPlane(DPL2_CONDFLAGS); left_sel->SetDisplayedPlane(DPL_TYPE); type_widget = TR_WIDGET_TYPE_COND; // setup condflags dropdown box left_2_sel->SetDisplayedPlane(DPL2_CONDFLAGS); switch (GetTraceRestrictCondFlags(item)) { case TRCF_DEFAULT: // opening if, leave disabled this->GetWidget(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_IF; break; case TRCF_ELSE: // else-if this->GetWidget(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_ELIF; this->EnableWidget(TR_WIDGET_CONDFLAGS); break; case TRCF_OR: // or-if this->GetWidget(TR_WIDGET_CONDFLAGS)->widget_data = STR_TRACE_RESTRICT_CONDITIONAL_ORIF; this->EnableWidget(TR_WIDGET_CONDFLAGS); break; default: NOT_REACHED(); break; } } else { left_2_sel->SetDisplayedPlane(DPL2_TYPE); type_widget = TR_WIDGET_TYPE_NONCOND; } this->EnableWidget(type_widget); this->GetWidget(type_widget)->widget_data = GetTypeString(GetTraceRestrictType(item)); if (properties.cond_type == TRCOT_BINARY || properties.cond_type == TRCOT_ALL) { middle_sel->SetDisplayedPlane(DPM_COMPARATOR); this->EnableWidget(TR_WIDGET_COMPARATOR); const TraceRestrictDropDownListSet *list_set = GetCondOpDropDownListSet(properties); if (list_set) { this->GetWidget(TR_WIDGET_COMPARATOR)->widget_data = GetDropDownStringByValue(list_set, GetTraceRestrictCondOp(item)); } } if (IsIntegerValueType(properties.value_type)) { right_sel->SetDisplayedPlane(DPR_VALUE_INT); this->EnableWidget(TR_WIDGET_VALUE_INT); } else { switch (properties.value_type) { case TRVT_DENY: right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_PF_ALLOW : STR_TRACE_RESTRICT_PF_DENY; break; case TRVT_ORDER: right_sel->SetDisplayedPlane(DPR_VALUE_DEST); this->EnableWidget(TR_WIDGET_VALUE_DEST); break; case TRVT_CARGO_ID: right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = GetCargoStringByID(GetTraceRestrictValue(item)); break; case TRVT_DIRECTION: right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = GetDropDownStringByValue(&_direction_value, GetTraceRestrictValue(item)); break; case TRVT_TILE_INDEX: right_sel->SetDisplayedPlane(DPR_VALUE_SIGNAL); this->EnableWidget(TR_WIDGET_VALUE_SIGNAL); break; case TRVT_PF_PENALTY: right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); if (GetTraceRestrictAuxField(item) == TRPPAF_VALUE) { this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_BLACK_COMMA; } else { this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = GetDropDownStringByValue(&_pf_penalty_dropdown, GetPathfinderPenaltyDropdownIndex(item)); } break; case TRVT_RESERVE_THROUGH: right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_RESERVE_THROUGH_CANCEL : STR_TRACE_RESTRICT_RESERVE_THROUGH; break; default: break; } } this->EnableWidget(TR_WIDGET_INSERT); this->EnableWidget(TR_WIDGET_REMOVE); } } this->SetDirty(); } /** * Show a drop down list using @p list_set, setting the pre-selected item to the one corresponding to @p value * This asserts if @p value is not in @p list_set, and @p missing_ok is false */ void ShowDropDownListWithValue(const TraceRestrictDropDownListSet *list_set, uint value, bool missing_ok, int button, uint32 disabled_mask, uint32 hidden_mask, uint width) { drop_down_list_mapping[button] = list_set; int selected = GetDropDownListIndexByValue(list_set, value, missing_ok); ShowDropDownMenu(this, list_set->string_array, selected, button, disabled_mask, hidden_mask, width); } /** * Helper function to set or unset a SetObjectToPlaceWnd, for the given widget and cursor type */ void SetObjectToPlaceAction(int widget, CursorID cursor) { if (this->current_placement_widget != -1 && widget != this->current_placement_widget) { ResetObjectToPlace(); } this->ToggleWidgetLoweredState(widget); this->SetWidgetDirty(widget); if (this->IsWidgetLowered(widget)) { SetObjectToPlaceWnd(cursor, PAL_NONE, HT_RECT, this); this->current_placement_widget = widget; } else { ResetObjectToPlace(); this->current_placement_widget = -1; } } /** * This used for testing whether else or else-if blocks could be inserted, or replace the selection * If @p replace is true, replace selection with @p item, else insert @p item before selection * Returns true if resulting instruction list passes validation */ bool GenericElseInsertionDryRun(TraceRestrictItem item, bool replace) { if (this->selected_instruction < 1) return false; uint offset = this->selected_instruction - 1; const TraceRestrictProgram *prog = this->GetProgram(); if (!prog) return false; std::vector items = prog->items; // copy if (offset >= (TraceRestrictProgram::GetInstructionCount(items) + (replace ? 0 : 1))) return false; // off the end of the program uint array_offset = TraceRestrictProgram::InstructionOffsetToArrayOffset(items, offset); if (replace) { items[array_offset] = item; } else { items.insert(items.begin() + array_offset, item); } TraceRestrictProgramActionsUsedFlags actions_used_flags; return TraceRestrictProgram::Validate(items, actions_used_flags).Succeeded(); } /** * Run GenericElseInsertionDryRun with an else instruction */ bool ElseInsertionDryRun(bool replace) { TraceRestrictItem item = 0; SetTraceRestrictType(item, TRIT_COND_ENDIF); SetTraceRestrictCondFlags(item, TRCF_ELSE); return GenericElseInsertionDryRun(item, replace); } /** * Run GenericElseInsertionDryRun with an elif instruction */ bool ElseIfInsertionDryRun(bool replace) { TraceRestrictItem item = 0; SetTraceRestrictType(item, TRIT_COND_UNDEFINED); SetTraceRestrictCondFlags(item, TRCF_ELSE); return GenericElseInsertionDryRun(item, replace); } }; static const NWidgetPart _nested_program_widgets[] = { // Title bar NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), NWidget(WWT_CAPTION, COLOUR_GREY, TR_WIDGET_CAPTION), SetDataTip(STR_TRACE_RESTRICT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), // Program display NWidget(NWID_HORIZONTAL), NWidget(WWT_PANEL, COLOUR_GREY, TR_WIDGET_INSTRUCTION_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_TRACE_RESTRICT_INSTRUCTION_LIST_TOOLTIP), SetResize(1, 1), EndContainer(), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, TR_WIDGET_SCROLLBAR), EndContainer(), // Button Bar NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT_2), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE_NONCOND), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_TRACE_RESTRICT_TYPE_TOOLTIP), SetResize(1, 0), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_CONDFLAGS), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_TRACE_RESTRICT_CONDFLAGS_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_L2), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE_COND), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_TRACE_RESTRICT_TYPE_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_L), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_MIDDLE), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_COMPARATOR_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_M), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_RIGHT), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_INT), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_BLACK_COMMA, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_VALUE_DROPDOWN), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_DEST), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_SELECT_TARGET, STR_TRACE_RESTRICT_SELECT_TARGET), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_SIGNAL), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_SELECT_SIGNAL, STR_TRACE_RESTRICT_SELECT_SIGNAL), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_R), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(WWT_IMGBTN, COLOUR_GREY, TR_WIDGET_GOTO_SIGNAL), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_RIGHT, STR_TRACE_RESTRICT_GOTO_SIGNAL_TOOLTIP), EndContainer(), /* Second button row. */ NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_INSERT), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_INSERT, STR_TRACE_RESTRICT_INSERT_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_REMOVE), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_REMOVE, STR_TRACE_RESTRICT_REMOVE_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_RESET), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_RESET, STR_TRACE_RESTRICT_RESET_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_COPY), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_COPY, STR_TRACE_RESTRICT_COPY_TOOLTIP), SetResize(1, 0), NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_SHARE), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_SHARE), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_SHARE, STR_TRACE_RESTRICT_SHARE_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_UNSHARE), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_UNSHARE, STR_TRACE_RESTRICT_UNSHARE_TOOLTIP), SetResize(1, 0), EndContainer(), EndContainer(), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), }; static WindowDesc _program_desc( WDP_AUTO, "trace_restrict_gui", 384, 100, WC_TRACE_RESTRICT, WC_BUILD_SIGNAL, WDF_CONSTRUCTION, _nested_program_widgets, lengthof(_nested_program_widgets) ); /** * Show or create program window for given @p tile and @p track */ void ShowTraceRestrictProgramWindow(TileIndex tile, Track track) { if (BringWindowToFrontById(WC_TRACE_RESTRICT, MakeTraceRestrictRefId(tile, track)) != NULL) { return; } new TraceRestrictWindow(&_program_desc, tile, track); }