@@ -10,6 +10,11 @@ | |||
namespace rack { | |||
namespace ui { | |||
struct Menu; | |||
} // namespace app | |||
namespace app { | |||
struct ModuleWidget; | |||
} // namespace app | |||
@@ -65,6 +70,9 @@ struct Model { | |||
std::string getFullName(); | |||
std::string getFactoryPresetDirectory(); | |||
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. | |||
Shell injection is possible, so make sure the URL is trusted or hard coded. | |||
Does nothing if string is blank. | |||
Does not block. | |||
*/ | |||
void openBrowser(const std::string& url); | |||
/** Opens Windows Explorer, Finder, etc at a directory location. | |||
Does nothing if string is blank. | |||
Does not block. | |||
*/ | |||
void openDirectory(const std::string& path); | |||
@@ -284,8 +284,11 @@ struct ModelBox : widget::OpaqueWidget { | |||
void createContextMenu() { | |||
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", | |||
[=]() { | |||
@@ -16,7 +16,6 @@ | |||
#include <settings.hpp> | |||
#include <history.hpp> | |||
#include <string.hpp> | |||
#include <tag.hpp> | |||
namespace rack { | |||
@@ -358,9 +357,7 @@ void ModuleWidget::onHoverKey(const HoverKeyEvent& e) { | |||
return; | |||
} | |||
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); | |||
} | |||
} | |||
@@ -919,92 +916,7 @@ void ModuleWidget::createContextMenu() { | |||
// Info | |||
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 | |||
@@ -6,6 +6,9 @@ | |||
#include <system.hpp> | |||
#include <string.hpp> | |||
#include <tag.hpp> | |||
#include <ui/Menu.hpp> | |||
#include <ui/MenuSeparator.hpp> | |||
#include <helpers.hpp> | |||
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 | |||
@@ -712,6 +712,9 @@ std::string getOperatingSystemInfo() { | |||
void openBrowser(const std::string& url) { | |||
if (url.empty()) | |||
return; | |||
std::string urlL = url; | |||
std::thread t([=] { | |||
#if defined ARCH_LIN | |||
@@ -731,6 +734,9 @@ void openBrowser(const std::string& url) { | |||
void openDirectory(const std::string& path) { | |||
if (path.empty()) | |||
return; | |||
std::string pathL = path; | |||
std::thread t([=] { | |||
#if defined ARCH_LIN | |||