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.

859 lines
21KB

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