From 65cc6623ddefba3325b558fc17120c203d96db68 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 5 May 2023 23:34:30 +0100 Subject: [PATCH] Tracerestrict: Add button to duplicate instruction/block --- src/lang/extra/english.txt | 3 + src/tracerestrict.cpp | 124 +++++++++++++++++++++++++++---------- src/tracerestrict.h | 3 + src/tracerestrict_gui.cpp | 41 +++++++++++- 4 files changed, 139 insertions(+), 32 deletions(-) diff --git a/src/lang/extra/english.txt b/src/lang/extra/english.txt index 8d0a5aa4a3..d483e20477 100644 --- a/src/lang/extra/english.txt +++ b/src/lang/extra/english.txt @@ -1010,6 +1010,7 @@ STR_TRACE_RESTRICT_REMOVE :{BLACK}Remove STR_TRACE_RESTRICT_RESET :{BLACK}Reset STR_TRACE_RESTRICT_COPY :{BLACK}Copy STR_TRACE_RESTRICT_APPEND :{BLACK}Append +STR_TRACE_RESTRICT_DUPLICATE :{BLACK}Duplicate STR_TRACE_RESTRICT_SHARE :{BLACK}Share STR_TRACE_RESTRICT_UNSHARE :{BLACK}Unshare STR_TRACE_RESTRICT_SELECT_TARGET :{BLACK}Select Target @@ -1019,6 +1020,7 @@ STR_TRACE_RESTRICT_INSERT_TOOLTIP :{BLACK}Insert a STR_TRACE_RESTRICT_REMOVE_TOOLTIP :{BLACK}Remove the selected instruction{}Ctrl+Click to remove the selected conditional instruction but retain its contents STR_TRACE_RESTRICT_RESET_TOOLTIP :{BLACK}Reset the current signal (without affecting shared programs) STR_TRACE_RESTRICT_COPY_TOOLTIP :{BLACK}Copy program from another signal{}Ctrl+click to append program from another signal +STR_TRACE_RESTRICT_DUPLICATE_TOOLTIP :{BLACK}Duplicate the selected instruction{}Ctrl+click to append program from another signal STR_TRACE_RESTRICT_SHARE_TOOLTIP :{BLACK}Share program with another signal STR_TRACE_RESTRICT_UNSHARE_TOOLTIP :{BLACK}Stop sharing program with other signals, create a copy of the program STR_TRACE_RESTRICT_SIGNAL_GUI_TOOLTIP :{BLACK}Routefinding restriction @@ -1031,6 +1033,7 @@ STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM :{WHITE}Can't in 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_CAN_T_DUPLICATE_ITEM :{WHITE}Can't duplicate 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 66b1751ff8..40c4b1da25 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -1949,46 +1949,76 @@ CommandCost TraceRestrictProgramRemoveItemAt(std::vector &ite return CommandCost(); } -CommandCost TraceRestrictProgramMoveItemAt(std::vector &items, uint32 &offset, bool up, bool shallow_mode) +static CommandCost AdvanceItemEndIteratorForBlock(const std::vector &items, + const std::vector::iterator &move_start, std::vector::iterator &move_end, bool allow_elif) { - 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); + if (IsTraceRestrictConditional(old_item)) { + if (GetTraceRestrictType(old_item) == TRIT_COND_ENDIF) { + // this is an else or end if, can't move these + return CMD_ERROR; + } + if (GetTraceRestrictCondFlags(old_item) != 0) { + if (allow_elif) { + uint32 recursion_depth = 0; + 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 + if (recursion_depth == 0) break; + recursion_depth--; + } else { + // this is an opening if + recursion_depth++; + } + } else if (recursion_depth == 0) { + // next elif/orif + break; + } + } + } + return CommandCost(); } + // can't move or/else blocks + return CMD_ERROR; + } - 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++; + 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 (recursion_depth != 0) return CMD_ERROR; // ran off the end + } + 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); + + if (!shallow_mode) { + CommandCost res = AdvanceItemEndIteratorForBlock(items, move_start, move_end, false); + if (res.Failed()) return CommandCost(STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); } if (up) { @@ -2003,6 +2033,32 @@ CommandCost TraceRestrictProgramMoveItemAt(std::vector &items return CommandCost(); } +CommandCost TraceRestrictProgramDuplicateItemAt(std::vector &items, uint32 offset) +{ + std::vector::iterator dup_start = TraceRestrictProgram::InstructionAt(items, offset); + std::vector::iterator dup_end = InstructionIteratorNext(dup_start); + + CommandCost res = AdvanceItemEndIteratorForBlock(items, dup_start, dup_end, true); + if (res.Failed()) return CommandCost(STR_TRACE_RESTRICT_ERROR_CAN_T_DUPLICATE_ITEM); + + std::vector new_items; + new_items.reserve(items.size() + (dup_end - dup_start)); + new_items.insert(new_items.end(), items.begin(), dup_end); + new_items.insert(new_items.end(), dup_start, dup_end); + new_items.insert(new_items.end(), dup_end, items.end()); + items = std::move(new_items); + return CommandCost(); +} + +bool TraceRestrictProgramDuplicateItemAtDryRun(const std::vector &items, uint32 offset) +{ + std::vector::iterator dup_start = TraceRestrictProgram::InstructionAt(const_cast &>(items), offset); + std::vector::iterator dup_end = InstructionIteratorNext(dup_start); + + CommandCost res = AdvanceItemEndIteratorForBlock(items, dup_start, dup_end, true); + return res.Succeeded(); +} + /** * The main command for editing a signal tracerestrict program. * @param tile The tile which contains the signal. @@ -2103,6 +2159,12 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u break; } + case TRDCT_DUPLICATE_ITEM: { + CommandCost res = TraceRestrictProgramDuplicateItemAt(items, offset); + if (res.Failed()) return res; + break; + } + default: return CMD_ERROR; } diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 2f24965ea9..f57f0fcd31 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -1036,6 +1036,7 @@ enum TraceRestrictDoCommandType { 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_DUPLICATE_ITEM, ///< duplicate instruction/block at offset field TRDCT_PROG_COPY, ///< copy program operation. Do not re-order this with respect to other values TRDCT_PROG_COPY_APPEND, ///< copy and append program operation @@ -1062,6 +1063,8 @@ CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag CommandCost TraceRestrictProgramRemoveItemAt(std::vector &items, uint32 offset, bool shallow_mode); CommandCost TraceRestrictProgramMoveItemAt(std::vector &items, uint32 &offset, bool up, bool shallow_mode); +CommandCost TraceRestrictProgramDuplicateItemAt(std::vector &items, uint32 offset); +bool TraceRestrictProgramDuplicateItemAtDryRun(const std::vector &items, uint32 offset); void ShowTraceRestrictProgramWindow(TileIndex tile, Track track); diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index b1e4ee1bd1..5047cbbe27 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -93,6 +93,7 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_RESET, TR_WIDGET_COPY, TR_WIDGET_COPY_APPEND, + TR_WIDGET_DUPLICATE, TR_WIDGET_SHARE, TR_WIDGET_UNSHARE, }; @@ -133,6 +134,7 @@ enum PanelWidgets { // Copy DPC_COPY = 0, DPC_APPEND, + DPC_DUPLICATE, }; /** @@ -1917,6 +1919,19 @@ public: break; } + case TR_WIDGET_DUPLICATE: { + TraceRestrictItem item = this->GetSelected(); + if (this->GetOwner() != _local_company || item == 0) { + return; + } + + uint32 offset = this->selected_instruction - 1; + this->expecting_inserted_item = item; + TraceRestrictDoCommandP(tile, track, TRDCT_DUPLICATE_ITEM, + offset, 0, STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + break; + } + case TR_WIDGET_CONDFLAGS: { TraceRestrictItem item = this->GetSelected(); if (this->GetOwner() != _local_company || item == 0) { @@ -2809,6 +2824,21 @@ private: return false; } + bool IsDuplicateBtnUsable() const { + const TraceRestrictProgram *prog = this->GetProgram(); + if (!prog) return false; + + TraceRestrictItem item = this->GetSelected(); + if (GetTraceRestrictType(item) == TRIT_NULL) return false; + + uint32 offset = this->selected_instruction - 1; + if (TraceRestrictProgramDuplicateItemAtDryRun(prog->items, offset)) { + return true; + } + + return false; + } + /** * Update button states, text values, etc. */ @@ -2866,6 +2896,7 @@ private: this->DisableWidget(TR_WIDGET_UP_BTN); this->DisableWidget(TR_WIDGET_DOWN_BTN); + this->DisableWidget(TR_WIDGET_DUPLICATE); this->EnableWidget(TR_WIDGET_COPY_APPEND); @@ -2875,7 +2906,6 @@ private: middle_sel->SetDisplayedPlane(DPM_BLANK); right_sel->SetDisplayedPlane(DPR_BLANK); share_sel->SetDisplayedPlane(DPS_SHARE); - copy_sel->SetDisplayedPlane(_ctrl_pressed ? DPC_APPEND : DPC_COPY); const TraceRestrictProgram *prog = this->GetProgram(); @@ -2899,6 +2929,8 @@ private: return; } + int copy_panel = DPC_DUPLICATE; + if (prog && prog->refcount > 1) { // program is shared, show and enable unshare button, and reset button share_sel->SetDisplayedPlane(DPS_UNSHARE); @@ -2911,8 +2943,12 @@ private: // program is empty and not shared, show copy and share buttons this->EnableWidget(TR_WIDGET_COPY); this->EnableWidget(TR_WIDGET_SHARE); + copy_panel = DPC_COPY; } + this->GetWidget(TR_WIDGET_COPY_APPEND)->tool_tip = (copy_panel == DPC_DUPLICATE) ? STR_TRACE_RESTRICT_DUPLICATE_TOOLTIP : STR_TRACE_RESTRICT_COPY_TOOLTIP; + copy_sel->SetDisplayedPlane(_ctrl_pressed ? DPC_APPEND : copy_panel); + // haven't selected instruction if (this->selected_instruction < 1) { this->SetDirty(); @@ -3266,6 +3302,7 @@ private: } if (this->IsUpDownBtnUsable(true)) this->EnableWidget(TR_WIDGET_UP_BTN); if (this->IsUpDownBtnUsable(false)) this->EnableWidget(TR_WIDGET_DOWN_BTN); + if (this->IsDuplicateBtnUsable()) this->EnableWidget(TR_WIDGET_DUPLICATE); } this->SetDirty(); @@ -3460,6 +3497,8 @@ static const NWidgetPart _nested_program_widgets[] = { SetDataTip(STR_TRACE_RESTRICT_COPY, STR_TRACE_RESTRICT_COPY_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_COPY_APPEND), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_APPEND, STR_TRACE_RESTRICT_COPY_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_DUPLICATE), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_DUPLICATE, STR_TRACE_RESTRICT_DUPLICATE_TOOLTIP), SetResize(1, 0), EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_SHARE), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_SHARE), SetMinimalSize(124, 12), SetFill(1, 0),