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.

857 lines
21KB

  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 ModuleDisableItem : ui::MenuItem {
  180. ModuleWidget* moduleWidget;
  181. void onAction(const event::Action& e) override {
  182. moduleWidget->disableAction();
  183. }
  184. };
  185. struct ModuleDeleteItem : ui::MenuItem {
  186. ModuleWidget* moduleWidget;
  187. void onAction(const event::Action& e) override {
  188. moduleWidget->removeAction();
  189. }
  190. };
  191. struct ModuleWidget::Internal {
  192. /** The position the user clicked on the module to start dragging in the RackWidget.
  193. */
  194. math::Vec dragPos;
  195. /** The position in the RackWidget when dragging began.
  196. Used for history::ModuleMove.
  197. Set by RackWidget::updateModuleOldPositions() when *any* module begins dragging, since force-dragging can move other modules around.
  198. */
  199. math::Vec oldPos;
  200. };
  201. ModuleWidget::ModuleWidget() {
  202. internal = new Internal;
  203. box.size = math::Vec(0, RACK_GRID_HEIGHT);
  204. }
  205. ModuleWidget::~ModuleWidget() {
  206. clearChildren();
  207. setModule(NULL);
  208. delete internal;
  209. }
  210. void ModuleWidget::draw(const DrawArgs& args) {
  211. nvgScissor(args.vg, RECT_ARGS(args.clipBox));
  212. if (module && module->disabled) {
  213. nvgGlobalAlpha(args.vg, 0.33);
  214. }
  215. Widget::draw(args);
  216. // Power meter
  217. if (module && settings::cpuMeter && !module->disabled) {
  218. nvgBeginPath(args.vg);
  219. nvgRect(args.vg,
  220. 0, box.size.y - 35,
  221. 65, 35);
  222. nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75));
  223. nvgFill(args.vg);
  224. float percent = module->cpuTime * APP->engine->getSampleRate() * 100;
  225. float microseconds = module->cpuTime * 1e6f;
  226. std::string cpuText = string::f("%.1f%%\n%.2f μs", percent, microseconds);
  227. bndLabel(args.vg, 2.0, box.size.y - 34.0, INFINITY, INFINITY, -1, cpuText.c_str());
  228. float p = math::clamp(module->cpuTime / APP->engine->getSampleTime(), 0.f, 1.f);
  229. nvgBeginPath(args.vg);
  230. nvgRect(args.vg,
  231. 0, (1.f - p) * box.size.y,
  232. 5, p * box.size.y);
  233. nvgFillColor(args.vg, nvgRGBAf(1, 0, 0, 1.0));
  234. nvgFill(args.vg);
  235. }
  236. // if (module) {
  237. // nvgBeginPath(args.vg);
  238. // nvgRect(args.vg, 0, 0, 20, 20);
  239. // nvgFillColor(args.vg, nvgRGBAf(0, 0, 0, 0.75));
  240. // nvgFill(args.vg);
  241. // std::string debugText = string::f("%d", module->id);
  242. // bndLabel(args.vg, 0, 0, INFINITY, INFINITY, -1, debugText.c_str());
  243. // }
  244. nvgResetScissor(args.vg);
  245. }
  246. void ModuleWidget::drawShadow(const DrawArgs& args) {
  247. nvgBeginPath(args.vg);
  248. float r = 20; // Blur radius
  249. float c = 20; // Corner radius
  250. math::Vec b = math::Vec(-10, 30); // Offset from each corner
  251. 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);
  252. NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.2);
  253. NVGcolor transparentColor = nvgRGBAf(0, 0, 0, 0);
  254. 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));
  255. nvgFill(args.vg);
  256. }
  257. void ModuleWidget::onButton(const event::Button& e) {
  258. OpaqueWidget::onButton(e);
  259. if (e.isConsumed())
  260. return;
  261. if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) {
  262. createContextMenu();
  263. e.consume(this);
  264. }
  265. }
  266. void ModuleWidget::onHoverKey(const event::HoverKey& e) {
  267. OpaqueWidget::onHoverKey(e);
  268. if (e.isConsumed())
  269. return;
  270. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  271. switch (e.key) {
  272. case GLFW_KEY_I: {
  273. if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  274. resetAction();
  275. e.consume(this);
  276. }
  277. } break;
  278. case GLFW_KEY_R: {
  279. if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  280. randomizeAction();
  281. e.consume(this);
  282. }
  283. } break;
  284. case GLFW_KEY_C: {
  285. if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  286. copyClipboard();
  287. e.consume(this);
  288. }
  289. } break;
  290. case GLFW_KEY_V: {
  291. if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  292. pasteClipboardAction();
  293. e.consume(this);
  294. }
  295. } break;
  296. case GLFW_KEY_D: {
  297. if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  298. cloneAction();
  299. e.consume(this);
  300. }
  301. } break;
  302. case GLFW_KEY_U: {
  303. if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  304. disconnectAction();
  305. e.consume(this);
  306. }
  307. } break;
  308. case GLFW_KEY_E: {
  309. if ((e.mods & RACK_MOD_MASK) == RACK_MOD_CTRL) {
  310. disableAction();
  311. e.consume(this);
  312. }
  313. } break;
  314. }
  315. }
  316. if (e.action == RACK_HELD) {
  317. switch (e.key) {
  318. case GLFW_KEY_DELETE:
  319. case GLFW_KEY_BACKSPACE: {
  320. if ((e.mods & RACK_MOD_MASK) == 0) {
  321. removeAction();
  322. e.consume(NULL);
  323. }
  324. } break;
  325. }
  326. }
  327. }
  328. void ModuleWidget::onDragStart(const event::DragStart& e) {
  329. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  330. internal->dragPos = APP->scene->rack->mousePos.minus(box.pos);
  331. APP->scene->rack->updateModuleOldPositions();
  332. }
  333. }
  334. void ModuleWidget::onDragEnd(const event::DragEnd& e) {
  335. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  336. history::ComplexAction* h = APP->scene->rack->getModuleDragAction();
  337. if (!h)
  338. return;
  339. APP->history->push(h);
  340. }
  341. }
  342. void ModuleWidget::onDragMove(const event::DragMove& e) {
  343. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  344. if (!settings::lockModules) {
  345. math::Vec pos = APP->scene->rack->mousePos.minus(internal->dragPos);
  346. if ((APP->window->getMods() & RACK_MOD_MASK) == RACK_MOD_CTRL)
  347. APP->scene->rack->setModulePosForce(this, pos);
  348. else
  349. APP->scene->rack->setModulePosNearest(this, pos);
  350. }
  351. }
  352. }
  353. void ModuleWidget::setModule(engine::Module* module) {
  354. if (this->module) {
  355. APP->engine->removeModule(this->module);
  356. delete this->module;
  357. this->module = NULL;
  358. }
  359. this->module = module;
  360. }
  361. void ModuleWidget::setPanel(std::shared_ptr<Svg> svg) {
  362. // Remove existing panel
  363. if (panel) {
  364. removeChild(panel);
  365. delete panel;
  366. panel = NULL;
  367. }
  368. // Create SvgPanel
  369. SvgPanel* svgPanel = new SvgPanel;
  370. svgPanel->setBackground(svg);
  371. panel = svgPanel;
  372. addChildBottom(panel);
  373. // Set ModuleWidget size based on panel
  374. box.size.x = std::round(panel->box.size.x / RACK_GRID_WIDTH) * RACK_GRID_WIDTH;
  375. }
  376. void ModuleWidget::addParam(ParamWidget* param) {
  377. params.push_back(param);
  378. addChild(param);
  379. }
  380. void ModuleWidget::addInput(PortWidget* input) {
  381. // Check that the port is an input
  382. assert(input->type == engine::Port::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. void ModuleWidget::addOutput(PortWidget* output) {
  392. // Check that the port is an output
  393. assert(output->type == engine::Port::OUTPUT);
  394. // Check that the port doesn't have a duplicate ID
  395. for (PortWidget* output2 : outputs) {
  396. assert(output->portId != output2->portId);
  397. }
  398. // Add port
  399. outputs.push_back(output);
  400. addChild(output);
  401. }
  402. ParamWidget* ModuleWidget::getParam(int paramId) {
  403. for (ParamWidget* param : params) {
  404. if (param->paramId == paramId)
  405. return param;
  406. }
  407. return NULL;
  408. }
  409. PortWidget* ModuleWidget::getInput(int inputId) {
  410. for (PortWidget* port : inputs) {
  411. if (port->portId == inputId)
  412. return port;
  413. }
  414. return NULL;
  415. }
  416. PortWidget* ModuleWidget::getOutput(int outputId) {
  417. for (PortWidget* port : outputs) {
  418. if (port->portId == outputId)
  419. return port;
  420. }
  421. return NULL;
  422. }
  423. json_t* ModuleWidget::toJson() {
  424. json_t* moduleJ = module->toJson();
  425. return moduleJ;
  426. }
  427. void ModuleWidget::fromJson(json_t* rootJ) {
  428. module->fromJson(rootJ);
  429. }
  430. void ModuleWidget::copyClipboard() {
  431. json_t* moduleJ = toJson();
  432. DEFER({
  433. json_decref(moduleJ);
  434. });
  435. char* moduleJson = json_dumps(moduleJ, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
  436. DEFER({
  437. free(moduleJson);
  438. });
  439. glfwSetClipboardString(APP->window->win, moduleJson);
  440. }
  441. void ModuleWidget::pasteClipboardAction() {
  442. const char* moduleJson = glfwGetClipboardString(APP->window->win);
  443. if (!moduleJson) {
  444. WARN("Could not get text from clipboard.");
  445. return;
  446. }
  447. json_error_t error;
  448. json_t* moduleJ = json_loads(moduleJson, 0, &error);
  449. if (!moduleJ) {
  450. WARN("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
  451. return;
  452. }
  453. DEFER({
  454. json_decref(moduleJ);
  455. });
  456. // history::ModuleChange
  457. history::ModuleChange* h = new history::ModuleChange;
  458. h->name = "paste module preset";
  459. h->moduleId = module->id;
  460. h->oldModuleJ = toJson();
  461. fromJson(moduleJ);
  462. h->newModuleJ = toJson();
  463. APP->history->push(h);
  464. }
  465. void ModuleWidget::loadAction(std::string filename) {
  466. INFO("Loading preset %s", filename.c_str());
  467. FILE* file = fopen(filename.c_str(), "r");
  468. if (!file) {
  469. WARN("Could not load patch file %s", filename.c_str());
  470. return;
  471. }
  472. DEFER({
  473. fclose(file);
  474. });
  475. json_error_t error;
  476. json_t* moduleJ = json_loadf(file, 0, &error);
  477. if (!moduleJ) {
  478. 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);
  479. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  480. return;
  481. }
  482. DEFER({
  483. json_decref(moduleJ);
  484. });
  485. // history::ModuleChange
  486. history::ModuleChange* h = new history::ModuleChange;
  487. h->name = "load module preset";
  488. h->moduleId = module->id;
  489. h->oldModuleJ = toJson();
  490. fromJson(moduleJ);
  491. h->newModuleJ = toJson();
  492. APP->history->push(h);
  493. }
  494. void ModuleWidget::save(std::string filename) {
  495. INFO("Saving preset %s", filename.c_str());
  496. json_t* moduleJ = toJson();
  497. assert(moduleJ);
  498. DEFER({
  499. json_decref(moduleJ);
  500. });
  501. FILE* file = fopen(filename.c_str(), "w");
  502. if (!file) {
  503. WARN("Could not write to patch file %s", filename.c_str());
  504. }
  505. DEFER({
  506. fclose(file);
  507. });
  508. json_dumpf(moduleJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
  509. }
  510. void ModuleWidget::loadDialog() {
  511. std::string dir = asset::user("presets");
  512. system::createDirectory(dir);
  513. osdialog_filters* filters = osdialog_filters_parse(PRESET_FILTERS);
  514. DEFER({
  515. osdialog_filters_free(filters);
  516. });
  517. char* path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters);
  518. if (!path) {
  519. // No path selected
  520. return;
  521. }
  522. DEFER({
  523. free(path);
  524. });
  525. loadAction(path);
  526. }
  527. void ModuleWidget::saveDialog() {
  528. std::string dir = asset::user("presets");
  529. system::createDirectory(dir);
  530. osdialog_filters* filters = osdialog_filters_parse(PRESET_FILTERS);
  531. DEFER({
  532. osdialog_filters_free(filters);
  533. });
  534. char* path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcvm", filters);
  535. if (!path) {
  536. // No path selected
  537. return;
  538. }
  539. DEFER({
  540. free(path);
  541. });
  542. std::string pathStr = path;
  543. std::string extension = string::filenameExtension(string::filename(pathStr));
  544. if (extension.empty()) {
  545. pathStr += ".vcvm";
  546. }
  547. save(pathStr);
  548. }
  549. void ModuleWidget::disconnect() {
  550. for (PortWidget* input : inputs) {
  551. APP->scene->rack->clearCablesOnPort(input);
  552. }
  553. for (PortWidget* output : outputs) {
  554. APP->scene->rack->clearCablesOnPort(output);
  555. }
  556. }
  557. void ModuleWidget::resetAction() {
  558. assert(module);
  559. // history::ModuleChange
  560. history::ModuleChange* h = new history::ModuleChange;
  561. h->name = "reset module";
  562. h->moduleId = module->id;
  563. h->oldModuleJ = toJson();
  564. APP->engine->resetModule(module);
  565. h->newModuleJ = toJson();
  566. APP->history->push(h);
  567. }
  568. void ModuleWidget::randomizeAction() {
  569. assert(module);
  570. // history::ModuleChange
  571. history::ModuleChange* h = new history::ModuleChange;
  572. h->name = "randomize module";
  573. h->moduleId = module->id;
  574. h->oldModuleJ = toJson();
  575. APP->engine->randomizeModule(module);
  576. h->newModuleJ = toJson();
  577. APP->history->push(h);
  578. }
  579. static void disconnectActions(ModuleWidget* mw, history::ComplexAction* complexAction) {
  580. // Add CableRemove action for all cables attached to inputs
  581. for (PortWidget* input : mw->inputs) {
  582. for (CableWidget* cw : APP->scene->rack->getCablesOnPort(input)) {
  583. if (!cw->isComplete())
  584. continue;
  585. // Avoid creating duplicate actions for self-patched cables
  586. if (cw->outputPort->module == mw->module)
  587. continue;
  588. // history::CableRemove
  589. history::CableRemove* h = new history::CableRemove;
  590. h->setCable(cw);
  591. complexAction->push(h);
  592. }
  593. }
  594. // Add CableRemove action for all cables attached to outputs
  595. for (PortWidget* output : mw->outputs) {
  596. for (CableWidget* cw : APP->scene->rack->getCablesOnPort(output)) {
  597. if (!cw->isComplete())
  598. continue;
  599. // history::CableRemove
  600. history::CableRemove* h = new history::CableRemove;
  601. h->setCable(cw);
  602. complexAction->push(h);
  603. }
  604. }
  605. }
  606. void ModuleWidget::disconnectAction() {
  607. history::ComplexAction* complexAction = new history::ComplexAction;
  608. complexAction->name = "disconnect cables";
  609. disconnectActions(this, complexAction);
  610. APP->history->push(complexAction);
  611. disconnect();
  612. }
  613. void ModuleWidget::cloneAction() {
  614. engine::Module* clonedModule = model->createModule();
  615. // JSON serialization is the obvious way to do this
  616. json_t* moduleJ = toJson();
  617. clonedModule->fromJson(moduleJ);
  618. json_decref(moduleJ);
  619. // Reset ID so the Engine automatically assigns a new one
  620. clonedModule->id = -1;
  621. APP->engine->addModule(clonedModule);
  622. ModuleWidget* clonedModuleWidget = model->createModuleWidget(clonedModule);
  623. APP->scene->rack->addModuleAtMouse(clonedModuleWidget);
  624. // history::ModuleAdd
  625. history::ModuleAdd* h = new history::ModuleAdd;
  626. h->name = "clone modules";
  627. h->setModule(clonedModuleWidget);
  628. APP->history->push(h);
  629. }
  630. void ModuleWidget::disableAction() {
  631. assert(module);
  632. APP->engine->disableModule(module, !module->disabled);
  633. // history::ModuleDisable
  634. history::ModuleDisable* h = new history::ModuleDisable;
  635. h->moduleId = module->id;
  636. h->disabled = module->disabled;
  637. APP->history->push(h);
  638. }
  639. void ModuleWidget::removeAction() {
  640. history::ComplexAction* complexAction = new history::ComplexAction;
  641. complexAction->name = "remove module";
  642. disconnectActions(this, complexAction);
  643. // history::ModuleRemove
  644. history::ModuleRemove* moduleRemove = new history::ModuleRemove;
  645. moduleRemove->setModule(this);
  646. complexAction->push(moduleRemove);
  647. APP->history->push(complexAction);
  648. // This disconnects cables, removes the module, and transfers ownership to caller
  649. APP->scene->rack->removeModule(this);
  650. delete this;
  651. }
  652. void ModuleWidget::createContextMenu() {
  653. ui::Menu* menu = createMenu();
  654. assert(model);
  655. ui::MenuLabel* modelLabel = new ui::MenuLabel;
  656. modelLabel->text = model->plugin->brand + " " + model->name;
  657. menu->addChild(modelLabel);
  658. ModulePluginItem* pluginItem = new ModulePluginItem;
  659. pluginItem->text = "Plugin";
  660. pluginItem->rightText = RIGHT_ARROW;
  661. pluginItem->plugin = model->plugin;
  662. menu->addChild(pluginItem);
  663. ModulePresetItem* presetsItem = new ModulePresetItem;
  664. presetsItem->text = "Preset";
  665. presetsItem->rightText = RIGHT_ARROW;
  666. presetsItem->moduleWidget = this;
  667. menu->addChild(presetsItem);
  668. ModuleResetItem* resetItem = new ModuleResetItem;
  669. resetItem->text = "Initialize";
  670. resetItem->rightText = RACK_MOD_CTRL_NAME "+I";
  671. resetItem->moduleWidget = this;
  672. menu->addChild(resetItem);
  673. ModuleRandomizeItem* randomizeItem = new ModuleRandomizeItem;
  674. randomizeItem->text = "Randomize";
  675. randomizeItem->rightText = RACK_MOD_CTRL_NAME "+R";
  676. randomizeItem->moduleWidget = this;
  677. menu->addChild(randomizeItem);
  678. ModuleDisconnectItem* disconnectItem = new ModuleDisconnectItem;
  679. disconnectItem->text = "Disconnect cables";
  680. disconnectItem->rightText = RACK_MOD_CTRL_NAME "+U";
  681. disconnectItem->moduleWidget = this;
  682. menu->addChild(disconnectItem);
  683. ModuleCloneItem* cloneItem = new ModuleCloneItem;
  684. cloneItem->text = "Duplicate";
  685. cloneItem->rightText = RACK_MOD_CTRL_NAME "+D";
  686. cloneItem->moduleWidget = this;
  687. menu->addChild(cloneItem);
  688. ModuleDisableItem* disableItem = new ModuleDisableItem;
  689. disableItem->text = "Disable";
  690. disableItem->rightText = RACK_MOD_CTRL_NAME "+E";
  691. if (module && module->disabled)
  692. disableItem->rightText = CHECKMARK_STRING " " + disableItem->rightText;
  693. disableItem->moduleWidget = this;
  694. menu->addChild(disableItem);
  695. ModuleDeleteItem* deleteItem = new ModuleDeleteItem;
  696. deleteItem->text = "Delete";
  697. deleteItem->rightText = "Backspace/Delete";
  698. deleteItem->moduleWidget = this;
  699. menu->addChild(deleteItem);
  700. appendContextMenu(menu);
  701. }
  702. math::Vec& ModuleWidget::dragPos() {
  703. return internal->dragPos;
  704. }
  705. math::Vec& ModuleWidget::oldPos() {
  706. return internal->oldPos;
  707. }
  708. } // namespace app
  709. } // namespace rack