@@ -82,7 +82,7 @@ struct RackWidget : widget::OpaqueWidget { | |||
bool hasSelection(); | |||
const std::set<ModuleWidget*>& getSelected(); | |||
bool isSelected(ModuleWidget* mw); | |||
json_t* selectionToJson(); | |||
json_t* selectionToJson(bool cables = true); | |||
void loadSelection(std::string path); | |||
void loadSelectionDialog(); | |||
void saveSelection(std::string path); | |||
@@ -322,11 +322,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(false); | |||
cloneAction(true); | |||
e.consume(this); | |||
} | |||
if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
@@ -1004,7 +1004,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("└ with cables", RACK_MOD_SHIFT_NAME "+" RACK_MOD_CTRL_NAME "+D", [=]() { | |||
if (!weakThis) | |||
return; | |||
weakThis->cloneAction(true); | |||
})); | |||
// Delete | |||
@@ -863,7 +863,7 @@ bool RackWidget::isSelected(ModuleWidget* mw) { | |||
return (it != internal->selectedModules.end()); | |||
} | |||
json_t* RackWidget::selectionToJson() { | |||
json_t* RackWidget::selectionToJson(bool cables) { | |||
json_t* rootJ = json_object(); | |||
std::set<engine::Module*> modules; | |||
@@ -884,26 +884,28 @@ json_t* RackWidget::selectionToJson() { | |||
} | |||
json_object_set_new(rootJ, "modules", modulesJ); | |||
// cables | |||
json_t* cablesJ = json_array(); | |||
for (CableWidget* cw : getCompleteCables()) { | |||
// Only add cables attached on both ends to selected modules | |||
engine::Cable* cable = cw->getCable(); | |||
if (!cable || !cable->inputModule || !cable->outputModule) | |||
continue; | |||
const auto inputIt = modules.find(cable->inputModule); | |||
if (inputIt == modules.end()) | |||
continue; | |||
const auto outputIt = modules.find(cable->outputModule); | |||
if (outputIt == modules.end()) | |||
continue; | |||
if (cables) { | |||
// cables | |||
json_t* cablesJ = json_array(); | |||
for (CableWidget* cw : getCompleteCables()) { | |||
// Only add cables attached on both ends to selected modules | |||
engine::Cable* cable = cw->getCable(); | |||
if (!cable || !cable->inputModule || !cable->outputModule) | |||
continue; | |||
const auto inputIt = modules.find(cable->inputModule); | |||
if (inputIt == modules.end()) | |||
continue; | |||
const auto outputIt = modules.find(cable->outputModule); | |||
if (outputIt == modules.end()) | |||
continue; | |||
json_t* cableJ = cable->toJson(); | |||
cw->mergeJson(cableJ); | |||
json_t* cableJ = cable->toJson(); | |||
cw->mergeJson(cableJ); | |||
json_array_append_new(cablesJ, cableJ); | |||
json_array_append_new(cablesJ, cableJ); | |||
} | |||
json_object_set_new(rootJ, "cables", cablesJ); | |||
} | |||
json_object_set_new(rootJ, "cables", cablesJ); | |||
return rootJ; | |||
} | |||
@@ -1056,7 +1058,7 @@ void RackWidget::disconnectSelectionAction() { | |||
} | |||
void RackWidget::cloneSelectionAction(bool cloneCables) { | |||
json_t* rootJ = selectionToJson(); | |||
json_t* rootJ = selectionToJson(cloneCables); | |||
DEFER({json_decref(rootJ);}); | |||
history::ComplexAction* complexAction = new history::ComplexAction; | |||
@@ -1257,7 +1259,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("└ with cables", RACK_MOD_SHIFT_NAME "+" RACK_MOD_CTRL_NAME "+D", [=]() { | |||
cloneSelectionAction(true); | |||
}, n == 0)); | |||
// Delete | |||
@@ -278,13 +278,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(false); | |||
rack->cloneSelectionAction(true); | |||
e.consume(this); | |||
} | |||
} | |||
@@ -34,7 +34,9 @@ void MenuItem::step() { | |||
const float rightPadding = 10.0; | |||
// HACK use APP->window->vg from the window. | |||
// All this does is inspect the font, so it shouldn't modify APP->window->vg and should work when called from a widget::FramebufferWidget for example. | |||
box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + bndLabelWidth(APP->window->vg, -1, rightText.c_str()) + rightPadding; | |||
box.size.x = bndLabelWidth(APP->window->vg, -1, text.c_str()) + rightPadding; | |||
if (!rightText.empty()) | |||
box.size.x += bndLabelWidth(APP->window->vg, -1, rightText.c_str()) - 10.0; | |||
Widget::step(); | |||
} | |||