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.

884 lines
22KB

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