From 5a652907c719d7fee4c77b8cce321e841825fa97 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 11 Dec 2021 17:54:17 +0000 Subject: [PATCH] override Scene.cpp; Move common menu/scene to new file; Cleanup Closes #50 Signed-off-by: falkTX --- src/Cardinal/CardinalCommon.cpp | 1 + src/Cardinal/MenuBar.cpp | 1 - src/CardinalCommon.cpp | 157 ++++++++++++++ src/CardinalCommon.hpp | 32 +++ src/CardinalFX/CardinalCommon.cpp | 1 + src/CardinalFX/MenuBar.cpp | 1 - src/CardinalSynth/CardinalCommon.cpp | 1 + src/CardinalSynth/MenuBar.cpp | 1 - src/CardinalUI.cpp | 4 +- src/Makefile | 3 + src/Makefile.cardinal.mk | 2 +- src/override/MenuBar.cpp | 106 ++-------- src/override/Scene.cpp | 300 +++++++++++++++++++++++++++ 13 files changed, 515 insertions(+), 95 deletions(-) create mode 120000 src/Cardinal/CardinalCommon.cpp delete mode 120000 src/Cardinal/MenuBar.cpp create mode 100644 src/CardinalCommon.cpp create mode 100644 src/CardinalCommon.hpp create mode 120000 src/CardinalFX/CardinalCommon.cpp delete mode 120000 src/CardinalFX/MenuBar.cpp create mode 120000 src/CardinalSynth/CardinalCommon.cpp delete mode 120000 src/CardinalSynth/MenuBar.cpp create mode 100644 src/override/Scene.cpp diff --git a/src/Cardinal/CardinalCommon.cpp b/src/Cardinal/CardinalCommon.cpp new file mode 120000 index 0000000..76b4b5f --- /dev/null +++ b/src/Cardinal/CardinalCommon.cpp @@ -0,0 +1 @@ +../CardinalCommon.cpp \ No newline at end of file diff --git a/src/Cardinal/MenuBar.cpp b/src/Cardinal/MenuBar.cpp deleted file mode 120000 index 8d1a35a..0000000 --- a/src/Cardinal/MenuBar.cpp +++ /dev/null @@ -1 +0,0 @@ -../override/MenuBar.cpp \ No newline at end of file diff --git a/src/CardinalCommon.cpp b/src/CardinalCommon.cpp new file mode 100644 index 0000000..a29ebf5 --- /dev/null +++ b/src/CardinalCommon.cpp @@ -0,0 +1,157 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the LICENSE file. + */ + +#include "CardinalCommon.hpp" + +#include "AsyncDialog.hpp" +#include "PluginContext.hpp" + +#include +#include +#include +#include +#include +#include + +#ifdef NDEBUG +# undef DEBUG +#endif + +// for finding home dir +#ifndef ARCH_WIN +# include +# include +#endif + +namespace patchUtils +{ + +static void promptClear(const char* const message, const std::function action) +{ + if (APP->history->isSaved() || APP->scene->rack->hasModules()) + return action(); + + asyncDialog::create(message, action); +} + +static std::string homeDir() +{ +#ifdef ARCH_WIN + if (const char* const userprofile = getenv("USERPROFILE")) + { + return userprofile; + } + else if (const char* const homedrive = getenv("HOMEDRIVE")) + { + if (const char* const homepath = getenv("HOMEPATH")) + return system::join(homedrive, homepath); + } +#else + if (const char* const home = getenv("HOME")) + return home; + else if (struct passwd* const pwd = getpwuid(getuid())) + return pwd->pw_dir; +#endif + return {}; +} + +using namespace rack; + +void loadDialog() +{ + promptClear("The current patch is unsaved. Clear it and open a new patch?", []() { + std::string dir; + if (! APP->patch->path.empty()) + dir = system::getDirectory(APP->patch->path); + else + dir = homeDir(); + + CardinalPluginContext* const pcontext = static_cast(APP); + DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,); + + CardinalBaseUI* const ui = static_cast(pcontext->ui); + DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); + + FileBrowserOptions opts; + opts.startDir = dir.c_str(); + opts.saving = ui->saving = false; + ui->openFileBrowser(opts); + }); +} + +void loadPathDialog(const std::string& path) +{ + promptClear("The current patch is unsaved. Clear it and open the new patch?", [path]() { + APP->patch->loadAction(path); + }); +} + +void loadTemplateDialog() +{ + promptClear("The current patch is unsaved. Clear it and start a new patch?", []() { + APP->patch->loadTemplate(); + }); +} + +void revertDialog() +{ + if (APP->patch->path.empty()) + return; + promptClear("Revert patch to the last saved state?", []{ + APP->patch->loadAction(APP->patch->path); + }); +} + +void saveDialog(const std::string& path) +{ + if (path.empty()) { + return; + } + + // Note: If save() fails below, this should probably be reset. But we need it so toJson() doesn't set the "unsaved" property. + APP->history->setSaved(); + + try { + APP->patch->save(path); + } + catch (Exception& e) { + asyncDialog::create(string::f("Could not save patch: %s", e.what()).c_str()); + return; + } +} + +void saveAsDialog() +{ + std::string dir; + if (! APP->patch->path.empty()) + dir = system::getDirectory(APP->patch->path); + else + dir = homeDir(); + + CardinalPluginContext* const pcontext = static_cast(APP); + DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,); + + CardinalBaseUI* const ui = static_cast(pcontext->ui); + DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,); + + FileBrowserOptions opts; + opts.startDir = dir.c_str(); + opts.saving = ui->saving = true; + ui->openFileBrowser(opts); +} + +} diff --git a/src/CardinalCommon.hpp b/src/CardinalCommon.hpp new file mode 100644 index 0000000..26becb6 --- /dev/null +++ b/src/CardinalCommon.hpp @@ -0,0 +1,32 @@ +/* + * DISTRHO Cardinal Plugin + * Copyright (C) 2021 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 3 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the LICENSE file. + */ + +#include + +#pragma once + +namespace patchUtils +{ + +void loadDialog(); +void loadPathDialog(const std::string& path); +void loadTemplateDialog(); +void revertDialog(); +void saveDialog(const std::string& path); +void saveAsDialog(); + +} diff --git a/src/CardinalFX/CardinalCommon.cpp b/src/CardinalFX/CardinalCommon.cpp new file mode 120000 index 0000000..76b4b5f --- /dev/null +++ b/src/CardinalFX/CardinalCommon.cpp @@ -0,0 +1 @@ +../CardinalCommon.cpp \ No newline at end of file diff --git a/src/CardinalFX/MenuBar.cpp b/src/CardinalFX/MenuBar.cpp deleted file mode 120000 index 8d1a35a..0000000 --- a/src/CardinalFX/MenuBar.cpp +++ /dev/null @@ -1 +0,0 @@ -../override/MenuBar.cpp \ No newline at end of file diff --git a/src/CardinalSynth/CardinalCommon.cpp b/src/CardinalSynth/CardinalCommon.cpp new file mode 120000 index 0000000..76b4b5f --- /dev/null +++ b/src/CardinalSynth/CardinalCommon.cpp @@ -0,0 +1 @@ +../CardinalCommon.cpp \ No newline at end of file diff --git a/src/CardinalSynth/MenuBar.cpp b/src/CardinalSynth/MenuBar.cpp deleted file mode 120000 index 8d1a35a..0000000 --- a/src/CardinalSynth/MenuBar.cpp +++ /dev/null @@ -1 +0,0 @@ -../override/MenuBar.cpp \ No newline at end of file diff --git a/src/CardinalUI.cpp b/src/CardinalUI.cpp index 236f12d..b2ced7d 100644 --- a/src/CardinalUI.cpp +++ b/src/CardinalUI.cpp @@ -179,7 +179,7 @@ GLFWAPI const char* glfwGetKeyName(const int key, int) namespace rack { namespace app { - widget::Widget* createMenuBar(CardinalBaseUI* const ui, bool isStandalone); + widget::Widget* createMenuBar(bool isStandalone); } namespace window { void WindowSetPluginUI(Window* window, DISTRHO_NAMESPACE::UI* ui); @@ -283,7 +283,7 @@ public: if (context->scene->menuBar != nullptr) context->scene->removeChild(context->scene->menuBar); - context->scene->menuBar = rack::app::createMenuBar(this, getApp().isStandalone()); + context->scene->menuBar = rack::app::createMenuBar(getApp().isStandalone()); context->scene->addChildBelow(context->scene->menuBar, context->scene->rackScroll); // hide "Browse VCV Library" button diff --git a/src/Makefile b/src/Makefile index 94aa678..ebe282d 100644 --- a/src/Makefile +++ b/src/Makefile @@ -88,6 +88,8 @@ BUILD_CXX_FLAGS += -DnsvgParseFromFile=nsvgParseFromFileCardinal RACK_FILES += AsyncDialog.cpp RACK_FILES += override/Engine.cpp +RACK_FILES += override/MenuBar.cpp +RACK_FILES += override/Scene.cpp RACK_FILES += override/asset.cpp RACK_FILES += override/context.cpp RACK_FILES += override/dep.cpp @@ -112,6 +114,7 @@ IGNORED_FILES += Rack/src/network.cpp IGNORED_FILES += Rack/src/rtaudio.cpp IGNORED_FILES += Rack/src/rtmidi.cpp IGNORED_FILES += Rack/src/app/MenuBar.cpp +IGNORED_FILES += Rack/src/app/Scene.cpp IGNORED_FILES += Rack/src/engine/Engine.cpp IGNORED_FILES += Rack/src/window/Window.cpp diff --git a/src/Makefile.cardinal.mk b/src/Makefile.cardinal.mk index 31ddcc9..055cab7 100644 --- a/src/Makefile.cardinal.mk +++ b/src/Makefile.cardinal.mk @@ -80,6 +80,7 @@ include ../../dpf/Makefile.base.mk # Files to build (DPF stuff) FILES_DSP = CardinalPlugin.cpp +FILES_DSP += CardinalCommon.cpp FILES_DSP += common.cpp ifeq ($(HEADLESS),true) @@ -87,7 +88,6 @@ FILES_DSP += RemoteNanoVG.cpp FILES_DSP += RemoteWindow.cpp else FILES_UI = CardinalUI.cpp -FILES_UI += MenuBar.cpp FILES_UI += Window.cpp endif diff --git a/src/override/MenuBar.cpp b/src/override/MenuBar.cpp index 8d5fee2..8b84d94 100644 --- a/src/override/MenuBar.cpp +++ b/src/override/MenuBar.cpp @@ -50,23 +50,11 @@ #include #include -#ifdef NDEBUG -# undef DEBUG -#endif - -// for finding home dir -#ifndef ARCH_WIN -# include -# include -#endif - #ifdef HAVE_LIBLO # include #endif -#include -#include "../AsyncDialog.hpp" -#include "../PluginContext.hpp" +#include "../CardinalCommon.hpp" // #define REMOTE_HOST "localhost" #define REMOTE_HOST "192.168.51.1" @@ -99,37 +87,7 @@ struct MenuButton : ui::Button { //////////////////// -static void promptClear(const char* const message, const std::function action) -{ - if (APP->history->isSaved() || APP->scene->rack->hasModules()) - return action(); - - asyncDialog::create(message, action); -} - -static std::string homeDir() -{ -#ifdef ARCH_WIN - if (const char* const userprofile = getenv("USERPROFILE")) - { - return userprofile; - } - else if (const char* const homedrive = getenv("HOMEDRIVE")) - { - if (const char* const homepath = getenv("HOMEPATH")) - return system::join(homedrive, homepath); - } -#else - if (const char* const home = getenv("HOME")) - return home; - else if (struct passwd* const pwd = getpwuid(getuid())) - return pwd->pw_dir; -#endif - return {}; -} - struct FileButton : MenuButton { - CardinalBaseUI* const ui; const bool isStandalone; #ifdef HAVE_LIBLO @@ -153,53 +111,29 @@ struct FileButton : MenuButton { } #endif - FileButton(CardinalBaseUI* const ui2, const bool standalone) - : MenuButton(), ui(ui2), isStandalone(standalone) {} + FileButton(const bool standalone) + : MenuButton(), isStandalone(standalone) {} void onAction(const ActionEvent& e) override { ui::Menu* menu = createMenu(); menu->cornerFlags = BND_CORNER_TOP; menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); - menu->addChild(createMenuItem("New", ""/*RACK_MOD_CTRL_NAME "+N"*/, []() { - // see APP->patch->loadTemplateDialog(); - promptClear("The current patch is unsaved. Clear it and start a new patch?", []() { - APP->patch->loadTemplate(); - }); + menu->addChild(createMenuItem("New", RACK_MOD_CTRL_NAME "+N", []() { + patchUtils::loadTemplateDialog(); })); - menu->addChild(createMenuItem("Open / Import...", ""/*RACK_MOD_CTRL_NAME "+O"*/, [this]() { - // see APP->patch->loadDialog(); - promptClear("The current patch is unsaved. Clear it and open a new patch?", [this]() { - std::string dir; - if (! APP->patch->path.empty()) - dir = system::getDirectory(APP->patch->path); - else - dir = homeDir(); - - Window::FileBrowserOptions opts; - opts.startDir = dir.c_str(); - opts.saving = ui->saving = false; - ui->openFileBrowser(opts); - }); + menu->addChild(createMenuItem("Open / Import...", RACK_MOD_CTRL_NAME "+O", []() { + patchUtils::loadDialog(); })); menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() { - APP->patch->saveDialog(); + // NOTE: will do nothing if path is empty, intentionally + patchUtils::saveDialog(APP->patch->path); }, APP->patch->path.empty())); - menu->addChild(createMenuItem("Save as / Export...", ""/*RACK_MOD_CTRL_NAME "+Shift+S"*/, [this]() { - // see APP->patch->saveAsDialog(); - std::string dir; - if (! APP->patch->path.empty()) - dir = system::getDirectory(APP->patch->path); - else - dir = homeDir(); - - Window::FileBrowserOptions opts; - opts.startDir = dir.c_str(); - opts.saving = ui->saving = true; - ui->openFileBrowser(opts); + menu->addChild(createMenuItem("Save as / Export...", RACK_MOD_CTRL_NAME "+Shift+S", []() { + patchUtils::saveAsDialog(); })); #ifdef HAVE_LIBLO @@ -236,13 +170,8 @@ struct FileButton : MenuButton { } #endif - menu->addChild(createMenuItem("Revert", ""/*RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O"*/, []() { - // APP->patch->revertDialog(); - if (APP->patch->path.empty()) - return; - promptClear("Revert patch to the last saved state?", []{ - APP->patch->loadAction(APP->patch->path); - }); + menu->addChild(createMenuItem("Revert", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+O", []() { + patchUtils::revertDialog(); }, APP->patch->path.empty())); if (isStandalone) { @@ -678,10 +607,9 @@ struct MeterLabel : ui::Label { struct MenuBar : widget::OpaqueWidget { - // CardinalPluginContext* const context; MeterLabel* meterLabel; - MenuBar(CardinalBaseUI* const ui, const bool isStandalone) + MenuBar(const bool isStandalone) : widget::OpaqueWidget() // : context(ctx) { @@ -693,7 +621,7 @@ struct MenuBar : widget::OpaqueWidget { layout->spacing = math::Vec(0, 0); addChild(layout); - FileButton* fileButton = new FileButton(ui, isStandalone); + FileButton* fileButton = new FileButton(isStandalone); fileButton->text = "File"; layout->addChild(fileButton); @@ -746,8 +674,8 @@ widget::Widget* createMenuBar() { return new widget::Widget; } -widget::Widget* createMenuBar(CardinalBaseUI* const ui, const bool isStandalone) { - menuBar::MenuBar* menuBar = new menuBar::MenuBar(ui, isStandalone); +widget::Widget* createMenuBar(const bool isStandalone) { + menuBar::MenuBar* menuBar = new menuBar::MenuBar(isStandalone); return menuBar; } diff --git a/src/override/Scene.cpp b/src/override/Scene.cpp new file mode 100644 index 0000000..914499d --- /dev/null +++ b/src/override/Scene.cpp @@ -0,0 +1,300 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../CardinalCommon.hpp" + + +namespace rack { +namespace app { + + +struct Scene::Internal { + bool heldArrowKeys[4] = {}; +}; + + +Scene::Scene() { + internal = new Internal; + + rackScroll = new RackScrollWidget; + addChild(rackScroll); + + rack = rackScroll->rackWidget; + + menuBar = createMenuBar(); + addChild(menuBar); + + browser = browserCreate(); + browser->hide(); + addChild(browser); +} + + +Scene::~Scene() { + delete internal; +} + + +math::Vec Scene::getMousePos() { + return mousePos; +} + + +void Scene::step() { + // Resize owned descendants + menuBar->box.size.x = box.size.x; + rackScroll->box.pos.y = menuBar->box.size.y; + rackScroll->box.size = box.size.minus(rackScroll->box.pos); + + // Scroll RackScrollWidget with arrow keys + math::Vec arrowDelta; + if (internal->heldArrowKeys[0]) { + arrowDelta.x -= 1; + } + if (internal->heldArrowKeys[1]) { + arrowDelta.x += 1; + } + if (internal->heldArrowKeys[2]) { + arrowDelta.y -= 1; + } + if (internal->heldArrowKeys[3]) { + arrowDelta.y += 1; + } + + if (!arrowDelta.isZero()) { + int mods = APP->window->getMods(); + float arrowSpeed = 32.f; + if ((mods & RACK_MOD_MASK) == RACK_MOD_CTRL) + arrowSpeed /= 4.f; + if ((mods & RACK_MOD_MASK) == GLFW_MOD_SHIFT) + arrowSpeed *= 4.f; + if ((mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) + arrowSpeed /= 16.f; + + rackScroll->offset += arrowDelta * arrowSpeed; + } + + Widget::step(); +} + + +void Scene::draw(const DrawArgs& args) { + Widget::draw(args); +} + + +void Scene::onHover(const HoverEvent& e) { + mousePos = e.pos; + if (mousePos.y < menuBar->box.size.y) { + menuBar->show(); + } + OpaqueWidget::onHover(e); +} + + +void Scene::onDragHover(const DragHoverEvent& e) { + mousePos = e.pos; + OpaqueWidget::onDragHover(e); +} + + +void Scene::onHoverKey(const HoverKeyEvent& e) { + // Key commands that override children + if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { + // DEBUG("key '%d '%c' scancode %d '%c' keyName '%s'", e.key, e.key, e.scancode, e.scancode, e.keyName.c_str()); + if (e.keyName == "n" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + patchUtils::loadTemplateDialog(); + e.consume(this); + } + if (e.keyName == "q" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + APP->window->close(); + e.consume(this); + } + if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + patchUtils::loadDialog(); + e.consume(this); + } + if (e.keyName == "o" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { + patchUtils::revertDialog(); + e.consume(this); + } + if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + // NOTE: will do nothing if path is empty, intentionally + patchUtils::saveDialog(APP->patch->path); + e.consume(this); + } + if (e.keyName == "s" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { + patchUtils::saveAsDialog(); + e.consume(this); + } + if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + APP->history->undo(); + e.consume(this); + } + if (e.keyName == "z" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { + APP->history->redo(); + e.consume(this); + } + if (e.keyName == "-" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + float zoom = std::log2(APP->scene->rackScroll->getZoom()); + zoom *= 2; + zoom = std::ceil(zoom - 0.01f) - 1; + zoom /= 2; + APP->scene->rackScroll->setZoom(std::pow(2.f, zoom)); + e.consume(this); + } + // Numpad has a "+" key, but the main keyboard section hides it under "=" + if ((e.keyName == "=" || e.keyName == "+") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + float zoom = std::log2(APP->scene->rackScroll->getZoom()); + zoom *= 2; + zoom = std::floor(zoom + 0.01f) + 1; + zoom /= 2; + APP->scene->rackScroll->setZoom(std::pow(2.f, zoom)); + e.consume(this); + } + if ((e.keyName == "0") && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + APP->scene->rackScroll->setZoom(1.f); + e.consume(this); + } + if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) { + system::openBrowser("https://vcvrack.com/manual/"); + e.consume(this); + } + if (e.key == GLFW_KEY_F3 && (e.mods & RACK_MOD_MASK) == 0) { + settings::cpuMeter ^= true; + e.consume(this); + } + + // Module selections + if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + rack->selectAll(); + e.consume(this); + } + if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { + rack->deselectAll(); + e.consume(this); + } + if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + if (rack->hasSelection()) { + rack->copyClipboardSelection(); + e.consume(this); + } + } + if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + if (rack->hasSelection()) { + rack->resetSelectionAction(); + e.consume(this); + } + } + if (e.keyName == "r" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + if (rack->hasSelection()) { + rack->randomizeSelectionAction(); + e.consume(this); + } + } + if (e.keyName == "u" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + if (rack->hasSelection()) { + rack->disconnectSelectionAction(); + e.consume(this); + } + } + if (e.keyName == "e" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + if (rack->hasSelection()) { + rack->bypassSelectionAction(!rack->isSelectionBypassed()); + e.consume(this); + } + } + if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + if (rack->hasSelection()) { + rack->cloneSelectionAction(false); + e.consume(this); + } + } + if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { + if (rack->hasSelection()) { + rack->cloneSelectionAction(true); + e.consume(this); + } + } + if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) { + if (rack->hasSelection()) { + rack->deleteSelectionAction(); + e.consume(this); + } + } + } + + // Scroll RackScrollWidget with arrow keys + if (e.action == GLFW_PRESS || e.action == GLFW_RELEASE) { + if (e.key == GLFW_KEY_LEFT) { + internal->heldArrowKeys[0] = (e.action == GLFW_PRESS); + e.consume(this); + } + if (e.key == GLFW_KEY_RIGHT) { + internal->heldArrowKeys[1] = (e.action == GLFW_PRESS); + e.consume(this); + } + if (e.key == GLFW_KEY_UP) { + internal->heldArrowKeys[2] = (e.action == GLFW_PRESS); + e.consume(this); + } + if (e.key == GLFW_KEY_DOWN) { + internal->heldArrowKeys[3] = (e.action == GLFW_PRESS); + e.consume(this); + } + } + + if (e.isConsumed()) + return; + OpaqueWidget::onHoverKey(e); + if (e.isConsumed()) + return; + + // Key commands that can be overridden by children + if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { + if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { + rack->pasteClipboardAction(); + e.consume(this); + } + if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) { + browser->show(); + e.consume(this); + } + } +} + + +void Scene::onPathDrop(const PathDropEvent& e) { + if (e.paths.size() >= 1) { + const std::string& path = e.paths[0]; + std::string extension = system::getExtension(path); + + if (extension == ".vcv") { + patchUtils::loadPathDialog(path); + e.consume(this); + return; + } + if (extension == ".vcvs") { + APP->scene->rack->loadSelection(path); + e.consume(this); + return; + } + } + + OpaqueWidget::onPathDrop(e); +} + + +} // namespace app +} // namespace rack