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.

936 lines
24KB

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