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.

461 lines
12KB

  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 "../ext/osdialog/osdialog.h"
  10. namespace rack {
  11. RackWidget::RackWidget() {
  12. rails = new FramebufferWidget();
  13. rails->box.size = Vec();
  14. rails->oversample = 1.0;
  15. {
  16. RackRail *rail = new RackRail();
  17. rail->box.size = Vec();
  18. rails->addChild(rail);
  19. }
  20. addChild(rails);
  21. moduleContainer = new Widget();
  22. addChild(moduleContainer);
  23. wireContainer = new WireContainer();
  24. addChild(wireContainer);
  25. }
  26. RackWidget::~RackWidget() {
  27. }
  28. void RackWidget::clear() {
  29. wireContainer->activeWire = NULL;
  30. wireContainer->clearChildren();
  31. moduleContainer->clearChildren();
  32. lastPath = "";
  33. /*
  34. // Add all modules to rack
  35. Vec pos;
  36. for (Plugin *plugin : gPlugins) {
  37. for (Model *model : plugin->models) {
  38. ModuleWidget *moduleWidget = model->createModuleWidget();
  39. moduleContainer->addChild(moduleWidget);
  40. // Move module nearest to the mouse position
  41. Rect box;
  42. box.size = moduleWidget->box.size;
  43. box.pos = pos;
  44. requestModuleBoxNearest(moduleWidget, box);
  45. pos.x += box.size.x;
  46. }
  47. pos.y += RACK_GRID_HEIGHT;
  48. pos.x = 0;
  49. }
  50. */
  51. }
  52. void RackWidget::reset() {
  53. if (osdialog_message(OSDIALOG_INFO, OSDIALOG_OK_CANCEL, "Clear your patch and start over?")) {
  54. clear();
  55. loadPatch(assetLocal("template.vcv"));
  56. }
  57. }
  58. void RackWidget::openDialog() {
  59. std::string dir = lastPath.empty() ? assetLocal("") : extractDirectory(lastPath);
  60. char *path = osdialog_file(OSDIALOG_OPEN, dir.c_str(), NULL, NULL);
  61. if (path) {
  62. loadPatch(path);
  63. lastPath = path;
  64. free(path);
  65. }
  66. }
  67. void RackWidget::saveDialog() {
  68. if (!lastPath.empty()) {
  69. savePatch(lastPath);
  70. }
  71. else {
  72. saveAsDialog();
  73. }
  74. }
  75. void RackWidget::saveAsDialog() {
  76. std::string dir = lastPath.empty() ? assetLocal("") : extractDirectory(lastPath);
  77. char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcv", NULL);
  78. if (path) {
  79. std::string pathStr = path;
  80. free(path);
  81. std::string extension = extractExtension(pathStr);
  82. if (extension.empty()) {
  83. pathStr += ".vcv";
  84. }
  85. savePatch(pathStr);
  86. lastPath = pathStr;
  87. }
  88. }
  89. void RackWidget::savePatch(std::string path) {
  90. info("Saving patch %s", path.c_str());
  91. json_t *rootJ = toJson();
  92. if (!rootJ)
  93. return;
  94. FILE *file = fopen(path.c_str(), "w");
  95. if (file) {
  96. json_dumpf(rootJ, file, JSON_INDENT(2) | JSON_REAL_PRECISION(9));
  97. fclose(file);
  98. }
  99. json_decref(rootJ);
  100. }
  101. void RackWidget::loadPatch(std::string path) {
  102. info("Loading patch %s", path.c_str());
  103. FILE *file = fopen(path.c_str(), "r");
  104. if (!file) {
  105. // Exit silently
  106. return;
  107. }
  108. json_error_t error;
  109. json_t *rootJ = json_loadf(file, 0, &error);
  110. if (rootJ) {
  111. clear();
  112. fromJson(rootJ);
  113. json_decref(rootJ);
  114. }
  115. else {
  116. std::string message = stringf("JSON parsing error at %s %d:%d %s\n", error.source, error.line, error.column, error.text);
  117. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  118. }
  119. fclose(file);
  120. }
  121. json_t *RackWidget::toJson() {
  122. // root
  123. json_t *rootJ = json_object();
  124. // version
  125. json_t *versionJ = json_string(gApplicationVersion.c_str());
  126. json_object_set_new(rootJ, "version", versionJ);
  127. // modules
  128. json_t *modulesJ = json_array();
  129. std::map<ModuleWidget*, int> moduleIds;
  130. int moduleId = 0;
  131. for (Widget *w : moduleContainer->children) {
  132. ModuleWidget *moduleWidget = dynamic_cast<ModuleWidget*>(w);
  133. assert(moduleWidget);
  134. moduleIds[moduleWidget] = moduleId;
  135. moduleId++;
  136. // module
  137. json_t *moduleJ = moduleWidget->toJson();
  138. json_array_append_new(modulesJ, moduleJ);
  139. }
  140. json_object_set_new(rootJ, "modules", modulesJ);
  141. // wires
  142. json_t *wires = json_array();
  143. for (Widget *w : wireContainer->children) {
  144. WireWidget *wireWidget = dynamic_cast<WireWidget*>(w);
  145. assert(wireWidget);
  146. // Only serialize WireWidgets connected on both ends
  147. if (!(wireWidget->outputPort && wireWidget->inputPort))
  148. continue;
  149. // wire
  150. json_t *wire = wireWidget->toJson();
  151. // Get the modules at each end of the wire
  152. ModuleWidget *outputModuleWidget = wireWidget->outputPort->getAncestorOfType<ModuleWidget>();
  153. assert(outputModuleWidget);
  154. int outputModuleId = moduleIds[outputModuleWidget];
  155. ModuleWidget *inputModuleWidget = wireWidget->inputPort->getAncestorOfType<ModuleWidget>();
  156. assert(inputModuleWidget);
  157. int inputModuleId = moduleIds[inputModuleWidget];
  158. // Get output/input ports
  159. int outputId = wireWidget->outputPort->portId;
  160. int inputId = wireWidget->inputPort->portId;
  161. json_object_set_new(wire, "outputModuleId", json_integer(outputModuleId));
  162. json_object_set_new(wire, "outputId", json_integer(outputId));
  163. json_object_set_new(wire, "inputModuleId", json_integer(inputModuleId));
  164. json_object_set_new(wire, "inputId", json_integer(inputId));
  165. json_array_append_new(wires, wire);
  166. }
  167. json_object_set_new(rootJ, "wires", wires);
  168. return rootJ;
  169. }
  170. void RackWidget::fromJson(json_t *rootJ) {
  171. std::string message;
  172. // version
  173. std::string version;
  174. json_t *versionJ = json_object_get(rootJ, "version");
  175. if (versionJ) {
  176. version = json_string_value(versionJ);
  177. if (!version.empty() && gApplicationVersion != version)
  178. 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.c_str());
  179. }
  180. // Detect old patches with ModuleWidget::params/inputs/outputs indices.
  181. // (We now use Module::params/inputs/outputs indices.)
  182. int legacy = 0;
  183. if (startsWith(version, "0.3.") || startsWith(version, "0.4.") || startsWith(version, "0.5.") || version == "" || version == "dev") {
  184. legacy = 1;
  185. }
  186. if (legacy) {
  187. info("Loading patch using legacy mode %d", legacy);
  188. }
  189. // modules
  190. std::map<int, ModuleWidget*> moduleWidgets;
  191. json_t *modulesJ = json_object_get(rootJ, "modules");
  192. if (!modulesJ) return;
  193. size_t moduleId;
  194. json_t *moduleJ;
  195. json_array_foreach(modulesJ, moduleId, moduleJ) {
  196. // Set legacy property
  197. if (legacy)
  198. json_object_set_new(moduleJ, "legacy", json_integer(legacy));
  199. json_t *pluginSlugJ = json_object_get(moduleJ, "plugin");
  200. if (!pluginSlugJ) continue;
  201. json_t *modelSlugJ = json_object_get(moduleJ, "model");
  202. if (!modelSlugJ) continue;
  203. std::string pluginSlug = json_string_value(pluginSlugJ);
  204. std::string modelSlug = json_string_value(modelSlugJ);
  205. // Search for plugin
  206. Plugin *plugin = NULL;
  207. for (Plugin *p : gPlugins) {
  208. if (p->slug == pluginSlug) {
  209. plugin = p;
  210. break;
  211. }
  212. }
  213. if (!plugin) {
  214. message += stringf("Could not find plugin \"%s\" for module \"%s\"\n", pluginSlug.c_str(), modelSlug.c_str());
  215. continue;
  216. }
  217. // Search for model
  218. Model *model = NULL;
  219. for (Model *m : plugin->models) {
  220. if (m->slug == modelSlug) {
  221. model = m;
  222. break;
  223. }
  224. }
  225. if (!model) {
  226. message += stringf("Could not find module \"%s\" in plugin \"%s\"\n", modelSlug.c_str(), pluginSlug.c_str());
  227. continue;
  228. }
  229. // Create ModuleWidget
  230. ModuleWidget *moduleWidget = model->createModuleWidget();
  231. assert(moduleWidget);
  232. moduleWidget->fromJson(moduleJ);
  233. moduleContainer->addChild(moduleWidget);
  234. moduleWidgets[moduleId] = moduleWidget;
  235. }
  236. // wires
  237. json_t *wiresJ = json_object_get(rootJ, "wires");
  238. if (!wiresJ) return;
  239. size_t wireId;
  240. json_t *wireJ;
  241. json_array_foreach(wiresJ, wireId, wireJ) {
  242. int outputModuleId = json_integer_value(json_object_get(wireJ, "outputModuleId"));
  243. int outputId = json_integer_value(json_object_get(wireJ, "outputId"));
  244. int inputModuleId = json_integer_value(json_object_get(wireJ, "inputModuleId"));
  245. int inputId = json_integer_value(json_object_get(wireJ, "inputId"));
  246. // Get module widgets
  247. ModuleWidget *outputModuleWidget = moduleWidgets[outputModuleId];
  248. if (!outputModuleWidget) continue;
  249. ModuleWidget *inputModuleWidget = moduleWidgets[inputModuleId];
  250. if (!inputModuleWidget) continue;
  251. // Get port widgets
  252. Port *outputPort = NULL;
  253. Port *inputPort = NULL;
  254. if (legacy && legacy <= 1) {
  255. outputPort = outputModuleWidget->outputs[outputId];
  256. inputPort = inputModuleWidget->inputs[inputId];
  257. }
  258. else {
  259. for (Port *port : outputModuleWidget->outputs) {
  260. if (port->portId == outputId) {
  261. outputPort = port;
  262. break;
  263. }
  264. }
  265. for (Port *port : inputModuleWidget->inputs) {
  266. if (port->portId == inputId) {
  267. inputPort = port;
  268. break;
  269. }
  270. }
  271. }
  272. if (!outputPort || !inputPort)
  273. continue;
  274. // Create WireWidget
  275. WireWidget *wireWidget = new WireWidget();
  276. wireWidget->fromJson(wireJ);
  277. wireWidget->outputPort = outputPort;
  278. wireWidget->inputPort = inputPort;
  279. wireWidget->updateWire();
  280. // Add wire to rack
  281. wireContainer->addChild(wireWidget);
  282. }
  283. // Display a message if we have something to say
  284. if (!message.empty()) {
  285. osdialog_message(OSDIALOG_INFO, OSDIALOG_OK, message.c_str());
  286. }
  287. }
  288. void RackWidget::addModule(ModuleWidget *m) {
  289. moduleContainer->addChild(m);
  290. m->create();
  291. }
  292. void RackWidget::deleteModule(ModuleWidget *m) {
  293. m->_delete();
  294. moduleContainer->removeChild(m);
  295. }
  296. void RackWidget::cloneModule(ModuleWidget *m) {
  297. // Create new module from model
  298. ModuleWidget *clonedModuleWidget = m->model->createModuleWidget();
  299. // JSON serialization is the most straightforward way to do this
  300. json_t *moduleJ = m->toJson();
  301. clonedModuleWidget->fromJson(moduleJ);
  302. json_decref(moduleJ);
  303. Rect clonedBox = clonedModuleWidget->box;
  304. clonedBox.pos = m->box.pos;
  305. requestModuleBoxNearest(clonedModuleWidget, clonedBox);
  306. addModule(clonedModuleWidget);
  307. }
  308. bool RackWidget::requestModuleBox(ModuleWidget *m, Rect box) {
  309. if (box.pos.x < 0 || box.pos.y < 0)
  310. return false;
  311. for (Widget *child2 : moduleContainer->children) {
  312. if (m == child2) continue;
  313. if (box.intersects(child2->box)) {
  314. return false;
  315. }
  316. }
  317. m->box = box;
  318. return true;
  319. }
  320. bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, Rect box) {
  321. // Create possible positions
  322. int x0 = roundf(box.pos.x / RACK_GRID_WIDTH);
  323. int y0 = roundf(box.pos.y / RACK_GRID_HEIGHT);
  324. std::vector<Vec> positions;
  325. for (int y = max(0, y0 - 8); y < y0 + 8; y++) {
  326. for (int x = max(0, x0 - 400); x < x0 + 400; x++) {
  327. positions.push_back(Vec(x * RACK_GRID_WIDTH, y * RACK_GRID_HEIGHT));
  328. }
  329. }
  330. // Sort possible positions by distance to the requested position
  331. std::sort(positions.begin(), positions.end(), [box](Vec a, Vec b) {
  332. return a.minus(box.pos).norm() < b.minus(box.pos).norm();
  333. });
  334. // Find a position that does not collide
  335. for (Vec position : positions) {
  336. Rect newBox = box;
  337. newBox.pos = position;
  338. if (requestModuleBox(m, newBox))
  339. return true;
  340. }
  341. return false;
  342. }
  343. void RackWidget::step() {
  344. // Expand size to fit modules
  345. Vec moduleSize = moduleContainer->getChildrenBoundingBox().getBottomRight();
  346. // We assume that the size is reset by a parent before calling step(). Otherwise it will grow unbounded.
  347. box.size = box.size.max(moduleSize);
  348. // Adjust size and position of rails
  349. Widget *rail = rails->children.front();
  350. Rect bound = getViewport(Rect(Vec(), box.size));
  351. if (!rails->box.contains(bound)) {
  352. Vec cellMargin = Vec(20, 1);
  353. rails->box.pos = bound.pos.div(RACK_GRID_SIZE).floor().minus(cellMargin).mult(RACK_GRID_SIZE);
  354. rails->box.size = bound.size.plus(cellMargin.mult(RACK_GRID_SIZE).mult(2));
  355. rails->dirty = true;
  356. rail->box.size = rails->box.size;
  357. }
  358. // Autosave every 15 seconds
  359. if (gGuiFrame % (60 * 15) == 0) {
  360. savePatch(assetLocal("autosave.vcv"));
  361. settingsSave(assetLocal("settings.json"));
  362. }
  363. Widget::step();
  364. }
  365. void RackWidget::draw(NVGcontext *vg) {
  366. Widget::draw(vg);
  367. }
  368. void RackWidget::onMouseMove(EventMouseMove &e) {
  369. OpaqueWidget::onMouseMove(e);
  370. lastMousePos = e.pos;
  371. }
  372. void RackWidget::onMouseDown(EventMouseDown &e) {
  373. Widget::onMouseDown(e);
  374. if (e.consumed)
  375. return;
  376. if (e.button == 1) {
  377. MenuOverlay *overlay = new MenuOverlay();
  378. AddModuleWindow *window = new AddModuleWindow();
  379. // Set center position
  380. window->box.pos = gMousePos.minus(window->box.getCenter());
  381. window->modulePos = lastMousePos;
  382. overlay->addChild(window);
  383. gScene->setOverlay(overlay);
  384. }
  385. e.consumed = true;
  386. e.target = this;
  387. }
  388. void RackWidget::onZoom(EventZoom &e) {
  389. rails->box.size = Vec();
  390. Widget::onZoom(e);
  391. }
  392. } // namespace rack