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