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.

524 lines
14KB

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