diff --git a/app/gui/CMakeLists.txt b/app/gui/CMakeLists.txt index e80f5d870b..4d0dad2da8 100644 --- a/app/gui/CMakeLists.txt +++ b/app/gui/CMakeLists.txt @@ -95,6 +95,8 @@ set(QT_SOURCES ${QTAPP_ROOT}/mainwindow.h ${QTAPP_ROOT}/model/sonicpitheme.cpp ${QTAPP_ROOT}/model/sonicpitheme.h + ${QTAPP_ROOT}/model/sonicpi_shortcuts.cpp + ${QTAPP_ROOT}/model/sonicpi_shortcuts.h ${QTAPP_ROOT}/widgets/infowidget.h ${QTAPP_ROOT}/widgets/settingswidget.h ) diff --git a/app/gui/mainwindow.cpp b/app/gui/mainwindow.cpp index 206a06f18d..d25b6803f5 100644 --- a/app/gui/mainwindow.cpp +++ b/app/gui/mainwindow.cpp @@ -14,6 +14,7 @@ // Standard stuff #include #include +#include #include // Qt stuff @@ -56,6 +57,7 @@ #include #include "model/sonicpitheme.h" +#include "model/sonicpi_shortcuts.h" #include "utils/scintilla_api.h" #include "widgets/sonicpilexer.h" #include "widgets/sonicpiscintilla.h" @@ -205,9 +207,11 @@ MainWindow::MainWindow(QApplication& app, QSplashScreen* splash) QPalette p = theme->createPalette(); QApplication::setPalette(p); + this->sonicPiShortcuts = new SonicPiShortcuts(sonicPiConfigPath()); + sonicPiShortcuts->loadUserShortcuts(); + setupWindowStructure(); - loadUserShortcuts(); createStatusBar(); createInfoPane(); setWindowTitle(tr("Sonic Pi")); @@ -436,7 +440,7 @@ void MainWindow::setupWindowStructure() prefsWidget->setParent(this); prefsWidget->hide(); - settingsWidget = new SettingsWidget(m_spAPI->GetPort(SonicPiPortId::tau_osc_cues), i18n, piSettings, sonicPii18n, this); + settingsWidget = new SettingsWidget(m_spAPI->GetPort(SonicPiPortId::tau_osc_cues), i18n, piSettings, sonicPiShortcuts, sonicPii18n, this); settingsWidget->setObjectName("settings"); settingsWidget->setAttribute(Qt::WA_StyledBackground, true); connect(settingsWidget, SIGNAL(restartApp()), this, SLOT(restartApp())); @@ -1697,6 +1701,7 @@ void MainWindow::saveWorkspaces() void MainWindow::closeEvent(QCloseEvent* event) { writeSettings(); + sonicPiShortcuts->writeUserShortcuts(); event->accept(); } @@ -2802,145 +2807,9 @@ QString MainWindow::tooltipStrMeta(const QString& key, const QString& str) #endif } -void MainWindow::updateAction(QAction* action, const QString& desc) -{ - QString shortcutDesc = action->shortcut().toString(QKeySequence::PortableText); - action->setToolTip(desc + " (" + shortcutDesc + ")"); - action->setText(action->iconText()); - action->setStatusTip(desc + " (" + shortcutDesc + ")"); -} - -QKeySequence MainWindow::resolveShortcut(QString keySequence) -{ - keySequence = keySequence.toLower().trimmed(); - - if (keySequence.startsWith("shiftmeta+")) - { - return shiftMetaKey(keySequence.mid(10)); - } - else if (keySequence.startsWith("metashift+")) - { - return shiftMetaKey(keySequence.mid(10)); - } - else if (keySequence.startsWith("ctrlmeta+")) - { - return ctrlMetaKey(keySequence.mid(9)); - } - else if (keySequence.startsWith("metactrl+")) - { - return ctrlMetaKey(keySequence.mid(9)); - } - else if (keySequence.startsWith("ctrlshift+")) - { - return ctrlShiftKey(keySequence.mid(10)); - } - else if (keySequence.startsWith("shiftctrl+")) - { - return ctrlShiftKey(keySequence.mid(10)); - // } else if (keySequence.startsWith("altshift")) { - // QChar key = keySequence.mid(9, 1).at(0); - // return shiftAltKey(key.toLatin1()); - // } else if (keySequence.startsWith("shiftalt")) { - // QChar key = keySequence.mid(9, 1).at(0); - // return shiftAltKey(key.toLatin1()); - } - else if (keySequence.startsWith("meta+")) - { - return metaKey(keySequence.mid(5)); - // } else if (keySequence.startsWith("alt")) { - // QChar key = keySequence.mid(5, 1).at(0); - // return altKey(key.toLatin1()); - } - else if (keySequence.startsWith("ctrl+")) - { - return ctrlKey(keySequence.mid(5)); - } - else - { - return QKeySequence(keySequence); // Default case: if it’s a standard sequence like "Alt+Space" - } -} - -void MainWindow::updateShortcut(const QString& id, QAction* action, const QString& desc) -{ - action->setShortcut(shortcutMap[id]); - updateAction(action, desc); -} - -void MainWindow::resetShortcuts() -{ - shortcutMap.clear(); -} - -void MainWindow::loadUserShortcut(const QString& id, QSettings& shortcut_settings) -{ - shortcutMap[id] = resolveShortcut(shortcut_settings.value(id, "").toString()); -} - -void MainWindow::loadUserShortcuts() -{ - QString shortcuts_path = sonicPiConfigPath() + QDir::separator() + "keyboard-shortcuts.ini"; - QFile shortcutFile(shortcuts_path); - QString base = "none"; - - QSettings* shortcut_settings; // Pointer to QSettings - - // Check if the file exists before proceeding - if (shortcutFile.exists()) - { - shortcut_settings = new QSettings(shortcuts_path, QSettings::IniFormat); - base = shortcut_settings->value("base", "none").toString().toLower(); - } - else - { - // Create default QSettings in memory - qDebug() << "Shortcut file not found, using default shortcuts."; - shortcut_settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, "defaultOrganization", "defaultApplication"); - } - - // Determine which shortcuts to load based on the 'base' value - if (base == "none") - { - // don't load any shortcuts - } - else if (base == "mac") - { - loadMacShortcuts(); - } - else if (base == "win") - { - loadWinShortcuts(); - } - else - { - // default - loadEmacsShortcuts(); - } - - // List of shortcut IDs - QStringList ids = { "Run", "Stop", "Record", "Save", "Load", "Align", "Comment", "Transpose", "ShiftUp", "ShiftDown", - "ContextualDocs", "TextZoomIn", "TextZoomOut", "Scope", "CycleThemes", "Info", "Help", "Prefs", - "TabPrev", "TabNext", "Tab1", "Tab2", "Tab3", "Tab4", "Tab5", "Tab6", "Tab7", "Tab8", "Tab9", "Tab0", - "Link", "TapTempo", "FocusEditor", "FocusLogs", "FocusContext", "FocusCues", "FocusPrefs", "FocusHelpListing", - "FocusHelpDetails", "FocusErrors", "FocusBPMScrubber", "FocusTimeWarpScrubber", "ShowButtons", "ShowCueLog", - "ShowLog", "SetMark", "logZoomIn", "logZoomOut", "Down", "Up", "UpTen", "DownTen", "CutToEnd", "Copy", "Cut", - "Paste", "Right", "Left", "DeleteForward", "DeleteBackward", "LineStart", "LineEnd", "DocStart", "DocEnd", - "WordRight", "WordLeft", "CenterVertically", "Undo", "Redo", "SelectAll", "DeleteWordRight", "DeleteWordLeft", - "UpcaseWord", "DowncaseWord" }; - - // Load user shortcuts - for (const QString& id : ids) - { - loadUserShortcut(id, *shortcut_settings); // Dereference the pointer to pass QSettings object - } - - // Clean up the dynamically allocated QSettings object - delete shortcut_settings; -} - void MainWindow::updateShortcuts() { - resetShortcuts(); + sonicPiShortcuts->resetShortcuts(); QSignalBlocker macShortcutBlocker(macShortcutModeAct); macShortcutModeAct->setChecked(false); QSignalBlocker winShortcutBlocker(winShortcutModeAct); @@ -2953,341 +2822,116 @@ void MainWindow::updateShortcuts() if (piSettings->shortcut_mode == 2) { winShortcutModeAct->setChecked(true); - loadWinShortcuts(); + sonicPiShortcuts->loadDefaultShortcuts(DefaultShortcutSet::WINDOWS); } else if (piSettings->shortcut_mode == 3) { macShortcutModeAct->setChecked(true); - - loadMacShortcuts(); + sonicPiShortcuts->loadDefaultShortcuts(DefaultShortcutSet::MACOS); } else if (piSettings->shortcut_mode == 4) { userShortcutModeAct->setChecked(true); - loadUserShortcuts(); + sonicPiShortcuts->loadUserShortcuts(); } else { piSettings->shortcut_mode = 1; emacsShortcutModeAct->setChecked(true); - loadEmacsShortcuts(); - } - - updateShortcut("Run", runAct, tr("Run the code in the current buffer")); - updateShortcut("Stop", stopAct, tr("Stop all running code")); - updateShortcut("Record", recAct, tr("Start recording to a WAV audio file")); - updateShortcut("Save", saveAsAct, tr("Save current buffer as an external file")); - updateShortcut("Load", loadFileAct, tr("Load an external file in the current buffer")); - updateShortcut("Align", textAlignAct, tr("Align code to improve readability")); - updateShortcut("Comment", textCommentAct, tr("Comment/Uncomment code")); - updateShortcut("Transpose", textTransposeAct, tr("Transpose Characters")); - updateShortcut("ShiftUp", textShiftLineUpAct, tr("Shift Line or Selection Up")); - updateShortcut("ShiftDown", textShiftLineDownAct, tr("Shift Line or Selection Down")); - updateShortcut("Down", textDownAct, tr("Move Cursor Down")); - updateShortcut("Up", textUpAct, tr("Move Cursor Up")); - updateShortcut("DownTen", textDownTenAct, tr("Move Cursor Down 10 Lines")); - updateShortcut("UpTen", textUpTenAct, tr("Move Cursor Up 10 Lines")); - updateShortcut("CutToEnd", textCutToEndOfLineAct, tr("Cut to the end of the line")); - updateShortcut("Copy", textCopyAct, tr("Copy the current selection")); - updateShortcut("Cut", textCutAct, tr("Cut the current selection")); - updateShortcut("Paste", textPasteAct, tr("Paste the current selection")); - updateShortcut("Right", textRightAct, tr("Move Cursor Right")); - updateShortcut("Left", textLeftAct, tr("Move Cursor Left")); - updateShortcut("DeleteForward", textDeleteForwardAct, tr("Delete Right")); - updateShortcut("DeleteBackward", textDeleteBackAct, tr("Delete Left")); - updateShortcut("LineStart", textLineStartAct, tr("Move Cursor to Start of Line")); - updateShortcut("LineEnd", textLineEndAct, tr("Move Cursor to End of Line")); - updateShortcut("DocStart", textDocStartAct, tr("Move Cursor to Start of Document")); - updateShortcut("DocEnd", textDocEndAct, tr("Move Cursor to End of Document")); - updateShortcut("WordRight", textWordRightAct, tr("Move Cursor Right by Word")); - updateShortcut("WordLeft", textWordLeftAct, tr("Move Cursor Left by Word")); - updateShortcut("CenterVertically", textCenterCaretAct, tr("Vertially center the caret in the editor")); - updateShortcut("Undo", textUndoAct, tr("Undo the last action")); - updateShortcut("Redo", textRedoAct, tr("Redo the last undo")); - updateShortcut("SelectAll", textSelectAllAct, tr("Select all text")); - updateShortcut("DeleteWordRight", textDeleteWordRightAct, tr("Delete word to the right")); - updateShortcut("DeleteWordLeft", textDeleteWordLeftAct, tr("Delete word to the left")); - updateShortcut("UpcaseWord", textUpcaseWordAct, tr("Uppercase word or selection")); - updateShortcut("DowncaseWord", textDowncaseWordAct, tr("Lowercase word or selection")); - - updateShortcut("SetMark", textSetMarkAct, tr("Set a mark in the text")); - - updateShortcut("ContextualDocs", contextHelpAct, tr("Look up documentation for the current word")); - updateShortcut("TextZoomIn", textIncAct, tr("Increase Text Size")); - updateShortcut("TextZoomOut", textDecAct, tr("Decrease Text Size")); - updateShortcut("Scope", scopeAct, tr("Toggle visibility of audio oscilloscope")); - updateShortcut("CycleThemes", cycleThemesAct, tr("Cycle through the available colour themes")); - updateShortcut("Info", infoAct, tr("Toggle information about Sonic Pi")); - updateShortcut("Help", helpAct, tr("Toggle the visibility of the help pane")); - updateShortcut("Prefs", prefsAct, tr("Toggle the visibility of the preferences pane")); - updateShortcut("TabPrev", tabPrevAct, tr("Switch to the previous tab")); - updateShortcut("TabNext", tabNextAct, tr("Switch to the next tab")); - updateShortcut("Tab1", tab1Act, tr("Switch to tab 1")); - updateShortcut("Tab2", tab2Act, tr("Switch to tab 2")); - updateShortcut("Tab3", tab3Act, tr("Switch to tab 3")); - updateShortcut("Tab4", tab4Act, tr("Switch to tab 4")); - updateShortcut("Tab5", tab5Act, tr("Switch to tab 5")); - updateShortcut("Tab6", tab6Act, tr("Switch to tab 6")); - updateShortcut("Tab7", tab7Act, tr("Switch to tab 7")); - updateShortcut("Tab8", tab8Act, tr("Switch to tab 8")); - updateShortcut("Tab9", tab9Act, tr("Switch to tab 9")); - updateShortcut("Tab0", tab0Act, tr("Switch to tab 0")); - updateShortcut("Link", enableLinkAct, tr("Connect or disconnect the Link Metronome from the network")); - updateShortcut("TapTempo", linkTapTempoAct, tr("Click Link Tap Tempo")); - updateShortcut("FocusEditor", focusEditorAct, tr("Place focus on the code editor")); - updateShortcut("FocusLogs", focusLogsAct, tr("Place focus on the logs")); - updateShortcut("FocusContext", focusContextAct, tr("Place focus on the context pane")); - updateShortcut("FocusCues", focusCuesAct, tr("Place focus on the cue event pane")); - updateShortcut("FocusPrefs", focusPreferencesAct, tr("Place focus on preferences")); - updateShortcut("FocusHelpListing", focusHelpListingAct, tr("Place focus on help listing")); - updateShortcut("FocusHelpDetails", focusHelpDetailsAct, tr("Place focus on help details")); - updateShortcut("FocusErrors", focusErrorsAct, tr("Place focus on errors")); - updateShortcut("FocusBPMScrubber", focusBPMScrubberAct, tr("Place focus on BPM Scrubber")); - updateShortcut("FocusTimeWarpScrubber", focusTimeWarpScrubberAct, tr("Place focus on TimeWarp Scrubber")); - updateShortcut("ShowButtons", showButtonsAct, tr("Show or hide the buttons")); - updateShortcut("ShowCueLog", showCuesAct, tr("Show or hide the cue log")); - updateShortcut("ShowLog", showLogAct, tr("Show or hide the log")); - updateShortcut("LogZoomIn", logZoomInAct, tr("Zoom in the log")); - updateShortcut("LogZoomOut", logZoomOutAct, tr("Zoom out the log")); - updateShortcut("FullScreen", fullScreenAct, tr("Toggle fullscreen mode")); + #ifdef Q_OS_MAC + sonicPiShortcuts->loadDefaultShortcuts(DefaultShortcutSet::EMACS_MACOS); + #else + sonicPiShortcuts->loadDefaultShortcuts(DefaultShortcutSet::EMACS); + #endif + } + settingsWidget->updateShortcutsTable(); + + sonicPiShortcuts->assignToAction("Run", runAct, tr("Run the code in the current buffer")); + sonicPiShortcuts->assignToAction("Stop", stopAct, tr("Stop all running code")); + sonicPiShortcuts->assignToAction("Record", recAct, tr("Start recording to a WAV audio file")); + sonicPiShortcuts->assignToAction("Save", saveAsAct, tr("Save current buffer as an external file")); + sonicPiShortcuts->assignToAction("Load", loadFileAct, tr("Load an external file in the current buffer")); + sonicPiShortcuts->assignToAction("Align", textAlignAct, tr("Align code to improve readability")); + sonicPiShortcuts->assignToAction("Comment", textCommentAct, tr("Comment/Uncomment code")); + sonicPiShortcuts->assignToAction("Transpose", textTransposeAct, tr("Transpose Characters")); + sonicPiShortcuts->assignToAction("ShiftUp", textShiftLineUpAct, tr("Shift Line or Selection Up")); + sonicPiShortcuts->assignToAction("ShiftDown", textShiftLineDownAct, tr("Shift Line or Selection Down")); + sonicPiShortcuts->assignToAction("Down", textDownAct, tr("Move Cursor Down")); + sonicPiShortcuts->assignToAction("Up", textUpAct, tr("Move Cursor Up")); + sonicPiShortcuts->assignToAction("DownTen", textDownTenAct, tr("Move Cursor Down 10 Lines")); + sonicPiShortcuts->assignToAction("UpTen", textUpTenAct, tr("Move Cursor Up 10 Lines")); + sonicPiShortcuts->assignToAction("CutToEnd", textCutToEndOfLineAct, tr("Cut to the end of the line")); + sonicPiShortcuts->assignToAction("Copy", textCopyAct, tr("Copy the current selection")); + sonicPiShortcuts->assignToAction("Cut", textCutAct, tr("Cut the current selection")); + sonicPiShortcuts->assignToAction("Paste", textPasteAct, tr("Paste the current selection")); + sonicPiShortcuts->assignToAction("Right", textRightAct, tr("Move Cursor Right")); + sonicPiShortcuts->assignToAction("Left", textLeftAct, tr("Move Cursor Left")); + sonicPiShortcuts->assignToAction("DeleteForward", textDeleteForwardAct, tr("Delete Right")); + sonicPiShortcuts->assignToAction("DeleteBackward", textDeleteBackAct, tr("Delete Left")); + sonicPiShortcuts->assignToAction("LineStart", textLineStartAct, tr("Move Cursor to Start of Line")); + sonicPiShortcuts->assignToAction("LineEnd", textLineEndAct, tr("Move Cursor to End of Line")); + sonicPiShortcuts->assignToAction("DocStart", textDocStartAct, tr("Move Cursor to Start of Document")); + sonicPiShortcuts->assignToAction("DocEnd", textDocEndAct, tr("Move Cursor to End of Document")); + sonicPiShortcuts->assignToAction("WordRight", textWordRightAct, tr("Move Cursor Right by Word")); + sonicPiShortcuts->assignToAction("WordLeft", textWordLeftAct, tr("Move Cursor Left by Word")); + sonicPiShortcuts->assignToAction("CenterVertically", textCenterCaretAct, tr("Vertially center the caret in the editor")); + sonicPiShortcuts->assignToAction("Undo", textUndoAct, tr("Undo the last action")); + sonicPiShortcuts->assignToAction("Redo", textRedoAct, tr("Redo the last undo")); + sonicPiShortcuts->assignToAction("SelectAll", textSelectAllAct, tr("Select all text")); + sonicPiShortcuts->assignToAction("DeleteWordRight", textDeleteWordRightAct, tr("Delete word to the right")); + sonicPiShortcuts->assignToAction("DeleteWordLeft", textDeleteWordLeftAct, tr("Delete word to the left")); + sonicPiShortcuts->assignToAction("UpcaseWord", textUpcaseWordAct, tr("Uppercase word or selection")); + sonicPiShortcuts->assignToAction("DowncaseWord", textDowncaseWordAct, tr("Lowercase word or selection")); + + sonicPiShortcuts->assignToAction("SetMark", textSetMarkAct, tr("Set a mark in the text")); + + sonicPiShortcuts->assignToAction("ContextualDocs", contextHelpAct, tr("Look up documentation for the current word")); + sonicPiShortcuts->assignToAction("TextZoomIn", textIncAct, tr("Increase Text Size")); + sonicPiShortcuts->assignToAction("TextZoomOut", textDecAct, tr("Decrease Text Size")); + sonicPiShortcuts->assignToAction("Scope", scopeAct, tr("Toggle visibility of audio oscilloscope")); + sonicPiShortcuts->assignToAction("CycleThemes", cycleThemesAct, tr("Cycle through the available colour themes")); + sonicPiShortcuts->assignToAction("Info", infoAct, tr("Toggle information about Sonic Pi")); + sonicPiShortcuts->assignToAction("Help", helpAct, tr("Toggle the visibility of the help pane")); + sonicPiShortcuts->assignToAction("Prefs", prefsAct, tr("Toggle the visibility of the preferences pane")); + sonicPiShortcuts->assignToAction("TabPrev", tabPrevAct, tr("Switch to the previous tab")); + sonicPiShortcuts->assignToAction("TabNext", tabNextAct, tr("Switch to the next tab")); + sonicPiShortcuts->assignToAction("Tab1", tab1Act, tr("Switch to tab 1")); + sonicPiShortcuts->assignToAction("Tab2", tab2Act, tr("Switch to tab 2")); + sonicPiShortcuts->assignToAction("Tab3", tab3Act, tr("Switch to tab 3")); + sonicPiShortcuts->assignToAction("Tab4", tab4Act, tr("Switch to tab 4")); + sonicPiShortcuts->assignToAction("Tab5", tab5Act, tr("Switch to tab 5")); + sonicPiShortcuts->assignToAction("Tab6", tab6Act, tr("Switch to tab 6")); + sonicPiShortcuts->assignToAction("Tab7", tab7Act, tr("Switch to tab 7")); + sonicPiShortcuts->assignToAction("Tab8", tab8Act, tr("Switch to tab 8")); + sonicPiShortcuts->assignToAction("Tab9", tab9Act, tr("Switch to tab 9")); + sonicPiShortcuts->assignToAction("Tab0", tab0Act, tr("Switch to tab 0")); + sonicPiShortcuts->assignToAction("Link", enableLinkAct, tr("Connect or disconnect the Link Metronome from the network")); + sonicPiShortcuts->assignToAction("TapTempo", linkTapTempoAct, tr("Click Link Tap Tempo")); + sonicPiShortcuts->assignToAction("FocusEditor", focusEditorAct, tr("Place focus on the code editor")); + sonicPiShortcuts->assignToAction("FocusLogs", focusLogsAct, tr("Place focus on the logs")); + sonicPiShortcuts->assignToAction("FocusContext", focusContextAct, tr("Place focus on the context pane")); + sonicPiShortcuts->assignToAction("FocusCues", focusCuesAct, tr("Place focus on the cue event pane")); + sonicPiShortcuts->assignToAction("FocusPrefs", focusPreferencesAct, tr("Place focus on preferences")); + sonicPiShortcuts->assignToAction("FocusHelpListing", focusHelpListingAct, tr("Place focus on help listing")); + sonicPiShortcuts->assignToAction("FocusHelpDetails", focusHelpDetailsAct, tr("Place focus on help details")); + sonicPiShortcuts->assignToAction("FocusErrors", focusErrorsAct, tr("Place focus on errors")); + sonicPiShortcuts->assignToAction("FocusBPMScrubber", focusBPMScrubberAct, tr("Place focus on BPM Scrubber")); + sonicPiShortcuts->assignToAction("FocusTimeWarpScrubber", focusTimeWarpScrubberAct, tr("Place focus on TimeWarp Scrubber")); + sonicPiShortcuts->assignToAction("ShowButtons", showButtonsAct, tr("Show or hide the buttons")); + sonicPiShortcuts->assignToAction("ShowCueLog", showCuesAct, tr("Show or hide the cue log")); + sonicPiShortcuts->assignToAction("ShowLog", showLogAct, tr("Show or hide the log")); + sonicPiShortcuts->assignToAction("LogZoomIn", logZoomInAct, tr("Zoom in the log")); + sonicPiShortcuts->assignToAction("LogZoomOut", logZoomOutAct, tr("Zoom out the log")); + sonicPiShortcuts->assignToAction("FullScreen", fullScreenAct, tr("Toggle fullscreen mode")); + + reloadServerCodeSc->setKey(sonicPiShortcuts->getShortcut("ReloadServerCode")); + toggleFocusModeSc->setKey(sonicPiShortcuts->getShortcut("ToggleFocusMode")); + toggleScopePausedSc->setKey(sonicPiShortcuts->getShortcut("ToggleScopePaused")); + // show code context // show metronome } -void MainWindow::loadMacShortcuts() -{ - shortcutMap["Run"] = resolveShortcut("Meta+R"); - shortcutMap["Stop"] = resolveShortcut("Meta+S"); - shortcutMap["Record"] = resolveShortcut("ShiftMeta+R"); - shortcutMap["Load"] = resolveShortcut("Ctrl+O"); - shortcutMap["Align"] = resolveShortcut("Meta+M"); - shortcutMap["Comment"] = resolveShortcut("Meta+/"); - shortcutMap["Transpose"] = resolveShortcut("Ctrl+T"); - shortcutMap["ShiftUp"] = resolveShortcut("CtrlMeta+P"); - shortcutMap["ShiftDown"] = resolveShortcut("CtrlMeta+N"); - shortcutMap["ContextualDocs"] = resolveShortcut("Shift+F1"); - shortcutMap["TextZoomIn"] = resolveShortcut("Meta+="); - shortcutMap["TextZoomOut"] = resolveShortcut("Meta+-"); - shortcutMap["Scope"] = resolveShortcut("Meta+O"); - shortcutMap["CycleThemes"] = resolveShortcut("ShiftMeta+M"); - shortcutMap["Info"] = resolveShortcut("Meta+1"); - shortcutMap["Help"] = resolveShortcut("F1"); - shortcutMap["Prefs"] = resolveShortcut("Meta+p"); - shortcutMap["TabPrev"] = resolveShortcut("ShiftMeta+["); - shortcutMap["TabNext"] = resolveShortcut("ShiftMeta+]"); - shortcutMap["Tab1"] = resolveShortcut("ShiftMeta+1"); - shortcutMap["Tab2"] = resolveShortcut("ShiftMeta+2"); - shortcutMap["Tab3"] = resolveShortcut("ShiftMeta+3"); - shortcutMap["Tab4"] = resolveShortcut("ShiftMeta+4"); - shortcutMap["Tab5"] = resolveShortcut("ShiftMeta+5"); - shortcutMap["Tab6"] = resolveShortcut("ShiftMeta+6"); - shortcutMap["Tab7"] = resolveShortcut("ShiftMeta+7"); - shortcutMap["Tab8"] = resolveShortcut("ShiftMeta+8"); - shortcutMap["Tab9"] = resolveShortcut("ShiftMeta+9"); - shortcutMap["Tab0"] = resolveShortcut("ShiftMeta+0"); - shortcutMap["Link"] = resolveShortcut("Meta+t"); - shortcutMap["TapTempo"] = resolveShortcut("Shift+Return"); - shortcutMap["FocusEditor"] = resolveShortcut("CtrlShift+e"); - shortcutMap["FocusLogs"] = resolveShortcut("CtrlShift+l"); - shortcutMap["FocusContext"] = resolveShortcut("CtrlShift+t"); - shortcutMap["FocusCues"] = resolveShortcut("CtrlShift+c"); - shortcutMap["FocusPrefs"] = resolveShortcut("Meta+,"); - shortcutMap["FocusHelpListing"] = resolveShortcut("CtrlShift+h"); - shortcutMap["FocusHelpDetails"] = resolveShortcut("CtrlShift+d"); - shortcutMap["FocusErrors"] = resolveShortcut("CtrlShift+R"); - shortcutMap["FocusBPMScrubber"] = resolveShortcut("CtrlShift+b"); - shortcutMap["FocusTimeWarpScrubber"] = resolveShortcut("CtrlShift+w"); - shortcutMap["ShowButtons"] = resolveShortcut("ShiftMeta+b"); - shortcutMap["ShowCueLog"] = resolveShortcut("ShiftMeta+c"); - shortcutMap["ShowLog"] = resolveShortcut("ShiftMeta+l"); - shortcutMap["SetMark"] = resolveShortcut("Ctrl+Space"); - shortcutMap["LogZoomIn"] = resolveShortcut("Ctrl+="); - shortcutMap["LogZoomOut"] = resolveShortcut("Ctrl+-"); - shortcutMap["Down"] = resolveShortcut("Ctrl+n"); - shortcutMap["Up"] = resolveShortcut("Ctrl+p"); - shortcutMap["UpTen"] = resolveShortcut("Meta+up"); - shortcutMap["DownTen"] = resolveShortcut("Meta+down"); - shortcutMap["CutToEnd"] = resolveShortcut("Ctrl+k"); - shortcutMap["Copy"] = resolveShortcut("Meta+c"); - shortcutMap["Cut"] = resolveShortcut("Meta+x"); - shortcutMap["Paste"] = resolveShortcut("Meta+v"); - shortcutMap["Right"] = resolveShortcut("Ctrl+f"); - shortcutMap["Left"] = resolveShortcut("Ctrl+b"); - shortcutMap["DeleteForward"] = resolveShortcut("Ctrl+d"); - shortcutMap["DeleteBackward"] = resolveShortcut("Ctrl+h"); - shortcutMap["LineStart"] = resolveShortcut("Meta+Left"); - shortcutMap["LineEnd"] = resolveShortcut("Meta+Right"); - shortcutMap["DocStart"] = resolveShortcut("MetaShift+,"); - shortcutMap["DocEnd"] = resolveShortcut("MetaShift+."); - shortcutMap["WordRight"] = resolveShortcut("Alt+Right"); - shortcutMap["WordLeft"] = resolveShortcut("Alt+Left"); - shortcutMap["CenterVertically"] = resolveShortcut("Ctrl+l"); - shortcutMap["Undo"] = resolveShortcut("Meta+z"); - shortcutMap["Redo"] = resolveShortcut("ShiftMeta+z"); - shortcutMap["SelectAll"] = resolveShortcut("Meta+a"); - shortcutMap["DeleteWordRight"] = resolveShortcut("Meta+d"); - shortcutMap["DeleteWordLeft"] = resolveShortcut("Meta+Backspace"); - shortcutMap["UpcaseWord"] = resolveShortcut("Meta+u"); - shortcutMap["DowncaseWord"] = resolveShortcut("Meta+l"); - shortcutMap["FullScreen"] = resolveShortcut("ShiftMeta+f"); -} - -void MainWindow::loadWinShortcuts() -{ - shortcutMap["Run"] = resolveShortcut("Meta+R"); - shortcutMap["Stop"] = resolveShortcut("Meta+S"); - shortcutMap["Record"] = resolveShortcut("ShiftMeta+R"); - shortcutMap["Load"] = resolveShortcut("Ctrl+O"); - shortcutMap["Align"] = resolveShortcut("Meta+M"); - shortcutMap["Comment"] = resolveShortcut("Meta+/"); - shortcutMap["Transpose"] = resolveShortcut("Ctrl+T"); - shortcutMap["ShiftUp"] = resolveShortcut("CtrlMeta+P"); - shortcutMap["ShiftDown"] = resolveShortcut("CtrlMeta+N"); - shortcutMap["ContextualDocs"] = resolveShortcut("Shift+F1"); - shortcutMap["TextZoomIn"] = resolveShortcut("Ctrl++"); - shortcutMap["TextZoomOut"] = resolveShortcut("Ctrl+-"); - shortcutMap["Scope"] = resolveShortcut("Meta+O"); - shortcutMap["CycleThemes"] = resolveShortcut("ShiftMeta+M"); - shortcutMap["Info"] = resolveShortcut("Meta+1"); - shortcutMap["Help"] = resolveShortcut("F1"); - shortcutMap["Prefs"] = resolveShortcut("Meta+p"); - shortcutMap["TabPrev"] = resolveShortcut("ShiftMeta+["); - shortcutMap["TabNext"] = resolveShortcut("ShiftMeta+]"); - shortcutMap["Tab1"] = resolveShortcut("ShiftMeta+1"); - shortcutMap["Tab2"] = resolveShortcut("ShiftMeta+2"); - shortcutMap["Tab3"] = resolveShortcut("ShiftMeta+3"); - shortcutMap["Tab4"] = resolveShortcut("ShiftMeta+4"); - shortcutMap["Tab5"] = resolveShortcut("ShiftMeta+5"); - shortcutMap["Tab6"] = resolveShortcut("ShiftMeta+6"); - shortcutMap["Tab7"] = resolveShortcut("ShiftMeta+7"); - shortcutMap["Tab8"] = resolveShortcut("ShiftMeta+8"); - shortcutMap["Tab9"] = resolveShortcut("ShiftMeta+9"); - shortcutMap["Tab0"] = resolveShortcut("ShiftMeta+0"); - shortcutMap["Link"] = resolveShortcut("Meta+t"); - shortcutMap["TapTempo"] = resolveShortcut("Shift+Return"); - shortcutMap["FocusEditor"] = resolveShortcut("CtrlShift+e"); - shortcutMap["FocusLogs"] = resolveShortcut("CtrlShift+l"); - shortcutMap["FocusContext"] = resolveShortcut("CtrlShift+t"); - shortcutMap["FocusCues"] = resolveShortcut("CtrlShift+c"); - shortcutMap["FocusPrefs"] = resolveShortcut("Meta+,"); - shortcutMap["FocusHelpListing"] = resolveShortcut("CtrlShift+h"); - shortcutMap["FocusHelpDetails"] = resolveShortcut("CtrlShift+d"); - shortcutMap["FocusErrors"] = resolveShortcut("CtrlShift+R"); - shortcutMap["FocusBPMScrubber"] = resolveShortcut("CtrlShift+b"); - shortcutMap["FocusTimeWarpScrubber"] = resolveShortcut("CtrlShift+w"); - shortcutMap["ShowButtons"] = resolveShortcut("ShiftMeta+b"); - shortcutMap["ShowCueLog"] = resolveShortcut("ShiftMeta+c"); - shortcutMap["ShowLog"] = resolveShortcut("ShiftMeta+l"); - shortcutMap["SetMark"] = resolveShortcut("Ctrl+Space"); - shortcutMap["LogZoomIn"] = resolveShortcut("Ctrl+="); - shortcutMap["LogZoomOut"] = resolveShortcut("Ctrl+-"); - shortcutMap["Down"] = resolveShortcut("Ctrl+n"); - shortcutMap["Up"] = resolveShortcut("Ctrl+p"); - shortcutMap["UpTen"] = resolveShortcut("PgUp"); - shortcutMap["DownTen"] = resolveShortcut("PgDown"); - shortcutMap["CutToEnd"] = resolveShortcut("Ctrl+k"); - shortcutMap["Copy"] = resolveShortcut("Ctrl+c"); - shortcutMap["Cut"] = resolveShortcut("Ctrl+x"); - shortcutMap["Paste"] = resolveShortcut("Ctrl+v"); - shortcutMap["Right"] = resolveShortcut("Ctrl+f"); - shortcutMap["Left"] = resolveShortcut("Ctrl+b"); - shortcutMap["DeleteForward"] = resolveShortcut("Ctrl+d"); - shortcutMap["DeleteBackward"] = resolveShortcut("Ctrl+h"); - shortcutMap["LineStart"] = resolveShortcut("Home"); - shortcutMap["LineEnd"] = resolveShortcut("End"); - shortcutMap["DocStart"] = resolveShortcut("MetaShift+,"); - shortcutMap["DocEnd"] = resolveShortcut("MetaShift+."); - shortcutMap["WordRight"] = resolveShortcut("Ctrl+Right"); - shortcutMap["WordLeft"] = resolveShortcut("Ctrl+Left"); - shortcutMap["CenterVertically"] = resolveShortcut("Ctrl+l"); - shortcutMap["Undo"] = resolveShortcut("Ctrl+z"); - shortcutMap["Redo"] = resolveShortcut("ShiftCtrl+z"); - shortcutMap["SelectAll"] = resolveShortcut("Ctrl+a"); - shortcutMap["DeleteWordRight"] = resolveShortcut("Meta+d"); - shortcutMap["DeleteWordLeft"] = resolveShortcut("Meta+Backspace"); - shortcutMap["UpcaseWord"] = resolveShortcut("Meta+u"); - shortcutMap["DowncaseWord"] = resolveShortcut("Meta+l"); - shortcutMap["FullScreen"] = resolveShortcut("F11"); -} - -void MainWindow::loadEmacsShortcuts() -{ - shortcutMap["Run"] = resolveShortcut("Meta+R"); - shortcutMap["Stop"] = resolveShortcut("Meta+S"); - shortcutMap["Record"] = resolveShortcut("ShiftMeta+R"); - shortcutMap["Load"] = resolveShortcut("ShiftMeta+O"); - shortcutMap["Align"] = resolveShortcut("Meta+M"); - shortcutMap["Comment"] = resolveShortcut("Meta+/"); - shortcutMap["Transpose"] = resolveShortcut("Ctrl+T"); - shortcutMap["ShiftUp"] = resolveShortcut("CtrlMeta+P"); - shortcutMap["ShiftDown"] = resolveShortcut("CtrlMeta+N"); - shortcutMap["ContextualDocs"] = resolveShortcut("Ctrl+I"); - shortcutMap["TextZoomIn"] = resolveShortcut("Meta+="); - shortcutMap["TextZoomOut"] = resolveShortcut("Meta+-"); - shortcutMap["Scope"] = resolveShortcut("Meta+O"); - shortcutMap["CycleThemes"] = resolveShortcut("ShiftMeta+M"); - shortcutMap["Info"] = resolveShortcut("Meta+1"); - shortcutMap["Help"] = resolveShortcut("Meta+i"); - shortcutMap["Prefs"] = resolveShortcut("Meta+p"); - shortcutMap["TabPrev"] = resolveShortcut("ShiftMeta+["); - shortcutMap["TabNext"] = resolveShortcut("ShiftMeta+]"); - shortcutMap["Tab1"] = resolveShortcut("ShiftMeta+1"); - shortcutMap["Tab2"] = resolveShortcut("ShiftMeta+2"); - shortcutMap["Tab3"] = resolveShortcut("ShiftMeta+3"); - shortcutMap["Tab4"] = resolveShortcut("ShiftMeta+4"); - shortcutMap["Tab5"] = resolveShortcut("ShiftMeta+5"); - shortcutMap["Tab6"] = resolveShortcut("ShiftMeta+6"); - shortcutMap["Tab7"] = resolveShortcut("ShiftMeta+7"); - shortcutMap["Tab8"] = resolveShortcut("ShiftMeta+8"); - shortcutMap["Tab9"] = resolveShortcut("ShiftMeta+9"); - shortcutMap["Tab0"] = resolveShortcut("ShiftMeta+0"); - shortcutMap["Link"] = resolveShortcut("Meta+t"); - shortcutMap["TapTempo"] = resolveShortcut("Shift+Return"); - shortcutMap["FocusEditor"] = resolveShortcut("CtrlShift+e"); - shortcutMap["FocusLogs"] = resolveShortcut("CtrlShift+l"); - shortcutMap["FocusContext"] = resolveShortcut("CtrlShift+t"); - shortcutMap["FocusCues"] = resolveShortcut("CtrlShift+c"); - shortcutMap["FocusPrefs"] = resolveShortcut("CtrlShift+p"); - shortcutMap["FocusHelpListing"] = resolveShortcut("CtrlShift+h"); - shortcutMap["FocusHelpDetails"] = resolveShortcut("CtrlShift+d"); - shortcutMap["FocusErrors"] = resolveShortcut("CtrlShift+R"); - shortcutMap["FocusBPMScrubber"] = resolveShortcut("CtrlShift+b"); - shortcutMap["FocusTimeWarpScrubber"] = resolveShortcut("CtrlShift+w"); - shortcutMap["ShowButtons"] = resolveShortcut("ShiftMeta+b"); - shortcutMap["ShowCueLog"] = resolveShortcut("ShiftMeta+c"); - shortcutMap["ShowLog"] = resolveShortcut("ShiftMeta+l"); - shortcutMap["SetMark"] = resolveShortcut("Ctrl+Space"); - shortcutMap["logZoomIn"] = resolveShortcut("Ctrl+="); - shortcutMap["logZoomOut"] = resolveShortcut("Ctrl+-"); - shortcutMap["Down"] = resolveShortcut("Ctrl+n"); - shortcutMap["Up"] = resolveShortcut("Ctrl+p"); - shortcutMap["UpTen"] = resolveShortcut("ShiftMeta+u"); - shortcutMap["DownTen"] = resolveShortcut("ShiftMeta+d"); - shortcutMap["CutToEnd"] = resolveShortcut("Ctrl+k"); - shortcutMap["Copy"] = resolveShortcut("Meta+]"); - shortcutMap["Cut"] = resolveShortcut("Ctrl+]"); - shortcutMap["Paste"] = resolveShortcut("Ctrl+y"); - shortcutMap["Right"] = resolveShortcut("Ctrl+f"); - shortcutMap["Left"] = resolveShortcut("Ctrl+b"); - shortcutMap["DeleteForward"] = resolveShortcut("Ctrl+d"); - shortcutMap["DeleteBackward"] = resolveShortcut("Ctrl+h"); - shortcutMap["LineStart"] = resolveShortcut("Ctrl+a"); - shortcutMap["LineEnd"] = resolveShortcut("Ctrl+e"); - shortcutMap["DocStart"] = resolveShortcut("MetaShift+,"); - shortcutMap["DocEnd"] = resolveShortcut("MetaShift+."); - shortcutMap["WordRight"] = resolveShortcut("Meta+f"); - shortcutMap["WordLeft"] = resolveShortcut("Meta+b"); - shortcutMap["CenterVertically"] = resolveShortcut("Ctrl+l"); - shortcutMap["Undo"] = resolveShortcut("Meta+z"); - shortcutMap["Redo"] = resolveShortcut("ShiftMeta+z"); - shortcutMap["SelectAll"] = resolveShortcut("Meta+a"); - shortcutMap["DeleteWordRight"] = resolveShortcut("Meta+d"); - shortcutMap["DeleteWordLeft"] = resolveShortcut("Meta+Backspace"); - shortcutMap["UpcaseWord"] = resolveShortcut("Meta+u"); - shortcutMap["DowncaseWord"] = resolveShortcut("Meta+l"); - shortcutMap["FullScreen"] = resolveShortcut("ShiftMeta+f"); -} - void MainWindow::createToolBar() { exitAct = new QAction(tr("Exit"), this); @@ -4054,9 +3698,9 @@ void MainWindow::createToolBar() } // for debugging purposes - reloadServerCodeSc = new QShortcut(QKeySequence("F8"), this, SLOT(reloadServerCode())); - toggleFocusModeSc = new QShortcut(QKeySequence("F10"), this, SLOT(toggleFocusMode())); - toggleScopePausedSc = new QShortcut(QKeySequence("F12"), this, SLOT(toggleScopePaused())); + reloadServerCodeSc = new QShortcut(QKeySequence(), this, SLOT(reloadServerCode())); + toggleFocusModeSc = new QShortcut(QKeySequence(), this, SLOT(toggleFocusMode())); + toggleScopePausedSc = new QShortcut(QKeySequence(), this, SLOT(toggleScopePaused())); escapeSc = new QShortcut(ctrlKey("g"), this, SLOT(escapeWorkspaces())); escape2Sc = new QShortcut(QKeySequence("Escape"), this, SLOT(escapeWorkspaces())); @@ -4079,6 +3723,7 @@ void MainWindow::createToolBar() connect(signalMapper, SIGNAL(mappedInt(int)), settingsWidget, SLOT(updateUILanguage(int))); connect(settingsWidget, SIGNAL(uiLanguageChanged(QString)), this, SLOT(updateSelectedUILanguageAction(QString))); + connect(settingsWidget, SIGNAL(shortcutModeChanged(int)), this, SLOT(updateShortcuts())); } void MainWindow::updateSelectedUILanguageAction(QString lang) @@ -4527,6 +4172,7 @@ void MainWindow::restartApp() qputenv("SONIC_PI_RESTART", "1"); // Save settings and perform some cleanup writeSettings(); + sonicPiShortcuts->writeUserShortcuts(); onExitCleanup(); std::cout << "[GUI] - performing application restart..." << std::endl; diff --git a/app/gui/mainwindow.h b/app/gui/mainwindow.h index e9a1e4aa01..b7a53675dc 100644 --- a/app/gui/mainwindow.h +++ b/app/gui/mainwindow.h @@ -70,6 +70,7 @@ class SettingsWidget; class Scope; class ScintillaAPI; class SonicPii18n; +class SonicPiShortcuts; class SonicPiLog; class SonicPiScintilla; class SonicPiEditor; @@ -135,7 +136,7 @@ class MainWindow : public QMainWindow void settingsChanged(); private slots: - + void updateShortcuts(); void updateSelectedUILanguageAction(QString lang); void updateContext(int line, int index); void updateContextWithCurrentWs(); @@ -335,14 +336,6 @@ private slots: void shortcutModeMenuChanged(int modeID); private: - QKeySequence resolveShortcut(QString keySequence); - void resetShortcuts(); - void loadWinShortcuts(); - void loadMacShortcuts(); - void loadEmacsShortcuts(); - void loadUserShortcuts(); - void loadUserShortcut(const QString& id, QSettings& shortcut_settings); - SonicPiScintilla* getCurrentWorkspace(); SonicPiEditor* getCurrentEditor(); void resizeEvent(QResizeEvent* e) override; @@ -370,8 +363,6 @@ private slots: bool saveFile(const QString& fileName, SonicPiScintilla* text); void loadWorkspaces(); void saveWorkspaces(); - void updateShortcuts(); - void updateShortcut(const QString& id, QAction* action, const QString& desc); std::string number_name(int); std::string workspaceFilename(SonicPiScintilla* text); SonicPiScintilla* filenameToWorkspace(std::string filename); @@ -406,6 +397,7 @@ private slots: QSettings* gui_settings; SonicPiSettings* piSettings; SonicPii18n* sonicPii18n; + SonicPiShortcuts* sonicPiShortcuts; bool fullScreenMode = false; bool focusMode; diff --git a/app/gui/model/sonicpi_shortcuts.cpp b/app/gui/model/sonicpi_shortcuts.cpp new file mode 100644 index 0000000000..aca235d5e2 --- /dev/null +++ b/app/gui/model/sonicpi_shortcuts.cpp @@ -0,0 +1,212 @@ +#include "sonicpi_shortcuts.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +SonicPiShortcuts::SonicPiShortcuts(QString config_path) { + this->config_path = config_path; + QString shortcuts_path = config_path + QDir::separator() + "keyboard-shortcuts.ini"; + this->shortcut_settings = new QSettings(shortcuts_path, QSettings::IniFormat); + this->user_base = shortcut_settings->value("base", "emacs").toString(); + + this->shortcut_ids = {}; + for (const auto& item : defaultBindings) { + QString id = item.first; + shortcut_ids.append(id); + } + std::cout << "[GUI] [Shortcuts] : " << shortcut_ids.length() << " shortcut IDs loaded" << std::endl; +} +SonicPiShortcuts::~SonicPiShortcuts() { +} + +QKeySequence SonicPiShortcuts::getShortcut(const QString& id) { + if (shortcutMap.find(id) == shortcutMap.end()) { + if (defaultShortcutMap.find(id) == defaultShortcutMap.end()) { + return QKeySequence(); + } + return defaultShortcutMap[id]; + } + return shortcutMap[id]; +} +std::map SonicPiShortcuts::getAllShortcuts() { + std::map current_shortcuts = {}; + for (const auto& id : shortcut_ids) { + current_shortcuts[id] = getShortcut(id); + // std::cout << "[Debug] " << getShortcut(id).toString().toStdString() << std::endl; + } + return current_shortcuts; +} +void SonicPiShortcuts::updateShortcut(const QString& id, QKeySequence key_sequence) { + if (key_sequence.isEmpty()) { + return resetShortcut(id); + } + shortcutMap[id].swap(key_sequence); +} +void SonicPiShortcuts::resetShortcut(const QString& id) { + shortcutMap.erase(id); +} + +void SonicPiShortcuts::resetShortcuts() { + shortcutMap.clear(); +} + +void SonicPiShortcuts::assignToAction(const QString& id, QAction* action, const QString& desc) { + action->setShortcut(getShortcut(id)); + updateActionText(action, desc); +} +void SonicPiShortcuts::updateActionText(QAction* action, const QString& desc) { + QString shortcutDesc = action->shortcut().toString(QKeySequence::PortableText); + action->setToolTip(desc + " (" + shortcutDesc + ")"); + action->setText(action->iconText()); + action->setStatusTip(desc + " (" + shortcutDesc + ")"); +} + +void SonicPiShortcuts::loadUserShortcuts() { + // Determine which shortcuts to load based on the 'base' value + if (user_base == "none") + { + // don't load any shortcuts + } + else if (user_base == "mac") + { + loadDefaultShortcuts(DefaultShortcutSet::MACOS); + } + else if (user_base == "win") + { + loadDefaultShortcuts(DefaultShortcutSet::WINDOWS); + } + else + { + // default + #ifdef Q_OS_MAC + loadDefaultShortcuts(DefaultShortcutSet::EMACS_MACOS); + #else + loadDefaultShortcuts(DefaultShortcutSet::EMACS); + #endif + } + + // Load user shortcuts + QStringList keys = shortcut_settings->allKeys(); + for (const QString& id : keys) + { + if (shortcut_ids.contains(id)) { + shortcutMap[id] = QKeySequence(shortcut_settings->value(id, "").toString()); + } else { + if (id == "base") { + continue; + } + shortcut_settings->remove(id); + } + } +} +void SonicPiShortcuts::writeUserShortcuts() { + for (auto const& x : shortcutMap) { + QString id = x.first; + QKeySequence sequence = x.second; + shortcut_settings->setValue(id, sequence.toString()); + } + shortcut_settings->setValue("base", user_base); + shortcut_settings->sync(); +} + +void SonicPiShortcuts::loadDefaultShortcuts(DefaultShortcutSet set_id) { + // 0: macos + // 1: windows + // 2: emacs + // 3: emacs (macos) + defaultShortcutMap = {}; + for (auto const& x : defaultBindings) { + QString id = x.first; + QStringList binding = x.second; + + defaultShortcutMap[id] = QKeySequence(binding[set_id]); + } +} + +std::map SonicPiShortcuts::defaultBindings = { + {"Run", {"Ctrl+R","Alt+R","Alt+R","Ctrl+R"}}, + {"Stop", {"Ctrl+S","Alt+S","Alt+S","Ctrl+S"}}, + {"Record", {"Ctrl+Shift+R","Alt+Shift+R","Alt+Shift+R","Ctrl+Shift+R"}}, + {"Load", {"Meta+O","Ctrl+O","Alt+Shift+O","Ctrl+Shift+O"}}, + {"Align", {"Ctrl+M","Alt+M","Alt+M","Ctrl+M"}}, + {"Comment", {"Ctrl+/","Alt+/","Alt+/","Ctrl+/"}}, + {"Transpose", {"Meta+T","Ctrl+T","Ctrl+T","Meta+T"}}, + {"ShiftUp", {"Ctrl+Meta+P","Ctrl+Alt+P","Ctrl+alt+P","Ctrl+Meta+P"}}, + {"ShiftDown", {"Ctrl+Meta+N","Ctrl+Alt+N","Ctrl+alt+N","Ctrl+Meta+N"}}, + {"ContextualDocs", {"Shift+F1","Shift+F1","Ctrl+I","Meta+I"}}, + {"TextZoomIn", {"Ctrl+=","Ctrl+=","Alt++","Ctrl++"}}, + {"TextZoomOut", {"Ctrl+-","Ctrl+-","Alt+-","Ctrl+-"}}, + {"Scope", {"Ctrl+O","Alt+O","Alt+O","Ctrl+O"}}, + {"CycleThemes", {"Ctrl+Shift+M","Alt+Shift+M","Alt+Shift+M","Ctrl+Shift+M"}}, + {"Info", {"Ctrl+1","Alt+1","Alt+1","Ctrl+1"}}, + {"Help", {"F1","F1","Alt+i","Ctrl+i"}}, + {"Prefs", {"Ctrl+p","Alt+p","Alt+p","Ctrl+p"}}, + {"TabPrev", {"Ctrl+Shift+[","Alt+Shift+[","Alt+Shift+[","Ctrl+Shift+["}}, + {"TabNext", {"Ctrl+Shift+]","Alt+Shift+]","Alt+Shift+]","Ctrl+Shift+]"}}, + {"Tab1", {"Ctrl+Shift+1","Alt+Shift+1","Alt+Shift+1","Ctrl+Shift+1"}}, + {"Tab2", {"Ctrl+Shift+2","Alt+Shift+2","Alt+Shift+2","Ctrl+Shift+2"}}, + {"Tab3", {"Ctrl+Shift+3","Alt+Shift+3","Alt+Shift+3","Ctrl+Shift+3"}}, + {"Tab4", {"Ctrl+Shift+4","Alt+Shift+4","Alt+Shift+4","Ctrl+Shift+4"}}, + {"Tab5", {"Ctrl+Shift+5","Alt+Shift+5","Alt+Shift+5","Ctrl+Shift+5"}}, + {"Tab6", {"Ctrl+Shift+6","Alt+Shift+6","Alt+Shift+6","Ctrl+Shift+6"}}, + {"Tab7", {"Ctrl+Shift+7","Alt+Shift+7","Alt+Shift+7","Ctrl+Shift+7"}}, + {"Tab8", {"Ctrl+Shift+8","Alt+Shift+8","Alt+Shift+8","Ctrl+Shift+8"}}, + {"Tab9", {"Ctrl+Shift+9","Alt+Shift+9","Alt+Shift+9","Ctrl+Shift+9"}}, + {"Tab0", {"Ctrl+Shift+0","Alt+Shift+0","Alt+Shift+0","Ctrl+Shift+0"}}, + {"Link", {"Ctrl+t","Alt+T","Alt+t","Ctrl+t"}}, + {"TapTempo", {"Shift+Return","Shift+Return","Shift+Return","Shift+Return"}}, + {"FocusEditor", {"Meta+Shift+e","Ctrl+Shift+e","Ctrl+Shift+e","Meta+Shift+e"}}, + {"FocusLogs", {"Meta+Shift+l","Ctrl+Shift+l","Ctrl+Shift+l","Meta+Shift+l"}}, + {"FocusContext", {"Meta+Shift+t","Ctrl+Shift+t","Ctrl+Shift+t","Meta+Shift+t"}}, + {"FocusCues", {"Meta+Shift+c","Ctrl+Shift+c","Ctrl+Shift+c","Meta+Shift+c"}}, + {"FocusPrefs", {"Meta+,","Alt+,","Ctrl+Shift+P","Meta+Shift+P"}}, + {"FocusHelpListing", {"Meta+Shift+h","Ctrl+Shift+h","Ctrl+Shift+h","Meta+Shift+h"}}, + {"FocusHelpDetails", {"Meta+Shift+d","Ctrl+Shift+d","Ctrl+Shift+d","Meta+Shift+d"}}, + {"FocusErrors", {"Meta+Shift+R","Ctrl+Shift+R","Ctrl+Shift+R","Meta+Shift+R"}}, + {"FocusBPMScrubber", {"Meta+Shift+b","Ctrl+Shift+b","Ctrl+Shift+b","Meta+Shift+b"}}, + {"FocusTimeWarpScrubber", {"Meta+Shift+w","Ctrl+Shift+w","Ctrl+Shift+w","Meta+Shift+w"}}, + {"ShowButtons", {"Ctrl+Shift+b","Alt+Shift+b","Alt+Shift+b","Ctrl+Shift+b"}}, + {"ShowCueLog", {"Ctrl+Shift+c","Alt+Shift+c","Alt+Shift+c","Ctrl+Shift+c"}}, + {"ShowLog", {"Ctrl+Shift+l","Alt+Shift+l","Alt+Shift+l","Ctrl+Shift+l"}}, + {"SetMark", {"Meta+Space","Ctrl+Space","Ctrl+Space","Meta+Space"}}, + {"LogZoomIn", {"Meta+=","Ctrl+Alt+=","Ctrl+=","Meta+="}}, + {"LogZoomOut", {"Meta+-","Ctrl+Alt+-","Ctrl+-","Meta+-"}}, + {"Down", {"Meta+n","Ctrl+n","Ctrl+n","Meta+n"}}, + {"Up", {"Meta+p","Ctrl+p","Ctrl+p","Meta+p"}}, + {"UpTen", {"Ctrl+up","PgUp","Alt+Shift+U","Ctrl+Shift+U"}}, + {"DownTen", {"Ctrl+down","PgDown","Alt+Shift+D","Ctrl+Shift+D"}}, + {"CutToEnd", {"Meta+k","Ctrl+k","Ctrl+k","Meta+k"}}, + {"Copy", {"Meta+c","Ctrl+c","Alt+]","Ctrl+]"}}, + {"Cut", {"Meta+x","Ctrl+x","Ctrl+]","Meta+]"}}, + {"Paste", {"Meta+v","Ctrl+v","Ctrl+y","Meta+y"}}, + {"Right", {"Meta+f","Ctrl+f","Ctrl+f","Meta+f"}}, + {"Left", {"Meta+b","Ctrl+b","Ctrl+b","Meta+b"}}, + {"DeleteForward", {"Meta+d","Ctrl+d","Ctrl+d","Meta+d"}}, + {"DeleteBackward", {"Meta+h","Ctrl+h","Ctrl+h","Meta+h"}}, + {"LineStart", {"Ctrl+Left","Home","Ctrl+A","Meta+A"}}, + {"LineEnd", {"Ctrl+Right","End","Ctrl+E","Meta+E"}}, + {"DocStart", {"Ctrl+Shift+,","Alt+Shift+,","Alt+Shift+,","Alt+Shift+,"}}, + {"DocEnd", {"Ctrl+Shift+.","Alt+Shift+.","Alt+Shift+.","Alt+Shift+."}}, + {"WordRight", {"Alt+Right","Ctrl+Right","Alt+F","Ctrl+F"}}, + {"WordLeft", {"Alt+Left","Ctrl+Left","Alt+B","Ctrl+B"}}, + {"CenterVertically", {"Meta+l","Ctrl+l","Ctrl+l","Meta+l"}}, + {"Undo", {"Ctrl+z","Ctrl+z","Alt+z","Ctrl+z"}}, + {"Redo", {"Ctrl+Shift+z","Ctrl+Shift+z","Alt+Shift+z","Ctrl+Shift+z"}}, + {"SelectAll", {"Ctrl+a","Ctrl+a","Alt+a","Ctrl+a"}}, + {"DeleteWordRight", {"Ctrl+d","Alt+d","Alt+d","Ctrl+d"}}, + {"DeleteWordLeft", {"Ctrl+Backspace","Alt+Backspace","Alt+Backspace","Ctrl+Backspace"}}, + {"UpcaseWord", {"Ctrl+u","Alt+u","Alt+u","Ctrl+u"}}, + {"DowncaseWord", {"Ctrl+l","Alt+l","Alt+l","Ctrl+l"}}, + {"FullScreen", {"Ctrl+Shift+f","F11","Alt+Shift+F","Ctrl+Shift+F"}}, + {"ReloadServerCode", {"F8", "F8", "F8", "F8"}}, + {"ToggleFocusMode", {"F10", "F10", "F10", "F10"}}, + {"ToggleScopePaused", {"F12", "F12", "F12", "F12"}} +}; diff --git a/app/gui/model/sonicpi_shortcuts.h b/app/gui/model/sonicpi_shortcuts.h new file mode 100644 index 0000000000..959a393d23 --- /dev/null +++ b/app/gui/model/sonicpi_shortcuts.h @@ -0,0 +1,55 @@ +#include + +#include +#include +#include +#include +#include +#include + + +#ifndef SONICPI_SHORTCUTS_H +#define SONICPI_SHORTCUTS_H + +enum DefaultShortcutSet { + MACOS = 0, + WINDOWS = 1, + EMACS = 2, + EMACS_MACOS = 3 +}; + +class SonicPiShortcuts : public QObject { + public: + SonicPiShortcuts(QString config_path); + ~SonicPiShortcuts(); + + QStringList shortcut_ids; + + public slots: + QKeySequence getShortcut(const QString& id); + std::map getAllShortcuts(); + void updateShortcut(const QString& id, QKeySequence key_sequence); + void resetShortcut(const QString& id); + + void resetShortcuts(); + + void updateActionText(QAction* action, const QString& desc); + void assignToAction(const QString& id, QAction* action, const QString& desc); + + void loadUserShortcut(const QString& id); + void loadUserShortcuts(); + void writeUserShortcuts(); + + void loadDefaultShortcuts(DefaultShortcutSet set_id); + + private: + std::map shortcutMap; + std::map defaultShortcutMap; + QString config_path; + QString user_base; + QSettings* shortcut_settings; + + static std::map defaultBindings; + +}; +#endif diff --git a/app/gui/widgets/settingswidget.cpp b/app/gui/widgets/settingswidget.cpp index e981df69e6..b805895ca6 100644 --- a/app/gui/widgets/settingswidget.cpp +++ b/app/gui/widgets/settingswidget.cpp @@ -1,5 +1,9 @@ #include "settingswidget.h" #include "utils/sonicpi_i18n.h" +#include "model/sonicpi_shortcuts.h" + +#include +#include #include #include @@ -11,19 +15,25 @@ #include #include #include -#include #include #include #include #include #include #include +#include +#include +#include +#include +#include +#include /** * Default Constructor */ -SettingsWidget::SettingsWidget(int tau_osc_cues_port, bool i18n, SonicPiSettings *piSettings, SonicPii18n *sonicPii18n, QWidget *parent) { +SettingsWidget::SettingsWidget(int tau_osc_cues_port, bool i18n, SonicPiSettings *piSettings, SonicPiShortcuts *sonicPiShortcuts, SonicPii18n *sonicPii18n, QWidget *parent) { this->piSettings = piSettings; + this->sonicPiShortcuts = sonicPiShortcuts; this->i18n = i18n; this->sonicPii18n = sonicPii18n; this->available_languages = sonicPii18n->getAvailableLanguages(); @@ -45,6 +55,9 @@ SettingsWidget::SettingsWidget(int tau_osc_cues_port, bool i18n, SonicPiSettings QGroupBox *editorTab = createEditorPrefsTab(); prefTabs->addTab(editorTab, tr("Editor")); + QGroupBox *shortcutsTab = createShortcutsPrefsTab(); + prefTabs->addTab(shortcutsTab, tr("Shortcuts")); + QGroupBox *visualizationTab = createVisualizationPrefsTab(); prefTabs->addTab(visualizationTab, tr("Visuals")); @@ -566,6 +579,54 @@ QGroupBox* SettingsWidget::createLanguagePrefsTab() { return language_prefs_box; } + +/** + * create Language Preferences Tab of Settings Widget + */ + QGroupBox* SettingsWidget::createShortcutsPrefsTab() { + QGroupBox *shortcuts_box = new QGroupBox(tr("Shortcuts")); + shortcuts_box->setToolTip(tr("Configure shortcuts")); + QSizePolicy shortcutsPrefSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + shortcuts_box->setSizePolicy(shortcutsPrefSizePolicy); + + shortcut_mode_label = new QLabel; + shortcut_mode_label->setText(tr("Shortcut mode")); + shortcut_mode_label->setToolTip(tr("Change the set of shortcuts used in the UI.")); + + shortcut_mode_combo = new QComboBox(); + shortcut_mode_combo->addItems({"Emacs (default)", "Windows", "Mac", "User"}); + shortcut_mode_combo->setToolTip(tr("Change the set of shortcuts used in the UI.")); + shortcut_mode_combo->setMinimumContentsLength(2); + shortcut_mode_combo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLengthWithIcon); + + shortcut_table = new QTableWidget(1, 2); + shortcut_table->setHorizontalHeaderLabels({"Shortcut ID", "Key Binding"}); + QHeaderView *column_header = shortcut_table->horizontalHeader(); + QHeaderView *row_header = shortcut_table->verticalHeader(); + column_header->setSectionResizeMode(QHeaderView::ResizeToContents); + row_header->setSectionResizeMode(QHeaderView::ResizeToContents); + + QScrollArea *scrollArea = new QScrollArea(); + // scrollArea->setBackgroundRole(QPalette::Dark); + scrollArea->setWidgetResizable(true); + scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents); + scrollArea->setWidget(shortcut_table); + + QVBoxLayout *shortcuts_box_layout = new QVBoxLayout; + + shortcuts_box_layout->addWidget(shortcut_mode_label); + shortcuts_box_layout->addWidget(shortcut_mode_combo); + shortcuts_box_layout->addWidget(scrollArea); + + shortcuts_box->setLayout(shortcuts_box_layout); + + QGroupBox *shortcut_prefs_box = new QGroupBox(); + QGridLayout *shortcut_prefs_box_layout = new QGridLayout; + shortcut_prefs_box_layout->addWidget(shortcuts_box, 0, 0, 0, 0); + shortcut_prefs_box->setLayout(shortcut_prefs_box_layout); + return shortcut_prefs_box; +} + // TODO utils? QString SettingsWidget::tooltipStrShiftMeta(char key, QString str) { #ifdef Q_OS_MAC @@ -676,6 +737,10 @@ void SettingsWidget::updateUILanguage(int index) { } } +void SettingsWidget::update_shortcut_mode(int mode) { + emit shortcutModeChanged(mode+1); +} + void SettingsWidget::updateEnableScsynthInputs() { emit enableScsynthInputsChanged(); @@ -854,6 +919,7 @@ void SettingsWidget::updateSettings() { std::cout << "[GUI] - update settings" << std::endl; piSettings->language = available_languages[language_combo->currentIndex()]; + piSettings->shortcut_mode = shortcut_mode_combo->currentIndex() + 1; piSettings->mixer_invert_stereo = mixer_invert_stereo->isChecked(); piSettings->enable_scsynth_inputs = enable_scsynth_inputs->isChecked(); piSettings->mixer_force_mono = mixer_force_mono->isChecked(); @@ -922,6 +988,9 @@ void SettingsWidget::settingsChanged() { } language_details_label->setText(language_detail_text); + shortcut_mode_combo->setCurrentIndex(piSettings->shortcut_mode - 1); + updateShortcutsTable(); + mixer_invert_stereo->setChecked(piSettings->mixer_invert_stereo); mixer_force_mono->setChecked(piSettings->mixer_force_mono); enable_scsynth_inputs->setChecked(piSettings->enable_scsynth_inputs); @@ -975,6 +1044,8 @@ void SettingsWidget::settingsChanged() { void SettingsWidget::connectAll() { //connect(language_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSettings())); connect(language_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUILanguage(int))); + connect(shortcut_mode_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSettings())); + connect(shortcut_mode_combo, SIGNAL(currentIndexChanged(int)), this, SLOT(update_shortcut_mode(int))); connect(mixer_invert_stereo, SIGNAL(clicked()), this, SLOT(updateSettings())); connect(mixer_force_mono, SIGNAL(clicked()), this, SLOT(updateSettings())); connect(check_args, SIGNAL(clicked()), this, SLOT(updateSettings())); @@ -1073,3 +1144,26 @@ void SettingsWidget::add_language_combo_box_entries(QComboBox* combo) { } } } + +void SettingsWidget::updateShortcutsTable() { + shortcut_table->clearContents(); + + std::map all_shortcuts = sonicPiShortcuts->getAllShortcuts(); + std::cout << "no. of shortcuts: " << all_shortcuts.size() << std::endl; + shortcut_table->setRowCount(all_shortcuts.size()); + + int row = 0; + for (auto const &item : all_shortcuts) { + QString id = item.first; + QKeySequence key_sequence = item.second; + QString keys = key_sequence.toString(); + + QTableWidgetItem* id_cell = new QTableWidgetItem(id); + id_cell->setFlags(Qt::ItemIsSelectable); + QTableWidgetItem* keys_cell = new QTableWidgetItem(keys); + keys_cell->setFlags(Qt::ItemIsSelectable); + shortcut_table->setItem(row, 0, id_cell); + shortcut_table->setItem(row, 1, keys_cell); + row += 1; + } +} diff --git a/app/gui/widgets/settingswidget.h b/app/gui/widgets/settingswidget.h index f984f0d48c..e3e3c2f050 100644 --- a/app/gui/widgets/settingswidget.h +++ b/app/gui/widgets/settingswidget.h @@ -2,9 +2,11 @@ #define SETTINGSWIDGET_H #include "model/settings.h" +#include "model/sonicpi_shortcuts.h" #include "utils/sonicpi_i18n.h" #include +#include class QSlider; class QTabWidget; @@ -25,7 +27,7 @@ class SettingsWidget : public QWidget Q_OBJECT public: - SettingsWidget(int tau_osc_cues_port, bool i18n, SonicPiSettings *piSettings, SonicPii18n *sonicPii18n, QWidget *parent = nullptr); + SettingsWidget(int tau_osc_cues_port, bool i18n, SonicPiSettings *piSettings, SonicPiShortcuts *sonicPiShortcuts, SonicPii18n *sonicPii18n, QWidget *parent = nullptr); ~SettingsWidget(); void updateVersionInfo( QString info_string, QString visit, bool sonic_pi_net_visible, bool check_now_visible); @@ -34,11 +36,13 @@ class SettingsWidget : public QWidget void updateScsynthInfo(QString scsynthInfo); void updateScopeNames(std::vector); void updateSelectedUILanguage(QString lang); + void updateShortcutsTable(); public slots: void updateUILanguage(int index); private slots: + void update_shortcut_mode(int mode); void update_mixer_invert_stereo(); void update_mixer_force_mono(); void updateEnableScsynthInputs(); @@ -80,6 +84,7 @@ private slots: signals: void restartApp(); void uiLanguageChanged(QString lang); // TODO: Implement real-time language switching + void shortcutModeChanged(int mode); void mixerSettingsChanged(); void enableScsynthInputsChanged(); void oscSettingsChanged(); @@ -116,6 +121,7 @@ private slots: private: SonicPiSettings* piSettings; + SonicPiShortcuts* sonicPiShortcuts; SonicPii18n* sonicPii18n; std::map localeNames; QStringList available_languages; @@ -184,6 +190,11 @@ private slots: QLabel *language_details_label; QLabel *language_info_label; + QComboBox *shortcut_mode_combo; + QLabel *shortcut_mode_label; + QTableWidget *shortcut_table; + + // TODO QGroupBox* createAudioPrefsTab(); QGroupBox* createIoPrefsTab(); @@ -191,6 +202,7 @@ private slots: QGroupBox* createVisualizationPrefsTab(); QGroupBox* createUpdatePrefsTab(); QGroupBox* createLanguagePrefsTab(); + QGroupBox* createShortcutsPrefsTab(); void add_language_combo_box_entries(QComboBox* combo);