From acd0e4d4309e6bf997b71de9d80cd25914d29883 Mon Sep 17 00:00:00 2001 From: Oskar Wallgren Date: Mon, 19 Oct 2020 22:18:21 +0200 Subject: [PATCH] Feature: Glue notes (#5721) Tool to glue selected notes together. Selected notes that are adjacent to each other or overlapping are transformed into one note stretching over the combined notes on/off times. Key command: + G Partially fixes: #746 which is part of #4877 Co-authored-by: Hyunjin Song Co-authored-by: IanCaio Co-authored-by: Spekular Co-authored-by: Kevin Zander Co-authored-by: Oskar Wallgren --- data/themes/default/glue.png | Bin 0 -> 407 bytes data/themes/default/tool.png | Bin 0 -> 466 bytes include/PianoRoll.h | 1 + src/gui/editors/PianoRoll.cpp | 83 ++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+) create mode 100644 data/themes/default/glue.png create mode 100644 data/themes/default/tool.png diff --git a/data/themes/default/glue.png b/data/themes/default/glue.png new file mode 100644 index 0000000000000000000000000000000000000000..de611b4bc34e7acdac92bd8117304971f08dc40f GIT binary patch literal 407 zcmV;I0cie-P)4!gYOmB-F?4z_kGV#ujlD`K2Jmsp@dy5^x`z4{W$X5 z8>aF=M{(nSj~MC&ox+pmouJqaI)|4QXuWEu=rX=iP^e)s)3LE05nB;a%CNi;5g%z_ z5!)F~S??7qX=nup9iV5JNJB?(nGyWPhAh*7)r{yZrc=R&yS;7$*06(0W;B7v@X!K% z#(YO$f)3+8K~9r9thbNBZqNZ-;S*~pV9D=qYd=P9(0-ib0+Wp!c8ivAh$_lmAx+S6 zY+^PS@DF^PJj_hq@OuIu=IQoD#9l002ovPDHLkV1k+} BqZR-F literal 0 HcmV?d00001 diff --git a/data/themes/default/tool.png b/data/themes/default/tool.png new file mode 100644 index 0000000000000000000000000000000000000000..6d818a53efc2acb81a295ebecaa6987068fa1ba2 GIT binary patch literal 466 zcmV;@0WJQCP)>W#1sZa@v}H1jwf^A%$)qyY}V#u?Y$S1 zvX@TCg+91}HrriWVCGnMZXNse`) z*OF{ER&mw;*}-^OyNRFp&=eiPvcuWJVrBif33?psHE4JCak^jhB%bwwzQb&i6n&sm zNj8(5sVT1{d6Lc}zDlxD6vg*`Iz5N?J-S;$^QqFA*S#9>H|{kCOLQIwHRvzgYKz{` znZ@pW!~vfC4`>z4;Wfs}D_O^Cd6L0`>-dbfc!=SF(F%SS3rFSs0pbo5)?qZQrvLx|07*qo IM6N<$f-z>s4gdfE literal 0 HcmV?d00001 diff --git a/include/PianoRoll.h b/include/PianoRoll.h index e3e4b312085..a876e03a013 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -208,6 +208,7 @@ protected slots: void selectRegionFromPixels( int xStart, int xEnd ); void clearGhostPattern(); + void glueNotes(); signals: diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 8f1a827a95e..27198ce8b19 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #ifndef __USE_XOPEN #define __USE_XOPEN @@ -644,6 +645,73 @@ void PianoRoll::clearGhostPattern() } +void PianoRoll::glueNotes() +{ + if (hasValidPattern()) + { + NoteVector selectedNotes = getSelectedNotes(); + if (selectedNotes.empty()) + { + TextFloat::displayMessage( tr( "Glue notes failed" ), + tr( "Please select notes to glue first." ), + embed::getIconPixmap( "glue", 24, 24 ), + 3000 ); + return; + } + + // Make undo possible + m_pattern->addJournalCheckPoint(); + + // Sort notes on key and then pos. + std::sort(selectedNotes.begin(), selectedNotes.end(), + [](const Note * note, const Note * compareNote) -> bool + { + if (note->key() == compareNote->key()) + { + return note->pos() < compareNote->pos(); + } + return note->key() < compareNote->key(); + }); + + QList noteToRemove; + + NoteVector::iterator note = selectedNotes.begin(); + auto nextNote = note+1; + NoteVector::iterator end = selectedNotes.end(); + + while (note != end && nextNote != end) + { + // key and position match for glue. The notes are already + // sorted so we don't need to test that nextNote is the same + // position or next in sequence. + if ((*note)->key() == (*nextNote)->key() + && (*nextNote)->pos() <= (*note)->pos() + + qMax(MidiTime(0), (*note)->length())) + { + (*note)->setLength(qMax((*note)->length(), + MidiTime((*nextNote)->endPos() - (*note)->pos()))); + noteToRemove.push_back(*nextNote); + ++nextNote; + } + // key or position doesn't match + else + { + note = nextNote; + nextNote = note+1; + } + } + + // Remove old notes + for (int i = 0; i < noteToRemove.count(); ++i) + { + m_pattern->removeNote(noteToRemove[i]); + } + + update(); + } +} + + void PianoRoll::loadMarkedSemiTones(const QDomElement & de) { // clear marked semitones to prevent leftover marks @@ -4361,6 +4429,21 @@ PianoRollWindow::PianoRollWindow() : DropToolBar *timeLineToolBar = addDropToolBarToTop( tr( "Timeline controls" ) ); m_editor->m_timeLine->addToolButtons( timeLineToolBar ); + // -- Note modifier tools + // Toolbar + QToolButton * noteToolsButton = new QToolButton(m_toolBar); + noteToolsButton->setIcon(embed::getIconPixmap("tool")); + noteToolsButton->setPopupMode(QToolButton::InstantPopup); + + // Glue + QAction * glueAction = new QAction(embed::getIconPixmap("glue"), + tr("Glue"), noteToolsButton); + connect(glueAction, SIGNAL(triggered()), m_editor, SLOT(glueNotes())); + glueAction->setShortcut( Qt::SHIFT | Qt::Key_G ); + + noteToolsButton->addAction(glueAction); + + notesActionsToolBar->addWidget(noteToolsButton); addToolBarBreak();