@@ -110,7 +110,7 @@ struct ModuleWidget : widget::OpaqueWidget { | |||||
void randomizeAction(); | void randomizeAction(); | ||||
void appendDisconnectActions(history::ComplexAction* complexAction); | void appendDisconnectActions(history::ComplexAction* complexAction); | ||||
void disconnectAction(); | void disconnectAction(); | ||||
void cloneAction(); | |||||
void cloneAction(bool cloneCables); | |||||
void bypassAction(bool bypassed); | void bypassAction(bool bypassed); | ||||
/** Deletes `this` */ | /** Deletes `this` */ | ||||
void removeAction(); | void removeAction(); | ||||
@@ -91,7 +91,7 @@ struct RackWidget : widget::OpaqueWidget { | |||||
void resetSelectionAction(); | void resetSelectionAction(); | ||||
void randomizeSelectionAction(); | void randomizeSelectionAction(); | ||||
void disconnectSelectionAction(); | void disconnectSelectionAction(); | ||||
void cloneSelectionAction(); | |||||
void cloneSelectionAction(bool cloneCables); | |||||
void bypassSelectionAction(bool bypassed); | void bypassSelectionAction(bool bypassed); | ||||
bool isSelectionBypassed(); | bool isSelectionBypassed(); | ||||
void deleteSelectionAction(); | void deleteSelectionAction(); | ||||
@@ -317,7 +317,11 @@ void ModuleWidget::onHoverKey(const HoverKeyEvent& e) { | |||||
} | } | ||||
} | } | ||||
if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | 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); | e.consume(this); | ||||
} | } | ||||
if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
@@ -755,7 +759,7 @@ void ModuleWidget::disconnectAction() { | |||||
delete complexAction; | delete complexAction; | ||||
} | } | ||||
void ModuleWidget::cloneAction() { | |||||
void ModuleWidget::cloneAction(bool cloneCables) { | |||||
// history::ComplexAction | // history::ComplexAction | ||||
history::ComplexAction* h = new history::ComplexAction; | history::ComplexAction* h = new history::ComplexAction; | ||||
h->name = "duplicate module"; | h->name = "duplicate module"; | ||||
@@ -787,30 +791,32 @@ void ModuleWidget::cloneAction() { | |||||
hma->setModule(clonedModuleWidget); | hma->setModule(clonedModuleWidget); | ||||
h->push(hma); | 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", [=]() { | menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() { | ||||
if (!weakThis) | if (!weakThis) | ||||
return; | 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 | // Delete | ||||
@@ -1025,7 +1025,7 @@ void RackWidget::disconnectSelectionAction() { | |||||
delete complexAction; | delete complexAction; | ||||
} | } | ||||
void RackWidget::cloneSelectionAction() { | |||||
void RackWidget::cloneSelectionAction(bool cloneCables) { | |||||
json_t* rootJ = selectionToJson(); | json_t* rootJ = selectionToJson(); | ||||
DEFER({json_decref(rootJ);}); | DEFER({json_decref(rootJ);}); | ||||
@@ -1041,35 +1041,37 @@ void RackWidget::cloneSelectionAction() { | |||||
auto p = RackWidget_pasteJson(this, rootJ, complexAction); | auto p = RackWidget_pasteJson(this, rootJ, complexAction); | ||||
// Clone cables attached to inputs of selected modules but outputs of non-selected modules | // 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 | // Duplicate | ||||
menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() { | 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)); | }, n == 0)); | ||||
// Delete | // Delete | ||||
@@ -280,7 +280,13 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { | |||||
} | } | ||||
if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
if (rack->hasSelection()) { | 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); | e.consume(this); | ||||
} | } | ||||
} | } | ||||