diff --git a/CHANGELOG b/CHANGELOG index 38ae816..aacaca8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ External contributors notice at the end of the line: (LastName, FirstName / nick New function: custom key signature for any combination. Add area in the GUI to set initial key signature, metrical instructions and clefs, accessed through the track editor Add two new functions to paste directly transposed (modal and real transposition) +Add function to duplicate more often than once. When not in F4-StepInput Mode use midi keyboard as pitch-cursor. Add midi-in selector drop down, as seen in Tembro and Fluajho. Add new widget that shows the current track as list of text items for easier navigation and overview diff --git a/engine/api.py b/engine/api.py index 665c5c3..fad5b31 100644 --- a/engine/api.py +++ b/engine/api.py @@ -576,7 +576,7 @@ def pasteObjectsTransposedModal(root:int=None, toPitch:int=None): pasteObjectsTransposedReal(root, toPitch, adjustToKeySignature=True) -def duplicate(): #ctrl+d +def duplicate(howOften:int): #ctrl+d """Duplicate a single object and put it right of the original. The cursor moves with it to enable follow up insertion. @@ -589,14 +589,20 @@ def duplicate(): #ctrl+d if customBuffer: #success session.data.goToSelectionStart() pos = session.data.where() #where even keeps the local position if a content linked block inserts items before our position - pasteObjects(customBuffer = customBuffer, updateCursor = False, overwriteSelection = False) #handles undo - session.data.goTo(*pos) + with session.history.sequence("duplicate selection"): + for i in range(howOften): + pasteObjects(customBuffer = customBuffer, updateCursor = False, overwriteSelection = False) #handles undo + session.data.goTo(*pos) callbacks._setCursor(destroySelection=False) else: item = session.data.currentItem() if item: - insertItem(item.copy()) + with session.history.sequence("duplicate item"): + for i in range(howOften): + insertItem(item.copy()) callbacks._setCursor() + + #Score def transposeScore(rootPitch, targetPitch): """Based on automatic transpose. The interval is caculated from two pitches. diff --git a/qtgui/designer/mainwindow.py b/qtgui/designer/mainwindow.py index 0e4f2a7..daa0499 100644 --- a/qtgui/designer/mainwindow.py +++ b/qtgui/designer/mainwindow.py @@ -691,6 +691,8 @@ class Ui_MainWindow(object): self.actionCustom_Key_Signature.setObjectName("actionCustom_Key_Signature") self.actionCustom_Metrical_Instruction = QtWidgets.QAction(MainWindow) self.actionCustom_Metrical_Instruction.setObjectName("actionCustom_Metrical_Instruction") + self.actionDuplicateItem_more = QtWidgets.QAction(MainWindow) + self.actionDuplicateItem_more.setObjectName("actionDuplicateItem_more") self.menuObjects.addAction(self.actionMetrical_Instruction) self.menuObjects.addAction(self.actionCustom_Metrical_Instruction) self.menuObjects.addAction(self.actionClef) @@ -859,6 +861,7 @@ class Ui_MainWindow(object): self.menuEdit_2.addAction(self.actionPaste_real_transposed) self.menuEdit_2.addAction(self.actionCut) self.menuEdit_2.addAction(self.actionDuplicateItem) + self.menuEdit_2.addAction(self.actionDuplicateItem_more) self.menuOrder.addAction(self.actionRandom) self.menuOrder.addAction(self.actionReverse) self.menuOrder.addAction(self.actionAscending) @@ -1054,3 +1057,5 @@ class Ui_MainWindow(object): self.actionCustom_Key_Signature.setShortcut(_translate("MainWindow", "Alt+K")) self.actionCustom_Metrical_Instruction.setText(_translate("MainWindow", "Custom Metrical Instruction")) self.actionCustom_Metrical_Instruction.setShortcut(_translate("MainWindow", "Alt+M")) + self.actionDuplicateItem_more.setText(_translate("MainWindow", "Duplicate more")) + self.actionDuplicateItem_more.setShortcut(_translate("MainWindow", "Ctrl+Shift+D")) diff --git a/qtgui/designer/mainwindow.ui b/qtgui/designer/mainwindow.ui index c894217..def747e 100644 --- a/qtgui/designer/mainwindow.ui +++ b/qtgui/designer/mainwindow.ui @@ -285,6 +285,7 @@ + @@ -1852,6 +1853,14 @@ Alt+M + + + Duplicate more + + + Ctrl+Shift+D + + diff --git a/qtgui/menu.py b/qtgui/menu.py index f0c21d2..ba12c80 100644 --- a/qtgui/menu.py +++ b/qtgui/menu.py @@ -37,7 +37,7 @@ from template.qtgui.midiinquickwidget import QuickMidiInputComboController import engine.api as api from engine.midiinput.stepmidiinput import stepMidiInput #singleton instance from .constantsAndConfigs import constantsAndConfigs -from .submenus import SecondaryClefMenu, SecondaryKeySignatureMenu, SecondaryDynamicsMenu, SecondaryMetricalInstructionMenu, SecondaryTempoChangeMenu, SecondaryTemporaryTempoChangeMenu, SecondarySplitMenu, TransposeMenu, pedalNoteChooser, SecondaryProperties, SecondaryProgramChangeMenu, SecondaryChannelChangeMenu, ChooseOne, forwardText, SecondaryMultimeasureRestMenu +from .submenus import SecondaryClefMenu, SecondaryKeySignatureMenu, SecondaryDynamicsMenu, SecondaryMetricalInstructionMenu, SecondaryTempoChangeMenu, SecondaryTemporaryTempoChangeMenu, SecondarySplitMenu, TransposeMenu, pedalNoteChooser, SecondaryProperties, SecondaryProgramChangeMenu, SecondaryChannelChangeMenu, ChooseOne, forwardText, SecondaryMultimeasureRestMenu, SecondaryDuplicateMenu from .customkeysignature import CustomKeySignatureWidget from .custommetricalinstruction import CustomMetricalInstructionWidget @@ -292,7 +292,8 @@ class MenuActionDatabase(object): self.mainWindow.ui.actionPaste : api.pasteObjects, self.mainWindow.ui.actionPaste_modal_transposed : api.pasteObjectsTransposedModal, self.mainWindow.ui.actionPaste_real_transposed : api.pasteObjectsTransposedReal, - self.mainWindow.ui.actionDuplicateItem : api.duplicate, + self.mainWindow.ui.actionDuplicateItem : lambda: api.duplicate(howOften=1), + self.mainWindow.ui.actionDuplicateItem_more : SecondaryDuplicateMenu(self.mainWindow), #no lambda for submenus. They get created here once and have a __call__ option that executes them. self.mainWindow.ui.actionUndo : api.undo, self.mainWindow.ui.actionRedo : api.redo, diff --git a/qtgui/submenus.py b/qtgui/submenus.py index b057e74..615b8c4 100644 --- a/qtgui/submenus.py +++ b/qtgui/submenus.py @@ -232,6 +232,30 @@ class SecondarySplitMenu(Submenu): button.clicked.connect(function) button.clicked.connect(self.done) +class SecondaryDuplicateMenu(Submenu): + + + duplicates = [("[1]", lambda: api.duplicate(1)), + ("[2]", lambda: api.duplicate(2)), + ("[3]", lambda: api.duplicate(3)), + ("[4]", lambda: api.duplicate(4)), + ("[5]", lambda: api.duplicate(5)), + ("[6]", lambda: api.duplicate(6)), + ("[7]", lambda: api.duplicate(7)), + ("[8]", lambda: api.duplicate(8)), + ("[9]", lambda: api.duplicate(9)), + ] + + def __init__(self, mainWindow): + super().__init__(mainWindow, translate("submenus", "duplicate how often?"), hasOkCancelButtons=2) + + for number, (prettyname, function) in enumerate(SecondaryDuplicateMenu.duplicates): + button = QtWidgets.QPushButton(prettyname) + button.setShortcut(QtGui.QKeySequence(str(number+1))) #+1 for enumerate from 0, +1 we start at 1. + self.layout.addWidget(button) + button.clicked.connect(function) + button.clicked.connect(self.done) + class SecondaryKeySignatureMenu(Submenu): def __init__(self, mainWindow): """Init is only called once per program, during startup"""