diff --git a/include/plugin.hpp b/include/plugin.hpp index b9d428cd..bd243473 100644 --- a/include/plugin.hpp +++ b/include/plugin.hpp @@ -66,19 +66,19 @@ struct Plugin { /** OS-dependent library handle */ void *handle = NULL; - /** Used when syncing plugins with the API */ + /** Must be unique. Used for patch files and the VCV store API. + To guarantee uniqueness, it is a good idea to prefix the slug by your "company name" if available, e.g. "MyCompany-MyPlugin" + */ std::string slug; - // Optional plugin metadata - - /** The version of your plugin + /** The version of your plugin (optional) Plugins should follow the versioning scheme described at https://github.com/VCVRack/Rack/issues/266 Do not include the "v" in "v1.0" for example. */ std::string version; - /** URL for plugin homepage */ + /** URL for plugin homepage (optional) */ std::string website; - /** URL for plugin manual */ + /** URL for plugin manual (optional) */ std::string manual; virtual ~Plugin(); @@ -91,11 +91,12 @@ struct Model { std::string slug; /** Human readable name for your model, e.g. "Voltage Controlled Oscillator" */ std::string name; - /** An identifier for the manufacturer, e.g. "foo". Used for saving patches. */ - std::string manufacturerSlug; - /** Human readable name for the manufacturer, e.g. "Foo Modular" */ - std::string manufacturerName; - /** List of tags representing the function(s) of the module */ + /** The name of the manufacturer group of the module. + This might be different than the plugin slug. For example, if you create multiple plugins but want them to be branded similarly, you may use the same manufacturer name in multiple plugins. + You may even have multiple manufacturers in one plugin, although this would be unusual. + */ + std::string manufacturer; + /** List of tags representing the function(s) of the module (optional) */ std::list tags; virtual ~Model() {} diff --git a/include/rack.hpp b/include/rack.hpp index b1bb3150..5d45a5e5 100644 --- a/include/rack.hpp +++ b/include/rack.hpp @@ -19,7 +19,7 @@ namespace rack { template -Model *createModel(std::string manufacturerSlug, std::string manufacturerName, std::string slug, std::string name, Tags... tags) { +Model *createModel(std::string manufacturer, std::string slug, std::string name, Tags... tags) { struct TModel : Model { ModuleWidget *createModuleWidget() override { ModuleWidget *moduleWidget = new TModuleWidget(); @@ -28,10 +28,9 @@ Model *createModel(std::string manufacturerSlug, std::string manufacturerName, s } }; Model *model = new TModel(); + model->manufacturer = manufacturer; model->slug = slug; model->name = name; - model->manufacturerSlug = manufacturerSlug; - model->manufacturerName = manufacturerName; model->tags = {tags...}; return model; } diff --git a/src/app/ModuleWidget.cpp b/src/app/ModuleWidget.cpp index 548bdc06..975cf835 100644 --- a/src/app/ModuleWidget.cpp +++ b/src/app/ModuleWidget.cpp @@ -61,7 +61,7 @@ json_t *ModuleWidget::toJson() { json_t *rootJ = json_object(); // manufacturer - json_object_set_new(rootJ, "manufacturer", json_string(model->manufacturerSlug.c_str())); + json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str())); // model json_object_set_new(rootJ, "model", json_string(model->slug.c_str())); // pos @@ -278,7 +278,7 @@ Menu *ModuleWidget::createContextMenu() { Menu *menu = gScene->createMenu(); MenuLabel *menuLabel = new MenuLabel(); - menuLabel->text = model->manufacturerName + " " + model->name; + menuLabel->text = model->manufacturer + " " + model->name; menu->pushChild(menuLabel); ResetMenuItem *resetItem = new ResetMenuItem(); diff --git a/src/app/RackWidget.cpp b/src/app/RackWidget.cpp index 02da93a3..af00c9a9 100644 --- a/src/app/RackWidget.cpp +++ b/src/app/RackWidget.cpp @@ -204,29 +204,38 @@ void RackWidget::fromJson(json_t *rootJ) { size_t moduleId; json_t *moduleJ; json_array_foreach(modulesJ, moduleId, moduleJ) { - json_t *manufacturerSlugJ = json_object_get(moduleJ, "manufacturer"); - if (!manufacturerSlugJ) { - // Backward compatibility with Rack v0.4 and lower - manufacturerSlugJ = json_object_get(moduleJ, "plugin"); - if (!manufacturerSlugJ) continue; - } + json_t *pluginSlugJ = json_object_get(moduleJ, "plugin"); + if (!pluginSlugJ) continue; json_t *modelSlugJ = json_object_get(moduleJ, "model"); if (!modelSlugJ) continue; - std::string manufacturerSlug = json_string_value(manufacturerSlugJ); + std::string pluginSlug = json_string_value(pluginSlugJ); std::string modelSlug = json_string_value(modelSlugJ); + // Search for plugin + Plugin *plugin = NULL; + for (Plugin *p : gPlugins) { + if (p->slug == pluginSlug) { + plugin = p; + break; + } + } + + if (!plugin) { + message += stringf("Could not find plugin \"%s\" for module \"%s\"\n", pluginSlug.c_str(), modelSlug.c_str()); + continue; + } + // Search for model Model *model = NULL; - for (Plugin *plugin : gPlugins) { - for (Model *m : plugin->models) { - if (m->manufacturerSlug == manufacturerSlug && m->slug == modelSlug) { - model = m; - } + for (Model *m : plugin->models) { + if (m->slug == modelSlug) { + model = m; + break; } } if (!model) { - message += stringf("Could not find \"%s %s\" module\n", manufacturerSlug.c_str(), modelSlug.c_str()); + message += stringf("Could not find module \"%s\" in plugin \"%s\"\n", pluginSlug.c_str(), modelSlug.c_str()); continue; } @@ -424,7 +433,7 @@ struct AddModuleMenuItem : MenuItem { }; struct AddManufacturerMenuItem : MenuItem { - std::string manufacturerName; + std::string manufacturer; Vec modulePos; void onAction(EventAction &e) override { e.consumed = false; @@ -435,7 +444,7 @@ struct AddManufacturerMenuItem : MenuItem { // Collect models which have this manufacturer name for (Plugin *plugin : gPlugins) { for (Model *model : plugin->models) { - if (model->manufacturerName == manufacturerName) { + if (model->manufacturer == manufacturer) { AddModuleMenuItem *item = new AddModuleMenuItem(); item->text = model->name; // item->rightText = model->plugin->slug; @@ -461,7 +470,7 @@ struct SearchModuleField : TextField { AddManufacturerMenuItem *a = dynamic_cast(w); if (!a) continue; - if (a->manufacturerName == text) { + if (a->manufacturer == text) { a->visible = true; } else { @@ -491,17 +500,17 @@ void RackWidget::onMouseDown(EventMouseDown &e) { gFocusedWidget = searchField; // Collect manufacturer names - std::set manufacturerNames; + std::set manufacturers; for (Plugin *plugin : gPlugins) { for (Model *model : plugin->models) { - manufacturerNames.insert(model->manufacturerName); + manufacturers.insert(model->manufacturer); } } // Add menu item for each manufacturer name - for (std::string manufacturerName : manufacturerNames) { + for (std::string manufacturer : manufacturers) { AddManufacturerMenuItem *item = new AddManufacturerMenuItem(); - item->text = manufacturerName; - item->manufacturerName = manufacturerName; + item->text = manufacturer; + item->manufacturer = manufacturer; item->modulePos = e.pos; menu->addChild(item); } diff --git a/src/core/core.cpp b/src/core/core.cpp index a7587071..416b3ca6 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -7,15 +7,15 @@ void init(rack::Plugin *p) { p->version = TOSTRING(VERSION); #endif - p->addModel(createModel("Core", "Core", "AudioInterface", "Audio Interface", EXTERNAL_TAG)); - p->addModel(createModel("Core", "Core", "MIDIToCVInterface", "MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); + p->addModel(createModel("Core", "AudioInterface", "Audio Interface", EXTERNAL_TAG)); + p->addModel(createModel("Core", "MIDIToCVInterface", "MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); - p->addModel(createModel("Core", "Core", "MIDICCToCVInterface", "MIDI CC-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); - p->addModel(createModel("Core", "Core", "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, CLOCK_TAG)); - p->addModel(createModel("Core", "Core", "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); - p->addModel(createModel("Core", "Core", "QuadMIDIToCVInterface", "Quad MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG)); + p->addModel(createModel("Core", "MIDICCToCVInterface", "MIDI CC-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); + p->addModel(createModel("Core", "MIDIClockToCVInterface", "MIDI Clock-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, CLOCK_TAG)); + p->addModel(createModel("Core", "MIDITriggerToCVInterface", "MIDI Trigger-to-CV Interface", MIDI_TAG, EXTERNAL_TAG)); + p->addModel(createModel("Core", "QuadMIDIToCVInterface", "Quad MIDI-to-CV Interface", MIDI_TAG, EXTERNAL_TAG, QUAD_TAG)); - // p->addModel(createModel("Core", "Core", "Bridge", "Bridge")); - p->addModel(createModel("Core", "Core", "Blank", "Blank", BLANK_TAG)); - p->addModel(createModel("Core", "Core", "Notes", "Notes", BLANK_TAG)); + // p->addModel(createModel("Core", "Bridge", "Bridge")); + p->addModel(createModel("Core", "Blank", "Blank", BLANK_TAG)); + p->addModel(createModel("Core", "Notes", "Notes", BLANK_TAG)); }