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.

479 lines
12KB

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