diff --git a/include/app/ModuleWidget.hpp b/include/app/ModuleWidget.hpp index f1071016..8b646d17 100644 --- a/include/app/ModuleWidget.hpp +++ b/include/app/ModuleWidget.hpp @@ -110,7 +110,7 @@ struct ModuleWidget : widget::OpaqueWidget { void randomizeAction(); void appendDisconnectActions(history::ComplexAction* complexAction); void disconnectAction(); - void cloneAction(); + void cloneAction(bool cloneCables); void bypassAction(bool bypassed); /** Deletes `this` */ void removeAction(); diff --git a/include/app/RackWidget.hpp b/include/app/RackWidget.hpp index c397008d..10390e20 100644 --- a/include/app/RackWidget.hpp +++ b/include/app/RackWidget.hpp @@ -91,7 +91,7 @@ struct RackWidget : widget::OpaqueWidget { void resetSelectionAction(); void randomizeSelectionAction(); void disconnectSelectionAction(); - void cloneSelectionAction(); + void cloneSelectionAction(bool cloneCables); void bypassSelectionAction(bool bypassed); bool isSelectionBypassed(); void deleteSelectionAction(); diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index 2096ca48..c2672cd0 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -317,7 +317,11 @@ void ModuleWidget::onHoverKey(const HoverKeyEvent& e) { } } if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { - cloneAction(); + cloneAction(false); + e.consume(this); + } + if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == (RACK_MOD_CTRL | GLFW_MOD_SHIFT)) { + cloneAction(true); e.consume(this); } if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { @@ -755,7 +759,7 @@ void ModuleWidget::disconnectAction() { delete complexAction; } -void ModuleWidget::cloneAction() { +void ModuleWidget::cloneAction(bool cloneCables) { // history::ComplexAction history::ComplexAction* h = new history::ComplexAction; h->name = "duplicate module"; @@ -787,30 +791,32 @@ void ModuleWidget::cloneAction() { hma->setModule(clonedModuleWidget); h->push(hma); - // Clone cables attached to input ports - for (PortWidget* pw : getInputs()) { - for (CableWidget* cw : APP->scene->rack->getCablesOnPort(pw)) { - // Create cable attached to cloned ModuleWidget's input - engine::Cable* clonedCable = new engine::Cable; - clonedCable->inputModule = clonedModule; - clonedCable->inputId = cw->cable->inputId; - // If cable is self-patched, attach to cloned module instead - if (cw->cable->outputModule == module) - clonedCable->outputModule = clonedModule; - else - clonedCable->outputModule = cw->cable->outputModule; - clonedCable->outputId = cw->cable->outputId; - APP->engine->addCable(clonedCable); - - app::CableWidget* clonedCw = new app::CableWidget; - clonedCw->setCable(clonedCable); - clonedCw->color = cw->color; - APP->scene->rack->addCable(clonedCw); - - // history::CableAdd - history::CableAdd* hca = new history::CableAdd; - hca->setCable(clonedCw); - h->push(hca); + if (cloneCables) { + // Clone cables attached to input ports + for (PortWidget* pw : getInputs()) { + for (CableWidget* cw : APP->scene->rack->getCablesOnPort(pw)) { + // Create cable attached to cloned ModuleWidget's input + engine::Cable* clonedCable = new engine::Cable; + clonedCable->inputModule = clonedModule; + clonedCable->inputId = cw->cable->inputId; + // If cable is self-patched, attach to cloned module instead + if (cw->cable->outputModule == module) + clonedCable->outputModule = clonedModule; + else + clonedCable->outputModule = cw->cable->outputModule; + clonedCable->outputId = cw->cable->outputId; + APP->engine->addCable(clonedCable); + + app::CableWidget* clonedCw = new app::CableWidget; + clonedCw->setCable(clonedCable); + clonedCw->color = cw->color; + APP->scene->rack->addCable(clonedCw); + + // history::CableAdd + history::CableAdd* hca = new history::CableAdd; + hca->setCable(clonedCw); + h->push(hca); + } } } @@ -993,7 +999,14 @@ void ModuleWidget::createContextMenu() { menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() { if (!weakThis) return; - weakThis->cloneAction(); + weakThis->cloneAction(false); + })); + + // Duplicate with cables + menu->addChild(createMenuItem("Duplicate with cables", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+D", [=]() { + if (!weakThis) + return; + weakThis->cloneAction(true); })); // Delete diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 2f5f93af..a683d934 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -1025,7 +1025,7 @@ void RackWidget::disconnectSelectionAction() { delete complexAction; } -void RackWidget::cloneSelectionAction() { +void RackWidget::cloneSelectionAction(bool cloneCables) { json_t* rootJ = selectionToJson(); DEFER({json_decref(rootJ);}); @@ -1041,35 +1041,37 @@ void RackWidget::cloneSelectionAction() { auto p = RackWidget_pasteJson(this, rootJ, complexAction); // Clone cables attached to inputs of selected modules but outputs of non-selected modules - for (CableWidget* cw : getCompleteCables()) { - auto inputIt = p.newModuleIds.find(cw->getCable()->inputModule->id); - if (inputIt == p.newModuleIds.end()) - continue; + if (cloneCables) { + for (CableWidget* cw : getCompleteCables()) { + auto inputIt = p.newModuleIds.find(cw->getCable()->inputModule->id); + if (inputIt == p.newModuleIds.end()) + continue; - auto outputIt = p.newModuleIds.find(cw->getCable()->outputModule->id); - if (outputIt != p.newModuleIds.end()) - continue; + auto outputIt = p.newModuleIds.find(cw->getCable()->outputModule->id); + if (outputIt != p.newModuleIds.end()) + continue; - int64_t clonedInputModuleId = inputIt->second; - engine::Module* clonedInputModule = APP->engine->getModule(clonedInputModuleId); + int64_t clonedInputModuleId = inputIt->second; + engine::Module* clonedInputModule = APP->engine->getModule(clonedInputModuleId); - // Create cable attached to cloned ModuleWidget's input - engine::Cable* clonedCable = new engine::Cable; - clonedCable->inputModule = clonedInputModule; - clonedCable->inputId = cw->cable->inputId; - clonedCable->outputModule = cw->cable->outputModule; - clonedCable->outputId = cw->cable->outputId; - APP->engine->addCable(clonedCable); + // Create cable attached to cloned ModuleWidget's input + engine::Cable* clonedCable = new engine::Cable; + clonedCable->inputModule = clonedInputModule; + clonedCable->inputId = cw->cable->inputId; + clonedCable->outputModule = cw->cable->outputModule; + clonedCable->outputId = cw->cable->outputId; + APP->engine->addCable(clonedCable); - app::CableWidget* clonedCw = new app::CableWidget; - clonedCw->setCable(clonedCable); - clonedCw->color = cw->color; - APP->scene->rack->addCable(clonedCw); + app::CableWidget* clonedCw = new app::CableWidget; + clonedCw->setCable(clonedCable); + clonedCw->color = cw->color; + APP->scene->rack->addCable(clonedCw); - // history::CableAdd - history::CableAdd* hca = new history::CableAdd; - hca->setCable(clonedCw); - complexAction->push(hca); + // history::CableAdd + history::CableAdd* hca = new history::CableAdd; + hca->setCable(clonedCw); + complexAction->push(hca); + } } } @@ -1225,7 +1227,12 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) { // Duplicate menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() { - cloneSelectionAction(); + cloneSelectionAction(false); + }, n == 0)); + + // Duplicate with cables + menu->addChild(createMenuItem("Duplicate with cables", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+D", [=]() { + cloneSelectionAction(true); }, n == 0)); // Delete diff --git a/src/app/Scene.cpp b/src/app/Scene.cpp index 56a31782..a622ec78 100644 --- a/src/app/Scene.cpp +++ b/src/app/Scene.cpp @@ -280,7 +280,13 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { } if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { if (rack->hasSelection()) { - rack->cloneSelectionAction(); + 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); } }