| @@ -39,6 +39,7 @@ struct CableWidget : widget::Widget { | |||||
| Adopts ownership. | Adopts ownership. | ||||
| */ | */ | ||||
| void setCable(engine::Cable* cable); | void setCable(engine::Cable* cable); | ||||
| engine::Cable* getCable(); | |||||
| math::Vec getInputPos(); | math::Vec getInputPos(); | ||||
| math::Vec getOutputPos(); | math::Vec getOutputPos(); | ||||
| json_t* toJson(); | json_t* toJson(); | ||||
| @@ -1,6 +1,4 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <map> | |||||
| #include <app/common.hpp> | #include <app/common.hpp> | ||||
| #include <widget/OpaqueWidget.hpp> | #include <widget/OpaqueWidget.hpp> | ||||
| #include <widget/FramebufferWidget.hpp> | #include <widget/FramebufferWidget.hpp> | ||||
| @@ -49,7 +47,7 @@ struct RackWidget : widget::OpaqueWidget { | |||||
| void clear(); | void clear(); | ||||
| void mergeJson(json_t* rootJ); | void mergeJson(json_t* rootJ); | ||||
| void fromJson(json_t* rootJ); | void fromJson(json_t* rootJ); | ||||
| void pastePresetClipboardAction(); | |||||
| void pasteClipboardAction(); | |||||
| // Module methods | // Module methods | ||||
| @@ -66,7 +64,7 @@ struct RackWidget : widget::OpaqueWidget { | |||||
| void setModulePosNearest(ModuleWidget* mw, math::Vec pos); | void setModulePosNearest(ModuleWidget* mw, math::Vec pos); | ||||
| void setModulePosForce(ModuleWidget* mw, math::Vec pos); | void setModulePosForce(ModuleWidget* mw, math::Vec pos); | ||||
| ModuleWidget* getModule(int64_t moduleId); | ModuleWidget* getModule(int64_t moduleId); | ||||
| std::list<ModuleWidget*> getModules(); | |||||
| std::vector<ModuleWidget*> getModules(); | |||||
| bool hasModules(); | bool hasModules(); | ||||
| void updateModuleOldPositions(); | void updateModuleOldPositions(); | ||||
| history::ComplexAction* getModuleDragAction(); | history::ComplexAction* getModuleDragAction(); | ||||
| @@ -78,13 +76,15 @@ struct RackWidget : widget::OpaqueWidget { | |||||
| void selectAllModules(); | void selectAllModules(); | ||||
| bool hasSelectedModules(); | bool hasSelectedModules(); | ||||
| int getNumSelectedModules(); | int getNumSelectedModules(); | ||||
| std::list<ModuleWidget*> getSelectedModules(); | |||||
| std::vector<ModuleWidget*> getSelectedModules(); | |||||
| json_t* selectedModulesToJson(); | |||||
| void resetSelectedModulesAction(); | void resetSelectedModulesAction(); | ||||
| void randomizeSelectedModulesAction(); | void randomizeSelectedModulesAction(); | ||||
| void disconnectSelectedModulesAction(); | void disconnectSelectedModulesAction(); | ||||
| void cloneSelectedModulesAction(); | void cloneSelectedModulesAction(); | ||||
| void bypassSelectedModulesAction(bool bypassed); | void bypassSelectedModulesAction(bool bypassed); | ||||
| bool areSelectedModulesBypassed(); | bool areSelectedModulesBypassed(); | ||||
| void copyClipboardSelectedModules(); | |||||
| void deleteSelectedModulesAction(); | void deleteSelectedModulesAction(); | ||||
| bool requestSelectedModulePos(math::Vec delta); | bool requestSelectedModulePos(math::Vec delta); | ||||
| void setSelectedModulesPosNearest(math::Vec delta); | void setSelectedModulesPosNearest(math::Vec delta); | ||||
| @@ -108,9 +108,9 @@ struct RackWidget : widget::OpaqueWidget { | |||||
| /** Returns the most recently added complete cable connected to the given Port, i.e. the top of the stack. */ | /** Returns the most recently added complete cable connected to the given Port, i.e. the top of the stack. */ | ||||
| CableWidget* getTopCable(PortWidget* port); | CableWidget* getTopCable(PortWidget* port); | ||||
| CableWidget* getCable(int64_t cableId); | CableWidget* getCable(int64_t cableId); | ||||
| std::list<CableWidget*> getCompleteCables(); | |||||
| std::vector<CableWidget*> getCompleteCables(); | |||||
| /** Returns all cables attached to port, complete or not. */ | /** Returns all cables attached to port, complete or not. */ | ||||
| std::list<CableWidget*> getCablesOnPort(PortWidget* port); | |||||
| std::vector<CableWidget*> getCablesOnPort(PortWidget* port); | |||||
| }; | }; | ||||
| @@ -185,6 +185,10 @@ void CableWidget::setCable(engine::Cable* cable) { | |||||
| } | } | ||||
| } | } | ||||
| engine::Cable* CableWidget::getCable() { | |||||
| return cable; | |||||
| } | |||||
| math::Vec CableWidget::getInputPos() { | math::Vec CableWidget::getInputPos() { | ||||
| if (inputPort) { | if (inputPort) { | ||||
| return inputPort->getRelativeOffset(inputPort->box.zeroPos().getCenter(), APP->scene->rack); | return inputPort->getRelativeOffset(inputPort->box.zeroPos().getCenter(), APP->scene->rack); | ||||
| @@ -318,24 +318,7 @@ void ModuleWidget::onHover(const HoverEvent& e) { | |||||
| } | } | ||||
| void ModuleWidget::onHoverKey(const HoverKeyEvent& e) { | void ModuleWidget::onHoverKey(const HoverKeyEvent& e) { | ||||
| OpaqueWidget::onHoverKey(e); | |||||
| if (e.isConsumed()) | |||||
| return; | |||||
| if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | ||||
| if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| // Don't handle key commands if modules are selected, since it will interfere with Scene's module selection key commands | |||||
| if (!APP->scene->rack->hasSelectedModules()) { | |||||
| resetAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| } | |||||
| if (e.keyName == "r" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| if (!APP->scene->rack->hasSelectedModules()) { | |||||
| randomizeAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| } | |||||
| if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
| copyClipboard(); | copyClipboard(); | ||||
| e.consume(this); | e.consume(this); | ||||
| @@ -345,40 +328,42 @@ void ModuleWidget::onHoverKey(const HoverKeyEvent& e) { | |||||
| e.consume(this); | e.consume(this); | ||||
| } | } | ||||
| 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 (!APP->scene->rack->hasSelectedModules()) { | |||||
| cloneAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| cloneAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) { | |||||
| removeAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| resetAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| if (e.keyName == "r" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| randomizeAction(); | |||||
| e.consume(this); | |||||
| } | } | ||||
| if (e.keyName == "u" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "u" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
| if (!APP->scene->rack->hasSelectedModules()) { | |||||
| disconnectAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| disconnectAction(); | |||||
| e.consume(this); | |||||
| } | } | ||||
| if (e.keyName == "e" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "e" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
| if (!APP->scene->rack->hasSelectedModules()) { | |||||
| bypassAction(!module->isBypassed()); | |||||
| e.consume(this); | |||||
| } | |||||
| } | |||||
| if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) { | |||||
| if (!APP->scene->rack->hasSelectedModules()) { | |||||
| removeAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| bypassAction(!module->isBypassed()); | |||||
| e.consume(this); | |||||
| } | } | ||||
| } | } | ||||
| if (e.action == RACK_HELD) { | if (e.action == RACK_HELD) { | ||||
| // Also handle Delete/Backspace when holding the key while hovering | // Also handle Delete/Backspace when holding the key while hovering | ||||
| if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) { | if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) { | ||||
| if (!APP->scene->rack->hasSelectedModules()) { | |||||
| removeAction(); | |||||
| e.consume(NULL); | |||||
| } | |||||
| removeAction(); | |||||
| e.consume(NULL); | |||||
| } | } | ||||
| } | } | ||||
| if (e.isConsumed()) | |||||
| return; | |||||
| OpaqueWidget::onHoverKey(e); | |||||
| } | } | ||||
| void ModuleWidget::onButton(const ButtonEvent& e) { | void ModuleWidget::onButton(const ButtonEvent& e) { | ||||
| @@ -1077,18 +1062,14 @@ void ModuleWidget::createContextMenu() { | |||||
| weakThis->randomizeAction(); | weakThis->randomizeAction(); | ||||
| })); | })); | ||||
| // Disconnect cables | |||||
| menu->addChild(createMenuItem("Disconnect cables", RACK_MOD_CTRL_NAME "+U", [=]() { | menu->addChild(createMenuItem("Disconnect cables", RACK_MOD_CTRL_NAME "+U", [=]() { | ||||
| if (!weakThis) | if (!weakThis) | ||||
| return; | return; | ||||
| weakThis->disconnectAction(); | weakThis->disconnectAction(); | ||||
| })); | })); | ||||
| menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() { | |||||
| if (!weakThis) | |||||
| return; | |||||
| weakThis->cloneAction(); | |||||
| })); | |||||
| // Bypass | |||||
| std::string bypassText = RACK_MOD_CTRL_NAME "+E"; | std::string bypassText = RACK_MOD_CTRL_NAME "+E"; | ||||
| bool bypassed = module && module->isBypassed(); | bool bypassed = module && module->isBypassed(); | ||||
| if (bypassed) | if (bypassed) | ||||
| @@ -1099,6 +1080,14 @@ void ModuleWidget::createContextMenu() { | |||||
| weakThis->bypassAction(!bypassed); | weakThis->bypassAction(!bypassed); | ||||
| })); | })); | ||||
| // Duplicate | |||||
| menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() { | |||||
| if (!weakThis) | |||||
| return; | |||||
| weakThis->cloneAction(); | |||||
| })); | |||||
| // Delete | |||||
| menu->addChild(createMenuItem("Delete", "Backspace/Delete", [=]() { | menu->addChild(createMenuItem("Delete", "Backspace/Delete", [=]() { | ||||
| if (!weakThis) | if (!weakThis) | ||||
| return; | return; | ||||
| @@ -39,8 +39,7 @@ struct PortTooltip : ui::Tooltip { | |||||
| text += string::f("% .3fV", math::normalizeZero(v)); | text += string::f("% .3fV", math::normalizeZero(v)); | ||||
| } | } | ||||
| // Connected to | // Connected to | ||||
| std::list<CableWidget*> cables = APP->scene->rack->getCablesOnPort(portWidget); | |||||
| for (CableWidget* cable : cables) { | |||||
| for (CableWidget* cable : APP->scene->rack->getCablesOnPort(portWidget)) { | |||||
| PortWidget* otherPw = (portWidget->type == engine::Port::INPUT) ? cable->outputPort : cable->inputPort; | PortWidget* otherPw = (portWidget->type == engine::Port::INPUT) ? cable->outputPort : cable->inputPort; | ||||
| if (!otherPw) | if (!otherPw) | ||||
| continue; | continue; | ||||
| @@ -138,15 +138,6 @@ void RackWidget::onHover(const HoverEvent& e) { | |||||
| void RackWidget::onHoverKey(const HoverKeyEvent& e) { | void RackWidget::onHoverKey(const HoverKeyEvent& e) { | ||||
| OpaqueWidget::onHoverKey(e); | OpaqueWidget::onHoverKey(e); | ||||
| if (e.isConsumed()) | |||||
| return; | |||||
| if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | |||||
| if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| pastePresetClipboardAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void RackWidget::onButton(const ButtonEvent& e) { | void RackWidget::onButton(const ButtonEvent& e) { | ||||
| @@ -268,8 +259,8 @@ void RackWidget::mergeJson(json_t* rootJ) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| // Merge CableWidget JSON | |||||
| json_t* cwJ = cw->toJson(); | json_t* cwJ = cw->toJson(); | ||||
| // Merge cable JSON object | |||||
| json_object_update(cableJ, cwJ); | json_object_update(cableJ, cwJ); | ||||
| json_decref(cwJ); | json_decref(cwJ); | ||||
| } | } | ||||
| @@ -368,7 +359,7 @@ void RackWidget::fromJson(json_t* rootJ) { | |||||
| } | } | ||||
| } | } | ||||
| void RackWidget::pastePresetClipboardAction() { | |||||
| void RackWidget::pasteClipboardAction() { | |||||
| const char* moduleJson = glfwGetClipboardString(APP->window->win); | const char* moduleJson = glfwGetClipboardString(APP->window->win); | ||||
| if (!moduleJson) { | if (!moduleJson) { | ||||
| WARN("Could not get text from clipboard."); | WARN("Could not get text from clipboard."); | ||||
| @@ -614,8 +605,8 @@ ModuleWidget* RackWidget::getModule(int64_t moduleId) { | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| std::list<ModuleWidget*> RackWidget::getModules() { | |||||
| std::list<ModuleWidget*> mws; | |||||
| std::vector<ModuleWidget*> RackWidget::getModules() { | |||||
| std::vector<ModuleWidget*> mws; | |||||
| for (widget::Widget* w : internal->moduleContainer->children) { | for (widget::Widget* w : internal->moduleContainer->children) { | ||||
| ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w); | ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w); | ||||
| assert(mw); | assert(mw); | ||||
| @@ -694,8 +685,8 @@ int RackWidget::getNumSelectedModules() { | |||||
| return count; | return count; | ||||
| } | } | ||||
| std::list<ModuleWidget*> RackWidget::getSelectedModules() { | |||||
| std::list<ModuleWidget*> mws; | |||||
| std::vector<ModuleWidget*> RackWidget::getSelectedModules() { | |||||
| std::vector<ModuleWidget*> mws; | |||||
| for (widget::Widget* w : internal->moduleContainer->children) { | for (widget::Widget* w : internal->moduleContainer->children) { | ||||
| ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w); | ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w); | ||||
| assert(mw); | assert(mw); | ||||
| @@ -705,6 +696,49 @@ std::list<ModuleWidget*> RackWidget::getSelectedModules() { | |||||
| return mws; | return mws; | ||||
| } | } | ||||
| json_t* RackWidget::selectedModulesToJson() { | |||||
| json_t* rootJ = json_object(); | |||||
| std::set<engine::Module*> modules; | |||||
| // modules | |||||
| json_t* modulesJ = json_array(); | |||||
| for (ModuleWidget* mw : getSelectedModules()) { | |||||
| json_t* moduleJ = mw->toJson(); | |||||
| json_array_append_new(modulesJ, moduleJ); | |||||
| modules.insert(mw->getModule()); | |||||
| } | |||||
| 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; | |||||
| json_t* cableJ = cable->toJson(); | |||||
| // Merge CableWidget JSON | |||||
| json_t* cwJ = cw->toJson(); | |||||
| json_object_update(cableJ, cwJ); | |||||
| json_decref(cwJ); | |||||
| json_array_append_new(cablesJ, cableJ); | |||||
| } | |||||
| json_object_set_new(rootJ, "cables", cablesJ); | |||||
| return rootJ; | |||||
| } | |||||
| void RackWidget::resetSelectedModulesAction() { | void RackWidget::resetSelectedModulesAction() { | ||||
| history::ComplexAction* complexAction = new history::ComplexAction; | history::ComplexAction* complexAction = new history::ComplexAction; | ||||
| complexAction->name = "reset modules"; | complexAction->name = "reset modules"; | ||||
| @@ -797,6 +831,14 @@ bool RackWidget::areSelectedModulesBypassed() { | |||||
| return true; | return true; | ||||
| } | } | ||||
| void RackWidget::copyClipboardSelectedModules() { | |||||
| json_t* rootJ = selectedModulesToJson(); | |||||
| DEFER({json_decref(rootJ);}); | |||||
| char* moduleJson = json_dumps(rootJ, JSON_INDENT(2) | JSON_REAL_PRECISION(9)); | |||||
| DEFER({std::free(moduleJson);}); | |||||
| glfwSetClipboardString(APP->window->win, moduleJson); | |||||
| } | |||||
| void RackWidget::deleteSelectedModulesAction() { | void RackWidget::deleteSelectedModulesAction() { | ||||
| history::ComplexAction* complexAction = new history::ComplexAction; | history::ComplexAction* complexAction = new history::ComplexAction; | ||||
| complexAction->name = "remove modules"; | complexAction->name = "remove modules"; | ||||
| @@ -866,6 +908,26 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) { | |||||
| deselectModules(); | deselectModules(); | ||||
| }, n == 0)); | }, n == 0)); | ||||
| // Copy | |||||
| menu->addChild(createMenuItem("Copy", RACK_MOD_CTRL_NAME "+C", [=]() { | |||||
| copyClipboardSelectedModules(); | |||||
| }, n == 0)); | |||||
| // Paste | |||||
| menu->addChild(createMenuItem("Paste", RACK_MOD_CTRL_NAME "+V", [=]() { | |||||
| pasteClipboardAction(); | |||||
| })); | |||||
| // Duplicate | |||||
| menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() { | |||||
| cloneSelectedModulesAction(); | |||||
| }, n == 0)); | |||||
| // Delete | |||||
| menu->addChild(createMenuItem("Delete", "Backspace/Delete", [=]() { | |||||
| deleteSelectedModulesAction(); | |||||
| }, n == 0)); | |||||
| // Initialize | // Initialize | ||||
| menu->addChild(createMenuItem("Initialize", RACK_MOD_CTRL_NAME "+I", [=]() { | menu->addChild(createMenuItem("Initialize", RACK_MOD_CTRL_NAME "+I", [=]() { | ||||
| resetSelectedModulesAction(); | resetSelectedModulesAction(); | ||||
| @@ -881,11 +943,6 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) { | |||||
| disconnectSelectedModulesAction(); | disconnectSelectedModulesAction(); | ||||
| }, n == 0)); | }, n == 0)); | ||||
| // Duplicate | |||||
| menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() { | |||||
| cloneSelectedModulesAction(); | |||||
| }, n == 0)); | |||||
| // Bypass | // Bypass | ||||
| std::string bypassText = RACK_MOD_CTRL_NAME "+E"; | std::string bypassText = RACK_MOD_CTRL_NAME "+E"; | ||||
| bool bypassed = (n > 0) && areSelectedModulesBypassed(); | bool bypassed = (n > 0) && areSelectedModulesBypassed(); | ||||
| @@ -894,11 +951,6 @@ void RackWidget::appendSelectionContextMenu(ui::Menu* menu) { | |||||
| menu->addChild(createMenuItem("Bypass", bypassText, [=]() { | menu->addChild(createMenuItem("Bypass", bypassText, [=]() { | ||||
| bypassSelectedModulesAction(!bypassed); | bypassSelectedModulesAction(!bypassed); | ||||
| }, n == 0)); | }, n == 0)); | ||||
| // Delete | |||||
| menu->addChild(createMenuItem("Delete", "Backspace/Delete", [=]() { | |||||
| deleteSelectedModulesAction(); | |||||
| }, n == 0)); | |||||
| } | } | ||||
| void RackWidget::clearCables() { | void RackWidget::clearCables() { | ||||
| @@ -994,8 +1046,8 @@ CableWidget* RackWidget::getCable(int64_t cableId) { | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| std::list<CableWidget*> RackWidget::getCompleteCables() { | |||||
| std::list<CableWidget*> cws; | |||||
| std::vector<CableWidget*> RackWidget::getCompleteCables() { | |||||
| std::vector<CableWidget*> cws; | |||||
| for (widget::Widget* w : internal->cableContainer->children) { | for (widget::Widget* w : internal->cableContainer->children) { | ||||
| CableWidget* cw = dynamic_cast<CableWidget*>(w); | CableWidget* cw = dynamic_cast<CableWidget*>(w); | ||||
| assert(cw); | assert(cw); | ||||
| @@ -1005,9 +1057,9 @@ std::list<CableWidget*> RackWidget::getCompleteCables() { | |||||
| return cws; | return cws; | ||||
| } | } | ||||
| std::list<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) { | |||||
| std::vector<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) { | |||||
| assert(port); | assert(port); | ||||
| std::list<CableWidget*> cws; | |||||
| std::vector<CableWidget*> cws; | |||||
| for (widget::Widget* w : internal->cableContainer->children) { | for (widget::Widget* w : internal->cableContainer->children) { | ||||
| CableWidget* cw = dynamic_cast<CableWidget*>(w); | CableWidget* cw = dynamic_cast<CableWidget*>(w); | ||||
| assert(cw); | assert(cw); | ||||
| @@ -125,10 +125,6 @@ void Scene::onDragHover(const DragHoverEvent& e) { | |||||
| } | } | ||||
| void Scene::onHoverKey(const HoverKeyEvent& e) { | void Scene::onHoverKey(const HoverKeyEvent& e) { | ||||
| OpaqueWidget::onHoverKey(e); | |||||
| if (e.isConsumed()) | |||||
| return; | |||||
| if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | ||||
| // DEBUG("key '%d '%c' scancode %d '%c' keyName '%s'", e.key, e.key, e.scancode, e.scancode, e.keyName.c_str()); | // DEBUG("key '%d '%c' scancode %d '%c' keyName '%s'", e.key, e.key, e.scancode, e.scancode, e.keyName.c_str()); | ||||
| if (e.keyName == "n" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "n" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
| @@ -220,35 +216,51 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { | |||||
| rack->deselectModules(); | rack->deselectModules(); | ||||
| e.consume(this); | e.consume(this); | ||||
| } | } | ||||
| if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| if (rack->hasSelectedModules()) { | |||||
| rack->copyClipboardSelectedModules(); | |||||
| e.consume(this); | |||||
| } | |||||
| } | |||||
| if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| rack->pasteClipboardAction(); | |||||
| 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) { | ||||
| if (rack->hasSelectedModules()) | |||||
| if (rack->hasSelectedModules()) { | |||||
| rack->resetSelectedModulesAction(); | rack->resetSelectedModulesAction(); | ||||
| e.consume(this); | |||||
| e.consume(this); | |||||
| } | |||||
| } | } | ||||
| if (e.keyName == "r" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "r" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
| if (rack->hasSelectedModules()) | |||||
| if (rack->hasSelectedModules()) { | |||||
| rack->randomizeSelectedModulesAction(); | rack->randomizeSelectedModulesAction(); | ||||
| e.consume(this); | |||||
| } | |||||
| if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| if (rack->hasSelectedModules()) | |||||
| rack->cloneSelectedModulesAction(); | |||||
| e.consume(this); | |||||
| e.consume(this); | |||||
| } | |||||
| } | } | ||||
| if (e.keyName == "u" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "u" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
| if (rack->hasSelectedModules()) | |||||
| if (rack->hasSelectedModules()) { | |||||
| rack->disconnectSelectedModulesAction(); | rack->disconnectSelectedModulesAction(); | ||||
| e.consume(this); | |||||
| e.consume(this); | |||||
| } | |||||
| } | } | ||||
| if (e.keyName == "e" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.keyName == "e" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
| if (rack->hasSelectedModules()) | |||||
| if (rack->hasSelectedModules()) { | |||||
| rack->bypassSelectedModulesAction(!rack->areSelectedModulesBypassed()); | rack->bypassSelectedModulesAction(!rack->areSelectedModulesBypassed()); | ||||
| e.consume(this); | |||||
| e.consume(this); | |||||
| } | |||||
| } | |||||
| if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||||
| if (rack->hasSelectedModules()) { | |||||
| rack->cloneSelectedModulesAction(); | |||||
| e.consume(this); | |||||
| } | |||||
| } | } | ||||
| if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) { | if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) { | ||||
| if (rack->hasSelectedModules()) | |||||
| if (rack->hasSelectedModules()) { | |||||
| rack->deleteSelectedModulesAction(); | rack->deleteSelectedModulesAction(); | ||||
| e.consume(this); | |||||
| e.consume(this); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -279,6 +291,10 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { | |||||
| e.consume(this); | e.consume(this); | ||||
| } | } | ||||
| } | } | ||||
| if (e.isConsumed()) | |||||
| return; | |||||
| OpaqueWidget::onHoverKey(e); | |||||
| } | } | ||||
| void Scene::onPathDrop(const PathDropEvent& e) { | void Scene::onPathDrop(const PathDropEvent& e) { | ||||
| @@ -1171,7 +1171,6 @@ json_t* Engine::toJson() { | |||||
| // modules | // modules | ||||
| json_t* modulesJ = json_array(); | json_t* modulesJ = json_array(); | ||||
| for (Module* module : internal->modules) { | for (Module* module : internal->modules) { | ||||
| // module | |||||
| json_t* moduleJ = module->toJson(); | json_t* moduleJ = module->toJson(); | ||||
| json_array_append_new(modulesJ, moduleJ); | json_array_append_new(modulesJ, moduleJ); | ||||
| } | } | ||||
| @@ -1180,7 +1179,6 @@ json_t* Engine::toJson() { | |||||
| // cables | // cables | ||||
| json_t* cablesJ = json_array(); | json_t* cablesJ = json_array(); | ||||
| for (Cable* cable : internal->cables) { | for (Cable* cable : internal->cables) { | ||||
| // cable | |||||
| json_t* cableJ = cable->toJson(); | json_t* cableJ = cable->toJson(); | ||||
| json_array_append_new(cablesJ, cableJ); | json_array_append_new(cablesJ, cableJ); | ||||
| } | } | ||||