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.

384 lines
10KB

  1. #include <map>
  2. #include <algorithm>
  3. #include "app.hpp"
  4. #include "engine.hpp"
  5. #include "plugin.hpp"
  6. #include "gui.hpp"
  7. #include "../ext/osdialog/osdialog.h"
  8. namespace rack {
  9. struct WireContainer : TransparentWidget {
  10. void draw(NVGcontext *vg) {
  11. // Wire plugs
  12. for (Widget *child : children) {
  13. WireWidget *wire = dynamic_cast<WireWidget*>(child);
  14. assert(wire);
  15. wire->drawPlugs(vg);
  16. }
  17. Widget::draw(vg);
  18. }
  19. };
  20. RackWidget::RackWidget() {
  21. rails = new FramebufferWidget();
  22. RackRail *rail = new RackRail();
  23. rail->box.size = Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
  24. rails->addChild(rail);
  25. rails->box.size = rail->box.size;
  26. moduleContainer = new Widget();
  27. addChild(moduleContainer);
  28. wireContainer = new WireContainer();
  29. addChild(wireContainer);
  30. }
  31. RackWidget::~RackWidget() {
  32. delete rails;
  33. }
  34. void RackWidget::clear() {
  35. activeWire = NULL;
  36. wireContainer->clearChildren();
  37. moduleContainer->clearChildren();
  38. lastPath = "";
  39. }
  40. void RackWidget::openDialog() {
  41. std::string dir = lastPath.empty() ? "." : 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() ? "." : extractDirectory(lastPath);
  59. char *path = osdialog_file(OSDIALOG_SAVE, dir.c_str(), "Untitled.vcv", NULL);
  60. if (path) {
  61. savePatch(path);
  62. lastPath = path;
  63. free(path);
  64. }
  65. }
  66. void RackWidget::savePatch(std::string filename) {
  67. printf("Saving patch %s\n", filename.c_str());
  68. FILE *file = fopen(filename.c_str(), "w");
  69. if (!file)
  70. return;
  71. json_t *rootJ = toJson();
  72. if (rootJ) {
  73. json_dumpf(rootJ, file, JSON_INDENT(2));
  74. json_decref(rootJ);
  75. }
  76. fclose(file);
  77. }
  78. void RackWidget::loadPatch(std::string filename) {
  79. printf("Loading patch %s\n", filename.c_str());
  80. FILE *file = fopen(filename.c_str(), "r");
  81. if (!file)
  82. return;
  83. json_error_t error;
  84. json_t *rootJ = json_loadf(file, 0, &error);
  85. if (rootJ) {
  86. clear();
  87. fromJson(rootJ);
  88. json_decref(rootJ);
  89. }
  90. else {
  91. printf("JSON parsing error at %s %d:%d %s\n", error.source, error.line, error.column, error.text);
  92. }
  93. fclose(file);
  94. }
  95. json_t *RackWidget::toJson() {
  96. // root
  97. json_t *rootJ = json_object();
  98. // version
  99. json_t *versionJ = json_string(gApplicationVersion.c_str());
  100. json_object_set_new(rootJ, "version", versionJ);
  101. // modules
  102. json_t *modulesJ = json_array();
  103. std::map<ModuleWidget*, int> moduleIds;
  104. int moduleId = 0;
  105. for (Widget *w : moduleContainer->children) {
  106. ModuleWidget *moduleWidget = dynamic_cast<ModuleWidget*>(w);
  107. assert(moduleWidget);
  108. moduleIds[moduleWidget] = moduleId;
  109. moduleId++;
  110. // module
  111. json_t *moduleJ = moduleWidget->toJson();
  112. json_array_append_new(modulesJ, moduleJ);
  113. }
  114. json_object_set_new(rootJ, "modules", modulesJ);
  115. // wires
  116. json_t *wires = json_array();
  117. for (Widget *w : wireContainer->children) {
  118. WireWidget *wireWidget = dynamic_cast<WireWidget*>(w);
  119. assert(wireWidget);
  120. // Only serialize WireWidgets connected on both ends
  121. if (!(wireWidget->outputPort && wireWidget->inputPort))
  122. continue;
  123. // wire
  124. json_t *wire = json_object();
  125. {
  126. // Get the modules at each end of the wire
  127. ModuleWidget *outputModuleWidget = wireWidget->outputPort->getAncestorOfType<ModuleWidget>();
  128. assert(outputModuleWidget);
  129. int outputModuleId = moduleIds[outputModuleWidget];
  130. ModuleWidget *inputModuleWidget = wireWidget->inputPort->getAncestorOfType<ModuleWidget>();
  131. assert(inputModuleWidget);
  132. int inputModuleId = moduleIds[inputModuleWidget];
  133. // Get output/input ports
  134. auto outputIt = std::find(outputModuleWidget->outputs.begin(), outputModuleWidget->outputs.end(), wireWidget->outputPort);
  135. assert(outputIt != outputModuleWidget->outputs.end());
  136. int outputId = outputIt - outputModuleWidget->outputs.begin();
  137. auto inputIt = std::find(inputModuleWidget->inputs.begin(), inputModuleWidget->inputs.end(), wireWidget->inputPort);
  138. assert(inputIt != inputModuleWidget->inputs.end());
  139. int inputId = inputIt - inputModuleWidget->inputs.begin();
  140. json_object_set_new(wire, "outputModuleId", json_integer(outputModuleId));
  141. json_object_set_new(wire, "outputId", json_integer(outputId));
  142. json_object_set_new(wire, "inputModuleId", json_integer(inputModuleId));
  143. json_object_set_new(wire, "inputId", json_integer(inputId));
  144. }
  145. json_array_append_new(wires, wire);
  146. }
  147. json_object_set_new(rootJ, "wires", wires);
  148. return rootJ;
  149. }
  150. void RackWidget::fromJson(json_t *rootJ) {
  151. // version
  152. json_t *versionJ = json_object_get(rootJ, "version");
  153. if (versionJ) {
  154. const char *version = json_string_value(versionJ);
  155. if (gApplicationVersion != version)
  156. printf("JSON version mismatch, attempting to convert JSON version %s to %s\n", version, gApplicationVersion.c_str());
  157. }
  158. // modules
  159. std::map<int, ModuleWidget*> moduleWidgets;
  160. json_t *modulesJ = json_object_get(rootJ, "modules");
  161. if (!modulesJ) return;
  162. size_t moduleId;
  163. json_t *moduleJ;
  164. json_array_foreach(modulesJ, moduleId, moduleJ) {
  165. // Get plugin
  166. json_t *pluginSlugJ = json_object_get(moduleJ, "plugin");
  167. if (!pluginSlugJ) continue;
  168. const char *pluginSlug = json_string_value(pluginSlugJ);
  169. Plugin *plugin = NULL;
  170. for (Plugin *p : gPlugins) {
  171. if (p->slug == pluginSlug) {
  172. plugin = p;
  173. break;
  174. }
  175. }
  176. if (!plugin) continue;
  177. // Get model
  178. json_t *modelSlug = json_object_get(moduleJ, "model");
  179. Model *model = NULL;
  180. for (Model *m : plugin->models) {
  181. if (m->slug == json_string_value(modelSlug)) {
  182. model = m;
  183. break;
  184. }
  185. }
  186. if (!model) continue;
  187. // Create ModuleWidget
  188. ModuleWidget *moduleWidget = model->createModuleWidget();
  189. assert(moduleWidget);
  190. moduleWidget->fromJson(moduleJ);
  191. moduleContainer->addChild(moduleWidget);
  192. moduleWidgets[moduleId] = moduleWidget;
  193. }
  194. // wires
  195. json_t *wiresJ = json_object_get(rootJ, "wires");
  196. if (!wiresJ) return;
  197. size_t wireId;
  198. json_t *wireJ;
  199. json_array_foreach(wiresJ, wireId, wireJ) {
  200. int outputModuleId, outputId;
  201. int inputModuleId, inputId;
  202. int err = json_unpack(wireJ, "{s:i, s:i, s:i, s:i}",
  203. "outputModuleId", &outputModuleId, "outputId", &outputId,
  204. "inputModuleId", &inputModuleId, "inputId", &inputId);
  205. if (err) continue;
  206. // Get ports
  207. ModuleWidget *outputModuleWidget = moduleWidgets[outputModuleId];
  208. if (!outputModuleWidget) continue;
  209. Port *outputPort = outputModuleWidget->outputs[outputId];
  210. if (!outputPort) continue;
  211. ModuleWidget *inputModuleWidget = moduleWidgets[inputModuleId];
  212. if (!inputModuleWidget) continue;
  213. Port *inputPort = inputModuleWidget->inputs[inputId];
  214. if (!inputPort) continue;
  215. // Create WireWidget
  216. WireWidget *wireWidget = new WireWidget();
  217. wireWidget->outputPort = outputPort;
  218. wireWidget->inputPort = inputPort;
  219. outputPort->connectedWire = wireWidget;
  220. inputPort->connectedWire = wireWidget;
  221. wireWidget->updateWire();
  222. // Add wire to rack
  223. wireContainer->addChild(wireWidget);
  224. }
  225. }
  226. void RackWidget::addModule(ModuleWidget *m) {
  227. moduleContainer->addChild(m);
  228. }
  229. void RackWidget::deleteModule(ModuleWidget *m) {
  230. moduleContainer->removeChild(m);
  231. }
  232. void RackWidget::cloneModule(ModuleWidget *m) {
  233. // Create new module from model
  234. ModuleWidget *clonedModuleWidget = m->model->createModuleWidget();
  235. // JSON serialization is the most straightforward way to do this
  236. json_t *moduleJ = m->toJson();
  237. clonedModuleWidget->fromJson(moduleJ);
  238. json_decref(moduleJ);
  239. clonedModuleWidget->requestedPos = m->box.pos;
  240. clonedModuleWidget->requested = true;
  241. addModule(clonedModuleWidget);
  242. }
  243. void RackWidget::repositionModule(ModuleWidget *m) {
  244. // Create possible positions
  245. int x0 = roundf(m->requestedPos.x / RACK_GRID_WIDTH);
  246. int y0 = roundf(m->requestedPos.y / RACK_GRID_HEIGHT);
  247. std::vector<Vec> positions;
  248. for (int y = maxi(0, y0 - 4); y < y0 + 4; y++) {
  249. for (int x = maxi(0, x0 - 200); x < x0 + 200; x++) {
  250. positions.push_back(Vec(x * RACK_GRID_WIDTH, y * RACK_GRID_HEIGHT));
  251. }
  252. }
  253. // Sort possible positions by distance to the requested position
  254. Vec requestedPos = m->requestedPos;
  255. std::sort(positions.begin(), positions.end(), [requestedPos](Vec a, Vec b) {
  256. return a.minus(requestedPos).norm() < b.minus(requestedPos).norm();
  257. });
  258. // Find a position that does not collide
  259. for (Vec pos : positions) {
  260. Rect newBox = Rect(pos, m->box.size);
  261. bool collides = false;
  262. for (Widget *child2 : moduleContainer->children) {
  263. if (m == child2) continue;
  264. if (newBox.intersects(child2->box)) {
  265. collides = true;
  266. break;
  267. }
  268. }
  269. if (collides) continue;
  270. m->box.pos = pos;
  271. break;
  272. }
  273. }
  274. void RackWidget::step() {
  275. rails->step();
  276. // Resize to be a bit larger than the ScrollWidget viewport
  277. assert(parent);
  278. assert(parent->parent);
  279. Vec moduleSize = moduleContainer->getChildrenBoundingBox().getBottomRight();
  280. Vec viewportSize = parent->parent->box.size.minus(parent->box.pos);
  281. box.size = moduleSize.max(viewportSize).plus(Vec(500, 500));
  282. // Reposition modules
  283. for (Widget *child : moduleContainer->children) {
  284. ModuleWidget *module = dynamic_cast<ModuleWidget*>(child);
  285. assert(module);
  286. if (module->requested) {
  287. repositionModule(module);
  288. module->requested = false;
  289. }
  290. }
  291. Widget::step();
  292. }
  293. void RackWidget::draw(NVGcontext *vg) {
  294. // Draw rails
  295. nvgBeginPath(vg);
  296. nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y);
  297. NVGpaint paint = nvgImagePattern(vg, rails->box.pos.x, rails->box.pos.y, rails->box.size.x, rails->box.size.y, 0.0, rails->getImageHandle(), 1.0);
  298. nvgFillPaint(vg, paint);
  299. nvgFill(vg);
  300. Widget::draw(vg);
  301. }
  302. struct AddModuleMenuItem : MenuItem {
  303. Model *model;
  304. Vec modulePos;
  305. void onAction() {
  306. ModuleWidget *moduleWidget = model->createModuleWidget();
  307. moduleWidget->requestedPos = modulePos.minus(moduleWidget->box.getCenter());
  308. moduleWidget->requested = true;
  309. gRackWidget->moduleContainer->addChild(moduleWidget);
  310. }
  311. };
  312. void RackWidget::onMouseDownOpaque(int button) {
  313. if (button == 1) {
  314. Vec modulePos = gMousePos.minus(getAbsolutePos());
  315. Menu *menu = gScene->createMenu();
  316. MenuLabel *menuLabel = new MenuLabel();
  317. menuLabel->text = "Add Module";
  318. menu->pushChild(menuLabel);
  319. for (Plugin *plugin : gPlugins) {
  320. for (Model *model : plugin->models) {
  321. AddModuleMenuItem *item = new AddModuleMenuItem();
  322. item->text = model->name;
  323. item->rightText = model->plugin->name;
  324. item->model = model;
  325. item->modulePos = modulePos;
  326. menu->pushChild(item);
  327. }
  328. }
  329. }
  330. }
  331. } // namespace rack