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.

1214 lines
32KB

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