From 572eb10e91403d5cf5c1ca859570ec8eb8f92cf7 Mon Sep 17 00:00:00 2001 From: Adam Lederer Date: Sat, 24 Aug 2024 18:00:11 -0700 Subject: [PATCH] add cursor undo/redo actions --- src/gui/cursor.cpp | 10 ++++++++ src/gui/debugWindow.cpp | 19 +++++++++++++++ src/gui/doAction.cpp | 9 ++++++-- src/gui/editing.cpp | 51 +++++++++++++++++++++++++++++++++++++++++ src/gui/findReplace.cpp | 1 + src/gui/gui.cpp | 1 + src/gui/gui.h | 26 +++++++++++++++++++++ src/gui/guiConst.cpp | 2 ++ src/gui/settings.cpp | 2 ++ src/gui/subSongs.cpp | 3 +++ 10 files changed, 122 insertions(+), 2 deletions(-) diff --git a/src/gui/cursor.cpp b/src/gui/cursor.cpp index 64f7114b3..de8f7b8c4 100644 --- a/src/gui/cursor.cpp +++ b/src/gui/cursor.cpp @@ -69,6 +69,9 @@ void FurnaceGUI::startSelection(int xCoarse, int xFine, int y, bool fullRow) { selStart.y=y; selEnd.y=y; } else { + if (xCoarse!=cursor.xCoarse || y!=cursor.y) { + makeCursorUndo(); + } cursor.xCoarse=xCoarse; cursor.xFine=xFine; cursor.y=y; @@ -208,6 +211,9 @@ void FurnaceGUI::finishSelection() { } void FurnaceGUI::moveCursor(int x, int y, bool select) { + if (y>=editStepCoarse || y<=-editStepCoarse || x<=-5 || x>=5 ) { + makeCursorUndo(); + } if (!select) { finishSelection(); } @@ -326,6 +332,7 @@ void FurnaceGUI::moveCursor(int x, int y, bool select) { } void FurnaceGUI::moveCursorPrevChannel(bool overflow) { + makeCursorUndo(); finishSelection(); curNibble=false; @@ -354,6 +361,7 @@ void FurnaceGUI::moveCursorPrevChannel(bool overflow) { } void FurnaceGUI::moveCursorNextChannel(bool overflow) { + makeCursorUndo(); finishSelection(); curNibble=false; @@ -382,6 +390,7 @@ void FurnaceGUI::moveCursorNextChannel(bool overflow) { } void FurnaceGUI::moveCursorTop(bool select) { + makeCursorUndo(); if (!select) { finishSelection(); } @@ -403,6 +412,7 @@ void FurnaceGUI::moveCursorTop(bool select) { } void FurnaceGUI::moveCursorBottom(bool select) { + makeCursorUndo(); if (!select) { finishSelection(); } diff --git a/src/gui/debugWindow.cpp b/src/gui/debugWindow.cpp index b28126fc5..d19bfce22 100644 --- a/src/gui/debugWindow.cpp +++ b/src/gui/debugWindow.cpp @@ -731,6 +731,25 @@ void FurnaceGUI::drawDebug() { ImGui::Text("result: %.0f%%",realVol*100.0f); ImGui::TreePop(); } + if (ImGui::TreeNode("Cursor Undo Debug")) { + auto DrawSpot=[&](const CursorJumpPoint& spot) { + ImGui::Text("[%d:%d] <%d:%d, %d>", spot.subSong, spot.order, spot.point.xCoarse, spot.point.xFine, spot.point.y); + }; + if (ImGui::BeginChild("##CursorUndoDebugChild", ImVec2(0, 300), true)) { + if (ImGui::BeginTable("##CursorUndoDebug", 2, ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) { + for (size_t row=0; row=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1; selStart=cursor; @@ -1220,6 +1221,7 @@ void FurnaceGUI::doPasteMPT(PasteMode mode, int arg, bool readClipboard, String if (readClipboard) { if (settings.cursorPastePos) { + makeCursorUndo(); cursor.y=j; if (cursor.y>=e->curSubSong->patLen) cursor.y=e->curSubSong->patLen-1; selStart=cursor; @@ -2061,3 +2063,52 @@ void FurnaceGUI::doRedo() { redoHist.pop_back(); } + +CursorJumpPoint FurnaceGUI::getCurrentCursorJumpPoint() { + return CursorJumpPoint(cursor, curOrder, e->getCurrentSubSong()); +} + +void FurnaceGUI::applyCursorJumpPoint(const CursorJumpPoint& spot) { + cursor=spot.point; + curOrder=MIN(e->curSubSong->ordersLen-1, spot.order); + e->setOrder(curOrder); + e->changeSongP(spot.subSong); + if (!settings.cursorMoveNoScroll) { + updateScroll(cursor.y); + } +} + +void FurnaceGUI::makeCursorUndo() { + CursorJumpPoint spot = getCurrentCursorJumpPoint(); + if (!cursorUndoHist.empty() && spot == cursorUndoHist.back()) return; + + if (cursorUndoHist.size()>=settings.maxUndoSteps) cursorUndoHist.pop_front(); + cursorUndoHist.push_back(spot); + + // redo history no longer relevant, we've changed timeline + cursorRedoHist.clear(); +} + +void FurnaceGUI::doCursorUndo() { + if (cursorUndoHist.empty()) return; + + // allow returning to current spot + if (cursorRedoHist.size()>=settings.maxUndoSteps) cursorRedoHist.pop_front(); + cursorRedoHist.push_back(getCurrentCursorJumpPoint()); + + // apply spot + applyCursorJumpPoint(cursorUndoHist.back()); + cursorUndoHist.pop_back(); +} + +void FurnaceGUI::doCursorRedo() { +if (cursorRedoHist.empty()) return; + + // allow returning to current spot + if (cursorUndoHist.size()>=settings.maxUndoSteps) cursorUndoHist.pop_front(); + cursorUndoHist.push_back(getCurrentCursorJumpPoint()); + + // apply spot + applyCursorJumpPoint(cursorRedoHist.back()); + cursorRedoHist.pop_back(); +} \ No newline at end of file diff --git a/src/gui/findReplace.cpp b/src/gui/findReplace.cpp index 711f15e62..43eeb9625 100644 --- a/src/gui/findReplace.cpp +++ b/src/gui/findReplace.cpp @@ -560,6 +560,7 @@ void FurnaceGUI::drawFindReplace() { if (ImGui::TableNextColumn()) { snprintf(tempID,1024,ICON_FA_CHEVRON_RIGHT "##_FR%d",index); if (ImGui::Selectable(tempID)) { + makeCursorUndo(); e->changeSongP(i.subsong); if (e->isPlaying()) { followPattern=false; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 48bf2332e..31844258d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1211,6 +1211,7 @@ void FurnaceGUI::play(int row) { memset(chanOscBright,0,DIV_MAX_CHANS*sizeof(float)); e->walkSong(loopOrder,loopRow,loopEnd); memset(lastIns,-1,sizeof(int)*DIV_MAX_CHANS); + if (followPattern) makeCursorUndo(); if (!followPattern) e->setOrder(curOrder); if (row>0) { if (!e->playToRow(row)) { diff --git a/src/gui/gui.h b/src/gui/gui.h index 4246a1982..f27972efa 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -818,6 +818,8 @@ enum FurnaceGUIActions { GUI_ACTION_PAT_SCROLL_MODE, GUI_ACTION_PAT_CLEAR_LATCH, GUI_ACTION_PAT_ABSORB_INSTRUMENT, + GUI_ACTION_PAT_CURSOR_UNDO, + GUI_ACTION_PAT_CURSOR_REDO, GUI_ACTION_PAT_MAX, GUI_ACTION_INS_LIST_MIN, @@ -1103,6 +1105,22 @@ struct UndoStep { newPatLen(0) {} }; +struct CursorJumpPoint { + SelectionPoint point; + int order; + int subSong; + CursorJumpPoint(const SelectionPoint& p, int o, int ss): + point(p), order(o), subSong(ss) {} + CursorJumpPoint(): + point(), order(0), subSong(0) {} + bool operator== (const CursorJumpPoint& spot) { + return point.xCoarse==spot.point.xCoarse && point.xFine==spot.point.xFine && point.y==spot.point.y && order==spot.order && subSong==spot.subSong; + } + bool operator!= (const CursorJumpPoint& spot) { + return !(*this == spot); + } +}; + // -1 = any struct MIDIBind { int type, channel, data1, data2; @@ -2500,6 +2518,8 @@ class FurnaceGUI { std::map oldPatMap; FixedQueue undoHist; FixedQueue redoHist; + FixedQueue cursorUndoHist; + FixedQueue cursorRedoHist; // sample editor specific double sampleZoom; @@ -2936,6 +2956,12 @@ class FurnaceGUI { void doGenerateWave(); + CursorJumpPoint getCurrentCursorJumpPoint(); + void applyCursorJumpPoint(const CursorJumpPoint& spot); + void makeCursorUndo(); + void doCursorUndo(); + void doCursorRedo(); + void doUndoSample(); void doRedoSample(); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index fbfdadc0c..fe20ae5e3 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -688,6 +688,8 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("PAT_SCROLL_MODE", _N("Change mobile scroll mode"), 0), D("PAT_CLEAR_LATCH", _N("Clear note input latch"), 0), D("PAT_ABSORB_INSTRUMENT", _N("Absorb instrument/octave from status at cursor"), 0), + D("PAT_CURSOR_UNDO", _N("Return cursor to previous jump point"), 0), + D("PAT_CURSOR_REDO", _N("Reverse recent cursor undo"), 0), D("PAT_MAX", "", NOT_AN_ACTION), D("INS_LIST_MIN", _N("---Instrument list"), NOT_AN_ACTION), diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index e2efb91e7..8d45c3340 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -2428,6 +2428,8 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_PAT_LATCH); UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CLEAR_LATCH); UI_KEYBIND_CONFIG(GUI_ACTION_PAT_ABSORB_INSTRUMENT); + UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CURSOR_UNDO); + UI_KEYBIND_CONFIG(GUI_ACTION_PAT_CURSOR_REDO); KEYBIND_CONFIG_END; ImGui::TreePop(); diff --git a/src/gui/subSongs.cpp b/src/gui/subSongs.cpp index fba8ddff1..cc69dbcd5 100644 --- a/src/gui/subSongs.cpp +++ b/src/gui/subSongs.cpp @@ -36,6 +36,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) { ImGui::TableNextRow(); ImGui::TableNextColumn(); if (ImGui::Selectable(id,i==e->getCurrentSubSong())) { + makeCursorUndo(); e->changeSongP(i); updateScroll(0); oldRow=0; @@ -72,6 +73,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) { if (!e->addSubSong()) { showError(_("too many subsongs!")); } else { + makeCursorUndo(); e->changeSongP(e->song.subsong.size()-1); updateScroll(0); oldRow=0; @@ -92,6 +94,7 @@ void FurnaceGUI::drawSubSongs(bool asChild) { if (!e->duplicateSubSong(e->getCurrentSubSong())) { showError(_("too many subsongs!")); } else { + makeCursorUndo(); e->changeSongP(e->song.subsong.size()-1); updateScroll(0); oldRow=0;