From a4b4cf1e285c59adac1950602c46388c053fc5f0 Mon Sep 17 00:00:00 2001 From: regulus79 <117475203+regulus79@users.noreply.github.com> Date: Sun, 16 Feb 2025 19:47:36 -0500 Subject: [PATCH] Initial Implementation --- include/PianoRoll.h | 1 + src/gui/editors/PianoRoll.cpp | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/include/PianoRoll.h b/include/PianoRoll.h index e9939101c3f..f100ef87920 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -247,6 +247,7 @@ protected slots: void clearGhostClip(); void glueNotes(); void fitNoteLengths(bool fill); + void invertSelection(bool up); void constrainNoteLengths(bool constrainMax); void changeSnapMode(); diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index cbd68e7ff4f..562bf9fb732 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -765,6 +765,42 @@ void PianoRoll::fitNoteLengths(bool fill) Engine::getSong()->setModified(); } +void PianoRoll::invertSelection(bool up) +{ + if (!hasValidMidiClip()) { return; } + m_midiClip->addJournalCheckPoint(); + + // Notes to edit + const NoteVector selectedNotes = getSelectedNotes(); + const auto& notes = selectedNotes.empty() ? m_midiClip->notes() : selectedNotes; + + const auto [minNote, maxNote] = std::minmax_element(notes.begin(), notes.end(), [](Note* n1, Note* n2) { return n1->key() < n2->key(); }); + const int minKey = (*minNote)->key(); + const int maxKey = (*maxNote)->key(); + + const auto microtuner = m_midiClip->instrumentTrack()->microtuner(); + const int octaveSize = microtuner->enabled() ? microtuner->octaveSize() : 12; + const int numOctaves = (maxKey - minKey) / octaveSize + 1; + + for (Note* note : notes) + { + if (up) + { + if (note->key() == minKey) + { + note->setKey(minKey + numOctaves * octaveSize); + } + } + else + { + if (note->key() == maxKey) + { + note->setKey(maxKey - numOctaves * octaveSize); + } + } + } +} + void PianoRoll::constrainNoteLengths(bool constrainMax) { @@ -4869,12 +4905,22 @@ PianoRollWindow::PianoRollWindow() : auto maxLengthAction = new QAction(embed::getIconPixmap("max_length"), tr("Max length as last"), noteToolsButton); connect(maxLengthAction, &QAction::triggered, [this](){ m_editor->constrainNoteLengths(true); }); + auto upwardInversionAction = new QAction(embed::getIconPixmap("flip_y"), tr("Upward Inversion"), noteToolsButton); + connect(upwardInversionAction, &QAction::triggered, [this](){ m_editor->invertSelection(true); }); + upwardInversionAction->setShortcut(combine(Qt::SHIFT, Qt::Key_I)); + + auto downwardInversionAction = new QAction(embed::getIconPixmap("flip_y"), tr("Downward Inversion"), noteToolsButton); + connect(downwardInversionAction, &QAction::triggered, [this](){ m_editor->invertSelection(false); }); + downwardInversionAction->setShortcut(combine(Qt::SHIFT, Qt::Key_U)); + noteToolsButton->addAction(glueAction); noteToolsButton->addAction(knifeAction); noteToolsButton->addAction(fillAction); noteToolsButton->addAction(cutOverlapsAction); noteToolsButton->addAction(minLengthAction); noteToolsButton->addAction(maxLengthAction); + noteToolsButton->addAction(upwardInversionAction); + noteToolsButton->addAction(downwardInversionAction); notesActionsToolBar->addWidget(noteToolsButton);