diff --git a/include/common.hpp b/include/common.hpp index 4de06be..f5ccbfc 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Cardinal Plugin - * Copyright (C) 2021-2022 Filipe Coelho + * Copyright (C) 2021-2023 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -97,3 +97,11 @@ void async_dialog_message(const char* message, std::function action); // opens a text input dialog, message and text can be null // action is always triggered on close (newText can be null), must be freed if not null void async_dialog_text_input(const char* message, const char* text, std::function action); + +// Cardinal specific config dir (might be equal to userDir) +namespace rack { +namespace asset { +extern std::string configDir; +std::string config(std::string filename = ""); +} +} diff --git a/patches/init/fx.vcv b/patches/templates/fx.vcv similarity index 100% rename from patches/init/fx.vcv rename to patches/templates/fx.vcv diff --git a/patches/init/main.vcv b/patches/templates/main.vcv similarity index 100% rename from patches/init/main.vcv rename to patches/templates/main.vcv diff --git a/patches/init/mini.vcv b/patches/templates/mini.vcv similarity index 100% rename from patches/init/mini.vcv rename to patches/templates/mini.vcv diff --git a/patches/init/native.vcv b/patches/templates/native.vcv similarity index 100% rename from patches/init/native.vcv rename to patches/templates/native.vcv diff --git a/patches/init/synth.vcv b/patches/templates/synth.vcv similarity index 100% rename from patches/init/synth.vcv rename to patches/templates/synth.vcv diff --git a/src/CardinalCommon.cpp b/src/CardinalCommon.cpp index 3611a23..747c751 100644 --- a/src/CardinalCommon.cpp +++ b/src/CardinalCommon.cpp @@ -76,15 +76,15 @@ #endif #if CARDINAL_VARIANT_FX -# define CARDINAL_TEMPLATE_NAME "init/fx.vcv" +# define CARDINAL_VARIANT_NAME "fx" #elif CARDINAL_VARIANT_MINI -# define CARDINAL_TEMPLATE_NAME "init/mini.vcv" +# define CARDINAL_VARIANT_NAME "mini" #elif CARDINAL_VARIANT_NATIVE -# define CARDINAL_TEMPLATE_NAME "init/native.vcv" +# define CARDINAL_VARIANT_NAME "native" #elif CARDINAL_VARIANT_SYNTH -# define CARDINAL_TEMPLATE_NAME "init/synth.vcv" +# define CARDINAL_VARIANT_NAME "synth" #else -# define CARDINAL_TEMPLATE_NAME "init/main.vcv" +# define CARDINAL_VARIANT_NAME "main" #endif #ifdef DISTRHO_OS_WASM @@ -379,12 +379,8 @@ Initializer::Initializer(const CardinalBasePlugin* const plugin, const CardinalB using namespace rack; settings::allowCursorLock = false; - settings::autoCheckUpdates = false; - settings::autosaveInterval = 0; settings::devMode = true; settings::isPlugin = true; - settings::skipLoadOnLaunch = true; - settings::showTipsOnLaunch = false; settings::windowPos = math::Vec(0, 0); #ifdef HEADLESS_BEHAVIOUR settings::headless = true; @@ -446,27 +442,58 @@ Initializer::Initializer(const CardinalBasePlugin* const plugin, const CardinalB #elif defined(ARCH_MAC) asset::systemDir = "/Library/Application Support/Cardinal"; #elif defined(ARCH_WIN) - const std::string commonprogfiles = getSpecialPath(kSpecialPathCommonProgramFiles); - if (! commonprogfiles.empty()) - asset::systemDir = system::join(commonprogfiles, "Cardinal"); + asset::systemDir = system::join(getSpecialPath(kSpecialPathCommonProgramFiles), "Cardinal"); #else asset::systemDir = CARDINAL_PLUGIN_PREFIX "/share/cardinal"; #endif - asset::bundlePath = system::join(asset::systemDir, "PluginManifests"); } } + } - asset::userDir = asset::systemDir; + if (asset::userDir.empty()) + { + #if defined(DISTRHO_OS_WASM) + asset::userDir = "/userfiles"; + #elif defined(ARCH_MAC) + asset::userDir = system::join(homeDir(), "Documents", "Cardinal"); + #elif defined(ARCH_WIN) + asset::userDir = system::join(getSpecialPath(kSpecialPathMyDocuments), "Cardinal"); + #else + if (const char* const xdgEnv = getenv("XDG_DOCUMENTS_DIR")) + asset::userDir = system::join(xdgEnv, "Cardinal"); + else + asset::userDir = system::join(homeDir(), "Documents", "Cardinal"); + #endif + system::createDirectory(asset::userDir); } + #ifndef CARDINAL_COMMON_DSP_ONLY + if (asset::configDir.empty()) + { + #if defined(ARCH_MAC) || defined(ARCH_WIN) || defined(DISTRHO_OS_WASM) + asset::configDir = asset::userDir; + #else + if (const char* const xdgEnv = getenv("XDG_CONFIG_HOME")) + asset::configDir = system::join(xdgEnv, "Cardinal"); + else + asset::configDir = system::join(homeDir(), ".config", "Cardinal"); + system::createDirectory(asset::configDir); + #endif + } + #endif + + if (settings::settingsPath.empty()) + settings::settingsPath = asset::config(CARDINAL_VARIANT_NAME ".json"); + const std::string patchesPath = asset::patchesPath(); #ifdef DISTRHO_OS_WASM - templatePath = system::join(patchesPath, CARDINAL_WASM_WELCOME_TEMPLATE_FILENAME); + templatePath = system::join(patchesPath, CARDINAL_WASM_WELCOME_TEMPLATE_FILENAME ".vcv"); + factoryTemplatePath = system::join(patchesPath, "templates/" CARDINAL_VARIANT_NAME ".vcv"); #else - templatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME); + templatePath = asset::user("templates/" CARDINAL_VARIANT_NAME ".vcv"); + factoryTemplatePath = system::join(patchesPath, "templates/" CARDINAL_VARIANT_NAME ".vcv"); #endif - factoryTemplatePath = system::join(patchesPath, CARDINAL_TEMPLATE_NAME); // Log environment INFO("%s %s %s, compatible with Rack version %s", APP_NAME.c_str(), APP_EDITION.c_str(), CARDINAL_VERSION.c_str(), APP_VERSION.c_str()); @@ -502,6 +529,27 @@ Initializer::Initializer(const CardinalBasePlugin* const plugin, const CardinalB INFO("Initializing plugin browser DB"); app::browserInit(); + if (! plugin->isDummyInstance()) + { + INFO("Loading settings"); + settings::load(); + } + + // enforce settings that do not make sense as anything else + settings::safeMode = false; + settings::token.clear(); + settings::windowMaximized = false; + settings::windowPos = math::Vec(0, 0); + settings::pixelRatio = 0.0; + settings::sampleRate = 0; + settings::threadCount = 1; + settings::frameSwapInterval = 1; + settings::autosaveInterval = 0; + settings::skipLoadOnLaunch = true; + settings::autoCheckUpdates = false; + settings::showTipsOnLaunch = false; + settings::tipIndex = -1; + #ifdef CARDINAL_INIT_OSC_THREAD INFO("Initializing OSC Remote control"); const char* port; @@ -538,6 +586,9 @@ Initializer::~Initializer() } #endif + INFO("Save settings"); + settings::save(); + INFO("Clearing asset paths"); asset::bundlePath.clear(); asset::systemDir.clear(); @@ -596,6 +647,8 @@ std::string getSpecialPath(const SpecialPath type) case kSpecialPathAppData: csidl = CSIDL_APPDATA; break; + case kSpecialPathMyDocuments: + csidl = CSIDL_MYDOCUMENTS; default: return {}; } @@ -617,14 +670,14 @@ char* patchStorageSlug = nullptr; std::string homeDir() { -# ifdef ARCH_WIN + #ifdef ARCH_WIN return getSpecialPath(kSpecialPathUserProfile); -# else + #else if (const char* const home = getenv("HOME")) return home; if (struct passwd* const pwd = getpwuid(getuid())) return pwd->pw_dir; -# endif + #endif return {}; } @@ -719,15 +772,38 @@ void loadSelectionDialog() }); } -void loadTemplateDialog() +void loadTemplate(const bool factory) { -#ifndef HEADLESS_BEHAVIOUR - promptClear("The current patch is unsaved. Clear it and start a new patch?", []() { - APP->patch->loadTemplate(); + try { + APP->patch->load(factory ? APP->patch->factoryTemplatePath : APP->patch->templatePath); + } + catch (Exception& e) { + // if user template failed, try the factory one + if (!factory) + return loadTemplate(true); - if (remoteUtils::RemoteDetails* const remoteDetails = remoteUtils::getRemote()) - if (remoteDetails->autoDeploy) - remoteUtils::sendFullPatchToRemote(remoteDetails); + const std::string message = string::f("Could not load template patch, clearing rack: %s", e.what()); + asyncDialog::create(message.c_str()); + + APP->patch->clear(); + APP->patch->clearAutosave(); + } + + // load() sets the patch's original patch, but we don't want to use that. + APP->patch->path.clear(); + APP->history->setSaved(); + + if (remoteUtils::RemoteDetails* const remoteDetails = remoteUtils::getRemote()) + if (remoteDetails->autoDeploy) + remoteUtils::sendFullPatchToRemote(remoteDetails); +} + + +void loadTemplateDialog(const bool factory) +{ +#ifndef HEADLESS_BEHAVIOUR + promptClear("The current patch is unsaved. Clear it and start a new patch?", [factory]() { + loadTemplate(factory); }); #endif } @@ -772,9 +848,14 @@ static void saveAsDialog(const bool uncompressed) { std::string dir; if (! APP->patch->path.empty()) + { dir = system::getDirectory(APP->patch->path); + } else - dir = homeDir(); + { + dir = asset::user("patches"); + system::createDirectories(dir); + } CardinalPluginContext* const pcontext = static_cast(APP); DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,); @@ -806,6 +887,21 @@ void saveAsDialogUncompressed() #endif } +void saveTemplateDialog() +{ + asyncDialog::create("Overwrite template patch?", []{ + rack::system::createDirectories(system::getDirectory(APP->patch->templatePath)); + + try { + APP->patch->save(APP->patch->templatePath); + } + catch (Exception& e) { + asyncDialog::create(string::f("Could not save template patch: %s", e.what()).c_str()); + return; + } + }); +} + void openBrowser(const std::string& url) { #ifdef DISTRHO_OS_WASM diff --git a/src/CardinalCommon.hpp b/src/CardinalCommon.hpp index 9de27b8..5c6e9c3 100644 --- a/src/CardinalCommon.hpp +++ b/src/CardinalCommon.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Cardinal Plugin - * Copyright (C) 2021-2022 Filipe Coelho + * Copyright (C) 2021-2023 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -44,6 +44,7 @@ enum SpecialPath { kSpecialPathCommonProgramFiles, kSpecialPathProgramFiles, kSpecialPathAppData, + kSpecialPathMyDocuments, }; std::string getSpecialPath(SpecialPath type); #endif @@ -54,6 +55,8 @@ extern char* patchRemoteURL; extern char* patchStorageSlug; #endif +std::string homeDir(); + } // namespace rack // ----------------------------------------------------------------------------------------------------------- @@ -63,11 +66,13 @@ namespace patchUtils { void loadDialog(); void loadPathDialog(const std::string& path, bool asTemplate = false); void loadSelectionDialog(); -void loadTemplateDialog(); +void loadTemplate(bool factory); +void loadTemplateDialog(bool factory); void revertDialog(); void saveDialog(const std::string& path); void saveAsDialog(); void saveAsDialogUncompressed(); +void saveTemplateDialog(); void appendSelectionContextMenu(rack::ui::Menu* menu); void openBrowser(const std::string& url); diff --git a/src/CardinalPlugin.cpp b/src/CardinalPlugin.cpp index cce5b0f..ea1584b 100644 --- a/src/CardinalPlugin.cpp +++ b/src/CardinalPlugin.cpp @@ -198,20 +198,20 @@ public: fWasBypassed(false) { #if CARDINAL_VARIANT_MINI || !defined(HEADLESS) - fWindowParameters[kWindowParameterShowTooltips] = 1.0f; - fWindowParameters[kWindowParameterCableOpacity] = 50.0f; - fWindowParameters[kWindowParameterCableTension] = 75.0f; - fWindowParameters[kWindowParameterRackBrightness] = 100.0f; - fWindowParameters[kWindowParameterHaloBrightness] = 25.0f; + fWindowParameters[kWindowParameterShowTooltips] = rack::settings::tooltips ? 1.f : 0.f; + fWindowParameters[kWindowParameterCableOpacity] = std::min(100.f, std::max(0.f, rack::settings::cableOpacity * 100)); + fWindowParameters[kWindowParameterCableTension] = std::min(100.f, std::max(0.f, rack::settings::cableTension * 100)); + fWindowParameters[kWindowParameterRackBrightness] = std::min(100.f, std::max(0.f, rack::settings::rackBrightness * 100)); + fWindowParameters[kWindowParameterHaloBrightness] = std::min(100.f, std::max(0.f, rack::settings::haloBrightness * 100)); fWindowParameters[kWindowParameterKnobMode] = 0.0f; - fWindowParameters[kWindowParameterWheelKnobControl] = 0.0f; - fWindowParameters[kWindowParameterWheelSensitivity] = 1.0f; - fWindowParameters[kWindowParameterLockModulePositions] = 0.0f; + fWindowParameters[kWindowParameterWheelKnobControl] = rack::settings::knobScroll ? 1.f : 0.f; + fWindowParameters[kWindowParameterWheelSensitivity] = std::min(10.f, std::max(0.1f, rack::settings::knobScrollSensitivity * 1000)); + fWindowParameters[kWindowParameterLockModulePositions] = rack::settings::lockModules ? 1.f : 0.f; fWindowParameters[kWindowParameterUpdateRateLimit] = 0.0f; fWindowParameters[kWindowParameterBrowserSort] = 3.0f; fWindowParameters[kWindowParameterBrowserZoom] = 50.0f; - fWindowParameters[kWindowParameterInvertZoom] = 0.0f; - fWindowParameters[kWindowParameterSqueezeModulePositions] = 1.0f; + fWindowParameters[kWindowParameterInvertZoom] = rack::settings::invertZoom ? 1.f : 0.f; + fWindowParameters[kWindowParameterSqueezeModulePositions] = rack::settings::squeezeModules ? 1.f : 0.f; #endif #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS std::memset(fMiniReportValues, 0, sizeof(fMiniReportValues)); @@ -283,8 +283,6 @@ public: { context->patch->loadTemplate(); context->scene->rackScroll->reset(); - // swap to factory template after first load - context->patch->templatePath = context->patch->factoryTemplatePath; } #ifdef CARDINAL_INIT_OSC_THREAD @@ -463,7 +461,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 1.0f; + parameter.ranges.def = rack::settings::tooltips ? 1.f : 0.f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; break; @@ -475,7 +473,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 50.0f; + parameter.ranges.def = std::min(100.f, std::max(0.f, rack::settings::cableOpacity * 100)); parameter.ranges.min = 0.0f; parameter.ranges.max = 100.0f; break; @@ -487,7 +485,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 75.0f; + parameter.ranges.def = std::min(100.f, std::max(0.f, rack::settings::cableTension * 100)); parameter.ranges.min = 0.0f; parameter.ranges.max = 100.0f; break; @@ -499,7 +497,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 100.0f; + parameter.ranges.def = std::min(100.f, std::max(0.f, rack::settings::rackBrightness * 100)); parameter.ranges.min = 0.0f; parameter.ranges.max = 100.0f; break; @@ -511,7 +509,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 25.0f; + parameter.ranges.def = std::min(100.f, std::max(0.f, rack::settings::haloBrightness * 100)); parameter.ranges.min = 0.0f; parameter.ranges.max = 100.0f; break; @@ -542,7 +540,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 0.0f; + parameter.ranges.def = rack::settings::knobScroll ? 1.f : 0.f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; break; @@ -553,7 +551,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 1.0f; + parameter.ranges.def = std::min(10.f, std::max(0.1f, rack::settings::knobScrollSensitivity * 1000)); parameter.ranges.min = 0.1f; parameter.ranges.max = 10.0f; break; @@ -564,7 +562,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 0.0f; + parameter.ranges.def = rack::settings::lockModules ? 1.f : 0.f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; break; @@ -650,7 +648,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 0.0f; + parameter.ranges.def = rack::settings::invertZoom ? 1.f : 0.f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; break; @@ -661,7 +659,7 @@ protected: #if CARDINAL_VARIANT_MINI && ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS parameter.hints |= kParameterIsHidden; #endif - parameter.ranges.def = 1.0f; + parameter.ranges.def = rack::settings::squeezeModules ? 1.f : 0.f; parameter.ranges.min = 0.0f; parameter.ranges.max = 1.0f; break; diff --git a/src/CardinalUI.cpp b/src/CardinalUI.cpp index acdf923..b28f216 100644 --- a/src/CardinalUI.cpp +++ b/src/CardinalUI.cpp @@ -277,8 +277,8 @@ static void downloadRemotePatchSucceeded(const char* const filename) return; } + context->patch->path.clear(); context->scene->rackScroll->reset(); - context->patch->path = ""; context->history->setSaved(); } #endif @@ -415,8 +415,16 @@ public: setGeometryConstraints(648 * scaleFactor, 538 * scaleFactor); - if (scaleFactor != 1.0) + if (rack::isStandalone() && rack::system::exists(rack::settings::settingsPath)) + { + const double width = std::max(648.f, rack::settings::windowSize.x) * scaleFactor; + const double height = std::max(538.f, rack::settings::windowSize.y) * scaleFactor; + setSize(width, height); + } + else if (scaleFactor != 1.0) + { setSize(DISTRHO_UI_DEFAULT_WIDTH * scaleFactor, DISTRHO_UI_DEFAULT_HEIGHT * scaleFactor); + } rack::window::WindowSetPluginUI(context->window, this); @@ -1137,10 +1145,15 @@ protected: WindowSetInternalSize(context->window, rack::math::Vec(ev.size.getWidth(), ev.size.getHeight())); const double scaleFactor = getScaleFactor(); - char sizeString[64]; - std::snprintf(sizeString, sizeof(sizeString), "%d:%d", - (int)(ev.size.getWidth() / scaleFactor), (int)(ev.size.getHeight() / scaleFactor)); + const int width = static_cast(ev.size.getWidth() / scaleFactor + 0.5); + const int height = static_cast(ev.size.getHeight() / scaleFactor + 0.5); + + char sizeString[64] = {}; + std::snprintf(sizeString, sizeof(sizeString), "%d:%d", width, height); setState("windowSize", sizeString); + + if (rack::isStandalone()) + rack::settings::windowSize = rack::math::Vec(width, height); } void uiFocus(const bool focus, CrossingMode) override @@ -1212,7 +1225,10 @@ protected: } context->patch->path = sfilename; + context->patch->pushRecentPath(sfilename); context->history->setSaved(); + + rack::settings::save(); } #if 0 diff --git a/src/custom/asset.cpp b/src/custom/asset.cpp index dcc4016..c1a2897 100644 --- a/src/custom/asset.cpp +++ b/src/custom/asset.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Cardinal Plugin - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2023 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -36,10 +36,19 @@ extern bool forceBlackScrew; extern bool forceSilverScrew; #endif -std::string userDir; // ignored +std::string configDir; // points to writable config dir (might be equal to userDir) +std::string userDir; // points to common writable dir std::string systemDir; // points to plugin resources dir (or installed/local Rack dir) std::string bundlePath; // points to plugin manifests dir (or empty) +std::string config(std::string filename) { + return system::join(configDir, filename); +} + +std::string user(std::string filename) { + return system::join(userDir, filename); +} + // get rid of "res/" prefix static inline std::string& trim(std::string& s) { @@ -48,11 +57,6 @@ static inline std::string& trim(std::string& s) return s; } -// ignored, returns the same as `system` -std::string user(std::string filename) { - return system(filename); -} - // get system resource, trimming "res/" prefix if we are loaded as a plugin bundle std::string system(std::string filename) { #ifndef HEADLESS diff --git a/src/override/MenuBar.cpp b/src/override/MenuBar.cpp index f8fdcb2..659c7a4 100644 --- a/src/override/MenuBar.cpp +++ b/src/override/MenuBar.cpp @@ -1,6 +1,6 @@ /* * DISTRHO Cardinal Plugin - * Copyright (C) 2021-2022 Filipe Coelho + * Copyright (C) 2021-2023 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -127,20 +127,33 @@ struct FileButton : MenuButton { menu->box.pos = getAbsoluteOffset(math::Vec(0, box.size.y)); #ifndef DISTRHO_OS_WASM - const char* const NewShortcut = RACK_MOD_CTRL_NAME "+N"; + constexpr const char* const NewShortcut = RACK_MOD_CTRL_NAME "+N"; #else - const char* const NewShortcut = ""; + constexpr const char* const NewShortcut = ""; #endif menu->addChild(createMenuItem("New", NewShortcut, []() { - patchUtils::loadTemplateDialog(); + patchUtils::loadTemplateDialog(false); })); #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS #ifndef DISTRHO_OS_WASM + menu->addChild(createMenuItem("New (factory template)", "", []() { + patchUtils::loadTemplateDialog(true); + })); + menu->addChild(createMenuItem("Open / Import...", RACK_MOD_CTRL_NAME "+O", []() { patchUtils::loadDialog(); })); + menu->addChild(createSubmenuItem("Open recent", "", [](ui::Menu* menu) { + for (const std::string& path : settings::recentPatchPaths) { + std::string name = system::getStem(path); + menu->addChild(createMenuItem(name, "", [=]() { + patchUtils::loadPathDialog(path, false); + })); + } + }, settings::recentPatchPaths.empty())); + menu->addChild(createMenuItem("Save", RACK_MOD_CTRL_NAME "+S", []() { // NOTE: will do nothing if path is empty, intentionally patchUtils::saveDialog(APP->patch->path); @@ -172,6 +185,10 @@ struct FileButton : MenuButton { patchUtils::revertDialog(); }, APP->patch->path.empty())); + menu->addChild(createMenuItem("Overwrite template", "", []() { + patchUtils::saveTemplateDialog(); + })); + #if defined(HAVE_LIBLO) || ! DISTRHO_PLUGIN_WANT_DIRECT_ACCESS #ifdef __MOD_DEVICES__ #define REMOTE_NAME "MOD" @@ -728,6 +745,12 @@ struct HelpButton : MenuButton { menu->addChild(new ui::MenuSeparator); + menu->addChild(createMenuItem("Open user folder", "", [=]() { + system::openDirectory(asset::user("")); + })); + + menu->addChild(new ui::MenuSeparator); + menu->addChild(createMenuLabel("Cardinal " + APP_EDITION + " " + CARDINAL_VERSION)); menu->addChild(createMenuLabel("Rack " + APP_VERSION + " Compatible")); } diff --git a/src/override/Scene.cpp b/src/override/Scene.cpp index 58dd32e..184507f 100644 --- a/src/override/Scene.cpp +++ b/src/override/Scene.cpp @@ -261,7 +261,7 @@ void Scene::onHoverKey(const HoverKeyEvent& e) { 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) { - patchUtils::loadTemplateDialog(); + patchUtils::loadTemplateDialog(false); e.consume(this); } if (e.keyName == "q" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {