From df92cff4ff01e81a7f7f5e3cb5545a5a06a51fc0 Mon Sep 17 00:00:00 2001 From: IanCaio Date: Sat, 12 Dec 2020 11:33:45 -0300 Subject: [PATCH] Adds feature to merge Instrument Track patterns (#5700) --- data/themes/classic/edit_merge.png | Bin 0 -> 611 bytes data/themes/default/edit_merge.png | Bin 0 -> 611 bytes include/Pattern.h | 1 + include/TrackContentObjectView.h | 7 +- src/gui/TrackContentObjectView.cpp | 117 ++++++++++++++++++++++++++++- src/tracks/Pattern.cpp | 11 +++ 6 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 data/themes/classic/edit_merge.png create mode 100644 data/themes/default/edit_merge.png diff --git a/data/themes/classic/edit_merge.png b/data/themes/classic/edit_merge.png new file mode 100644 index 0000000000000000000000000000000000000000..3e8742b86efca57563e9b1036d8fa15621987864 GIT binary patch literal 611 zcmV-p0-XJcP)r;zp4O@dpG&1<5KK!5HxYL=gl* z<}rK&HyXqj@CkGwxN|8)$ig8JLx_rG69_0Sgc;*ZRrhg`PMDafjvq91RUf|k?(O0N zpx#rL)o5OS)c5K<4GQXA;58ty3B(M06qp7Qa4m$;9ZRVn)TG{QI=N;$b=p@=&_MkH zU`h{+@wqA8S5J>&TXpH5n+#P~0gQ3pl&qhh>cug9PuuOO$0F5G^RnE9xe2 zd4z2N6Y6Zeu7Iy0gx-OKb=fbC@mZa6XjKgw{`{_z`cy6UCj=;pP6%OOSx%uHs!Cd| zMMBuhR9OJmfk(ji0|`fZ_13D|?PuQ@v-eLuqi(C~O@`mp-^Td3HvOf}nbPfy>6%)i zmg?FVU_4u?8+ZXM09|zvC~Ia4 zm{d;!Q-rVroC2naaRyk&%LlMeiJy4FtTwBxWvxI1$YCT0FH+chGWzG zV+dT`>qQ8mKU#)51gifr8&#{}+Yo50P0Xk!fMFB-377;P9_FOp0xkd_LjyDmEU_~b xwt(#ndIUIEzu!6RyP^U={!X002ovPDHLkV1h*i{JQ`E literal 0 HcmV?d00001 diff --git a/data/themes/default/edit_merge.png b/data/themes/default/edit_merge.png new file mode 100644 index 0000000000000000000000000000000000000000..3e8742b86efca57563e9b1036d8fa15621987864 GIT binary patch literal 611 zcmV-p0-XJcP)r;zp4O@dpG&1<5KK!5HxYL=gl* z<}rK&HyXqj@CkGwxN|8)$ig8JLx_rG69_0Sgc;*ZRrhg`PMDafjvq91RUf|k?(O0N zpx#rL)o5OS)c5K<4GQXA;58ty3B(M06qp7Qa4m$;9ZRVn)TG{QI=N;$b=p@=&_MkH zU`h{+@wqA8S5J>&TXpH5n+#P~0gQ3pl&qhh>cug9PuuOO$0F5G^RnE9xe2 zd4z2N6Y6Zeu7Iy0gx-OKb=fbC@mZa6XjKgw{`{_z`cy6UCj=;pP6%OOSx%uHs!Cd| zMMBuhR9OJmfk(ji0|`fZ_13D|?PuQ@v-eLuqi(C~O@`mp-^Td3HvOf}nbPfy>6%)i zmg?FVU_4u?8+ZXM09|zvC~Ia4 zm{d;!Q-rVroC2naaRyk&%LlMeiJy4FtTwBxWvxI1$YCT0FH+chGWzG zV+dT`>qQ8mKU#)51gifr8&#{}+Yo50P0Xk!fMFB-377;P9_FOp0xkd_LjyDmEU_~b xwt(#ndIUIEzu!6RyP^U={!X002ovPDHLkV1h*i{JQ`E literal 0 HcmV?d00001 diff --git a/include/Pattern.h b/include/Pattern.h index 0bc1f61f37a..0a73376c024 100644 --- a/include/Pattern.h +++ b/include/Pattern.h @@ -176,6 +176,7 @@ class PatternView : public TrackContentObjectView void setMutedNoteBorderColor(QColor const & color) { m_mutedNoteBorderColor = color; } public slots: + Pattern* getPattern(); void update() override; diff --git a/include/TrackContentObjectView.h b/include/TrackContentObjectView.h index 0f99af96479..e9461c5dd50 100644 --- a/include/TrackContentObjectView.h +++ b/include/TrackContentObjectView.h @@ -111,6 +111,10 @@ class TrackContentObjectView : public selectableObject, public ModelView // some metadata to be written to the clipboard. static void remove( QVector tcovs ); static void toggleMute( QVector tcovs ); + static void mergeTCOs(QVector tcovs); + + // Returns true if selection can be merged and false if not + static bool canMergeSelection(QVector tcovs); QColor getColorForDisplay( QColor ); @@ -129,7 +133,8 @@ public slots: Cut, Copy, Paste, - Mute + Mute, + Merge }; virtual void constructContextMenu( QMenu * ) diff --git a/src/gui/TrackContentObjectView.cpp b/src/gui/TrackContentObjectView.cpp index 0cd34bdd117..6aa78a1dd6e 100644 --- a/src/gui/TrackContentObjectView.cpp +++ b/src/gui/TrackContentObjectView.cpp @@ -24,6 +24,8 @@ #include "TrackContentObjectView.h" +#include + #include #include #include @@ -33,9 +35,14 @@ #include "ColorChooser.h" #include "ComboBoxModel.h" #include "DataFile.h" +#include "Engine.h" #include "embed.h" #include "GuiApplication.h" +#include "InstrumentTrack.h" +#include "Note.h" +#include "Pattern.h" #include "SampleTrack.h" +#include "Song.h" #include "SongEditor.h" #include "StringPairDrag.h" #include "TextFloat.h" @@ -937,9 +944,11 @@ void TrackContentObjectView::mouseReleaseEvent( QMouseEvent * me ) */ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) { + QVector selectedTCOs = getClickedTCOs(); + // Depending on whether we right-clicked a selection or an individual TCO we will have // different labels for the actions. - bool individualTCO = getClickedTCOs().size() <= 1; + bool individualTCO = selectedTCOs.size() <= 1; if( cme->modifiers() ) { @@ -965,6 +974,15 @@ void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme ) ? tr("Cut") : tr("Cut selection"), [this](){ contextMenuAction( Cut ); } ); + + if (canMergeSelection(selectedTCOs)) + { + contextMenu.addAction( + embed::getIconPixmap("edit_merge"), + tr("Merge Selection"), + [this]() { contextMenuAction(Merge); } + ); + } } contextMenu.addAction( @@ -1023,6 +1041,9 @@ void TrackContentObjectView::contextMenuAction( ContextMenuAction action ) case Mute: toggleMute( active ); break; + case Merge: + mergeTCOs(active); + break; } } @@ -1106,6 +1127,100 @@ void TrackContentObjectView::toggleMute( QVector tcovs } } +bool TrackContentObjectView::canMergeSelection(QVector tcovs) +{ + // Can't merge a single TCO + if (tcovs.size() < 2) { return false; } + + // We check if the owner of the first TCO is an Instrument Track + bool isInstrumentTrack = dynamic_cast(tcovs.at(0)->getTrackView()); + + // Then we create a set with all the TCOs owners + std::set ownerTracks; + for (auto tcov: tcovs) { ownerTracks.insert(tcov->getTrackView()); } + + // Can merge if there's only one owner track and it's an Instrument Track + return isInstrumentTrack && ownerTracks.size() == 1; +} + +void TrackContentObjectView::mergeTCOs(QVector tcovs) +{ + // Get the track that we are merging TCOs in + InstrumentTrack* track = + dynamic_cast(tcovs.at(0)->getTrackView()->getTrack()); + + if (!track) + { + qWarning("Warning: Couldn't retrieve InstrumentTrack in mergeTCOs()"); + return; + } + + // For Undo/Redo + track->addJournalCheckPoint(); + track->saveJournallingState(false); + + // Find the earliest position of all the selected TCOVs + const auto earliestTCOV = std::min_element(tcovs.constBegin(), tcovs.constEnd(), + [](TrackContentObjectView* a, TrackContentObjectView* b) + { + return a->getTrackContentObject()->startPosition() < + b->getTrackContentObject()->startPosition(); + } + ); + + const TimePos earliestPos = (*earliestTCOV)->getTrackContentObject()->startPosition(); + + // Create a pattern where all notes will be added + Pattern* newPattern = dynamic_cast(track->createTCO(earliestPos)); + if (!newPattern) + { + qWarning("Warning: Failed to convert TCO to Pattern on mergeTCOs"); + return; + } + + newPattern->saveJournallingState(false); + + // Add the notes and remove the TCOs that are being merged + for (auto tcov: tcovs) + { + // Convert TCOV to PatternView + PatternView* pView = dynamic_cast(tcov); + + if (!pView) + { + qWarning("Warning: Non-pattern TCO on InstrumentTrack"); + continue; + } + + NoteVector currentTCONotes = pView->getPattern()->notes(); + TimePos pViewPos = pView->getPattern()->startPosition(); + + for (Note* note: currentTCONotes) + { + Note* newNote = newPattern->addNote(*note, false); + TimePos originalNotePos = newNote->pos(); + newNote->setPos(originalNotePos + (pViewPos - earliestPos)); + } + + // We disable the journalling system before removing, so the + // removal doesn't get added to the undo/redo history + tcov->getTrackContentObject()->saveJournallingState(false); + // No need to check for nullptr because we check while building the tcovs QVector + tcov->remove(); + } + + // Update length since we might have moved notes beyond the end of the pattern length + newPattern->updateLength(); + // Rearrange notes because we might have moved them + newPattern->rearrangeAllNotes(); + // Restore journalling states now that the operation is finished + newPattern->restoreJournallingState(); + track->restoreJournallingState(); + // Update song + Engine::getSong()->setModified(); + gui->songEditor()->update(); +} + diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index ded78ffa8c6..659a1919c19 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -627,6 +627,17 @@ PatternView::PatternView( Pattern* pattern, TrackView* parent ) : setStyle( QApplication::style() ); } + + + +Pattern* PatternView::getPattern() +{ + return m_pat; +} + + + + void PatternView::update() { ToolTip::add(this, m_pat->name());