Browse Source

Add module preset copy, paste, load, and save

tags/v0.6.2
Andrew Belt 6 years ago
parent
commit
6c817f8d4c
7 changed files with 321 additions and 76 deletions
  1. +14
    -3
      include/app.hpp
  2. +202
    -26
      src/app/ModuleWidget.cpp
  3. +9
    -3
      src/app/RackScene.cpp
  4. +91
    -39
      src/app/RackWidget.cpp
  5. +1
    -1
      src/app/Toolbar.cpp
  6. +1
    -1
      src/engine.cpp
  7. +3
    -3
      src/main.cpp

+ 14
- 3
include/app.hpp View File

@@ -46,6 +46,8 @@ struct SVGPanel;
static const float RACK_GRID_WIDTH = 15; static const float RACK_GRID_WIDTH = 15;
static const float RACK_GRID_HEIGHT = 380; static const float RACK_GRID_HEIGHT = 380;
static const Vec RACK_GRID_SIZE = Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT); static const Vec RACK_GRID_SIZE = Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
static const std::string PRESET_FILTERS = "VCV Rack module preset (.vcvm):vcvm";
static const std::string PATCH_FILTERS = "VCV Rack patch (.vcv):vcv";




struct ModuleWidget : OpaqueWidget { struct ModuleWidget : OpaqueWidget {
@@ -68,6 +70,12 @@ struct ModuleWidget : OpaqueWidget {


virtual json_t *toJson(); virtual json_t *toJson();
virtual void fromJson(json_t *rootJ); virtual void fromJson(json_t *rootJ);
void copyClipboard();
void pasteClipboard();
void save(std::string filename);
void load(std::string filename);
void loadDialog();
void saveDialog();


virtual void create(); virtual void create();
virtual void _delete(); virtual void _delete();
@@ -154,17 +162,20 @@ struct RackWidget : OpaqueWidget {
void clear(); void clear();
/** Clears the rack and loads the template patch */ /** Clears the rack and loads the template patch */
void reset(); void reset();
void openDialog();
void loadDialog();
void saveDialog(); void saveDialog();
void saveAsDialog(); void saveAsDialog();
/** If `lastPath` is defined, ask the user to reload it */ /** If `lastPath` is defined, ask the user to reload it */
void revert(); void revert();
/** Disconnects all wires */ /** Disconnects all wires */
void disconnect(); void disconnect();
void savePatch(std::string filename);
void loadPatch(std::string filename);
void save(std::string filename);
void load(std::string filename);
json_t *toJson(); json_t *toJson();
void fromJson(json_t *rootJ); void fromJson(json_t *rootJ);
/** Creates a module and adds it to the rack */
ModuleWidget *moduleFromJson(json_t *moduleJ);
void pastePresetClipboard();


void addModule(ModuleWidget *m); void addModule(ModuleWidget *m);
/** Removes the module and transfers ownership to the caller */ /** Removes the module and transfers ownership to the caller */


+ 202
- 26
src/app/ModuleWidget.cpp View File

@@ -2,6 +2,8 @@
#include "engine.hpp" #include "engine.hpp"
#include "plugin.hpp" #include "plugin.hpp"
#include "window.hpp" #include "window.hpp"
#include "asset.hpp"
#include "osdialog.h"




namespace rack { namespace rack {
@@ -68,10 +70,6 @@ json_t *ModuleWidget::toJson() {
json_object_set_new(rootJ, "version", json_string(model->plugin->version.c_str())); json_object_set_new(rootJ, "version", json_string(model->plugin->version.c_str()));
// model // model
json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); json_object_set_new(rootJ, "model", json_string(model->slug.c_str()));
// pos
Vec pos = box.pos.div(RACK_GRID_SIZE).round();
json_t *posJ = json_pack("[i, i]", (int) pos.x, (int) pos.y);
json_object_set_new(rootJ, "pos", posJ);
// params // params
json_t *paramsJ = json_array(); json_t *paramsJ = json_array();
for (ParamWidget *paramWidget : params) { for (ParamWidget *paramWidget : params) {
@@ -91,24 +89,42 @@ json_t *ModuleWidget::toJson() {
} }


void ModuleWidget::fromJson(json_t *rootJ) { void ModuleWidget::fromJson(json_t *rootJ) {
// Check if plugin and model are incorrect
json_t *pluginJ = json_object_get(rootJ, "plugin");
std::string pluginSlug;
if (pluginJ) {
pluginSlug = json_string_value(pluginJ);
if (pluginSlug != model->plugin->slug) {
warn("Plugin %s does not match ModuleWidget's plugin %s.", pluginSlug.c_str(), model->plugin->slug.c_str());
return;
}
}

json_t *modelJ = json_object_get(rootJ, "model");
std::string modelSlug;
if (modelJ) {
modelSlug = json_string_value(modelJ);
if (modelSlug != model->slug) {
warn("Model %s does not match ModuleWidget's model %s.", modelSlug.c_str(), model->slug.c_str());
return;
}
}

// Check plugin version
json_t *versionJ = json_object_get(rootJ, "version");
if (versionJ) {
std::string version = json_string_value(versionJ);
if (version != model->plugin->version) {
info("Patch created with %s version %s, using version %s.", pluginSlug.c_str(), version.c_str(), model->plugin->version.c_str());
}
}

// legacy // legacy
int legacy = 0; int legacy = 0;
json_t *legacyJ = json_object_get(rootJ, "legacy"); json_t *legacyJ = json_object_get(rootJ, "legacy");
if (legacyJ) if (legacyJ)
legacy = json_integer_value(legacyJ); legacy = json_integer_value(legacyJ);


// pos
json_t *posJ = json_object_get(rootJ, "pos");
double x, y;
json_unpack(posJ, "[F, F]", &x, &y);
Vec pos = Vec(x, y);
if (legacy && legacy <= 1) {
box.pos = pos;
}
else {
box.pos = pos.mult(RACK_GRID_SIZE);
}

// params // params
json_t *paramsJ = json_object_get(rootJ, "params"); json_t *paramsJ = json_object_get(rootJ, "params");
size_t i; size_t i;
@@ -146,6 +162,102 @@ void ModuleWidget::fromJson(json_t *rootJ) {
} }
} }


void ModuleWidget::copyClipboard() {
json_t *moduleJ = toJson();
char *moduleJson = json_dumps(moduleJ, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
glfwSetClipboardString(gWindow, moduleJson);
free(moduleJson);
json_decref(moduleJ);
}

void ModuleWidget::pasteClipboard() {
const char *moduleJson = glfwGetClipboardString(gWindow);
if (!moduleJson) {
warn("Could not get text from clipboard.");
return;
}

json_error_t error;
json_t *moduleJ = json_loads(moduleJson, 0, &error);
if (moduleJ) {
fromJson(moduleJ);
json_decref(moduleJ);
}
else {
warn("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
}
}

void ModuleWidget::load(std::string filename) {
info("Loading preset %s", filename.c_str());
FILE *file = fopen(filename.c_str(), "r");
if (!file) {
// Exit silently
return;
}

json_error_t error;
json_t *moduleJ = json_loadf(file, 0, &error);
if (moduleJ) {
fromJson(moduleJ);
json_decref(moduleJ);
}
else {
std::string message = stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
}

fclose(file);
}

void ModuleWidget::save(std::string filename) {
info("Saving preset %s", filename.c_str());
json_t *moduleJ = toJson();
if (!moduleJ)
return;

FILE *file = fopen(filename.c_str(), "w");
if (file) {
json_dumpf(moduleJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
fclose(file);
}

json_decref(moduleJ);
}

void ModuleWidget::loadDialog() {
std::string dir = assetLocal("presets");
systemCreateDirectory(dir);

osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str());
char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters);
if (path) {
load(path);
free(path);
}
osdialog_filters_free(filters);
}

void ModuleWidget::saveDialog() {
std::string dir = assetLocal("presets");
systemCreateDirectory(dir);

osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str());
char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcvm", filters);

if (path) {
std::string pathStr = path;
free(path);
std::string extension = stringExtension(pathStr);
if (extension.empty()) {
pathStr += ".vcvm";
}

save(pathStr);
}
osdialog_filters_free(filters);
}

void ModuleWidget::disconnect() { void ModuleWidget::disconnect() {
for (Port *input : inputs) { for (Port *input : inputs) {
gRackWidget->wireContainer->removeAllWires(input); gRackWidget->wireContainer->removeAllWires(input);
@@ -268,6 +380,20 @@ void ModuleWidget::onHoverKey(EventHoverKey &e) {
return; return;
} }
} break; } break;
case GLFW_KEY_C: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
copyClipboard();
e.consumed = true;
return;
}
} break;
case GLFW_KEY_V: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
pasteClipboard();
e.consumed = true;
return;
}
} break;
case GLFW_KEY_D: { case GLFW_KEY_D: {
if (windowIsModPressed() && !windowIsShiftPressed()) { if (windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->cloneModule(this); gRackWidget->cloneModule(this);
@@ -303,35 +429,63 @@ void ModuleWidget::onDragMove(EventDragMove &e) {
} }




struct DisconnectMenuItem : MenuItem {
struct ModuleDisconnectItem : MenuItem {
ModuleWidget *moduleWidget; ModuleWidget *moduleWidget;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
moduleWidget->disconnect(); moduleWidget->disconnect();
} }
}; };


struct ResetMenuItem : MenuItem {
struct ModuleResetItem : MenuItem {
ModuleWidget *moduleWidget; ModuleWidget *moduleWidget;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
moduleWidget->reset(); moduleWidget->reset();
} }
}; };


struct RandomizeMenuItem : MenuItem {
struct ModuleRandomizeItem : MenuItem {
ModuleWidget *moduleWidget; ModuleWidget *moduleWidget;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
moduleWidget->randomize(); moduleWidget->randomize();
} }
}; };


