diff --git a/src/lang/english.txt b/src/lang/english.txt index 428ae843ab..47d8fe8a87 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2452,6 +2452,8 @@ STR_TRACE_RESTRICT_DIRECTION_NW :north-west STR_TRACE_RESTRICT_VALUE_CAPTION :{WHITE}Value STR_TRACE_RESTRICT_CAPTION :{WHITE}Routefinding restriction STR_TRACE_RESTRICT_CAPTION_SHARED :{WHITE}Routefinding restriction - shared by {COMMA} signals +STR_TRACE_RESTRICT_UP_BTN_TOOLTIP :{BLACK}Move the selected instruction up{}Ctrl+Click to move conditional instruction instead of whole block +STR_TRACE_RESTRICT_DOWN_BTN_TOOLTIP :{BLACK}Move the selected instruction down{}Ctrl+Click to move conditional instruction instead of whole block STR_TRACE_RESTRICT_TYPE_TOOLTIP :{BLACK}Type STR_TRACE_RESTRICT_COND_COMPARATOR_TOOLTIP :{BLACK}Comparison operator STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP :{BLACK}Value @@ -2476,6 +2478,7 @@ STR_TRACE_RESTRICT_INSTRUCTION_LIST_TOOLTIP :{BLACK}Click an STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM :{WHITE}Can't insert instruction STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM :{WHITE}Can't modify instruction STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ITEM :{WHITE}Can't remove instruction +STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM :{WHITE}Can't move instruction STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE :{WHITE}Value too large, maximum is {DECIMAL} STR_TRACE_RESTRICT_ERROR_NO_PROGRAM :No trace restrict program exists STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE :Offset too large diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index de56f64687..6b484e16d9 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -21,6 +21,7 @@ #include "group.h" #include "pathfinder/yapf/yapf_cache.h" #include +#include /** @file * @@ -929,13 +930,67 @@ CommandCost TraceRestrictProgramRemoveItemAt(std::vector &ite return CommandCost(); } +CommandCost TraceRestrictProgramMoveItemAt(std::vector &items, uint32 &offset, bool up, bool shallow_mode) +{ + std::vector::iterator move_start = TraceRestrictProgram::InstructionAt(items, offset); + std::vector::iterator move_end = InstructionIteratorNext(move_start); + + TraceRestrictItem old_item = *move_start; + if (!shallow_mode) { + if (IsTraceRestrictConditional(old_item)) { + if (GetTraceRestrictCondFlags(old_item) != 0) { + // can't move or/else blocks + return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + } + if (GetTraceRestrictType(old_item) == TRIT_COND_ENDIF) { + // this is an end if, can't move these + return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + } + + uint32 recursion_depth = 1; + // iterate until matching end block found + for (; move_end != items.end(); InstructionIteratorAdvance(move_end)) { + TraceRestrictItem current_item = *move_end; + if (IsTraceRestrictConditional(current_item)) { + if (GetTraceRestrictCondFlags(current_item) == 0) { + if (GetTraceRestrictType(current_item) == TRIT_COND_ENDIF) { + // this is an end if + recursion_depth--; + if (recursion_depth == 0) { + // inclusively remove up to here + InstructionIteratorAdvance(move_end); + break; + } + } else { + // this is an opening if + recursion_depth++; + } + } + } + } + if (recursion_depth != 0) return CMD_ERROR; // ran off the end + } + } + + if (up) { + if (move_start == items.begin()) return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + std::rotate(TraceRestrictProgram::InstructionAt(items, offset - 1), move_start, move_end); + offset--; + } else { + if (move_end == items.end()) return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + std::rotate(move_start, move_end, InstructionIteratorNext(move_end)); + offset++; + } + return CommandCost(); +} + /** * The main command for editing a signal tracerestrict program. * @param tile The tile which contains the signal. * @param flags Internal command handler stuff. * Below apply for instruction modification actions only * @param p1 Bitstuffed items - * @param p2 Item, for insert and modify operations + * @param p2 Item, for insert and modify operations. Flags for instruction move operations * @return the cost of this operation (which is free), or an error */ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) @@ -1020,6 +1075,12 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u break; } + case TRDCT_MOVE_ITEM: { + CommandCost res = TraceRestrictProgramMoveItemAt(items, offset, p2 & 1, p2 & 2); + if (res.Failed()) return res; + break; + } + default: NOT_REACHED(); break; diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 78b24e5c13..3b6521e3e7 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -623,6 +623,7 @@ enum TraceRestrictDoCommandType { TRDCT_MODIFY_DUAL_ITEM, ///< modify second item of dual-part instruction at offset field to given value TRDCT_REMOVE_ITEM, ///< remove instruction at offset field TRDCT_SHALLOW_REMOVE_ITEM, ///< shallow remove instruction at offset field, does not delete contents of block + TRDCT_MOVE_ITEM, ///< move instruction or block at offset field TRDCT_PROG_COPY, ///< copy program operation. Do not re-order this with respect to other values TRDCT_PROG_SHARE, ///< share program operation @@ -647,6 +648,7 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text); CommandCost TraceRestrictProgramRemoveItemAt(std::vector &items, uint32 offset, bool shallow_mode); +CommandCost TraceRestrictProgramMoveItemAt(std::vector &items, uint32 &offset, bool up, bool shallow_mode); void ShowTraceRestrictProgramWindow(TileIndex tile, Track track); diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 2da6221227..1705c4a26f 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -51,6 +51,9 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_SEL_TOP_RIGHT, TR_WIDGET_SEL_SHARE, + TR_WIDGET_UP_BTN, + TR_WIDGET_DOWN_BTN, + TR_WIDGET_TYPE_COND, TR_WIDGET_TYPE_NONCOND, TR_WIDGET_CONDFLAGS, @@ -1011,6 +1014,26 @@ public: break; } + case TR_WIDGET_UP_BTN: + case TR_WIDGET_DOWN_BTN: { + TraceRestrictItem item = this->GetSelected(); + if (this->GetOwner() != _local_company || item == 0) { + return; + } + + uint32 p2 = 0; + if (widget == TR_WIDGET_UP_BTN) p2 |= 1; + if (_ctrl_pressed) p2 |= 2; + + uint32 offset = this->selected_instruction - 1; + + this->IsUpDownBtnUsable(widget == TR_WIDGET_UP_BTN, true); + + TraceRestrictDoCommandP(tile, track, TRDCT_MOVE_ITEM, + offset, p2, STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + break; + } + case TR_WIDGET_CONDFLAGS: { TraceRestrictItem item = this->GetSelected(); if (this->GetOwner() != _local_company || item == 0) { @@ -1560,6 +1583,11 @@ public: } } + virtual EventState OnCTRLStateChange() { + this->UpdateButtonState(); + return ES_NOT_HANDLED; + } + private: /** * Helper function to make start and end instructions (these are not stored in the actual program) @@ -1685,6 +1713,26 @@ private: this->UpdateButtonState(); } + bool IsUpDownBtnUsable(bool up, bool update_selection = false) { + const TraceRestrictProgram *prog = this->GetProgram(); + if (!prog) return false; + + TraceRestrictItem item = this->GetSelected(); + if (GetTraceRestrictType(item) == TRIT_NULL) return false; + + std::vector items = prog->items; // copy + uint32 offset = this->selected_instruction - 1; + if (TraceRestrictProgramMoveItemAt(items, offset, up, _ctrl_pressed).Succeeded()) { + TraceRestrictProgramActionsUsedFlags actions_used_flags; + if (TraceRestrictProgram::Validate(items, actions_used_flags).Succeeded()) { + if (update_selection) this->selected_instruction = offset + 1; + return true; + } + } + + return false; + } + /** * Update button states, text values, etc. */ @@ -1730,6 +1778,9 @@ private: this->DisableWidget(TR_WIDGET_BLANK_M); this->DisableWidget(TR_WIDGET_BLANK_R); + this->DisableWidget(TR_WIDGET_UP_BTN); + this->DisableWidget(TR_WIDGET_DOWN_BTN); + left_2_sel->SetDisplayedPlane(DPL2_BLANK); left_sel->SetDisplayedPlane(DPL_BLANK); middle_sel->SetDisplayedPlane(DPM_BLANK); @@ -1936,6 +1987,8 @@ private: this->EnableWidget(TR_WIDGET_INSERT); this->EnableWidget(TR_WIDGET_REMOVE); } + if (this->IsUpDownBtnUsable(true)) this->EnableWidget(TR_WIDGET_UP_BTN); + if (this->IsUpDownBtnUsable(false)) this->EnableWidget(TR_WIDGET_DOWN_BTN); } this->SetDirty(); @@ -2041,6 +2094,8 @@ static const NWidgetPart _nested_program_widgets[] = { // Button Bar NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, TR_WIDGET_UP_BTN), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_UP, STR_TRACE_RESTRICT_UP_BTN_TOOLTIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, TR_WIDGET_DOWN_BTN), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_DOWN, STR_TRACE_RESTRICT_DOWN_BTN_TOOLTIP), 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),