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.

584 lines
14KB

  1. #include "app/ModuleWidget.hpp"
  2. #include "engine/Engine.hpp"
  3. #include "system.hpp"
  4. #include "asset.hpp"
  5. #include "app/Scene.hpp"
  6. #include "app/SVGPanel.hpp"
  7. #include "helpers.hpp"
  8. #include "context.hpp"
  9. #include "settings.hpp"
  10. #include "osdialog.h"
  11. namespace rack {
  12. ModuleWidget::ModuleWidget(Module *module) {
  13. if (module) {
  14. context()->engine->addModule(module);
  15. }
  16. this->module = module;
  17. }
  18. ModuleWidget::~ModuleWidget() {
  19. // Make sure WireWidget destructors are called *before* removing `module` from the rack.
  20. disconnect();
  21. // Remove and delete the Module instance
  22. if (module) {
  23. context()->engine->removeModule(module);
  24. delete module;
  25. module = NULL;
  26. }
  27. }
  28. void ModuleWidget::addInput(PortWidget *input) {
  29. assert(input->type == PortWidget::INPUT);
  30. inputs.push_back(input);
  31. addChild(input);
  32. }
  33. void ModuleWidget::addOutput(PortWidget *output) {
  34. assert(output->type == PortWidget::OUTPUT);
  35. outputs.push_back(output);
  36. addChild(output);
  37. }
  38. void ModuleWidget::addParam(ParamWidget *param) {
  39. params.push_back(param);
  40. addChild(param);
  41. }
  42. void ModuleWidget::setPanel(std::shared_ptr<SVG> svg) {
  43. // Remove old panel
  44. if (panel) {
  45. removeChild(panel);
  46. delete panel;
  47. panel = NULL;
  48. }
  49. {
  50. SVGPanel *panel = new SVGPanel;
  51. panel->setBackground(svg);
  52. addChild(panel);
  53. box.size = panel->box.size;
  54. this->panel = panel;
  55. }
  56. }
  57. json_t *ModuleWidget::toJson() {
  58. json_t *rootJ = json_object();
  59. // plugin
  60. json_object_set_new(rootJ, "plugin", json_string(model->plugin->slug.c_str()));
  61. // version of plugin
  62. if (!model->plugin->version.empty())
  63. json_object_set_new(rootJ, "version", json_string(model->plugin->version.c_str()));
  64. // model
  65. json_object_set_new(rootJ, "model", json_string(model->slug.c_str()));
  66. // Other properties
  67. if (module) {
  68. json_t *moduleJ = module->toJson();
  69. // Merge with rootJ
  70. json_object_update(rootJ, moduleJ);
  71. json_decref(moduleJ);
  72. }
  73. return rootJ;
  74. }
  75. void ModuleWidget::fromJson(json_t *rootJ) {
  76. // Check if plugin and model are incorrect
  77. json_t *pluginJ = json_object_get(rootJ, "plugin");
  78. std::string pluginSlug;
  79. if (pluginJ) {
  80. pluginSlug = json_string_value(pluginJ);
  81. if (pluginSlug != model->plugin->slug) {
  82. WARN("Plugin %s does not match ModuleWidget's plugin %s.", pluginSlug.c_str(), model->plugin->slug.c_str());
  83. return;
  84. }
  85. }
  86. json_t *modelJ = json_object_get(rootJ, "model");
  87. std::string modelSlug;
  88. if (modelJ) {
  89. modelSlug = json_string_value(modelJ);
  90. if (modelSlug != model->slug) {
  91. WARN("Model %s does not match ModuleWidget's model %s.", modelSlug.c_str(), model->slug.c_str());
  92. return;
  93. }
  94. }
  95. // Check plugin version
  96. json_t *versionJ = json_object_get(rootJ, "version");
  97. if (versionJ) {
  98. std::string version = json_string_value(versionJ);
  99. if (version != model->plugin->version) {
  100. INFO("Patch created with %s version %s, using version %s.", pluginSlug.c_str(), version.c_str(), model->plugin->version.c_str());
  101. }
  102. }
  103. if (module) {
  104. module->fromJson(rootJ);
  105. }
  106. }
  107. void ModuleWidget::copyClipboard() {
  108. json_t *moduleJ = toJson();
  109. DEFER({
  110. json_decref(moduleJ);
  111. });
  112. char *moduleJson = json_dumps(moduleJ, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
  113. DEFER({
  114. free(moduleJson);
  115. });
  116. glfwSetClipboardString(context()->window->win, moduleJson);
  117. }
  118. void ModuleWidget::pasteClipboard() {
  119. const char *moduleJson = glfwGetClipboardString(context()->window->win);
  120. if (!moduleJson) {
  121. WARN("Could not get text from clipboard.");
  122. return;
  123. }
  124. json_error_t error;
  125. json_t *moduleJ = json_loads(moduleJson, 0, &error);
  126. if (!moduleJ) {
  127. WARN("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
  128. return;
  129. }
  130. DEFER({
  131. json_decref(moduleJ);
  132. });
  133. fromJson(moduleJ);
  134. }
  135. void ModuleWidget::load(std::string filename) {
  136. INFO("Loading preset %s", filename.c_str());
  137. FILE *file = fopen(filename.c_str(), "r");
  138. if (!file) {
  139. WARN("Could not load patch file %s", filename.c_str());
  140. return;
  141. }
  142. DEFER({
  143. fclose(file);
  144. });
  145. json_error_t error;
  146. json_t *moduleJ = json_loadf(file, 0, &error);
  147. if (!moduleJ) {
  148. 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);
  149. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  150. return;
  151. }
  152. DEFER({
  153. json_decref(moduleJ);
  154. });
  155. fromJson(moduleJ);
  156. }
  157. void ModuleWidget::save(std::string filename) {
  158. INFO("Saving preset %s", filename.c_str());
  159. json_t *moduleJ = toJson();
  160. assert(moduleJ);
  161. DEFER({
  162. json_decref(moduleJ);
  163. });
  164. FILE *file = fopen(filename.c_str(), "w");
  165. if (!file) {
  166. WARN("Could not write to patch file %s", filename.c_str());
  167. }
  168. DEFER({
  169. fclose(file);
  170. });
  171. json_dumpf(moduleJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
  172. }
  173. void ModuleWidget::loadDialog() {
  174. std::string dir = asset::user("presets");
  175. system::createDirectory(dir);
  176. osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str());
  177. DEFER({
  178. osdialog_filters_free(filters);
  179. });
  180. char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, filters);
  181. if (!path) {
  182. // No path selected
  183. return;
  184. }
  185. DEFER({
  186. free(path);
  187. });
  188. load(path);
  189. }
  190. void ModuleWidget::saveDialog() {
  191. std::string dir = asset::user("presets");
  192. system::createDirectory(dir);
  193. osdialog_filters *filters = osdialog_filters_parse(PRESET_FILTERS.c_str());
  194. DEFER({
  195. osdialog_filters_free(filters);
  196. });
  197. char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcvm", filters);
  198. if (!path) {
  199. // No path selected
  200. return;
  201. }
  202. DEFER({
  203. free(path);
  204. });
  205. std::string pathStr = path;
  206. std::string extension = string::extension(pathStr);
  207. if (extension.empty()) {
  208. pathStr += ".vcvm";
  209. }
  210. save(pathStr);
  211. }
  212. void ModuleWidget::toggleBypass() {
  213. if (!module)
  214. return;
  215. module->bypass ^= true;
  216. }
  217. void ModuleWidget::disconnect() {
  218. for (PortWidget *input : inputs) {
  219. context()->scene->rackWidget->wireContainer->removeAllWires(input);
  220. }
  221. for (PortWidget *output : outputs) {
  222. context()->scene->rackWidget->wireContainer->removeAllWires(output);
  223. }
  224. }
  225. void ModuleWidget::reset() {
  226. if (module) {
  227. context()->engine->resetModule(module);
  228. }
  229. }
  230. void ModuleWidget::randomize() {
  231. if (module) {
  232. context()->engine->randomizeModule(module);
  233. }
  234. }
  235. void ModuleWidget::draw(NVGcontext *vg) {
  236. if (module && module->bypass) {
  237. nvgGlobalAlpha(vg, 0.5);
  238. }
  239. nvgScissor(vg, 0, 0, box.size.x, box.size.y);
  240. Widget::draw(vg);
  241. // Power meter
  242. if (module && settings::powerMeter) {
  243. nvgBeginPath(vg);
  244. nvgRect(vg,
  245. 0, box.size.y - 20,
  246. 55, 20);
  247. nvgFillColor(vg, nvgRGBAf(0, 0, 0, 0.5));
  248. nvgFill(vg);
  249. std::string cpuText = string::f("%.0f mS", module->cpuTime * 1000.f);
  250. // TODO Use blendish text function
  251. nvgFontFaceId(vg, context()->window->uiFont->handle);
  252. nvgFontSize(vg, 12);
  253. nvgFillColor(vg, nvgRGBf(1, 1, 1));
  254. nvgText(vg, 10.0, box.size.y - 6.0, cpuText.c_str(), NULL);
  255. float p = math::clamp(module->cpuTime, 0.f, 1.f);
  256. nvgBeginPath(vg);
  257. nvgRect(vg,
  258. 0, (1.f - p) * box.size.y,
  259. 5, p * box.size.y);
  260. nvgFillColor(vg, nvgRGBAf(1, 0, 0, 1.0));
  261. nvgFill(vg);
  262. }
  263. nvgResetScissor(vg);
  264. }
  265. void ModuleWidget::drawShadow(NVGcontext *vg) {
  266. nvgBeginPath(vg);
  267. float r = 20; // Blur radius
  268. float c = 20; // Corner radius
  269. math::Vec b = math::Vec(-10, 30); // Offset from each corner
  270. nvgRect(vg, b.x - r, b.y - r, box.size.x - 2*b.x + 2*r, box.size.y - 2*b.y + 2*r);
  271. NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.2);
  272. NVGcolor transparentColor = nvgRGBAf(0, 0, 0, 0);
  273. nvgFillPaint(vg, nvgBoxGradient(vg, b.x, b.y, box.size.x - 2*b.x, box.size.y - 2*b.y, c, r, shadowColor, transparentColor));
  274. nvgFill(vg);
  275. }
  276. void ModuleWidget::onHover(const event::Hover &e) {
  277. OpaqueWidget::onHover(e);
  278. // 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.
  279. if (glfwGetKey(context()->window->win, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(context()->window->win, GLFW_KEY_BACKSPACE) == GLFW_PRESS) {
  280. if (!context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  281. requestedDelete = true;
  282. return;
  283. }
  284. }
  285. }
  286. void ModuleWidget::onButton(const event::Button &e) {
  287. OpaqueWidget::onButton(e);
  288. if (e.getConsumed() == this) {
  289. if (e.button == 1) {
  290. createContextMenu();
  291. }
  292. }
  293. }
  294. void ModuleWidget::onHoverKey(const event::HoverKey &e) {
  295. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  296. switch (e.key) {
  297. case GLFW_KEY_I: {
  298. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  299. reset();
  300. e.consume(this);
  301. }
  302. } break;
  303. case GLFW_KEY_R: {
  304. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  305. randomize();
  306. e.consume(this);
  307. }
  308. } break;
  309. case GLFW_KEY_C: {
  310. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  311. copyClipboard();
  312. e.consume(this);
  313. }
  314. } break;
  315. case GLFW_KEY_V: {
  316. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  317. pasteClipboard();
  318. e.consume(this);
  319. }
  320. } break;
  321. case GLFW_KEY_D: {
  322. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  323. context()->scene->rackWidget->cloneModule(this);
  324. e.consume(this);
  325. }
  326. } break;
  327. case GLFW_KEY_U: {
  328. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  329. disconnect();
  330. e.consume(this);
  331. }
  332. } break;
  333. case GLFW_KEY_E: {
  334. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  335. toggleBypass();
  336. e.consume(this);
  337. }
  338. } break;
  339. }
  340. }
  341. if (!e.getConsumed())
  342. OpaqueWidget::onHoverKey(e);
  343. }
  344. void ModuleWidget::onDragStart(const event::DragStart &e) {
  345. dragPos = context()->scene->rackWidget->lastMousePos.minus(box.pos);
  346. }
  347. void ModuleWidget::onDragEnd(const event::DragEnd &e) {
  348. }
  349. void ModuleWidget::onDragMove(const event::DragMove &e) {
  350. if (!settings::lockModules) {
  351. math::Rect newBox = box;
  352. newBox.pos = context()->scene->rackWidget->lastMousePos.minus(dragPos);
  353. context()->scene->rackWidget->requestModuleBoxNearest(this, newBox);
  354. }
  355. }
  356. struct ModuleDisconnectItem : MenuItem {
  357. ModuleWidget *moduleWidget;
  358. ModuleDisconnectItem() {
  359. text = "Disconnect cables";
  360. rightText = WINDOW_MOD_KEY_NAME "+U";
  361. }
  362. void onAction(const event::Action &e) override {
  363. moduleWidget->disconnect();
  364. }
  365. };
  366. struct ModuleResetItem : MenuItem {
  367. ModuleWidget *moduleWidget;
  368. ModuleResetItem() {
  369. text = "Initialize";
  370. rightText = WINDOW_MOD_KEY_NAME "+I";
  371. }
  372. void onAction(const event::Action &e) override {
  373. moduleWidget->reset();
  374. }
  375. };
  376. struct ModuleRandomizeItem : MenuItem {
  377. ModuleWidget *moduleWidget;
  378. ModuleRandomizeItem() {
  379. text = "Randomize";
  380. rightText = WINDOW_MOD_KEY_NAME "+R";
  381. }
  382. void onAction(const event::Action &e) override {
  383. moduleWidget->randomize();
  384. }
  385. };
  386. struct ModuleCopyItem : MenuItem {
  387. ModuleWidget *moduleWidget;
  388. ModuleCopyItem() {
  389. text = "Copy preset";
  390. rightText = WINDOW_MOD_KEY_NAME "+C";
  391. }
  392. void onAction(const event::Action &e) override {
  393. moduleWidget->copyClipboard();
  394. }
  395. };
  396. struct ModulePasteItem : MenuItem {
  397. ModuleWidget *moduleWidget;
  398. ModulePasteItem() {
  399. text = "Paste preset";
  400. rightText = WINDOW_MOD_KEY_NAME "+V";
  401. }
  402. void onAction(const event::Action &e) override {
  403. moduleWidget->pasteClipboard();
  404. }
  405. };
  406. struct ModuleSaveItem : MenuItem {
  407. ModuleWidget *moduleWidget;
  408. ModuleSaveItem() {
  409. text = "Save preset as";
  410. }
  411. void onAction(const event::Action &e) override {
  412. moduleWidget->saveDialog();
  413. }
  414. };
  415. struct ModuleLoadItem : MenuItem {
  416. ModuleWidget *moduleWidget;
  417. ModuleLoadItem() {
  418. text = "Load preset";
  419. }
  420. void onAction(const event::Action &e) override {
  421. moduleWidget->loadDialog();
  422. }
  423. };
  424. struct ModuleCloneItem : MenuItem {
  425. ModuleWidget *moduleWidget;
  426. ModuleCloneItem() {
  427. text = "Duplicate";
  428. rightText = WINDOW_MOD_KEY_NAME "+D";
  429. }
  430. void onAction(const event::Action &e) override {
  431. context()->scene->rackWidget->cloneModule(moduleWidget);
  432. }
  433. };
  434. struct ModuleBypassItem : MenuItem {
  435. ModuleWidget *moduleWidget;
  436. ModuleBypassItem() {
  437. text = "Bypass";
  438. }
  439. void step() override {
  440. rightText = WINDOW_MOD_KEY_NAME "+E";
  441. if (!moduleWidget->module)
  442. return;
  443. if (moduleWidget->module->bypass)
  444. rightText = CHECKMARK_STRING " " + rightText;
  445. }
  446. void onAction(const event::Action &e) override {
  447. moduleWidget->toggleBypass();
  448. }
  449. };
  450. struct ModuleDeleteItem : MenuItem {
  451. ModuleWidget *moduleWidget;
  452. ModuleDeleteItem() {
  453. text = "Delete";
  454. rightText = "Backspace/Delete";
  455. }
  456. void onAction(const event::Action &e) override {
  457. context()->scene->rackWidget->deleteModule(moduleWidget);
  458. delete moduleWidget;
  459. }
  460. };
  461. Menu *ModuleWidget::createContextMenu() {
  462. Menu *menu = createMenu();
  463. MenuLabel *menuLabel = new MenuLabel;
  464. menuLabel->text = model->plugin->name + " " + model->name + " " + model->plugin->version;
  465. menu->addChild(menuLabel);
  466. ModuleResetItem *resetItem = new ModuleResetItem;
  467. resetItem->moduleWidget = this;
  468. menu->addChild(resetItem);
  469. ModuleRandomizeItem *randomizeItem = new ModuleRandomizeItem;
  470. randomizeItem->moduleWidget = this;
  471. menu->addChild(randomizeItem);
  472. ModuleDisconnectItem *disconnectItem = new ModuleDisconnectItem;
  473. disconnectItem->moduleWidget = this;
  474. menu->addChild(disconnectItem);
  475. ModuleCloneItem *cloneItem = new ModuleCloneItem;
  476. cloneItem->moduleWidget = this;
  477. menu->addChild(cloneItem);
  478. ModuleCopyItem *copyItem = new ModuleCopyItem;
  479. copyItem->moduleWidget = this;
  480. menu->addChild(copyItem);
  481. ModulePasteItem *pasteItem = new ModulePasteItem;
  482. pasteItem->moduleWidget = this;
  483. menu->addChild(pasteItem);
  484. ModuleLoadItem *loadItem = new ModuleLoadItem;
  485. loadItem->moduleWidget = this;
  486. menu->addChild(loadItem);
  487. ModuleSaveItem *saveItem = new ModuleSaveItem;
  488. saveItem->moduleWidget = this;
  489. menu->addChild(saveItem);
  490. ModuleBypassItem *bypassItem = new ModuleBypassItem;
  491. bypassItem->moduleWidget = this;
  492. menu->addChild(bypassItem);
  493. ModuleDeleteItem *deleteItem = new ModuleDeleteItem;
  494. deleteItem->moduleWidget = this;
  495. menu->addChild(deleteItem);
  496. appendContextMenu(menu);
  497. return menu;
  498. }
  499. } // namespace rack