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.

467 lines
12KB

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