struct CloneMenuItem : MenuItem {
struct ModuleCopyItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
moduleWidget->copyClipboard();
}
};

struct ModulePasteItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
moduleWidget->pasteClipboard();
}
};

struct ModuleSaveItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
moduleWidget->saveDialog();
}
};

struct ModuleLoadItem : MenuItem {
ModuleWidget *moduleWidget;
void onAction(EventAction &e) override {
moduleWidget->loadDialog();
}
};

struct ModuleCloneItem : MenuItem {
ModuleWidget *moduleWidget; ModuleWidget *moduleWidget;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
gRackWidget->cloneModule(moduleWidget); gRackWidget->cloneModule(moduleWidget);
} }
}; };


struct DeleteMenuItem : MenuItem {
struct ModuleDeleteItem : MenuItem {
ModuleWidget *moduleWidget; ModuleWidget *moduleWidget;
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
gRackWidget->deleteModule(moduleWidget); gRackWidget->deleteModule(moduleWidget);
@@ -347,31 +501,53 @@ Menu *ModuleWidget::createContextMenu() {
menuLabel->text = model->author + " " + model->name + " " + model->plugin->version; menuLabel->text = model->author + " " + model->name + " " + model->plugin->version;
menu->addChild(menuLabel); menu->addChild(menuLabel);


ResetMenuItem *resetItem = new ResetMenuItem();
ModuleResetItem *resetItem = new ModuleResetItem();
resetItem->text = "Initialize"; resetItem->text = "Initialize";
resetItem->rightText = WINDOW_MOD_KEY_NAME "+I"; resetItem->rightText = WINDOW_MOD_KEY_NAME "+I";
resetItem->moduleWidget = this; resetItem->moduleWidget = this;
menu->addChild(resetItem); menu->addChild(resetItem);


RandomizeMenuItem *randomizeItem = new RandomizeMenuItem();
ModuleRandomizeItem *randomizeItem = new ModuleRandomizeItem();
randomizeItem->text = "Randomize"; randomizeItem->text = "Randomize";
randomizeItem->rightText = WINDOW_MOD_KEY_NAME "+R"; randomizeItem->rightText = WINDOW_MOD_KEY_NAME "+R";
randomizeItem->moduleWidget = this; randomizeItem->moduleWidget = this;
menu->addChild(randomizeItem); menu->addChild(randomizeItem);


DisconnectMenuItem *disconnectItem = new DisconnectMenuItem();
ModuleDisconnectItem *disconnectItem = new ModuleDisconnectItem();
disconnectItem->text = "Disconnect cables"; disconnectItem->text = "Disconnect cables";
disconnectItem->rightText = WINDOW_MOD_KEY_NAME "+U"; disconnectItem->rightText = WINDOW_MOD_KEY_NAME "+U";
disconnectItem->moduleWidget = this; disconnectItem->moduleWidget = this;
menu->addChild(disconnectItem); menu->addChild(disconnectItem);


CloneMenuItem *cloneItem = new CloneMenuItem();
ModuleCloneItem *cloneItem = new ModuleCloneItem();
cloneItem->text = "Duplicate"; cloneItem->text = "Duplicate";
cloneItem->rightText = WINDOW_MOD_KEY_NAME "+D"; cloneItem->rightText = WINDOW_MOD_KEY_NAME "+D";
cloneItem->moduleWidget = this; cloneItem->moduleWidget = this;
menu->addChild(cloneItem); menu->addChild(cloneItem);


DeleteMenuItem *deleteItem = new DeleteMenuItem();
ModuleCopyItem *copyItem = new ModuleCopyItem();
copyItem->text = "Copy preset";
copyItem->rightText = WINDOW_MOD_KEY_NAME "+C";
copyItem->moduleWidget = this;
menu->addChild(copyItem);

ModulePasteItem *pasteItem = new ModulePasteItem();
pasteItem->text = "Paste preset";
pasteItem->rightText = WINDOW_MOD_KEY_NAME "+V";
pasteItem->moduleWidget = this;
menu->addChild(pasteItem);

ModuleLoadItem *loadItem = new ModuleLoadItem();
loadItem->text = "Load preset";
loadItem->moduleWidget = this;
menu->addChild(loadItem);

ModuleSaveItem *saveItem = new ModuleSaveItem();
saveItem->text = "Save preset";
saveItem->moduleWidget = this;
menu->addChild(saveItem);

ModuleDeleteItem *deleteItem = new ModuleDeleteItem();
deleteItem->text = "Delete"; deleteItem->text = "Delete";
deleteItem->rightText = "Backspace/Delete"; deleteItem->rightText = "Backspace/Delete";
deleteItem->moduleWidget = this; deleteItem->moduleWidget = this;


+ 9
- 3
src/app/RackScene.cpp View File

@@ -77,7 +77,7 @@ void RackScene::onHoverKey(EventHoverKey &e) {
} break; } break;
case GLFW_KEY_O: { case GLFW_KEY_O: {
if (windowIsModPressed() && !windowIsShiftPressed()) { if (windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->openDialog();
gRackWidget->loadDialog();
e.consumed = true; e.consumed = true;
} }
if (windowIsModPressed() && windowIsShiftPressed()) { if (windowIsModPressed() && windowIsShiftPressed()) {
@@ -95,6 +95,12 @@ void RackScene::onHoverKey(EventHoverKey &e) {
e.consumed = true; e.consumed = true;
} }
} break; } break;
case GLFW_KEY_V: {
if (windowIsModPressed() && !windowIsShiftPressed()) {
gRackWidget->pastePresetClipboard();
e.consumed = true;
}
} break;
case GLFW_KEY_ENTER: case GLFW_KEY_ENTER:
case GLFW_KEY_KP_ENTER: { case GLFW_KEY_KP_ENTER: {
appModuleBrowserCreate(); appModuleBrowserCreate();
@@ -109,9 +115,9 @@ void RackScene::onHoverKey(EventHoverKey &e) {


void RackScene::onPathDrop(EventPathDrop &e) { void RackScene::onPathDrop(EventPathDrop &e) {
if (e.paths.size() >= 1) { if (e.paths.size() >= 1) {
const std::string& firstPath = e.paths.front();
const std::string &firstPath = e.paths.front();
if (stringExtension(firstPath) == "vcv") { if (stringExtension(firstPath) == "vcv") {
gRackWidget->loadPatch(firstPath);
gRackWidget->load(firstPath);
e.consumed = true; e.consumed = true;
} }
} }


+ 91
- 39
src/app/RackWidget.cpp View File

@@ -12,9 +12,6 @@
namespace rack { namespace rack {




static const char *FILTERS = "VCV Rack patch (.vcv):vcv";


struct ModuleContainer : Widget { struct ModuleContainer : Widget {
void draw(NVGcontext *vg) override { void draw(NVGcontext *vg) override {
// Draw shadows behind each ModuleWidget first, so the shadow doesn't overlap the front. // Draw shadows behind each ModuleWidget first, so the shadow doesn't overlap the front.
@@ -66,12 +63,12 @@ void RackWidget::reset() {
if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "Clear patch and start over?")) { if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "Clear patch and start over?")) {
clear(); clear();
// Fails silently if file does not exist // Fails silently if file does not exist
loadPatch(assetLocal("template.vcv"));
load(assetLocal("template.vcv"));
lastPath = ""; lastPath = "";
} }
} }


void RackWidget::openDialog() {
void RackWidget::loadDialog() {
std::string dir; std::string dir;
if (lastPath.empty()) { if (lastPath.empty()) {
dir = assetLocal("patches"); dir = assetLocal("patches");
@@ -80,10 +77,10 @@ void RackWidget::openDialog() {
else { else {
dir = stringDirectory(lastPath); dir = stringDirectory(lastPath);
} }
osdialog_filters *filters = osdialog_filters_parse(FILTERS);
osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str());
char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters); char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters);
if (path) { if (path) {
loadPatch(path);
load(path);
lastPath = path; lastPath = path;
free(path); free(path);
} }
@@ -92,7 +89,7 @@ void RackWidget::openDialog() {


void RackWidget::saveDialog() { void RackWidget::saveDialog() {
if (!lastPath.empty()) { if (!lastPath.empty()) {
savePatch(lastPath);
save(lastPath);
} }
else { else {
saveAsDialog(); saveAsDialog();
@@ -110,7 +107,7 @@ void RackWidget::saveAsDialog() {
dir = stringDirectory(lastPath); dir = stringDirectory(lastPath);
filename = stringFilename(lastPath); filename = stringFilename(lastPath);
} }
osdialog_filters *filters = osdialog_filters_parse(FILTERS);
osdialog_filters *filters = osdialog_filters_parse(PATCH_FILTERS.c_str());
char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), filename.c_str(), filters); char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), filename.c_str(), filters);


if (path) { if (path) {
@@ -121,19 +118,19 @@ void RackWidget::saveAsDialog() {
pathStr += ".vcv"; pathStr += ".vcv";
} }


savePatch(pathStr);
save(pathStr);
lastPath = pathStr; lastPath = pathStr;
} }
osdialog_filters_free(filters); osdialog_filters_free(filters);
} }


void RackWidget::savePatch(std::string path) {
info("Saving patch %s", path.c_str());
void RackWidget::save(std::string filename) {
info("Saving patch %s", filename.c_str());
json_t *rootJ = toJson(); json_t *rootJ = toJson();
if (!rootJ) if (!rootJ)
return; return;


FILE *file = fopen(path.c_str(), "w");
FILE *file = fopen(filename.c_str(), "w");
if (file) { if (file) {
json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9)); json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
fclose(file); fclose(file);
@@ -142,9 +139,9 @@ void RackWidget::savePatch(std::string path) {
json_decref(rootJ); json_decref(rootJ);
} }


void RackWidget::loadPatch(std::string path) {
info("Loading patch %s", path.c_str());
FILE *file = fopen(path.c_str(), "r");
void RackWidget::load(std::string filename) {
info("Loading patch %s", filename.c_str());
FILE *file = fopen(filename.c_str(), "r");
if (!file) { if (!file) {
// Exit silently // Exit silently
return; return;
@@ -158,7 +155,7 @@ void RackWidget::loadPatch(std::string path) {
json_decref(rootJ); json_decref(rootJ);
} }
else { else {
std::string message = stringf("JSON parsing error at %s %d:%d %s\n", error.source, error.line, error.column, error.text);
std::string message = stringf("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str()); osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
} }


@@ -169,7 +166,7 @@ void RackWidget::revert() {
if (lastPath.empty()) if (lastPath.empty())
return; return;
if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "Revert patch to the last saved state?")) { if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "Revert patch to the last saved state?")) {
loadPatch(lastPath);
load(lastPath);
} }
} }


@@ -203,6 +200,12 @@ json_t *RackWidget::toJson() {
moduleId++; moduleId++;
// module // module
json_t *moduleJ = moduleWidget->toJson(); json_t *moduleJ = moduleWidget->toJson();
{
// pos
Vec pos = moduleWidget->box.pos.div(RACK_GRID_SIZE).round();
json_t *posJ = json_pack("[i, i]", (int) pos.x, (int) pos.y);
json_object_set_new(moduleJ, "pos", posJ);
}
json_array_append_new(modulesJ, moduleJ); json_array_append_new(modulesJ, moduleJ);
} }
json_object_set_new(rootJ, "modules", modulesJ); json_object_set_new(rootJ, "modules", modulesJ);
@@ -275,25 +278,30 @@ void RackWidget::fromJson(json_t *rootJ) {
json_object_set(moduleJ, "legacy", json_integer(legacy)); json_object_set(moduleJ, "legacy", json_integer(legacy));
} }


json_t *pluginSlugJ = json_object_get(moduleJ, "plugin");
if (!pluginSlugJ) continue;
json_t *modelSlugJ = json_object_get(moduleJ, "model");
if (!modelSlugJ) continue;
std::string pluginSlug = json_string_value(pluginSlugJ);
std::string modelSlug = json_string_value(modelSlugJ);
ModuleWidget *moduleWidget = moduleFromJson(moduleJ);

if (moduleWidget) {
// pos
json_t *posJ = json_object_get(moduleJ, "pos");
double x, y;
json_unpack(posJ, "[F, F]", &x, &y);
Vec pos = Vec(x, y);
if (legacy && legacy <= 1) {
moduleWidget->box.pos = pos;
}
else {
moduleWidget->box.pos = pos.mult(RACK_GRID_SIZE);
}


Model *model = pluginGetModel(pluginSlug, modelSlug);
if (!model) {
moduleWidgets[moduleId] = moduleWidget;
}
else {
json_t *pluginSlugJ = json_object_get(moduleJ, "plugin");
json_t *modelSlugJ = json_object_get(moduleJ, "model");
std::string pluginSlug = json_string_value(pluginSlugJ);
std::string modelSlug = json_string_value(modelSlugJ);
message += stringf("Could not find module \"%s\" of plugin \"%s\"\n", modelSlug.c_str(), pluginSlug.c_str()); message += stringf("Could not find module \"%s\" of plugin \"%s\"\n", modelSlug.c_str(), pluginSlug.c_str());
continue;
} }

// Create ModuleWidget
ModuleWidget *moduleWidget = model->createModuleWidget();
assert(moduleWidget);
moduleWidget->fromJson(moduleJ);
moduleContainer->addChild(moduleWidget);
moduleWidgets[moduleId] = moduleWidget;
} }


// wires // wires
@@ -355,6 +363,53 @@ void RackWidget::fromJson(json_t *rootJ) {
} }
} }


ModuleWidget *RackWidget::moduleFromJson(json_t *moduleJ) {
// Get slugs
json_t *pluginSlugJ = json_object_get(moduleJ, "plugin");
if (!pluginSlugJ)
return NULL;
json_t *modelSlugJ = json_object_get(moduleJ, "model");
if (!modelSlugJ)
return NULL;
std::string pluginSlug = json_string_value(pluginSlugJ);
std::string modelSlug = json_string_value(modelSlugJ);

// Get Model
Model *model = pluginGetModel(pluginSlug, modelSlug);
if (!model)
return NULL;

// Create ModuleWidget
ModuleWidget *moduleWidget = model->createModuleWidget();
assert(moduleWidget);
moduleWidget->fromJson(moduleJ);
moduleContainer->addChild(moduleWidget);
return moduleWidget;
}

void RackWidget::pastePresetClipboard() {
const char *moduleJson = glfwGetClipboardString(gWindow);
if (!moduleJson) {
warn("Could not get text from clipboard.");
return;
}

json_error_t error;
json_t *moduleJ = json_loads(moduleJson, 0, &error);
if (moduleJ) {
ModuleWidget *moduleWidget = moduleFromJson(moduleJ);
// Set moduleWidget position
Rect newBox = moduleWidget->box;
newBox.pos = lastMousePos.minus(newBox.size.div(2));
requestModuleBoxNearest(moduleWidget, newBox);

json_decref(moduleJ);
}
else {
warn("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
}
}

void RackWidget::addModule(ModuleWidget *m) { void RackWidget::addModule(ModuleWidget *m) {
moduleContainer->addChild(m); moduleContainer->addChild(m);
m->create(); m->create();
@@ -366,16 +421,13 @@ void RackWidget::deleteModule(ModuleWidget *m) {
} }


void RackWidget::cloneModule(ModuleWidget *m) { void RackWidget::cloneModule(ModuleWidget *m) {
// Create new module from model
ModuleWidget *clonedModuleWidget = m->model->createModuleWidget();
// JSON serialization is the most straightforward way to do this // JSON serialization is the most straightforward way to do this
json_t *moduleJ = m->toJson(); json_t *moduleJ = m->toJson();
clonedModuleWidget->fromJson(moduleJ);
ModuleWidget *clonedModuleWidget = moduleFromJson(moduleJ);
json_decref(moduleJ); json_decref(moduleJ);
Rect clonedBox = clonedModuleWidget->box; Rect clonedBox = clonedModuleWidget->box;
clonedBox.pos = m->box.pos; clonedBox.pos = m->box.pos;
requestModuleBoxNearest(clonedModuleWidget, clonedBox); requestModuleBoxNearest(clonedModuleWidget, clonedBox);
addModule(clonedModuleWidget);
} }


bool RackWidget::requestModuleBox(ModuleWidget *m, Rect box) { bool RackWidget::requestModuleBox(ModuleWidget *m, Rect box) {
@@ -438,7 +490,7 @@ void RackWidget::step() {


// Autosave every 15 seconds // Autosave every 15 seconds
if (gGuiFrame % (60 * 15) == 0) { if (gGuiFrame % (60 * 15) == 0) {
savePatch(assetLocal("autosave.vcv"));
save(assetLocal("autosave.vcv"));
settingsSave(assetLocal("settings.json")); settingsSave(assetLocal("settings.json"));
} }




+ 1
- 1
src/app/Toolbar.cpp View File

@@ -45,7 +45,7 @@ struct OpenButton : TooltipIconButton {
tooltipText = "Open (" WINDOW_MOD_KEY_NAME "+O)"; tooltipText = "Open (" WINDOW_MOD_KEY_NAME "+O)";
} }
void onAction(EventAction &e) override { void onAction(EventAction &e) override {
gRackWidget->openDialog();
gRackWidget->loadDialog();
} }
}; };




+ 1
- 1
src/engine.cpp View File

@@ -295,9 +295,9 @@ void engineSetParamSmooth(Module *module, int paramId, float value) {
if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) { if (smoothModule && !(smoothModule == module && smoothParamId == paramId)) {
smoothModule->params[smoothParamId].value = smoothValue; smoothModule->params[smoothParamId].value = smoothValue;
} }
smoothModule = module;
smoothParamId = paramId; smoothParamId = paramId;
smoothValue = value; smoothValue = value;
smoothModule = module;
} }


void engineSetSampleRate(float newSampleRate) { void engineSetSampleRate(float newSampleRate) {


+ 3
- 3
src/main.cpp View File

@@ -73,13 +73,13 @@ int main(int argc, char* argv[]) {
else { else {
// Load autosave // Load autosave
std::string oldLastPath = gRackWidget->lastPath; std::string oldLastPath = gRackWidget->lastPath;
gRackWidget->loadPatch(assetLocal("autosave.vcv"));
gRackWidget->load(assetLocal("autosave.vcv"));
gRackWidget->lastPath = oldLastPath; gRackWidget->lastPath = oldLastPath;
} }
} }
else { else {
// Load patch // Load patch
gRackWidget->loadPatch(patchFile);
gRackWidget->load(patchFile);
} }


engineStart(); engineStart();
@@ -87,7 +87,7 @@ int main(int argc, char* argv[]) {
engineStop(); engineStop();


// Destroy namespaces // Destroy namespaces
gRackWidget->savePatch(assetLocal("autosave.vcv"));
gRackWidget->save(assetLocal("autosave.vcv"));
settingsSave(assetLocal("settings.json")); settingsSave(assetLocal("settings.json"));
appDestroy(); appDestroy();
windowDestroy(); windowDestroy();


Loading…
Cancel
Save