| @@ -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(); | |||
| @@ -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(); | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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); | |||
| } | |||
| } | |||