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.

541 lines
14KB

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