You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1082 lines
28KB

  1. #include <thread>
  2. #include <regex>
  3. #include <osdialog.h>
  4. #include <app/ModuleWidget.hpp>
  5. #include <app/Scene.hpp>
  6. #include <engine/Engine.hpp>
  7. #include <plugin/Plugin.hpp>
  8. #include <app/SvgPanel.hpp>
  9. #include <ui/MenuSeparator.hpp>
  10. #include <system.hpp>
  11. #include <asset.hpp>
  12. #include <helpers.hpp>
  13. #include <context.hpp>
  14. #include <settings.hpp>
  15. #include <history.hpp>
  16. #include <string.hpp>
  17. #include <tag.hpp>
  18. namespace rack {
  19. namespace app {
  20. static const char PRESET_FILTERS[] = "VCV Rack module preset (.vcvm):vcvm";
  21. struct ModuleUrlItem : ui::MenuItem {
  22. std::string url;
  23. void onAction(const event::Action& e) override {
  24. std::thread t(system::openBrowser, url);
  25. t.detach();
  26. }
  27. };
  28. struct ModuleFolderItem : ui::MenuItem {
  29. std::string path;
  30. void onAction(const event::Action& e) override {
  31. std::thread t(system::openFolder, path);
  32. t.detach();
  33. }
  34. };
  35. struct ModuleInfoItem : ui::MenuItem {
  36. plugin::Model* model;
  37. ui::Menu* createChildMenu() override {
  38. ui::Menu* menu = new ui::Menu;
  39. // plugin
  40. ModuleUrlItem* pluginItem = new ModuleUrlItem;
  41. pluginItem->text = "Plugin: " + model->plugin->name + " v" + model->plugin->version;
  42. if (model->plugin->pluginUrl != "") {
  43. pluginItem->url = model->plugin->pluginUrl;
  44. }
  45. else {
  46. pluginItem->disabled = true;
  47. }
  48. menu->addChild(pluginItem);
  49. // ui::MenuLabel* versionLabel = new ui::MenuLabel;
  50. // versionLabel->text = "v" + model->plugin->version;
  51. // menu->addChild(versionLabel);
  52. // author
  53. if (model->plugin->author != "") {
  54. ModuleUrlItem* authorItem = new ModuleUrlItem;
  55. authorItem->text = "Author: " + model->plugin->author;
  56. if (model->plugin->authorUrl != "") {
  57. authorItem->url = model->plugin->authorUrl;
  58. }
  59. else {
  60. authorItem->disabled = true;
  61. }
  62. menu->addChild(authorItem);
  63. }
  64. // license
  65. if (model->plugin->license != "") {
  66. ui::MenuLabel* licenseLabel = new ui::MenuLabel;
  67. licenseLabel->text = "License: " + model->plugin->license;
  68. menu->addChild(licenseLabel);
  69. }
  70. // tags
  71. if (!model->tags.empty()) {
  72. ui::MenuLabel* tagsLabel = new ui::MenuLabel;
  73. tagsLabel->text = "Tags:";
  74. menu->addChild(tagsLabel);
  75. for (int tagId : model->tags) {
  76. ui::MenuLabel* tagLabel = new ui::MenuLabel;
  77. tagLabel->text = "• " + tag::getTag(tagId);
  78. menu->addChild(tagLabel);
  79. }
  80. }
  81. menu->addChild(new ui::MenuSeparator);
  82. // VCV Library page
  83. ModuleUrlItem* libraryItem = new ModuleUrlItem;
  84. libraryItem->text = "VCV Library page";
  85. libraryItem->url = "https://library.vcvrack.com/" + model->plugin->slug + "/" + model->slug;
  86. menu->addChild(libraryItem);
  87. // modularGridUrl
  88. if (model->modularGridUrl != "") {
  89. ModuleUrlItem* modularGridItem = new ModuleUrlItem;
  90. modularGridItem->text = "ModularGrid page";
  91. modularGridItem->url = model->modularGridUrl;
  92. menu->addChild(modularGridItem);
  93. }
  94. // manual
  95. std::string manualUrl = (model->manualUrl != "") ? model->manualUrl : model->plugin->manualUrl;
  96. if (manualUrl != "") {
  97. ModuleUrlItem* manualItem = new ModuleUrlItem;
  98. manualItem->text = "User manual";
  99. manualItem->url = manualUrl;
  100. menu->addChild(manualItem);
  101. }
  102. // donate
  103. if (model->plugin->donateUrl != "") {
  104. ModuleUrlItem* donateItem = new ModuleUrlItem;
  105. donateItem->text = "Donate";
  106. donateItem->url = model->plugin->donateUrl;
  107. menu->addChild(donateItem);
  108. }
  109. // changelog
  110. if (model->plugin->changelogUrl != "") {
  111. ModuleUrlItem* changelogItem = new ModuleUrlItem;
  112. changelogItem->text = "Changelog";
  113. changelogItem->url = model->plugin->changelogUrl;
  114. menu->addChild(changelogItem);
  115. }
  116. // source code
  117. if (model->plugin->sourceUrl != "") {
  118. ModuleUrlItem* sourceItem = new ModuleUrlItem;
  119. sourceItem->text = "Source code";
  120. sourceItem->url = model->plugin->sourceUrl;
  121. menu->addChild(sourceItem);
  122. }
  123. // plugin folder
  124. if (model->plugin->path != "") {
  125. ModuleFolderItem* pathItem = new ModuleFolderItem;
  126. pathItem->text = "Open plugin folder";
  127. pathItem->path = model->plugin->path;
  128. menu->addChild(pathItem);
  129. }
  130. return menu;
  131. }
  132. };
  133. struct ModuleDisconnectItem : ui::MenuItem {
  134. WeakPtr<ModuleWidget> moduleWidget;
  135. void onAction(const event::Action& e) override {
  136. if (!moduleWidget)
  137. return;
  138. moduleWidget->disconnectAction();
  139. }
  140. };
  141. struct ModuleResetItem : ui::MenuItem {
  142. WeakPtr<ModuleWidget> moduleWidget;
  143. void onAction(const event::Action& e) override {
  144. if (!moduleWidget)
  145. return;
  146. moduleWidget->resetAction();
  147. }
  148. };
  149. struct ModuleRandomizeItem : ui::MenuItem {
  150. WeakPtr<ModuleWidget> moduleWidget;
  151. void onAction(const event::Action& e) override {
  152. if (!moduleWidget)
  153. return;
  154. moduleWidget->randomizeAction();
  155. }
  156. };
  157. struct ModuleCopyItem : ui::MenuItem {
  158. WeakPtr<ModuleWidget> moduleWidget;
  159. void onAction(const event::Action& e) override {
  160. if (!moduleWidget)
  161. return;
  162. moduleWidget->copyClipboard();
  163. }
  164. };
  165. struct ModulePasteItem : ui::MenuItem {
  166. WeakPtr<ModuleWidget> moduleWidget;
  167. void onAction(const event::Action& e) override {
  168. if (!moduleWidget)
  169. return;
  170. moduleWidget->pasteClipboardAction();
  171. }
  172. };
  173. struct ModuleSaveItem : ui::MenuItem {
  174. WeakPtr<ModuleWidget> moduleWidget;
  175. void onAction(const event::Action& e) override {
  176. if (!moduleWidget)
  177. return;
  178. moduleWidget->saveDialog();
  179. }
  180. };
  181. struct ModuleSaveTemplateItem : ui::MenuItem {
  182. WeakPtr<ModuleWidget> moduleWidget;
  183. void onAction(const event::Action& e) override {
  184. if (!moduleWidget)
  185. return;
  186. moduleWidget->saveTemplate();
  187. }
  188. };
  189. struct ModuleLoadItem : ui::MenuItem {
  190. WeakPtr<ModuleWidget> moduleWidget;
  191. void onAction(const event::Action& e) override {
  192. if (!moduleWidget)
  193. return;
  194. moduleWidget->loadDialog();
  195. }
  196. };
  197. struct ModulePresetPathItem : ui::MenuItem {
  198. WeakPtr<ModuleWidget> moduleWidget;
  199. std::string presetPath;
  200. void onAction(const event::Action& e) override {
  201. if (!moduleWidget)
  202. return;
  203. try {
  204. moduleWidget->loadAction(presetPath);
  205. }
  206. catch (Exception& e) {
  207. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, e.what());
  208. }
  209. }
  210. };
  211. struct ModulePresetItem : ui::MenuItem {
  212. WeakPtr<ModuleWidget> moduleWidget;
  213. ui::Menu* createChildMenu() override {
  214. if (!moduleWidget)
  215. return NULL;
  216. ui::Menu* menu = new ui::Menu;
  217. ModuleCopyItem* copyItem = new ModuleCopyItem;
  218. copyItem->text = "Copy";
  219. copyItem->rightText = RACK_MOD_CTRL_NAME "+C";
  220. copyItem->moduleWidget = moduleWidget;
  221. menu->addChild(copyItem);
  222. ModulePasteItem* pasteItem = new ModulePasteItem;
  223. pasteItem->text = "Paste";
  224. pasteItem->rightText = RACK_MOD_CTRL_NAME "+V";
  225. pasteItem->moduleWidget = moduleWidget;
  226. menu->addChild(pasteItem);
  227. ModuleLoadItem* loadItem = new ModuleLoadItem;
  228. loadItem->text = "Open";
  229. loadItem->moduleWidget = moduleWidget;
  230. menu->addChild(loadItem);
  231. ModuleSaveItem* saveItem = new ModuleSaveItem;
  232. saveItem->text = "Save as";
  233. saveItem->moduleWidget = moduleWidget;
  234. menu->addChild(saveItem);
  235. ModuleSaveTemplateItem* saveTemplateItem = new ModuleSaveTemplateItem;
  236. saveTemplateItem->text = "Save template";
  237. saveTemplateItem->moduleWidget = moduleWidget;
  238. menu->addChild(saveTemplateItem);
  239. // Create ModulePresetPathItems for each patch in a directory.
  240. auto createPresetItems = [&](std::string presetDir) {
  241. bool hasPresets = false;
  242. // Note: This is not cached, so opening this menu each time might have a bit of latency.
  243. if (system::isDirectory(presetDir)) {
  244. for (const std::string& presetPath : system::getEntries(presetDir)) {
  245. if (system::getExtension(presetPath) != ".vcvm")
  246. continue;
  247. hasPresets = true;
  248. std::string presetName = system::getStem(presetPath);
  249. // Remove "1_", "42_", "001_", etc at the beginning of preset filenames
  250. std::regex r("^\\d*_");
  251. presetName = std::regex_replace(presetName, r, "");
  252. ModulePresetPathItem* presetItem = new ModulePresetPathItem;
  253. presetItem->text = presetName;
  254. presetItem->presetPath = presetPath;
  255. presetItem->moduleWidget = moduleWidget;
  256. menu->addChild(presetItem);
  257. }
  258. }
  259. if (!hasPresets) {
  260. menu->addChild(createMenuLabel("(None)"));
  261. }
  262. };
  263. // Scan `<user dir>/presets/<plugin slug>/<module slug>` for presets.
  264. menu->addChild(new ui::MenuSeparator);
  265. menu->addChild(createMenuLabel("User presets"));
  266. createPresetItems(moduleWidget->model->getUserPresetDir());
  267. // Scan `<plugin dir>/presets/<module slug>` for presets.
  268. menu->addChild(new ui::MenuSeparator);
  269. menu->addChild(createMenuLabel("Factory presets"));
  270. createPresetItems(moduleWidget->model->getFactoryPresetDir());
  271. return menu;
  272. }
  273. };
  274. struct ModuleCloneItem : ui::MenuItem {
  275. WeakPtr<ModuleWidget> moduleWidget;
  276. void onAction(const event::Action& e) override {
  277. if (!moduleWidget)
  278. return;
  279. moduleWidget->cloneAction();
  280. }
  281. };
  282. struct ModuleBypassItem : ui::MenuItem {
  283. WeakPtr<ModuleWidget> moduleWidget;
  284. void onAction(const event::Action& e) override {
  285. if (!moduleWidget)
  286. return;
  287. moduleWidget->bypassAction();
  288. }
  289. };
  290. struct ModuleDeleteItem : ui::MenuItem {
  291. WeakPtr<ModuleWidget> moduleWidget;
  292. void onAction(const event::Action& e) override {
  293. if (!moduleWidget)
  294. return;
  295. moduleWidget->removeAction();
  296. }
  297. };
  298. struct ModuleWidget::Internal {
  299. /** The position the user clicked on the module to start dragging in the RackWidget.
  300. */
  301. math::Vec dragPos;
  302. /** The position in the RackWidget when dragging began.
  303. Used for history::ModuleMove.
  304. Set by RackWidget::updateModuleOldPositions() when *any* module begins dragging, since force-dragging can move other modules around.
  305. */
  306. math::Vec oldPos;
  307. widget::Widget* panel = NULL;
  308. };
  309. ModuleWidget::ModuleWidget() {
  310. internal = new Internal;
  311. box.size = math::Vec(0, RACK_GRID_HEIGHT);
  312. }
  313. ModuleWidget::~ModuleWidget() {
  314. clearChildren();
  315. setModule(NULL);
  316. delete internal;
  317. }
  318. void ModuleWidget::draw(const DrawArgs& args) {
  319. nvgScissor(args.vg, RECT_ARGS(args.clipBox));
  320. if (module && module->bypass()) {
  321. nvgGlobalAlpha(args.vg, 0.33);
  322. }
  323. Widget::draw(args);
  324. // Meter
  325. if (module && settings::cpuMeter) {
  326. float sampleRate = APP->engine->getSampleRate();
  327. const float* meterBuffer = module->meterBuffer();
  328. int meterLength = module->meterLength();
  329. int meterIndex = module->meterIndex();
  330. float meterMax = 0.f;
  331. float meterAvg = 0.f;
  332. for (int i = 0; i < meterLength; i++) {
  333. float m = meterBuffer[i];
  334. meterAvg += m;
  335. meterMax = std::max(meterMax, m);
  336. }
  337. meterAvg /= meterLength;
  338. float percentMax = meterMax * sampleRate;
  339. float mult = (percentMax <= 0.1f) ? 10.f : 1.f;
  340. // // Text background
  341. // nvgBeginPath(args.vg);
  342. // nvgRect(args.vg, 0.0, box.size.y - infoHeight, box.size.x, infoHeight);
  343. // nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75));
  344. // nvgFill(args.vg);
  345. // Draw time plot
  346. nvgBeginPath(args.vg);
  347. nvgMoveTo(args.vg, box.size.x, box.size.y);
  348. for (int i = 0; i < meterLength; i++) {
  349. int index = (meterIndex - i + meterLength) % meterLength;
  350. float percent = math::clamp(meterBuffer[index] * mult * sampleRate, 0.f, 1.f);
  351. math::Vec p;
  352. p.x = (1.f - (float) i / (meterLength - 1)) * box.size.x;
  353. p.y = (1.f - percent) * (box.size.y);
  354. nvgLineTo(args.vg, p.x, p.y);
  355. }
  356. NVGcolor color;
  357. if (mult == 1.f)
  358. color = nvgRGBAf(0.5, 0, 0, 0.85);
  359. else if (mult == 10.f)
  360. color = nvgRGBAf(0.85, 0, 0, 0.85);
  361. nvgLineTo(args.vg, 0.0, box.size.y);
  362. nvgClosePath(args.vg);
  363. nvgFillColor(args.vg, color);
  364. nvgFill(args.vg);
  365. // Text
  366. float percent = meterAvg * sampleRate * 100.f;
  367. float microseconds = meterAvg * 1e6f;
  368. std::string meterText = string::f("%.0fx\n%.2f μs\n%.1f%%", mult, microseconds, percent);
  369. bndLabel(args.vg, 0.0, box.size.y - 60, INFINITY, INFINITY, -1, meterText.c_str());
  370. // Draw border
  371. nvgStrokeColor(args.vg, color);
  372. nvgBeginPath(args.vg);
  373. nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
  374. nvgStrokeWidth(args.vg, 2.0);
  375. nvgStroke(args.vg);
  376. }
  377. // if (module) {
  378. // nvgBeginPath(args.vg);
  379. // nvgRect(args.vg, 0, 0, 20, 20);
  380. // nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75));
  381. // nvgFill(args.vg);
  382. // std::string debugText = string::f("%d", module->id);
  383. // bndLabel(args.vg, 0, 0, INFINITY, INFINITY, -1, debugText.c_str());
  384. // }
  385. nvgResetScissor(args.vg);
  386. }
  387. void ModuleWidget::drawShadow(const DrawArgs& args) {
  388. nvgBeginPath(args.vg);
  389. float r = 20; // Blur radius
  390. float c = 20; // Corner radius
  391. math::Vec b = math::Vec(-10, 30); // Offset from each corner
  392. nvgRect(args.vg, b.x - r, b.y - r, box.size.x - 2 * b.x + 2 * r, box.size.y - 2 * b.y + 2 * r);
  393. NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.2);
  394. NVGcolor transparentColor = nvgRGBAf(0, 0, 0, 0);
  395. nvgFillPaint(args.vg, nvgBoxGradient(args.vg, b.x, b.y, box.size.x - 2 * b.x, box.size.y - 2 * b.y, c, r, shadowColor, transparentColor));
  396. nvgFill(args.vg);
  397. }
  398. void ModuleWidget::onButton(const event::Button& e) {
  399. // Don't consume left button if `lockModules` is enabled.
  400. if (settings::lockModules)
  401. Widget::onButton(e);
  402. else
  403. OpaqueWidget::onButton(e);
  404. // Set starting drag position even if we don't consume it
  405. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_LEFT) {
  406. internal->dragPos = e.pos;
  407. }
  408. if (e.isConsumed())
  409. return;
  410. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
  411. createContextMenu();
  412. e.consume(this);
  413. }
  414. }
  415. void ModuleWidget::onHoverKey(const event::HoverKey& e) {
  416. OpaqueWidget::onHoverKey(e);
  417. if (e.isConsumed())
  418. return;
  419. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  420. if (e.keyName == "i" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  421. resetAction();
  422. e.consume(this);
  423. }
  424. if (e.keyName == "r" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  425. randomizeAction();
  426. e.consume(this);
  427. }
  428. if (e.keyName == "c" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  429. copyClipboard();
  430. e.consume(this);
  431. }
  432. if (e.keyName == "v" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  433. pasteClipboardAction();
  434. e.consume(this);
  435. }
  436. if (e.keyName == "d" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  437. cloneAction();
  438. e.consume(this);
  439. }
  440. if (e.keyName == "u" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  441. disconnectAction();
  442. e.consume(this);
  443. }
  444. if (e.keyName == "e" && (e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  445. bypassAction();
  446. e.consume(this);
  447. }
  448. }
  449. if (e.action == RACK_HELD) {
  450. if ((e.key == GLFW_KEY_DELETE || e.key == GLFW_KEY_BACKSPACE) && (e.mods & RACK_MOD_MASK) == 0) {
  451. removeAction();
  452. e.consume(NULL);
  453. }
  454. }
  455. }
  456. void ModuleWidget::onDragStart(const event::DragStart& e) {
  457. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  458. APP->scene->rack->updateModuleOldPositions();
  459. }
  460. }
  461. void ModuleWidget::onDragEnd(const event::DragEnd& e) {
  462. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  463. history::ComplexAction* h = APP->scene->rack->getModuleDragAction();
  464. if (!h)
  465. return;
  466. APP->history->push(h);
  467. }
  468. }
  469. void ModuleWidget::onDragMove(const event::DragMove& e) {
  470. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  471. if (!settings::lockModules) {
  472. math::Vec pos = APP->scene->rack->mousePos.minus(internal->dragPos);
  473. if ((APP->window->getMods() & RACK_MOD_MASK) == RACK_MOD_CTRL)
  474. APP->scene->rack->setModulePosForce(this, pos);
  475. else
  476. APP->scene->rack->setModulePosNearest(this, pos);
  477. }
  478. }
  479. }
  480. void ModuleWidget::setModule(engine::Module* module) {
  481. if (this->module) {
  482. APP->engine->removeModule(this->module);
  483. delete this->module;
  484. this->module = NULL;
  485. }
  486. this->module = module;
  487. }
  488. void ModuleWidget::setPanel(widget::Widget* panel) {
  489. // Remove existing panel
  490. if (internal->panel) {
  491. removeChild(internal->panel);
  492. delete internal->panel;
  493. internal->panel = NULL;
  494. }
  495. if (panel) {
  496. addChildBottom(panel);
  497. internal->panel = panel;
  498. box.size.x = std::round(panel->box.size.x / RACK_GRID_WIDTH) * RACK_GRID_WIDTH;
  499. }
  500. }
  501. void ModuleWidget::setPanel(std::shared_ptr<Svg> svg) {
  502. // Create SvgPanel
  503. SvgPanel* panel = new SvgPanel;
  504. panel->setBackground(svg);
  505. setPanel(panel);
  506. }
  507. void ModuleWidget::addParam(ParamWidget* param) {
  508. addChild(param);
  509. }
  510. void ModuleWidget::addInput(PortWidget* input) {
  511. // Check that the port is an input
  512. assert(input->type == engine::Port::INPUT);
  513. // Check that the port doesn't have a duplicate ID
  514. PortWidget* input2 = getInput(input->portId);
  515. assert(!input2);
  516. // Add port
  517. addChild(input);
  518. }
  519. void ModuleWidget::addOutput(PortWidget* output) {
  520. // Check that the port is an output
  521. assert(output->type == engine::Port::OUTPUT);
  522. // Check that the port doesn't have a duplicate ID
  523. PortWidget* output2 = getOutput(output->portId);
  524. assert(!output2);
  525. // Add port
  526. addChild(output);
  527. }
  528. template <class T, typename F>
  529. T* getFirstDescendantOfTypeWithCondition(widget::Widget* w, F f) {
  530. T* t = dynamic_cast<T*>(w);
  531. if (t && f(t))
  532. return t;
  533. for (widget::Widget* child : w->children) {
  534. T* foundT = getFirstDescendantOfTypeWithCondition<T>(child, f);
  535. if (foundT)
  536. return foundT;
  537. }
  538. return NULL;
  539. }
  540. ParamWidget* ModuleWidget::getParam(int paramId) {
  541. return getFirstDescendantOfTypeWithCondition<ParamWidget>(this, [&](ParamWidget* pw) -> bool {
  542. return pw->paramId == paramId;
  543. });
  544. }
  545. PortWidget* ModuleWidget::getInput(int portId) {
  546. return getFirstDescendantOfTypeWithCondition<PortWidget>(this, [&](PortWidget* pw) -> bool {
  547. return pw->type == engine::Port::INPUT && pw->portId == portId;
  548. });
  549. }
  550. PortWidget* ModuleWidget::getOutput(int portId) {
  551. return getFirstDescendantOfTypeWithCondition<PortWidget>(this, [&](PortWidget* pw) -> bool {
  552. return pw->type == engine::Port::OUTPUT && pw->portId == portId;
  553. });
  554. }
  555. json_t* ModuleWidget::toJson() {
  556. json_t* moduleJ = APP->engine->moduleToJson(module);
  557. return moduleJ;
  558. }
  559. void ModuleWidget::fromJson(json_t* rootJ) {
  560. APP->engine->moduleFromJson(module, rootJ);
  561. }
  562. void ModuleWidget::copyClipboard() {
  563. json_t* moduleJ = toJson();
  564. DEFER({json_decref(moduleJ);});
  565. char* moduleJson = json_dumps(moduleJ, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
  566. DEFER({std::free(moduleJson);});
  567. glfwSetClipboardString(APP->window->win, moduleJson);
  568. }
  569. void ModuleWidget::pasteClipboardAction() {
  570. const char* moduleJson = glfwGetClipboardString(APP->window->win);
  571. if (!moduleJson) {
  572. WARN("Could not get text from clipboard.");
  573. return;
  574. }
  575. json_error_t error;
  576. json_t* moduleJ = json_loads(moduleJson, 0, &error);
  577. if (!moduleJ) {
  578. WARN("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
  579. return;
  580. }
  581. DEFER({json_decref(moduleJ);});
  582. // Don't use IDs from JSON
  583. json_object_del(moduleJ, "id");
  584. json_object_del(moduleJ, "leftModuleId");
  585. json_object_del(moduleJ, "rightModuleId");
  586. // history::ModuleChange
  587. history::ModuleChange* h = new history::ModuleChange;
  588. h->name = "paste module preset";
  589. h->moduleId = module->id;
  590. h->oldModuleJ = toJson();
  591. fromJson(moduleJ);
  592. h->newModuleJ = toJson();
  593. APP->history->push(h);
  594. }
  595. void ModuleWidget::load(std::string filename) {
  596. FILE* file = std::fopen(filename.c_str(), "r");
  597. if (!file)
  598. throw Exception(string::f("Could not load patch file %s", filename.c_str()));
  599. DEFER({std::fclose(file);});
  600. INFO("Loading preset %s", filename.c_str());
  601. json_error_t error;
  602. json_t* moduleJ = json_loadf(file, 0, &error);
  603. if (!moduleJ)
  604. throw Exception(string::f("File is not a valid patch file. JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text));
  605. DEFER({json_decref(moduleJ);});
  606. // Don't use IDs from JSON
  607. json_object_del(moduleJ, "id");
  608. json_object_del(moduleJ, "leftModuleId");
  609. json_object_del(moduleJ, "rightModuleId");
  610. fromJson(moduleJ);
  611. }
  612. void ModuleWidget::loadAction(std::string filename) {
  613. // history::ModuleChange
  614. history::ModuleChange* h = new history::ModuleChange;
  615. h->name = "load module preset";
  616. h->moduleId = module->id;
  617. h->oldModuleJ = toJson();
  618. try {
  619. load(filename);
  620. }
  621. catch (Exception& e) {
  622. delete h;
  623. throw;
  624. }
  625. h->newModuleJ = toJson();
  626. APP->history->push(h);
  627. }
  628. void ModuleWidget::loadTemplate() {
  629. std::string templatePath = system::join(model->getUserPresetDir(), "template.vcvm");
  630. try {
  631. load(templatePath);
  632. }
  633. catch (Exception& e) {
  634. // Do nothing
  635. }
  636. }
  637. void ModuleWidget::loadDialog() {
  638. std::string presetDir = model->getUserPresetDir();
  639. system::createDirectories(presetDir);
  640. // Delete directories if empty
  641. DEFER({
  642. system::remove(presetDir);
  643. system::remove(system::getDirectory(presetDir));
  644. });
  645. osdialog_filters* filters = osdialog_filters_parse(PRESET_FILTERS);
  646. DEFER({osdialog_filters_free(filters);});
  647. char* pathC = osdialog_file(OSDIALOG_OPEN, presetDir.c_str(), NULL, filters);
  648. if (!pathC) {
  649. // No path selected
  650. return;
  651. }
  652. DEFER({std::free(pathC);});
  653. try {
  654. loadAction(pathC);
  655. }
  656. catch (Exception& e) {
  657. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, e.what());
  658. }
  659. }
  660. void ModuleWidget::save(std::string filename) {
  661. INFO("Saving preset %s", filename.c_str());
  662. json_t* moduleJ = toJson();
  663. assert(moduleJ);
  664. DEFER({json_decref(moduleJ);});
  665. FILE* file = std::fopen(filename.c_str(), "w");
  666. if (!file) {
  667. WARN("Could not write to patch file %s", filename.c_str());
  668. }
  669. DEFER({std::fclose(file);});
  670. json_dumpf(moduleJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
  671. }
  672. void ModuleWidget::saveTemplate() {
  673. std::string presetDir = model->getUserPresetDir();
  674. system::createDirectories(presetDir);
  675. std::string templatePath = system::join(presetDir, "template.vcvm");
  676. save(templatePath);
  677. }
  678. void ModuleWidget::saveDialog() {
  679. std::string presetDir = model->getUserPresetDir();
  680. system::createDirectories(presetDir);
  681. // Delete directories if empty
  682. DEFER({
  683. // These fail silently if the directories are not empty
  684. system::remove(presetDir);
  685. system::remove(system::getDirectory(presetDir));
  686. });
  687. osdialog_filters* filters = osdialog_filters_parse(PRESET_FILTERS);
  688. DEFER({osdialog_filters_free(filters);});
  689. char* pathC = osdialog_file(OSDIALOG_SAVE, presetDir.c_str(), "Untitled.vcvm", filters);
  690. if (!pathC) {
  691. // No path selected
  692. return;
  693. }
  694. DEFER({std::free(pathC);});
  695. std::string path = pathC;
  696. if (system::getExtension(path) == "")
  697. path += ".vcvm";
  698. save(path);
  699. }
  700. template <class T, typename F>
  701. void doOfType(widget::Widget* w, F f) {
  702. T* t = dynamic_cast<T*>(w);
  703. if (t)
  704. f(t);
  705. for (widget::Widget* child : w->children) {
  706. doOfType<T>(child, f);
  707. }
  708. }
  709. void ModuleWidget::disconnect() {
  710. doOfType<PortWidget>(this, [&](PortWidget* pw) {
  711. APP->scene->rack->clearCablesOnPort(pw);
  712. });
  713. }
  714. void ModuleWidget::resetAction() {
  715. assert(module);
  716. // history::ModuleChange
  717. history::ModuleChange* h = new history::ModuleChange;
  718. h->name = "reset module";
  719. h->moduleId = module->id;
  720. h->oldModuleJ = toJson();
  721. APP->engine->resetModule(module);
  722. h->newModuleJ = toJson();
  723. APP->history->push(h);
  724. }
  725. void ModuleWidget::randomizeAction() {
  726. assert(module);
  727. // history::ModuleChange
  728. history::ModuleChange* h = new history::ModuleChange;
  729. h->name = "randomize module";
  730. h->moduleId = module->id;
  731. h->oldModuleJ = toJson();
  732. APP->engine->randomizeModule(module);
  733. h->newModuleJ = toJson();
  734. APP->history->push(h);
  735. }
  736. static void disconnectActions(ModuleWidget* mw, history::ComplexAction* complexAction) {
  737. // Add CableRemove action for all cables
  738. doOfType<PortWidget>(mw, [&](PortWidget* pw) {
  739. for (CableWidget* cw : APP->scene->rack->getCablesOnPort(pw)) {
  740. if (!cw->isComplete())
  741. continue;
  742. // Avoid creating duplicate actions for self-patched cables
  743. if (pw->type == engine::Port::INPUT && cw->outputPort->module == mw->module)
  744. continue;
  745. // history::CableRemove
  746. history::CableRemove* h = new history::CableRemove;
  747. h->setCable(cw);
  748. complexAction->push(h);
  749. }
  750. });
  751. }
  752. void ModuleWidget::disconnectAction() {
  753. history::ComplexAction* complexAction = new history::ComplexAction;
  754. complexAction->name = "disconnect cables";
  755. disconnectActions(this, complexAction);
  756. APP->history->push(complexAction);
  757. disconnect();
  758. }
  759. void ModuleWidget::cloneAction() {
  760. // history::ComplexAction
  761. history::ComplexAction* h = new history::ComplexAction;
  762. // JSON serialization is the obvious way to do this
  763. json_t* moduleJ = toJson();
  764. // Don't use IDs from JSON
  765. json_object_del(moduleJ, "id");
  766. json_object_del(moduleJ, "leftModuleId");
  767. json_object_del(moduleJ, "rightModuleId");
  768. // Clone Module
  769. engine::Module* clonedModule = model->createModule();
  770. // This doesn't need a lock (via Engine::moduleFromJson()) because the Module is not added to the Engine yet.
  771. clonedModule->fromJson(moduleJ);
  772. json_decref(moduleJ);
  773. APP->engine->addModule(clonedModule);
  774. // Clone ModuleWidget
  775. ModuleWidget* clonedModuleWidget = model->createModuleWidget(clonedModule);
  776. APP->scene->rack->addModuleAtMouse(clonedModuleWidget);
  777. // history::ModuleAdd
  778. history::ModuleAdd* hma = new history::ModuleAdd;
  779. hma->name = "clone modules";
  780. hma->setModule(clonedModuleWidget);
  781. h->push(hma);
  782. // Clone cables attached to input ports
  783. doOfType<PortWidget>(this, [&](PortWidget* pw) {
  784. if (pw->type != engine::Port::INPUT)
  785. return;
  786. std::list<CableWidget*> cables = APP->scene->rack->getCablesOnPort(pw);
  787. for (CableWidget* cw : cables) {
  788. // Create cable attached to cloned ModuleWidget's input
  789. engine::Cable* clonedCable = new engine::Cable;
  790. clonedCable->id = -1;
  791. clonedCable->inputModule = clonedModule;
  792. clonedCable->inputId = cw->cable->inputId;
  793. // If cable is self-patched, attach to cloned module instead
  794. if (cw->cable->outputModule == module)
  795. clonedCable->outputModule = clonedModule;
  796. else
  797. clonedCable->outputModule = cw->cable->outputModule;
  798. clonedCable->outputId = cw->cable->outputId;
  799. APP->engine->addCable(clonedCable);
  800. app::CableWidget* clonedCw = new app::CableWidget;
  801. clonedCw->setCable(clonedCable);
  802. clonedCw->color = cw->color;
  803. APP->scene->rack->addCable(clonedCw);
  804. // history::CableAdd
  805. history::CableAdd* hca = new history::CableAdd;
  806. hca->setCable(clonedCw);
  807. h->push(hca);
  808. }
  809. });
  810. APP->history->push(h);
  811. }
  812. void ModuleWidget::bypassAction() {
  813. assert(module);
  814. APP->engine->bypassModule(module, !module->bypass());
  815. // history::ModuleBypass
  816. history::ModuleBypass* h = new history::ModuleBypass;
  817. h->moduleId = module->id;
  818. h->bypass = module->bypass();
  819. APP->history->push(h);
  820. }
  821. void ModuleWidget::removeAction() {
  822. history::ComplexAction* complexAction = new history::ComplexAction;
  823. complexAction->name = "remove module";
  824. disconnectActions(this, complexAction);
  825. // history::ModuleRemove
  826. history::ModuleRemove* moduleRemove = new history::ModuleRemove;
  827. moduleRemove->setModule(this);
  828. complexAction->push(moduleRemove);
  829. APP->history->push(complexAction);
  830. // This disconnects cables, removes the module, and transfers ownership to caller
  831. APP->scene->rack->removeModule(this);
  832. delete this;
  833. }
  834. void ModuleWidget::createContextMenu() {
  835. ui::Menu* menu = createMenu();
  836. assert(model);
  837. ui::MenuLabel* modelLabel = new ui::MenuLabel;
  838. modelLabel->text = model->plugin->brand + " " + model->name;
  839. menu->addChild(modelLabel);
  840. ModuleInfoItem* infoItem = new ModuleInfoItem;
  841. infoItem->text = "Info";
  842. infoItem->rightText = RIGHT_ARROW;
  843. infoItem->model = model;
  844. menu->addChild(infoItem);
  845. ModulePresetItem* presetsItem = new ModulePresetItem;
  846. presetsItem->text = "Preset";
  847. presetsItem->rightText = RIGHT_ARROW;
  848. presetsItem->moduleWidget = this;
  849. menu->addChild(presetsItem);
  850. ModuleResetItem* resetItem = new ModuleResetItem;
  851. resetItem->text = "Initialize";
  852. resetItem->rightText = RACK_MOD_CTRL_NAME "+I";
  853. resetItem->moduleWidget = this;
  854. menu->addChild(resetItem);
  855. ModuleRandomizeItem* randomizeItem = new ModuleRandomizeItem;
  856. randomizeItem->text = "Randomize";
  857. randomizeItem->rightText = RACK_MOD_CTRL_NAME "+R";
  858. randomizeItem->moduleWidget = this;
  859. menu->addChild(randomizeItem);
  860. ModuleDisconnectItem* disconnectItem = new ModuleDisconnectItem;
  861. disconnectItem->text = "Disconnect cables";
  862. disconnectItem->rightText = RACK_MOD_CTRL_NAME "+U";
  863. disconnectItem->moduleWidget = this;
  864. menu->addChild(disconnectItem);
  865. ModuleCloneItem* cloneItem = new ModuleCloneItem;
  866. cloneItem->text = "Duplicate";
  867. cloneItem->rightText = RACK_MOD_CTRL_NAME "+D";
  868. cloneItem->moduleWidget = this;
  869. menu->addChild(cloneItem);
  870. ModuleBypassItem* bypassItem = new ModuleBypassItem;
  871. bypassItem->text = "Bypass";
  872. bypassItem->rightText = RACK_MOD_CTRL_NAME "+E";
  873. if (module && module->bypass())
  874. bypassItem->rightText = CHECKMARK_STRING " " + bypassItem->rightText;
  875. bypassItem->moduleWidget = this;
  876. menu->addChild(bypassItem);
  877. ModuleDeleteItem* deleteItem = new ModuleDeleteItem;
  878. deleteItem->text = "Delete";
  879. deleteItem->rightText = "Backspace/Delete";
  880. deleteItem->moduleWidget = this;
  881. menu->addChild(deleteItem);
  882. appendContextMenu(menu);
  883. }
  884. math::Vec& ModuleWidget::dragPos() {
  885. return internal->dragPos;
  886. }
  887. math::Vec& ModuleWidget::oldPos() {
  888. return internal->oldPos;
  889. }
  890. engine::Module* ModuleWidget::releaseModule() {
  891. engine::Module* module = this->module;
  892. this->module = NULL;
  893. return module;
  894. }
  895. } // namespace app
  896. } // namespace rack