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.

489 lines
13KB

  1. #include "app.hpp"
  2. #include "engine.hpp"
  3. #include "plugin.hpp"
  4. #include "gui.hpp"
  5. #include "settings.hpp"
  6. #include "asset.hpp"
  7. #include <map>
  8. #include <algorithm>
  9. #include <thread>
  10. #include <set>
  11. #include "../ext/osdialog/osdialog.h"
  12. namespace rack {
  13. RackWidget::RackWidget() {
  14. rails = new FramebufferWidget();
  15. RackRail *rail = new RackRail();
  16. rail->box.size = Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
  17. rails->addChild(rail);
  18. rails->box.size = rail->box.size;
  19. moduleContainer = new Widget();
  20. addChild(moduleContainer);
  21. wireContainer = new WireContainer();
  22. addChild(wireContainer);
  23. }
  24. RackWidget::~RackWidget() {
  25. delete rails;
  26. }
  27. void RackWidget::clear() {
  28. wireContainer->activeWire = NULL;
  29. wireContainer->clearChildren();
  30. moduleContainer->clearChildren();
  31. lastPath = "";
  32. }
  33. void RackWidget::reset() {
  34. clear();
  35. loadPatch(assetLocal("template.vcv"));
  36. }
  37. void RackWidget::openDialog() {
  38. std::string dir = lastPath.empty() ? assetLocal("") : extractDirectory(lastPath);
  39. char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, NULL);
  40. if (path) {
  41. loadPatch(path);
  42. lastPath = path;
  43. free(path);
  44. }
  45. }
  46. void RackWidget::saveDialog() {
  47. if (!lastPath.empty()) {
  48. savePatch(lastPath);
  49. }
  50. else {
  51. saveAsDialog();
  52. }
  53. }
  54. void RackWidget::saveAsDialog() {
  55. std::string dir = lastPath.empty() ? assetLocal("") : extractDirectory(lastPath);
  56. char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcv", NULL);
  57. if (path) {
  58. std::string pathStr = path;
  59. free(path);
  60. std::string extension = extractExtension(pathStr);
  61. if (extension.empty()) {
  62. pathStr += ".vcv";
  63. }
  64. savePatch(pathStr);
  65. lastPath = pathStr;
  66. }
  67. }
  68. void RackWidget::savePatch(std::string path) {
  69. printf("Saving patch %s\n", path.c_str());
  70. FILE *file = fopen(path.c_str(), "w");
  71. if (!file)
  72. return;
  73. json_t *rootJ = toJson();
  74. if (rootJ) {
  75. json_dumpf(rootJ, file, JSON_INDENT(2));
  76. json_decref(rootJ);
  77. }
  78. fclose(file);
  79. }
  80. void RackWidget::loadPatch(std::string path) {
  81. printf("Loading patch %s\n", path.c_str());
  82. FILE *file = fopen(path.c_str(), "r");
  83. if (!file) {
  84. // Exit silently
  85. return;
  86. }
  87. json_error_t error;
  88. json_t *rootJ = json_loadf(file, 0, &error);
  89. if (rootJ) {
  90. clear();
  91. fromJson(rootJ);
  92. json_decref(rootJ);
  93. }
  94. else {
  95. std::string message = stringf("JSON parsing error at %s %d:%d %s\n", error.source, error.line, error.column, error.text);
  96. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  97. }
  98. fclose(file);
  99. }
  100. json_t *RackWidget::toJson() {
  101. // root
  102. json_t *rootJ = json_object();
  103. // version
  104. json_t *versionJ = json_string(gApplicationVersion.c_str());
  105. json_object_set_new(rootJ, "version", versionJ);
  106. // modules
  107. json_t *modulesJ = json_array();
  108. std::map<ModuleWidget*, int> moduleIds;
  109. int moduleId = 0;
  110. for (Widget *w : moduleContainer->children) {
  111. ModuleWidget *moduleWidget = dynamic_cast<ModuleWidget*>(w);
  112. assert(moduleWidget);
  113. moduleIds[moduleWidget] = moduleId;
  114. moduleId++;
  115. // module
  116. json_t *moduleJ = moduleWidget->toJson();
  117. json_array_append_new(modulesJ, moduleJ);
  118. }
  119. json_object_set_new(rootJ, "modules", modulesJ);
  120. // wires
  121. json_t *wires = json_array();
  122. for (Widget *w : wireContainer->children) {
  123. WireWidget *wireWidget = dynamic_cast<WireWidget*>(w);
  124. assert(wireWidget);
  125. // Only serialize WireWidgets connected on both ends
  126. if (!(wireWidget->outputPort && wireWidget->inputPort))
  127. continue;
  128. // wire
  129. json_t *wire = json_object();
  130. {
  131. // Get the modules at each end of the wire
  132. ModuleWidget *outputModuleWidget = wireWidget->outputPort->getAncestorOfType<ModuleWidget>();
  133. assert(outputModuleWidget);
  134. int outputModuleId = moduleIds[outputModuleWidget];
  135. ModuleWidget *inputModuleWidget = wireWidget->inputPort->getAncestorOfType<ModuleWidget>();
  136. assert(inputModuleWidget);
  137. int inputModuleId = moduleIds[inputModuleWidget];
  138. // Get output/input ports
  139. auto outputIt = std::find(outputModuleWidget->outputs.begin(), outputModuleWidget->outputs.end(), wireWidget->outputPort);
  140. assert(outputIt != outputModuleWidget->outputs.end());
  141. int outputId = outputIt - outputModuleWidget->outputs.begin();
  142. auto inputIt = std::find(inputModuleWidget->inputs.begin(), inputModuleWidget->inputs.end(), wireWidget->inputPort);
  143. assert(inputIt != inputModuleWidget->inputs.end());
  144. int inputId = inputIt - inputModuleWidget->inputs.begin();
  145. json_object_set_new(wire, "outputModuleId", json_integer(outputModuleId));
  146. json_object_set_new(wire, "outputId", json_integer(outputId));
  147. json_object_set_new(wire, "inputModuleId", json_integer(inputModuleId));
  148. json_object_set_new(wire, "inputId", json_integer(inputId));
  149. }
  150. json_array_append_new(wires, wire);
  151. }
  152. json_object_set_new(rootJ, "wires", wires);
  153. return rootJ;
  154. }
  155. void RackWidget::fromJson(json_t *rootJ) {
  156. std::string message;
  157. // version
  158. json_t *versionJ = json_object_get(rootJ, "version");
  159. if (versionJ) {
  160. const char *version = json_string_value(versionJ);
  161. if (gApplicationVersion != version)
  162. message += stringf("This patch was created with Rack %s. Saving it will convert it to a Rack %s patch.\n\n", version, gApplicationVersion.c_str());
  163. }
  164. // modules
  165. std::map<int, ModuleWidget*> moduleWidgets;
  166. json_t *modulesJ = json_object_get(rootJ, "modules");
  167. if (!modulesJ) return;
  168. size_t moduleId;
  169. json_t *moduleJ;
  170. json_array_foreach(modulesJ, moduleId, moduleJ) {
  171. json_t *manufacturerSlugJ = json_object_get(moduleJ, "manufacturer");
  172. if (!manufacturerSlugJ) {
  173. // Backward compatibility with Rack v0.4 and lower
  174. manufacturerSlugJ = json_object_get(moduleJ, "plugin");
  175. if (!manufacturerSlugJ) continue;
  176. }
  177. json_t *modelSlugJ = json_object_get(moduleJ, "model");
  178. if (!modelSlugJ) continue;
  179. std::string manufacturerSlug = json_string_value(manufacturerSlugJ);
  180. std::string modelSlug = json_string_value(modelSlugJ);
  181. // Search for model
  182. Model *model = NULL;
  183. for (Plugin *plugin : gPlugins) {
  184. for (Model *m : plugin->models) {
  185. if (m->manufacturerSlug == manufacturerSlug && m->slug == modelSlug) {
  186. model = m;
  187. }
  188. }
  189. }
  190. if (!model) {
  191. message += stringf("Could not find \"%s %s\" module\n", manufacturerSlug.c_str(), modelSlug.c_str());
  192. continue;
  193. }
  194. // Create ModuleWidget
  195. ModuleWidget *moduleWidget = model->createModuleWidget();
  196. assert(moduleWidget);
  197. moduleWidget->fromJson(moduleJ);
  198. moduleContainer->addChild(moduleWidget);
  199. moduleWidgets[moduleId] = moduleWidget;
  200. }
  201. // wires
  202. json_t *wiresJ = json_object_get(rootJ, "wires");
  203. if (!wiresJ) return;
  204. size_t wireId;
  205. json_t *wireJ;
  206. json_array_foreach(wiresJ, wireId, wireJ) {
  207. int outputModuleId, outputId;
  208. int inputModuleId, inputId;
  209. int err = json_unpack(wireJ, "{s:i, s:i, s:i, s:i}",
  210. "outputModuleId", &outputModuleId, "outputId", &outputId,
  211. "inputModuleId", &inputModuleId, "inputId", &inputId);
  212. if (err) continue;
  213. // Get ports
  214. ModuleWidget *outputModuleWidget = moduleWidgets[outputModuleId];
  215. if (!outputModuleWidget) continue;
  216. Port *outputPort = outputModuleWidget->outputs[outputId];
  217. if (!outputPort) continue;
  218. ModuleWidget *inputModuleWidget = moduleWidgets[inputModuleId];
  219. if (!inputModuleWidget) continue;
  220. Port *inputPort = inputModuleWidget->inputs[inputId];
  221. if (!inputPort) continue;
  222. // Create WireWidget
  223. WireWidget *wireWidget = new WireWidget();
  224. wireWidget->outputPort = outputPort;
  225. wireWidget->inputPort = inputPort;
  226. wireWidget->updateWire();
  227. // Add wire to rack
  228. wireContainer->addChild(wireWidget);
  229. }
  230. // Display a message if we have something to say
  231. if (!message.empty()) {
  232. osdialog_message(OSDIALOG_INFO, OSDIALOG_OK, message.c_str());
  233. }
  234. }
  235. void RackWidget::addModule(ModuleWidget *m) {
  236. moduleContainer->addChild(m);
  237. }
  238. void RackWidget::deleteModule(ModuleWidget *m) {
  239. moduleContainer->removeChild(m);
  240. }
  241. void RackWidget::cloneModule(ModuleWidget *m) {
  242. // Create new module from model
  243. ModuleWidget *clonedModuleWidget = m->model->createModuleWidget();
  244. // JSON serialization is the most straightforward way to do this
  245. json_t *moduleJ = m->toJson();
  246. clonedModuleWidget->fromJson(moduleJ);
  247. json_decref(moduleJ);
  248. Rect clonedBox = clonedModuleWidget->box;
  249. clonedBox.pos = m->box.pos;
  250. requestModuleBoxNearest(clonedModuleWidget, clonedBox);
  251. addModule(clonedModuleWidget);
  252. }
  253. bool RackWidget::requestModuleBox(ModuleWidget *m, Rect box) {
  254. if (box.pos.x < 0 || box.pos.y < 0)
  255. return false;
  256. for (Widget *child2 : moduleContainer->children) {
  257. if (m == child2) continue;
  258. if (box.intersects(child2->box)) {
  259. return false;
  260. }
  261. }
  262. m->box = box;
  263. return true;
  264. }
  265. bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, Rect box) {
  266. // Create possible positions
  267. int x0 = roundf(box.pos.x / RACK_GRID_WIDTH);
  268. int y0 = roundf(box.pos.y / RACK_GRID_HEIGHT);
  269. std::vector<Vec> positions;
  270. for (int y = maxi(0, y0 - 4); y < y0 + 4; y++) {
  271. for (int x = maxi(0, x0 - 200); x < x0 + 200; x++) {
  272. positions.push_back(Vec(x * RACK_GRID_WIDTH, y * RACK_GRID_HEIGHT));
  273. }
  274. }
  275. // Sort possible positions by distance to the requested position
  276. std::sort(positions.begin(), positions.end(), [box](Vec a, Vec b) {
  277. return a.minus(box.pos).norm() < b.minus(box.pos).norm();
  278. });
  279. // Find a position that does not collide
  280. for (Vec position : positions) {
  281. Rect newBox = box;
  282. newBox.pos = position;
  283. if (requestModuleBox(m, newBox))
  284. return true;
  285. }
  286. return false;
  287. }
  288. void RackWidget::step() {
  289. rails->step();
  290. // Expand size to fit modules
  291. Vec moduleSize = moduleContainer->getChildrenBoundingBox().getBottomRight();
  292. // We assume that the size is reset by a parent before calling step(). Otherwise it will grow unbounded.
  293. box.size = box.size.max(moduleSize);
  294. // Autosave every 15 seconds
  295. if (gGuiFrame % (60*15) == 0) {
  296. savePatch(assetLocal("autosave.vcv"));
  297. settingsSave(assetLocal("settings.json"));
  298. }
  299. Widget::step();
  300. }
  301. void RackWidget::draw(NVGcontext *vg) {
  302. // Draw rails
  303. nvgBeginPath(vg);
  304. nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y);
  305. NVGpaint paint = nvgImagePattern(vg, rails->box.pos.x, rails->box.pos.y, rails->box.size.x, rails->box.size.y, 0.0, rails->getImageHandle(), 1.0);
  306. nvgFillPaint(vg, paint);
  307. nvgFill(vg);
  308. Widget::draw(vg);
  309. }
  310. struct AddModuleMenuItem : MenuItem {
  311. Model *model;
  312. Vec modulePos;
  313. void onAction() override {
  314. ModuleWidget *moduleWidget = model->createModuleWidget();
  315. gRackWidget->moduleContainer->addChild(moduleWidget);
  316. // Move module nearest to the mouse position
  317. Rect box;
  318. box.size = moduleWidget->box.size;
  319. box.pos = modulePos.minus(box.getCenter());
  320. gRackWidget->requestModuleBoxNearest(moduleWidget, box);
  321. }
  322. };
  323. struct UrlItem : MenuItem {
  324. std::string url;
  325. void onAction() override {
  326. std::thread t(openBrowser, url);
  327. t.detach();
  328. }
  329. };
  330. struct AddManufacturerMenuItem : MenuItem {
  331. std::string manufacturerName;
  332. Vec modulePos;
  333. Menu *createChildMenu() override {
  334. // Collect models which have this manufacturer name
  335. std::set<Model*> models;
  336. for (Plugin *plugin : gPlugins) {
  337. for (Model *model : plugin->models) {
  338. if (model->manufacturerName == manufacturerName) {
  339. models.insert(model);
  340. }
  341. }
  342. }
  343. if (models.empty())
  344. return NULL;
  345. // Model items
  346. Menu *menu = new Menu();
  347. for (Model *model : models) {
  348. AddModuleMenuItem *item = new AddModuleMenuItem();
  349. item->text = model->name;
  350. item->model = model;
  351. item->modulePos = modulePos;
  352. menu->pushChild(item);
  353. }
  354. // Metadata items
  355. /*
  356. {
  357. MenuLabel *label = new MenuLabel();
  358. menu->pushChild(label);
  359. }
  360. {
  361. MenuLabel *label = new MenuLabel();
  362. label->text = plugin->name;
  363. menu->pushChild(label);
  364. }
  365. if (!plugin->homepageUrl.empty()) {
  366. UrlItem *item = new UrlItem();
  367. item->text = "Homepage";
  368. item->url = plugin->homepageUrl;
  369. menu->pushChild(item);
  370. }
  371. if (!plugin->manualUrl.empty()) {
  372. UrlItem *item = new UrlItem();
  373. item->text = "Manual";
  374. item->url = plugin->manualUrl;
  375. menu->pushChild(item);
  376. }
  377. if (!plugin->path.empty()) {
  378. UrlItem *item = new UrlItem();
  379. item->text = "Browse directory";
  380. item->url = plugin->path;
  381. menu->pushChild(item);
  382. }
  383. if (!plugin->version.empty()) {
  384. MenuLabel *item = new MenuLabel();
  385. item->text = "Version: v" + plugin->version;
  386. menu->pushChild(item);
  387. }
  388. */
  389. return menu;
  390. }
  391. };
  392. void RackWidget::onMouseDownOpaque(int button) {
  393. if (button == 1) {
  394. Vec modulePos = gMousePos.minus(getAbsolutePos());
  395. Menu *menu = gScene->createMenu();
  396. menu->pushChild(construct<MenuLabel>(&MenuLabel::text, "Add module"));
  397. // TODO make functional
  398. TextField *searchField = construct<TextField>();
  399. menu->pushChild(searchField);
  400. // Focus search field
  401. gFocusedWidget = searchField;
  402. // Collect manufacturer names
  403. std::set<std::string> manufacturerNames;
  404. for (Plugin *plugin : gPlugins) {
  405. for (Model *model : plugin->models) {
  406. manufacturerNames.insert(model->manufacturerName);
  407. }
  408. }
  409. // Add menu item for each manufacturer name
  410. for (std::string manufacturerName : manufacturerNames) {
  411. AddManufacturerMenuItem *item = new AddManufacturerMenuItem();
  412. item->text = manufacturerName;
  413. item->manufacturerName = manufacturerName;
  414. item->modulePos = modulePos;
  415. menu->pushChild(item);
  416. }
  417. }
  418. }
  419. } // namespace rack