@@ -10,6 +10,11 @@ | |||||
namespace rack { | namespace rack { | ||||
namespace ui { | |||||
struct Menu; | |||||
} // namespace app | |||||
namespace app { | namespace app { | ||||
struct ModuleWidget; | struct ModuleWidget; | ||||
} // namespace app | } // namespace app | ||||
@@ -65,6 +70,9 @@ struct Model { | |||||
std::string getFullName(); | std::string getFullName(); | ||||
std::string getFactoryPresetDirectory(); | std::string getFactoryPresetDirectory(); | ||||
std::string getUserPresetDirectory(); | std::string getUserPresetDirectory(); | ||||
/** Returns the module or plugin manual URL, whichever exists. */ | |||||
std::string getManualUrl(); | |||||
void appendContextMenu(ui::Menu* menu); | |||||
}; | }; | ||||
@@ -168,10 +168,12 @@ std::string getOperatingSystemInfo(); | |||||
/** Opens a URL in a browser. | /** Opens a URL in a browser. | ||||
Shell injection is possible, so make sure the URL is trusted or hard coded. | Shell injection is possible, so make sure the URL is trusted or hard coded. | ||||
Does nothing if string is blank. | |||||
Does not block. | Does not block. | ||||
*/ | */ | ||||
void openBrowser(const std::string& url); | void openBrowser(const std::string& url); | ||||
/** Opens Windows Explorer, Finder, etc at a directory location. | /** Opens Windows Explorer, Finder, etc at a directory location. | ||||
Does nothing if string is blank. | |||||
Does not block. | Does not block. | ||||
*/ | */ | ||||
void openDirectory(const std::string& path); | void openDirectory(const std::string& path); | ||||
@@ -284,8 +284,11 @@ struct ModelBox : widget::OpaqueWidget { | |||||
void createContextMenu() { | void createContextMenu() { | ||||
ui::Menu* menu = createMenu(); | ui::Menu* menu = createMenu(); | ||||
// menu->addChild(createMenuLabel(model->name)); | |||||
// menu->addChild(createMenuLabel(model->plugin->brand)); | |||||
menu->addChild(createMenuLabel(model->name)); | |||||
menu->addChild(createMenuLabel(model->plugin->brand)); | |||||
model->appendContextMenu(menu); | |||||
menu->addChild(new ui::MenuSeparator); | |||||
menu->addChild(createBoolMenuItem("Favorite", | menu->addChild(createBoolMenuItem("Favorite", | ||||
[=]() { | [=]() { | ||||
@@ -16,7 +16,6 @@ | |||||
#include <settings.hpp> | #include <settings.hpp> | ||||
#include <history.hpp> | #include <history.hpp> | ||||
#include <string.hpp> | #include <string.hpp> | ||||
#include <tag.hpp> | |||||
namespace rack { | namespace rack { | ||||
@@ -358,9 +357,7 @@ void ModuleWidget::onHoverKey(const HoverKeyEvent& e) { | |||||
return; | return; | ||||
} | } | ||||
if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | if (e.key == GLFW_KEY_F1 && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) { | ||||
std::string manualUrl = (model->manualUrl != "") ? model->manualUrl : model->plugin->manualUrl; | |||||
if (!manualUrl.empty()) | |||||
system::openBrowser(manualUrl); | |||||
system::openBrowser(model->getManualUrl()); | |||||
e.consume(this); | e.consume(this); | ||||
} | } | ||||
} | } | ||||
@@ -919,92 +916,7 @@ void ModuleWidget::createContextMenu() { | |||||
// Info | // Info | ||||
menu->addChild(createSubmenuItem("Info", "", [=](ui::Menu* menu) { | menu->addChild(createSubmenuItem("Info", "", [=](ui::Menu* menu) { | ||||
if (!weakThis) | |||||
return; | |||||
// plugin | |||||
menu->addChild(createMenuItem("Plugin: " + model->plugin->name, "", [=]() { | |||||
system::openBrowser(model->plugin->pluginUrl); | |||||
}, model->plugin->pluginUrl == "")); | |||||
// version | |||||
menu->addChild(createMenuLabel(model->plugin->version)); | |||||
// author | |||||
if (model->plugin->author != "") { | |||||
menu->addChild(createMenuItem("Author: " + model->plugin->author, "", [=]() { | |||||
system::openBrowser(model->plugin->authorUrl); | |||||
}, model->plugin->authorUrl.empty())); | |||||
} | |||||
// license | |||||
std::string license = model->plugin->license; | |||||
if (string::startsWith(license, "https://") || string::startsWith(license, "http://")) { | |||||
menu->addChild(createMenuItem("License: Open in browser", "", [=]() { | |||||
system::openBrowser(license); | |||||
})); | |||||
} | |||||
else if (license != "") { | |||||
menu->addChild(createMenuLabel("License: " + license)); | |||||
} | |||||
// tags | |||||
if (!model->tagIds.empty()) { | |||||
menu->addChild(createMenuLabel("Tags:")); | |||||
for (int tagId : model->tagIds) { | |||||
menu->addChild(createMenuLabel("• " + tag::getTag(tagId))); | |||||
} | |||||
} | |||||
menu->addChild(new ui::MenuSeparator); | |||||
// VCV Library page | |||||
menu->addChild(createMenuItem("VCV Library page", "", [=]() { | |||||
system::openBrowser("https://library.vcvrack.com/" + model->plugin->slug + "/" + model->slug); | |||||
})); | |||||
// modularGridUrl | |||||
if (model->modularGridUrl != "") { | |||||
menu->addChild(createMenuItem("ModularGrid page", "", [=]() { | |||||
system::openBrowser(model->modularGridUrl); | |||||
})); | |||||
} | |||||
// manual | |||||
std::string manualUrl = (model->manualUrl != "") ? model->manualUrl : model->plugin->manualUrl; | |||||
if (manualUrl != "") { | |||||
menu->addChild(createMenuItem("User manual", RACK_MOD_CTRL_NAME "+F1", [=]() { | |||||
system::openBrowser(manualUrl); | |||||
})); | |||||
} | |||||
// donate | |||||
if (model->plugin->donateUrl != "") { | |||||
menu->addChild(createMenuItem("Donate", "", [=]() { | |||||
system::openBrowser(model->plugin->donateUrl); | |||||
})); | |||||
} | |||||
// source code | |||||
if (model->plugin->sourceUrl != "") { | |||||
menu->addChild(createMenuItem("Source code", "", [=]() { | |||||
system::openBrowser(model->plugin->sourceUrl); | |||||
})); | |||||
} | |||||
// changelog | |||||
if (model->plugin->changelogUrl != "") { | |||||
menu->addChild(createMenuItem("Changelog", "", [=]() { | |||||
system::openBrowser(model->plugin->changelogUrl); | |||||
})); | |||||
} | |||||
// plugin folder | |||||
if (model->plugin->path != "") { | |||||
menu->addChild(createMenuItem("Open plugin folder", "", [=]() { | |||||
system::openDirectory(model->plugin->path); | |||||
})); | |||||
} | |||||
model->appendContextMenu(menu); | |||||
})); | })); | ||||
// Preset | // Preset | ||||
@@ -6,6 +6,9 @@ | |||||
#include <system.hpp> | #include <system.hpp> | ||||
#include <string.hpp> | #include <string.hpp> | ||||
#include <tag.hpp> | #include <tag.hpp> | ||||
#include <ui/Menu.hpp> | |||||
#include <ui/MenuSeparator.hpp> | |||||
#include <helpers.hpp> | |||||
namespace rack { | namespace rack { | ||||
@@ -84,6 +87,98 @@ std::string Model::getUserPresetDirectory() { | |||||
} | } | ||||
std::string Model::getManualUrl() { | |||||
if (!manualUrl.empty()) | |||||
return manualUrl; | |||||
return plugin->manualUrl; | |||||
} | |||||
void Model::appendContextMenu(ui::Menu* menu) { | |||||
// plugin | |||||
menu->addChild(createMenuItem("Plugin: " + plugin->name, "", [=]() { | |||||
system::openBrowser(plugin->pluginUrl); | |||||
}, plugin->pluginUrl == "")); | |||||
// version | |||||
menu->addChild(createMenuLabel(plugin->version)); | |||||
// author | |||||
if (plugin->author != "") { | |||||
menu->addChild(createMenuItem("Author: " + plugin->author, "", [=]() { | |||||
system::openBrowser(plugin->authorUrl); | |||||
}, plugin->authorUrl.empty())); | |||||
} | |||||
// license | |||||
std::string license = plugin->license; | |||||
if (string::startsWith(license, "https://") || string::startsWith(license, "http://")) { | |||||
menu->addChild(createMenuItem("License: Open in browser", "", [=]() { | |||||
system::openBrowser(license); | |||||
})); | |||||
} | |||||
else if (license != "") { | |||||
menu->addChild(createMenuLabel("License: " + license)); | |||||
} | |||||
// tags | |||||
if (!tagIds.empty()) { | |||||
menu->addChild(createMenuLabel("Tags:")); | |||||
for (int tagId : tagIds) { | |||||
menu->addChild(createMenuLabel("• " + tag::getTag(tagId))); | |||||
} | |||||
} | |||||
menu->addChild(new ui::MenuSeparator); | |||||
// VCV Library page | |||||
menu->addChild(createMenuItem("VCV Library page", "", [=]() { | |||||
system::openBrowser("https://library.vcvrack.com/" + plugin->slug + "/" + slug); | |||||
})); | |||||
// modularGridUrl | |||||
if (modularGridUrl != "") { | |||||
menu->addChild(createMenuItem("ModularGrid page", "", [=]() { | |||||
system::openBrowser(modularGridUrl); | |||||
})); | |||||
} | |||||
// manual | |||||
std::string manualUrl = getManualUrl(); | |||||
if (manualUrl != "") { | |||||
menu->addChild(createMenuItem("User manual", RACK_MOD_CTRL_NAME "+F1", [=]() { | |||||
system::openBrowser(manualUrl); | |||||
})); | |||||
} | |||||
// donate | |||||
if (plugin->donateUrl != "") { | |||||
menu->addChild(createMenuItem("Donate", "", [=]() { | |||||
system::openBrowser(plugin->donateUrl); | |||||
})); | |||||
} | |||||
// source code | |||||
if (plugin->sourceUrl != "") { | |||||
menu->addChild(createMenuItem("Source code", "", [=]() { | |||||
system::openBrowser(plugin->sourceUrl); | |||||
})); | |||||
} | |||||
// changelog | |||||
if (plugin->changelogUrl != "") { | |||||
menu->addChild(createMenuItem("Changelog", "", [=]() { | |||||
system::openBrowser(plugin->changelogUrl); | |||||
})); | |||||
} | |||||
// plugin folder | |||||
if (plugin->path != "") { | |||||
menu->addChild(createMenuItem("Open plugin folder", "", [=]() { | |||||
system::openDirectory(plugin->path); | |||||
})); | |||||
} | |||||
} | |||||
} // namespace plugin | } // namespace plugin | ||||
@@ -712,6 +712,9 @@ std::string getOperatingSystemInfo() { | |||||
void openBrowser(const std::string& url) { | void openBrowser(const std::string& url) { | ||||
if (url.empty()) | |||||
return; | |||||
std::string urlL = url; | std::string urlL = url; | ||||
std::thread t([=] { | std::thread t([=] { | ||||
#if defined ARCH_LIN | #if defined ARCH_LIN | ||||
@@ -731,6 +734,9 @@ void openBrowser(const std::string& url) { | |||||
void openDirectory(const std::string& path) { | void openDirectory(const std::string& path) { | ||||
if (path.empty()) | |||||
return; | |||||
std::string pathL = path; | std::string pathL = path; | ||||
std::thread t([=] { | std::thread t([=] { | ||||
#if defined ARCH_LIN | #if defined ARCH_LIN | ||||