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.

548 lines
13KB

  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(Port *input) {
  29. assert(input->type == Port::INPUT);
  30. inputs.push_back(input);
  31. addChild(input);
  32. }
  33. void ModuleWidget::addOutput(Port *output) {
  34. assert(output->type == Port::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::disconnect() {
  213. for (Port *input : inputs) {
  214. context()->scene->rackWidget->wireContainer->removeAllWires(input);
  215. }
  216. for (Port *output : outputs) {
  217. context()->scene->rackWidget->wireContainer->removeAllWires(output);
  218. }
  219. }
  220. void ModuleWidget::reset() {
  221. if (module) {
  222. context()->engine->resetModule(module);
  223. }
  224. }
  225. void ModuleWidget::randomize() {
  226. if (module) {
  227. context()->engine->randomizeModule(module);
  228. }
  229. }
  230. void ModuleWidget::draw(NVGcontext *vg) {
  231. nvgScissor(vg, 0, 0, box.size.x, box.size.y);
  232. Widget::draw(vg);
  233. // Power meter
  234. if (module && settings::powerMeter) {
  235. nvgBeginPath(vg);
  236. nvgRect(vg,
  237. 0, box.size.y - 20,
  238. 55, 20);
  239. nvgFillColor(vg, nvgRGBAf(0, 0, 0, 0.5));
  240. nvgFill(vg);
  241. std::string cpuText = string::f("%.0f mS", module->cpuTime * 1000.f);
  242. // TODO Use blendish text function
  243. nvgFontFaceId(vg, context()->window->uiFont->handle);
  244. nvgFontSize(vg, 12);
  245. nvgFillColor(vg, nvgRGBf(1, 1, 1));
  246. nvgText(vg, 10.0, box.size.y - 6.0, cpuText.c_str(), NULL);
  247. float p = math::clamp(module->cpuTime, 0.f, 1.f);
  248. nvgBeginPath(vg);
  249. nvgRect(vg,
  250. 0, (1.f - p) * box.size.y,
  251. 5, p * box.size.y);
  252. nvgFillColor(vg, nvgRGBAf(1, 0, 0, 1.0));
  253. nvgFill(vg);
  254. }
  255. nvgResetScissor(vg);
  256. }
  257. void ModuleWidget::drawShadow(NVGcontext *vg) {
  258. nvgBeginPath(vg);
  259. float r = 20; // Blur radius
  260. float c = 20; // Corner radius
  261. math::Vec b = math::Vec(-10, 30); // Offset from each corner
  262. nvgRect(vg, b.x - r, b.y - r, box.size.x - 2*b.x + 2*r, box.size.y - 2*b.y + 2*r);
  263. NVGcolor shadowColor = nvgRGBAf(0, 0, 0, 0.2);
  264. NVGcolor transparentColor = nvgRGBAf(0, 0, 0, 0);
  265. nvgFillPaint(vg, nvgBoxGradient(vg, b.x, b.y, box.size.x - 2*b.x, box.size.y - 2*b.y, c, r, shadowColor, transparentColor));
  266. nvgFill(vg);
  267. }
  268. void ModuleWidget::onHover(event::Hover &e) {
  269. OpaqueWidget::onHover(e);
  270. // 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.
  271. if (glfwGetKey(context()->window->win, GLFW_KEY_DELETE) == GLFW_PRESS || glfwGetKey(context()->window->win, GLFW_KEY_BACKSPACE) == GLFW_PRESS) {
  272. if (!context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  273. requestedDelete = true;
  274. return;
  275. }
  276. }
  277. }
  278. void ModuleWidget::onButton(event::Button &e) {
  279. OpaqueWidget::onButton(e);
  280. if (e.target == this) {
  281. if (e.button == 1) {
  282. createContextMenu();
  283. }
  284. }
  285. }
  286. void ModuleWidget::onHoverKey(event::HoverKey &e) {
  287. if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) {
  288. switch (e.key) {
  289. case GLFW_KEY_I: {
  290. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  291. reset();
  292. e.target = this;
  293. }
  294. } break;
  295. case GLFW_KEY_R: {
  296. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  297. randomize();
  298. e.target = this;
  299. }
  300. } break;
  301. case GLFW_KEY_C: {
  302. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  303. copyClipboard();
  304. e.target = this;
  305. }
  306. } break;
  307. case GLFW_KEY_V: {
  308. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  309. pasteClipboard();
  310. e.target = this;
  311. }
  312. } break;
  313. case GLFW_KEY_D: {
  314. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  315. context()->scene->rackWidget->cloneModule(this);
  316. e.target = this;
  317. }
  318. } break;
  319. case GLFW_KEY_U: {
  320. if (context()->window->isModPressed() && !context()->window->isShiftPressed()) {
  321. disconnect();
  322. e.target = this;
  323. }
  324. } break;
  325. }
  326. }
  327. if (!e.target)
  328. OpaqueWidget::onHoverKey(e);
  329. }
  330. void ModuleWidget::onDragStart(event::DragStart &e) {
  331. dragPos = context()->scene->rackWidget->lastMousePos.minus(box.pos);
  332. }
  333. void ModuleWidget::onDragEnd(event::DragEnd &e) {
  334. }
  335. void ModuleWidget::onDragMove(event::DragMove &e) {
  336. if (!settings::lockModules) {
  337. math::Rect newBox = box;
  338. newBox.pos = context()->scene->rackWidget->lastMousePos.minus(dragPos);
  339. context()->scene->rackWidget->requestModuleBoxNearest(this, newBox);
  340. }
  341. }
  342. struct ModuleDisconnectItem : MenuItem {
  343. ModuleWidget *moduleWidget;
  344. ModuleDisconnectItem() {
  345. text = "Disconnect cables";
  346. rightText = WINDOW_MOD_KEY_NAME "+U";
  347. }
  348. void onAction(event::Action &e) override {
  349. moduleWidget->disconnect();
  350. }
  351. };
  352. struct ModuleResetItem : MenuItem {
  353. ModuleWidget *moduleWidget;
  354. ModuleResetItem() {
  355. text = "Initialize";
  356. rightText = WINDOW_MOD_KEY_NAME "+I";
  357. }
  358. void onAction(event::Action &e) override {
  359. moduleWidget->reset();
  360. }
  361. };
  362. struct ModuleRandomizeItem : MenuItem {
  363. ModuleWidget *moduleWidget;
  364. ModuleRandomizeItem() {
  365. text = "Randomize";
  366. rightText = WINDOW_MOD_KEY_NAME "+R";
  367. }
  368. void onAction(event::Action &e) override {
  369. moduleWidget->randomize();
  370. }
  371. };
  372. struct ModuleCopyItem : MenuItem {
  373. ModuleWidget *moduleWidget;
  374. ModuleCopyItem() {
  375. text = "Copy preset";
  376. rightText = WINDOW_MOD_KEY_NAME "+C";
  377. }
  378. void onAction(event::Action &e) override {
  379. moduleWidget->copyClipboard();
  380. }
  381. };
  382. struct ModulePasteItem : MenuItem {
  383. ModuleWidget *moduleWidget;
  384. ModulePasteItem() {
  385. text = "Paste preset";
  386. rightText = WINDOW_MOD_KEY_NAME "+V";
  387. }
  388. void onAction(event::Action &e) override {
  389. moduleWidget->pasteClipboard();
  390. }
  391. };
  392. struct ModuleSaveItem : MenuItem {
  393. ModuleWidget *moduleWidget;
  394. ModuleSaveItem() {
  395. text = "Save preset";
  396. }
  397. void onAction(event::Action &e) override {
  398. moduleWidget->saveDialog();
  399. }
  400. };
  401. struct ModuleLoadItem : MenuItem {
  402. ModuleWidget *moduleWidget;
  403. ModuleLoadItem() {
  404. text = "Load preset";
  405. }
  406. void onAction(event::Action &e) override {
  407. moduleWidget->loadDialog();
  408. }
  409. };
  410. struct ModuleCloneItem : MenuItem {
  411. ModuleWidget *moduleWidget;
  412. ModuleCloneItem() {
  413. text = "Duplicate";
  414. rightText = WINDOW_MOD_KEY_NAME "+D";
  415. }
  416. void onAction(event::Action &e) override {
  417. context()->scene->rackWidget->cloneModule(moduleWidget);
  418. }
  419. };
  420. struct ModuleDeleteItem : MenuItem {
  421. ModuleWidget *moduleWidget;
  422. ModuleDeleteItem() {
  423. text = "Delete";
  424. rightText = "Backspace/Delete";
  425. }
  426. void onAction(event::Action &e) override {
  427. context()->scene->rackWidget->deleteModule(moduleWidget);
  428. delete moduleWidget;
  429. }
  430. };
  431. Menu *ModuleWidget::createContextMenu() {
  432. Menu *menu = createMenu();
  433. MenuLabel *menuLabel = new MenuLabel;
  434. menuLabel->text = model->plugin->author + " " + model->name + " " + model->plugin->version;
  435. menu->addChild(menuLabel);
  436. ModuleResetItem *resetItem = new ModuleResetItem;
  437. resetItem->moduleWidget = this;
  438. menu->addChild(resetItem);
  439. ModuleRandomizeItem *randomizeItem = new ModuleRandomizeItem;
  440. randomizeItem->moduleWidget = this;
  441. menu->addChild(randomizeItem);
  442. ModuleDisconnectItem *disconnectItem = new ModuleDisconnectItem;
  443. disconnectItem->moduleWidget = this;
  444. menu->addChild(disconnectItem);
  445. ModuleCloneItem *cloneItem = new ModuleCloneItem;
  446. cloneItem->moduleWidget = this;
  447. menu->addChild(cloneItem);
  448. ModuleCopyItem *copyItem = new ModuleCopyItem;
  449. copyItem->moduleWidget = this;
  450. menu->addChild(copyItem);
  451. ModulePasteItem *pasteItem = new ModulePasteItem;
  452. pasteItem->moduleWidget = this;
  453. menu->addChild(pasteItem);
  454. ModuleLoadItem *loadItem = new ModuleLoadItem;
  455. loadItem->moduleWidget = this;
  456. menu->addChild(loadItem);
  457. ModuleSaveItem *saveItem = new ModuleSaveItem;
  458. saveItem->moduleWidget = this;
  459. menu->addChild(saveItem);
  460. ModuleDeleteItem *deleteItem = new ModuleDeleteItem;
  461. deleteItem->moduleWidget = this;
  462. menu->addChild(deleteItem);
  463. appendContextMenu(menu);
  464. return menu;
  465. }
  466. } // namespace rack