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.

533 lines
13KB

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