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.

848 lines
20KB

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