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.

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