@@ -79,9 +79,10 @@ struct ModuleWidget : widget::OpaqueWidget { | |||
json_t* toJson(); | |||
void fromJson(json_t* rootJ); | |||
void pasteJsonAction(json_t* rootJ); | |||
/** Returns whether paste was successful. */ | |||
bool pasteJsonAction(json_t* rootJ); | |||
void copyClipboard(); | |||
void pasteClipboardAction(); | |||
bool pasteClipboardAction(); | |||
void load(std::string filename); | |||
void loadAction(std::string filename); | |||
void loadTemplate(); | |||
@@ -20,6 +20,7 @@ struct Cable { | |||
json_t* toJson(); | |||
void fromJson(json_t* rootJ); | |||
INTERNAL static void jsonStripIds(json_t* rootJ); | |||
}; | |||
@@ -757,7 +757,7 @@ struct HelpButton : MenuButton { | |||
APP->scene->addChild(tipWindowCreate()); | |||
})); | |||
menu->addChild(createMenuItem("Manual", "F1", [=]() { | |||
menu->addChild(createMenuItem("User manual", "F1", [=]() { | |||
system::openBrowser("https://vcvrack.com/manual/"); | |||
})); | |||
@@ -312,8 +312,9 @@ void ModuleWidget::onHoverKey(const HoverKeyEvent& e) { | |||
e.consume(this); | |||
} | |||
if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
pasteClipboardAction(); | |||
e.consume(this); | |||
if (pasteClipboardAction()) { | |||
e.consume(this); | |||
} | |||
} | |||
if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
cloneAction(); | |||
@@ -342,7 +343,9 @@ void ModuleWidget::onHoverKey(const HoverKeyEvent& e) { | |||
return; | |||
} | |||
if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
system::openBrowser(model->getManualUrl()); | |||
std::string manualUrl = model->getManualUrl(); | |||
if (!manualUrl.empty()) | |||
system::openBrowser(manualUrl); | |||
e.consume(this); | |||
} | |||
} | |||
@@ -468,7 +471,7 @@ void ModuleWidget::fromJson(json_t* moduleJ) { | |||
APP->engine->moduleFromJson(module, moduleJ); | |||
} | |||
void ModuleWidget::pasteJsonAction(json_t* moduleJ) { | |||
bool ModuleWidget::pasteJsonAction(json_t* moduleJ) { | |||
engine::Module::jsonStripIds(moduleJ); | |||
json_t* oldModuleJ = toJson(); | |||
@@ -479,7 +482,7 @@ void ModuleWidget::pasteJsonAction(json_t* moduleJ) { | |||
catch (Exception& e) { | |||
WARN("%s", e.what()); | |||
json_decref(oldModuleJ); | |||
return; | |||
return false; | |||
} | |||
// history::ModuleChange | |||
@@ -489,6 +492,7 @@ void ModuleWidget::pasteJsonAction(json_t* moduleJ) { | |||
h->oldModuleJ = oldModuleJ; | |||
h->newModuleJ = moduleJ; | |||
APP->history->push(h); | |||
return true; | |||
} | |||
void ModuleWidget::copyClipboard() { | |||
@@ -501,22 +505,22 @@ void ModuleWidget::copyClipboard() { | |||
glfwSetClipboardString(APP->window->win, json); | |||
} | |||
void ModuleWidget::pasteClipboardAction() { | |||
bool ModuleWidget::pasteClipboardAction() { | |||
const char* json = glfwGetClipboardString(APP->window->win); | |||
if (!json) { | |||
WARN("Could not get text from clipboard."); | |||
return; | |||
return false; | |||
} | |||
json_error_t error; | |||
json_t* moduleJ = json_loads(json, 0, &error); | |||
if (!moduleJ) { | |||
WARN("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); | |||
return; | |||
return false; | |||
} | |||
DEFER({json_decref(moduleJ);}); | |||
pasteJsonAction(moduleJ); | |||
return pasteJsonAction(moduleJ); | |||
} | |||
void ModuleWidget::load(std::string filename) { | |||
@@ -758,6 +762,9 @@ void ModuleWidget::cloneAction() { | |||
// JSON serialization is the obvious way to do this | |||
json_t* moduleJ = toJson(); | |||
DEFER({ | |||
json_decref(moduleJ); | |||
}); | |||
engine::Module::jsonStripIds(moduleJ); | |||
// Clone Module | |||
@@ -769,7 +776,6 @@ void ModuleWidget::cloneAction() { | |||
catch (Exception& e) { | |||
WARN("%s", e.what()); | |||
} | |||
json_decref(moduleJ); | |||
APP->engine->addModule(clonedModule); | |||
// Clone ModuleWidget | |||
@@ -454,7 +454,7 @@ static PasteJsonReturn RackWidget_pasteJson(RackWidget* that, json_t* rootJ, his | |||
size_t cableIndex; | |||
json_t* cableJ; | |||
json_array_foreach(cablesJ, cableIndex, cableJ) { | |||
json_object_del(cableJ, "id"); | |||
engine::Cable::jsonStripIds(cableJ); | |||
// Remap old module IDs to new IDs | |||
json_t* inputModuleIdJ = json_object_get(cableJ, "inputModuleId"); | |||
@@ -600,6 +600,9 @@ void RackWidget::removeModule(ModuleWidget* m) { | |||
// Disconnect cables | |||
m->disconnect(); | |||
// Deselect module if selected | |||
internal->selectedModules.erase(m); | |||
// Remove module from ModuleContainer | |||
internal->moduleContainer->removeChild(m); | |||
} | |||
@@ -1105,7 +1108,9 @@ void RackWidget::deleteSelectionAction() { | |||
history::ComplexAction* complexAction = new history::ComplexAction; | |||
complexAction->name = "remove modules"; | |||
for (ModuleWidget* mw : getSelected()) { | |||
// Copy selected set since removing ModuleWidgets modifies it. | |||
std::set<ModuleWidget*> selectedModules = getSelected(); | |||
for (ModuleWidget* mw : selectedModules) { | |||
mw->appendDisconnectActions(complexAction); | |||
// history::ModuleRemove | |||
@@ -136,6 +136,7 @@ void Scene::onDragHover(const DragHoverEvent& e) { | |||
void Scene::onHoverKey(const HoverKeyEvent& e) { | |||
// Key commands that override children | |||
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()); | |||
if (e.keyName == "n" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
@@ -193,10 +194,6 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { | |||
settings::zoom = 0.f; | |||
e.consume(this); | |||
} | |||
if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) { | |||
browser->show(); | |||
e.consume(this); | |||
} | |||
if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == 0) { | |||
system::openBrowser("https://vcvrack.com/manual/"); | |||
e.consume(this); | |||
@@ -211,13 +208,6 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { | |||
// menuBar->hide(); | |||
e.consume(this); | |||
} | |||
// Alternate key command for exiting fullscreen, since F11 doesn't work reliably on Mac due to "Show desktop" OS binding. | |||
if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) { | |||
if (APP->window->isFullScreen()) { | |||
APP->window->setFullScreen(false); | |||
e.consume(this); | |||
} | |||
} | |||
// Module selections | |||
if (e.keyName == "a" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
@@ -234,10 +224,6 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { | |||
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 (rack->hasSelection()) { | |||
rack->resetSelectionAction(); | |||
@@ -307,6 +293,27 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { | |||
if (e.isConsumed()) | |||
return; | |||
OpaqueWidget::onHoverKey(e); | |||
if (e.isConsumed()) | |||
return; | |||
// Key commands that can be overridden by children | |||
if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { | |||
// Alternate key command for exiting fullscreen, since F11 doesn't work reliably on Mac due to "Show desktop" OS binding. | |||
if (e.key == GLFW_KEY_ESCAPE && (e.mods & RACK_MOD_MASK) == 0) { | |||
if (APP->window->isFullScreen()) { | |||
APP->window->setFullScreen(false); | |||
e.consume(this); | |||
} | |||
} | |||
if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | |||
rack->pasteClipboardAction(); | |||
e.consume(this); | |||
} | |||
if ((e.key == GLFW_KEY_ENTER || e.key == GLFW_KEY_KP_ENTER) && (e.mods & RACK_MOD_MASK) == 0) { | |||
browser->show(); | |||
e.consume(this); | |||
} | |||
} | |||
} | |||
@@ -60,5 +60,10 @@ void Cable::fromJson(json_t* rootJ) { | |||
} | |||
void Cable::jsonStripIds(json_t* rootJ) { | |||
json_object_del(rootJ, "id"); | |||
} | |||
} // namespace engine | |||
} // namespace rack |