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.

1467 lines
38KB

  1. #include <map>
  2. #include <algorithm>
  3. #include <queue>
  4. #include <functional>
  5. #include <osdialog.h>
  6. #include <app/RackWidget.hpp>
  7. #include <widget/TransparentWidget.hpp>
  8. #include <app/RailWidget.hpp>
  9. #include <app/Scene.hpp>
  10. #include <settings.hpp>
  11. #include <plugin.hpp>
  12. #include <engine/Engine.hpp>
  13. #include <context.hpp>
  14. #include <system.hpp>
  15. #include <asset.hpp>
  16. #include <patch.hpp>
  17. #include <helpers.hpp>
  18. namespace rack {
  19. namespace app {
  20. struct RackWidget::Internal {
  21. RailWidget* rail = NULL;
  22. widget::Widget* moduleContainer = NULL;
  23. widget::Widget* cableContainer = NULL;
  24. CableWidget* incompleteCable = NULL;
  25. int nextCableColorId = 0;
  26. /** The last mouse position in the RackWidget */
  27. math::Vec mousePos;
  28. bool selecting = false;
  29. math::Vec selectionStart;
  30. math::Vec selectionEnd;
  31. std::set<ModuleWidget*> selectedModules;
  32. std::map<widget::Widget*, math::Vec> moduleOldPositions;
  33. };
  34. /** Creates a new Module and ModuleWidget */
  35. static ModuleWidget* moduleWidgetFromJson(json_t* moduleJ) {
  36. plugin::Model* model = plugin::modelFromJson(moduleJ);
  37. assert(model);
  38. INFO("Creating module %s", model->getFullName().c_str());
  39. engine::Module* module = model->createModule();
  40. assert(module);
  41. module->fromJson(moduleJ);
  42. // Create ModuleWidget
  43. INFO("Creating module widget %s", model->getFullName().c_str());
  44. ModuleWidget* moduleWidget = module->model->createModuleWidget(module);
  45. assert(moduleWidget);
  46. return moduleWidget;
  47. }
  48. struct ModuleContainer : widget::Widget {
  49. void draw(const DrawArgs& args) override {
  50. // Draw ModuleWidget shadows
  51. Widget::drawLayer(args, -1);
  52. Widget::draw(args);
  53. }
  54. void drawLayer(const DrawArgs& args, int layer) override {
  55. // Draw lights after translucent rectangle
  56. if (layer == 1) {
  57. Widget::drawLayer(args, 1);
  58. }
  59. }
  60. };
  61. struct CableContainer : widget::TransparentWidget {
  62. void draw(const DrawArgs& args) override {
  63. // Don't draw on layer 0
  64. }
  65. void drawLayer(const DrawArgs& args, int layer) override {
  66. if (layer == 2) {
  67. // Draw Plugs
  68. Widget::draw(args);
  69. // Draw cable lights
  70. nvgSave(args.vg);
  71. nvgGlobalTint(args.vg, color::WHITE);
  72. Widget::drawLayer(args, 1);
  73. nvgRestore(args.vg);
  74. // Draw cable shadows
  75. Widget::drawLayer(args, 2);
  76. // Draw cables
  77. Widget::drawLayer(args, 3);
  78. }
  79. }
  80. };
  81. RackWidget::RackWidget() {
  82. internal = new Internal;
  83. internal->rail = new RailWidget;
  84. addChild(internal->rail);
  85. internal->moduleContainer = new ModuleContainer;
  86. addChild(internal->moduleContainer);
  87. internal->cableContainer = new CableContainer;
  88. addChild(internal->cableContainer);
  89. }
  90. RackWidget::~RackWidget() {
  91. clear();
  92. delete internal;
  93. }
  94. void RackWidget::step() {
  95. Widget::step();
  96. }
  97. void RackWidget::draw(const DrawArgs& args) {
  98. float b = settings::rackBrightness;
  99. // Draw rack rails and modules
  100. Widget::draw(args);
  101. // Draw translucent dark rectangle
  102. if (b < 1.f) {
  103. // Get zoom level
  104. float t[6];
  105. nvgCurrentTransform(args.vg, t);
  106. float zoom = t[3];
  107. float radius = 300.0 / zoom;
  108. float brightness = 0.2f;
  109. // Draw mouse spotlight
  110. nvgBeginPath(args.vg);
  111. nvgRect(args.vg, 0.0, 0.0, VEC_ARGS(box.size));
  112. nvgFillPaint(args.vg, nvgRadialGradient(args.vg, VEC_ARGS(internal->mousePos), 0.0, radius, nvgRGBAf(0, 0, 0, 1.f - b - brightness), nvgRGBAf(0, 0, 0, 1.f - b)));
  113. nvgFill(args.vg);
  114. }
  115. // Draw lights and halos
  116. Widget::drawLayer(args, 1);
  117. // Tint all draws after this point
  118. nvgGlobalTint(args.vg, nvgRGBAf(b, b, b, 1));
  119. // Draw cables
  120. Widget::drawLayer(args, 2);
  121. // Draw selection rectangle
  122. if (internal->selecting) {
  123. nvgBeginPath(args.vg);
  124. math::Rect selectionBox = math::Rect::fromCorners(internal->selectionStart, internal->selectionEnd);
  125. nvgRect(args.vg, RECT_ARGS(selectionBox));
  126. nvgFillColor(args.vg, nvgRGBAf(1, 0, 0, 0.25));
  127. nvgFill(args.vg);
  128. nvgStrokeWidth(args.vg, 2.0);
  129. nvgStrokeColor(args.vg, nvgRGBAf(1, 0, 0, 0.5));
  130. nvgStroke(args.vg);
  131. }
  132. }
  133. void RackWidget::onHover(const HoverEvent& e) {
  134. // Set before calling children's onHover()
  135. internal->mousePos = e.pos;
  136. OpaqueWidget::onHover(e);
  137. }
  138. void RackWidget::onHoverKey(const HoverKeyEvent& e) {
  139. OpaqueWidget::onHoverKey(e);
  140. }
  141. void RackWidget::onButton(const ButtonEvent& e) {
  142. OpaqueWidget::onButton(e);
  143. if (e.isConsumed())
  144. return;
  145. if (e.button == GLFW_MOUSE_BUTTON_RIGHT) {
  146. if (e.action == GLFW_PRESS) {
  147. APP->scene->browser->show();
  148. }
  149. e.consume(this);
  150. }
  151. }
  152. void RackWidget::onDragStart(const DragStartEvent& e) {
  153. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  154. // Deselect all modules
  155. updateSelectionFromRect();
  156. internal->selecting = true;
  157. internal->selectionStart = internal->mousePos;
  158. internal->selectionEnd = internal->mousePos;
  159. }
  160. }
  161. void RackWidget::onDragEnd(const DragEndEvent& e) {
  162. if (e.button == GLFW_MOUSE_BUTTON_LEFT) {
  163. internal->selecting = false;
  164. }
  165. }
  166. void RackWidget::onDragHover(const DragHoverEvent& e) {
  167. // Set before calling children's onDragHover()
  168. internal->mousePos = e.pos;
  169. if (internal->selecting) {
  170. internal->selectionEnd = internal->mousePos;
  171. updateSelectionFromRect();
  172. }
  173. OpaqueWidget::onDragHover(e);
  174. }
  175. widget::Widget* RackWidget::getModuleContainer() {
  176. return internal->moduleContainer;
  177. }
  178. widget::Widget* RackWidget::getCableContainer() {
  179. return internal->cableContainer;
  180. }
  181. math::Vec RackWidget::getMousePos() {
  182. return internal->mousePos;
  183. }
  184. void RackWidget::clear() {
  185. // This isn't required because removing all ModuleWidgets should remove all cables, but do it just in case.
  186. clearCables();
  187. // Remove ModuleWidgets
  188. for (ModuleWidget* mw : getModules()) {
  189. removeModule(mw);
  190. delete mw;
  191. }
  192. }
  193. void RackWidget::mergeJson(json_t* rootJ) {
  194. // modules
  195. json_t* modulesJ = json_object_get(rootJ, "modules");
  196. if (!modulesJ)
  197. return;
  198. size_t moduleIndex;
  199. json_t* moduleJ;
  200. json_array_foreach(modulesJ, moduleIndex, moduleJ) {
  201. // module
  202. json_t* idJ = json_object_get(moduleJ, "id");
  203. if (!idJ)
  204. continue;
  205. int64_t id = json_integer_value(idJ);
  206. // TODO Legacy v0.6?
  207. ModuleWidget* mw = getModule(id);
  208. if (!mw) {
  209. WARN("Cannot find ModuleWidget %lld", (long long) id);
  210. continue;
  211. }
  212. // pos
  213. math::Vec pos = mw->box.pos.minus(RACK_OFFSET);
  214. pos = pos.div(RACK_GRID_SIZE).round();
  215. json_t* posJ = json_pack("[i, i]", (int) pos.x, (int) pos.y);
  216. json_object_set_new(moduleJ, "pos", posJ);
  217. }
  218. // cables
  219. json_t* cablesJ = json_object_get(rootJ, "cables");
  220. if (!cablesJ)
  221. return;
  222. size_t cableIndex;
  223. json_t* cableJ;
  224. json_array_foreach(cablesJ, cableIndex, cableJ) {
  225. // cable
  226. json_t* idJ = json_object_get(cableJ, "id");
  227. if (!idJ)
  228. continue;
  229. int64_t id = json_integer_value(idJ);
  230. CableWidget* cw = getCable(id);
  231. if (!cw) {
  232. WARN("Cannot find CableWidget %lld", (long long) id);
  233. continue;
  234. }
  235. cw->mergeJson(cableJ);
  236. }
  237. }
  238. void RackWidget::fromJson(json_t* rootJ) {
  239. // version
  240. std::string version;
  241. json_t* versionJ = json_object_get(rootJ, "version");
  242. if (versionJ)
  243. version = json_string_value(versionJ);
  244. bool legacyV05 = false;
  245. if (string::startsWith(version, "0.3.") || string::startsWith(version, "0.4.") || string::startsWith(version, "0.5.") || version == "dev") {
  246. legacyV05 = true;
  247. }
  248. // modules
  249. json_t* modulesJ = json_object_get(rootJ, "modules");
  250. if (!modulesJ)
  251. return;
  252. size_t moduleIndex;
  253. json_t* moduleJ;
  254. json_array_foreach(modulesJ, moduleIndex, moduleJ) {
  255. // Get module ID
  256. json_t* idJ = json_object_get(moduleJ, "id");
  257. int64_t id;
  258. if (idJ)
  259. id = json_integer_value(idJ);
  260. else
  261. id = moduleIndex;
  262. // Get Module
  263. engine::Module* module = APP->engine->getModule(id);
  264. if (!module) {
  265. WARN("Cannot find Module %lld", (long long) id);
  266. continue;
  267. }
  268. // Create ModuleWidget
  269. INFO("Creating module widget %s", module->model->getFullName().c_str());
  270. ModuleWidget* mw = module->model->createModuleWidget(module);
  271. // pos
  272. json_t* posJ = json_object_get(moduleJ, "pos");
  273. double x = 0.0, y = 0.0;
  274. json_unpack(posJ, "[F, F]", &x, &y);
  275. math::Vec pos = math::Vec(x, y);
  276. if (legacyV05) {
  277. // In <=v0.5, positions were in pixel units
  278. }
  279. else {
  280. pos = pos.mult(RACK_GRID_SIZE);
  281. }
  282. pos = pos.plus(RACK_OFFSET);
  283. setModulePosNearest(mw, pos);
  284. internal->moduleContainer->addChild(mw);
  285. }
  286. updateExpanders();
  287. // cables
  288. json_t* cablesJ = json_object_get(rootJ, "cables");
  289. // In <=v0.6, cables were called wires
  290. if (!cablesJ)
  291. cablesJ = json_object_get(rootJ, "wires");
  292. if (!cablesJ)
  293. return;
  294. size_t cableIndex;
  295. json_t* cableJ;
  296. json_array_foreach(cablesJ, cableIndex, cableJ) {
  297. // Get cable ID
  298. json_t* idJ = json_object_get(cableJ, "id");
  299. int64_t id;
  300. // In <=v0.6, the cable ID was the index in the array.
  301. if (idJ)
  302. id = json_integer_value(idJ);
  303. else
  304. id = cableIndex;
  305. // Get Cable
  306. engine::Cable* cable = APP->engine->getCable(id);
  307. if (!cable) {
  308. WARN("Cannot find Cable %lld", (long long) id);
  309. continue;
  310. }
  311. // Create CableWidget
  312. CableWidget* cw = new CableWidget;
  313. try {
  314. cw->setCable(cable);
  315. cw->fromJson(cableJ);
  316. // In <=v1, cable colors were not serialized, so choose one from the available colors.
  317. if (cw->color.a == 0.f) {
  318. cw->color = getNextCableColor();
  319. }
  320. }
  321. catch (Exception& e) {
  322. delete cw;
  323. // If creating CableWidget fails, remove Cable from Engine.
  324. APP->engine->removeCable(cable);
  325. delete cable;
  326. continue;
  327. }
  328. addCable(cw);
  329. }
  330. }
  331. struct PasteJsonReturn {
  332. std::map<int64_t, int64_t> newModuleIds;
  333. };
  334. static PasteJsonReturn RackWidget_pasteJson(RackWidget* that, json_t* rootJ, history::ComplexAction* complexAction) {
  335. that->deselectAll();
  336. std::map<int64_t, int64_t> newModuleIds;
  337. // modules
  338. json_t* modulesJ = json_object_get(rootJ, "modules");
  339. if (!modulesJ)
  340. return {};
  341. size_t moduleIndex;
  342. json_t* moduleJ;
  343. json_array_foreach(modulesJ, moduleIndex, moduleJ) {
  344. json_t* idJ = json_object_get(moduleJ, "id");
  345. if (!idJ)
  346. continue;
  347. int64_t id = json_integer_value(idJ);
  348. engine::Module::jsonStripIds(moduleJ);
  349. ModuleWidget* mw;
  350. try {
  351. mw = moduleWidgetFromJson(moduleJ);
  352. }
  353. catch (Exception& e) {
  354. WARN("%s", e.what());
  355. continue;
  356. }
  357. assert(mw);
  358. assert(mw->module);
  359. APP->engine->addModule(mw->module);
  360. // pos
  361. json_t* posJ = json_object_get(moduleJ, "pos");
  362. double x = 0.0, y = 0.0;
  363. json_unpack(posJ, "[F, F]", &x, &y);
  364. math::Vec pos = math::Vec(x, y);
  365. pos = pos.mult(RACK_GRID_SIZE);
  366. mw->box.pos = pos.plus(RACK_OFFSET);
  367. that->internal->moduleContainer->addChild(mw);
  368. that->select(mw);
  369. newModuleIds[id] = mw->module->id;
  370. }
  371. // This calls updateExpanders()
  372. that->setSelectionPosNearest(math::Vec(0, 0));
  373. // Add positioned selected modules to history
  374. for (ModuleWidget* mw : that->getSelected()) {
  375. // history::ModuleAdd
  376. history::ModuleAdd* h = new history::ModuleAdd;
  377. h->setModule(mw);
  378. complexAction->push(h);
  379. }
  380. // cables
  381. json_t* cablesJ = json_object_get(rootJ, "cables");
  382. if (cablesJ) {
  383. size_t cableIndex;
  384. json_t* cableJ;
  385. json_array_foreach(cablesJ, cableIndex, cableJ) {
  386. engine::Cable::jsonStripIds(cableJ);
  387. // Remap old module IDs to new IDs
  388. json_t* inputModuleIdJ = json_object_get(cableJ, "inputModuleId");
  389. if (!inputModuleIdJ)
  390. continue;
  391. int64_t inputModuleId = json_integer_value(inputModuleIdJ);
  392. inputModuleId = get(newModuleIds, inputModuleId, -1);
  393. if (inputModuleId < 0)
  394. continue;
  395. json_object_set(cableJ, "inputModuleId", json_integer(inputModuleId));
  396. json_t* outputModuleIdJ = json_object_get(cableJ, "outputModuleId");
  397. if (!outputModuleIdJ)
  398. continue;
  399. int64_t outputModuleId = json_integer_value(outputModuleIdJ);
  400. outputModuleId = get(newModuleIds, outputModuleId, -1);
  401. if (outputModuleId < 0)
  402. continue;
  403. json_object_set(cableJ, "outputModuleId", json_integer(outputModuleId));
  404. // Create Cable
  405. engine::Cable* cable = new engine::Cable;
  406. try {
  407. cable->fromJson(cableJ);
  408. APP->engine->addCable(cable);
  409. }
  410. catch (Exception& e) {
  411. WARN("Cannot paste cable: %s", e.what());
  412. delete cable;
  413. continue;
  414. }
  415. // Create CableWidget
  416. app::CableWidget* cw = new app::CableWidget;
  417. cw->setCable(cable);
  418. cw->fromJson(cableJ);
  419. that->addCable(cw);
  420. // history::CableAdd
  421. history::CableAdd* h = new history::CableAdd;
  422. h->setCable(cw);
  423. complexAction->push(h);
  424. }
  425. }
  426. return {newModuleIds};
  427. }
  428. void RackWidget::pasteJsonAction(json_t* rootJ) {
  429. history::ComplexAction* complexAction = new history::ComplexAction;
  430. complexAction->name = "paste modules";
  431. DEFER({
  432. if (!complexAction->isEmpty())
  433. APP->history->push(complexAction);
  434. else
  435. delete complexAction;
  436. });
  437. RackWidget_pasteJson(this, rootJ, complexAction);
  438. }
  439. void RackWidget::pasteModuleJsonAction(json_t* moduleJ) {
  440. engine::Module::jsonStripIds(moduleJ);
  441. ModuleWidget* mw;
  442. try {
  443. mw = moduleWidgetFromJson(moduleJ);
  444. }
  445. catch (Exception& e) {
  446. WARN("%s", e.what());
  447. return;
  448. }
  449. assert(mw);
  450. assert(mw->module);
  451. APP->engine->addModule(mw->module);
  452. addModuleAtMouse(mw);
  453. // history::ModuleAdd
  454. history::ModuleAdd* h = new history::ModuleAdd;
  455. h->name = "paste module";
  456. h->setModule(mw);
  457. APP->history->push(h);
  458. }
  459. void RackWidget::pasteClipboardAction() {
  460. const char* json = glfwGetClipboardString(APP->window->win);
  461. if (!json) {
  462. WARN("Could not get text from clipboard.");
  463. return;
  464. }
  465. json_error_t error;
  466. json_t* rootJ = json_loads(json, 0, &error);
  467. if (!rootJ) {
  468. WARN("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
  469. return;
  470. }
  471. DEFER({json_decref(rootJ);});
  472. json_t* modulesJ = json_object_get(rootJ, "modules");
  473. if (modulesJ) {
  474. // JSON is a selection of modules
  475. pasteJsonAction(rootJ);
  476. }
  477. else {
  478. // JSON is a single module
  479. pasteModuleJsonAction(rootJ);
  480. }
  481. }
  482. void RackWidget::addModule(ModuleWidget* m) {
  483. assert(m);
  484. // Module must be 3U high and at least 1HP wide
  485. if (m->box.size.x < RACK_GRID_WIDTH / 2)
  486. throw Exception("Module %s width is %g px, must be at least %g px", m->model->getFullName().c_str(), m->box.size.x, RACK_GRID_WIDTH);
  487. if (m->box.size.y != RACK_GRID_HEIGHT)
  488. throw Exception("Module %s height is %g px, must be %g px", m->model->getFullName().c_str(), m->box.size.y, RACK_GRID_HEIGHT);
  489. internal->moduleContainer->addChild(m);
  490. updateExpanders();
  491. }
  492. void RackWidget::addModuleAtMouse(ModuleWidget* mw) {
  493. assert(mw);
  494. // Move module nearest to the mouse position
  495. math::Vec pos = internal->mousePos.minus(mw->box.size.div(2));
  496. setModulePosNearest(mw, pos);
  497. addModule(mw);
  498. }
  499. void RackWidget::removeModule(ModuleWidget* m) {
  500. // Unset touchedParamWidget
  501. if (touchedParam) {
  502. ModuleWidget* touchedModule = touchedParam->getAncestorOfType<ModuleWidget>();
  503. if (touchedModule == m)
  504. touchedParam = NULL;
  505. }
  506. // Disconnect cables
  507. m->disconnect();
  508. // Deselect module if selected
  509. internal->selectedModules.erase(m);
  510. // Remove module from ModuleContainer
  511. internal->moduleContainer->removeChild(m);
  512. }
  513. bool RackWidget::requestModulePos(ModuleWidget* mw, math::Vec pos) {
  514. // Check intersection with other modules
  515. math::Rect mwBox = math::Rect(pos, mw->box.size);
  516. for (widget::Widget* w2 : internal->moduleContainer->children) {
  517. // Don't intersect with self
  518. if (mw == w2)
  519. continue;
  520. // Check intersection
  521. math::Rect w2Box = w2->box;
  522. if (mwBox.intersects(w2Box))
  523. return false;
  524. }
  525. // Accept requested position
  526. mw->setPosition(mwBox.pos);
  527. updateExpanders();
  528. return true;
  529. }
  530. static math::Vec eachNearestGridPos(math::Vec pos, std::function<bool(math::Vec pos)> f) {
  531. // Dijkstra's algorithm to generate a sorted list of Vecs closest to `pos`.
  532. // Comparison of distance of Vecs to `pos`
  533. auto cmpNearest = [&](const math::Vec& a, const math::Vec& b) {
  534. return a.minus(pos).square() > b.minus(pos).square();
  535. };
  536. // Comparison of dictionary order of Vecs
  537. auto cmp = [](const math::Vec& a, const math::Vec& b) {
  538. if (a.y != b.y)
  539. return a.y < b.y;
  540. return a.x < b.x;
  541. };
  542. // Priority queue sorted by distance from `pos`
  543. std::priority_queue<math::Vec, std::vector<math::Vec>, decltype(cmpNearest)> queue(cmpNearest);
  544. // Set of already-tested Vecs
  545. std::set<math::Vec, decltype(cmp)> visited(cmp);
  546. // Seed priority queue with closest Vec
  547. math::Vec closestPos = pos.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE);
  548. queue.push(closestPos);
  549. while (!queue.empty()) {
  550. math::Vec testPos = queue.top();
  551. // Check testPos
  552. if (f(testPos))
  553. return testPos;
  554. // Move testPos to visited set
  555. queue.pop();
  556. visited.insert(testPos);
  557. // Add adjacent Vecs
  558. static const std::vector<math::Vec> deltas = {
  559. math::Vec(1, 0).mult(RACK_GRID_SIZE),
  560. math::Vec(0, 1).mult(RACK_GRID_SIZE),
  561. math::Vec(-1, 0).mult(RACK_GRID_SIZE),
  562. math::Vec(0, -1).mult(RACK_GRID_SIZE),
  563. };
  564. for (math::Vec delta : deltas) {
  565. math::Vec newPos = testPos.plus(delta);
  566. if (visited.find(newPos) == visited.end()) {
  567. queue.push(newPos);
  568. }
  569. }
  570. }
  571. // We failed to find a box. This shouldn't happen on an infinite rack.
  572. assert(false);
  573. return math::Vec();
  574. }
  575. void RackWidget::setModulePosNearest(ModuleWidget* mw, math::Vec pos) {
  576. eachNearestGridPos(pos, [&](math::Vec pos) -> bool {
  577. return requestModulePos(mw, pos);
  578. });
  579. }
  580. void RackWidget::setModulePosForce(ModuleWidget* mw, math::Vec pos) {
  581. mw->setPosition(pos.div(RACK_GRID_SIZE).round().mult(RACK_GRID_SIZE));
  582. // Comparison of X coordinates
  583. auto cmp = [&](const widget::Widget* a, const widget::Widget* b) {
  584. return a->box.pos.x < b->box.pos.x;
  585. };
  586. // Collect modules to the left and right of `mw`
  587. std::set<widget::Widget*, decltype(cmp)> leftModules(cmp);
  588. std::set<widget::Widget*, decltype(cmp)> rightModules(cmp);
  589. for (widget::Widget* w2 : internal->moduleContainer->children) {
  590. // Skip this module
  591. if (w2 == mw)
  592. continue;
  593. // Reset position to old position
  594. auto it = internal->moduleOldPositions.find(w2);
  595. if (it != internal->moduleOldPositions.end()) {
  596. w2->box.pos = it->second;
  597. }
  598. // Modules must be on the same row as `mw`
  599. if (w2->box.getTop() != mw->box.getTop())
  600. continue;
  601. // Insert into leftModules or rightModules
  602. if (w2->box.getCenter().x < mw->box.getCenter().x)
  603. leftModules.insert(w2);
  604. else
  605. rightModules.insert(w2);
  606. }
  607. if (!leftModules.empty()) {
  608. widget::Widget* leftModule = *leftModules.rbegin();
  609. // Make sure module is to the right of the last leftModule.
  610. if (leftModule->box.getRight() > mw->box.getLeft()) {
  611. mw->box.pos.x = leftModule->box.getRight();
  612. }
  613. // If there isn't enough space between the last leftModule and first rightModule, place module to the right of the leftModule.
  614. if (!rightModules.empty()) {
  615. widget::Widget* rightModule = *rightModules.begin();
  616. if (rightModule->box.getLeft() - leftModule->box.getRight() < mw->box.getWidth()) {
  617. mw->box.pos.x = leftModule->box.getRight();
  618. }
  619. }
  620. }
  621. // Shove right modules
  622. float xLimit = mw->box.getRight();
  623. for (auto it = rightModules.begin(); it != rightModules.end(); it++) {
  624. widget::Widget* w2 = *it;
  625. math::Vec newPos = w2->box.pos;
  626. newPos.x = xLimit;
  627. newPos.x = std::round(newPos.x / RACK_GRID_WIDTH) * RACK_GRID_WIDTH;
  628. if (w2->box.pos.x > newPos.x)
  629. break;
  630. w2->box.pos = newPos;
  631. xLimit = w2->box.getRight();
  632. }
  633. updateExpanders();
  634. }
  635. ModuleWidget* RackWidget::getModule(int64_t moduleId) {
  636. for (widget::Widget* w : internal->moduleContainer->children) {
  637. ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
  638. assert(mw);
  639. if (mw->module->id == moduleId)
  640. return mw;
  641. }
  642. return NULL;
  643. }
  644. std::vector<ModuleWidget*> RackWidget::getModules() {
  645. std::vector<ModuleWidget*> mws;
  646. mws.reserve(internal->moduleContainer->children.size());
  647. for (widget::Widget* w : internal->moduleContainer->children) {
  648. ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
  649. assert(mw);
  650. mws.push_back(mw);
  651. }
  652. mws.shrink_to_fit();
  653. return mws;
  654. }
  655. bool RackWidget::hasModules() {
  656. return internal->moduleContainer->children.empty();
  657. }
  658. void RackWidget::updateModuleOldPositions() {
  659. internal->moduleOldPositions.clear();
  660. for (ModuleWidget* mw : getModules()) {
  661. internal->moduleOldPositions[mw] = mw->getPosition();
  662. }
  663. }
  664. history::ComplexAction* RackWidget::getModuleDragAction() {
  665. history::ComplexAction* h = new history::ComplexAction;
  666. h->name = "move modules";
  667. for (ModuleWidget* mw : getModules()) {
  668. // Create ModuleMove action if the module was moved.
  669. auto it = internal->moduleOldPositions.find(mw);
  670. if (it == internal->moduleOldPositions.end())
  671. continue;
  672. math::Vec oldPos = it->second;
  673. if (!oldPos.equals(mw->box.pos)) {
  674. history::ModuleMove* mmh = new history::ModuleMove;
  675. mmh->moduleId = mw->module->id;
  676. mmh->oldPos = oldPos;
  677. mmh->newPos = mw->box.pos;
  678. h->push(mmh);
  679. }
  680. }
  681. return h;
  682. }
  683. void RackWidget::updateSelectionFromRect() {
  684. math::Rect selectionBox = math::Rect::fromCorners(internal->selectionStart, internal->selectionEnd);
  685. deselectAll();
  686. for (ModuleWidget* mw : getModules()) {
  687. bool selected = internal->selecting && selectionBox.intersects(mw->box);
  688. if (selected)
  689. select(mw);
  690. }
  691. }
  692. void RackWidget::selectAll() {
  693. internal->selectedModules.clear();
  694. for (widget::Widget* w : internal->moduleContainer->children) {
  695. ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
  696. assert(mw);
  697. internal->selectedModules.insert(mw);
  698. }
  699. }
  700. void RackWidget::deselectAll() {
  701. internal->selectedModules.clear();
  702. }
  703. void RackWidget::select(ModuleWidget* mw, bool selected) {
  704. if (selected) {
  705. internal->selectedModules.insert(mw);
  706. }
  707. else {
  708. auto it = internal->selectedModules.find(mw);
  709. if (it != internal->selectedModules.end())
  710. internal->selectedModules.erase(it);
  711. }
  712. }
  713. bool RackWidget::hasSelection() {
  714. return !internal->selectedModules.empty();
  715. }
  716. const std::set<ModuleWidget*>& RackWidget::getSelected() {
  717. return internal->selectedModules;
  718. }
  719. bool RackWidget::isSelected(ModuleWidget* mw) {
  720. auto it = internal->selectedModules.find(mw);
  721. return (it != internal->selectedModules.end());
  722. }
  723. json_t* RackWidget::selectionToJson(bool cables) {
  724. json_t* rootJ = json_object();
  725. std::set<engine::Module*> modules;
  726. // modules
  727. json_t* modulesJ = json_array();
  728. for (ModuleWidget* mw : getSelected()) {
  729. json_t* moduleJ = mw->toJson();
  730. // pos
  731. math::Vec pos = mw->box.pos.minus(RACK_OFFSET);
  732. pos = pos.div(RACK_GRID_SIZE).round();
  733. json_t* posJ = json_pack("[i, i]", (int) pos.x, (int) pos.y);
  734. json_object_set_new(moduleJ, "pos", posJ);
  735. json_array_append_new(modulesJ, moduleJ);
  736. modules.insert(mw->getModule());
  737. }
  738. json_object_set_new(rootJ, "modules", modulesJ);
  739. if (cables) {
  740. // cables
  741. json_t* cablesJ = json_array();
  742. for (CableWidget* cw : getCompleteCables()) {
  743. // Only add cables attached on both ends to selected modules
  744. engine::Cable* cable = cw->getCable();
  745. if (!cable || !cable->inputModule || !cable->outputModule)
  746. continue;
  747. const auto inputIt = modules.find(cable->inputModule);
  748. if (inputIt == modules.end())
  749. continue;
  750. const auto outputIt = modules.find(cable->outputModule);
  751. if (outputIt == modules.end())
  752. continue;
  753. json_t* cableJ = cable->toJson();
  754. cw->mergeJson(cableJ);
  755. json_array_append_new(cablesJ, cableJ);
  756. }
  757. json_object_set_new(rootJ, "cables", cablesJ);
  758. }
  759. return rootJ;
  760. }
  761. static const char SELECTION_FILTERS[] = "VCV Rack module selection (.vcvs):vcvs";
  762. void RackWidget::loadSelection(std::string path) {
  763. FILE* file = std::fopen(path.c_str(), "r");
  764. if (!file)
  765. throw Exception("Could not load selection file %s", path.c_str());
  766. DEFER({std::fclose(file);});
  767. INFO("Loading selection %s", path.c_str());
  768. json_error_t error;
  769. json_t* rootJ = json_loadf(file, 0, &error);
  770. if (!rootJ)
  771. throw Exception("File is not a valid selection file. JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text);
  772. DEFER({json_decref(rootJ);});
  773. pasteJsonAction(rootJ);
  774. }
  775. void RackWidget::loadSelectionDialog() {
  776. std::string selectionDir = asset::user("selections");
  777. system::createDirectories(selectionDir);
  778. osdialog_filters* filters = osdialog_filters_parse(SELECTION_FILTERS);
  779. DEFER({osdialog_filters_free(filters);});
  780. char* pathC = osdialog_file(OSDIALOG_OPEN, selectionDir.c_str(), NULL, filters);
  781. if (!pathC) {
  782. // No path selected
  783. return;
  784. }
  785. DEFER({std::free(pathC);});
  786. try {
  787. loadSelection(pathC);
  788. }
  789. catch (Exception& e) {
  790. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, e.what());
  791. }
  792. }
  793. void RackWidget::saveSelection(std::string path) {
  794. INFO("Saving selection %s", path.c_str());
  795. json_t* rootJ = selectionToJson();
  796. assert(rootJ);
  797. DEFER({json_decref(rootJ);});
  798. engine::Module::jsonStripIds(rootJ);
  799. FILE* file = std::fopen(path.c_str(), "w");
  800. if (!file) {
  801. std::string message = string::f("Could not save selection to file %s", path.c_str());
  802. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  803. return;
  804. }
  805. DEFER({std::fclose(file);});
  806. json_dumpf(rootJ, file, JSON_INDENT(2));
  807. }
  808. void RackWidget::saveSelectionDialog() {
  809. std::string selectionDir = asset::user("selections");
  810. system::createDirectories(selectionDir);
  811. osdialog_filters* filters = osdialog_filters_parse(SELECTION_FILTERS);
  812. DEFER({osdialog_filters_free(filters);});
  813. char* pathC = osdialog_file(OSDIALOG_SAVE, selectionDir.c_str(), "Untitled.vcvs", filters);
  814. if (!pathC) {
  815. // No path selected
  816. return;
  817. }
  818. DEFER({std::free(pathC);});
  819. std::string path = pathC;
  820. // Automatically append .vcvs extension
  821. if (system::getExtension(path) != ".vcvs")
  822. path += ".vcvs";
  823. saveSelection(path);
  824. }
  825. void RackWidget::copyClipboardSelection() {
  826. json_t* rootJ = selectionToJson();
  827. DEFER({json_decref(rootJ);});
  828. char* moduleJson = json_dumps(rootJ, JSON_INDENT(2));
  829. DEFER({std::free(moduleJson);});
  830. glfwSetClipboardString(APP->window->win, moduleJson);
  831. }
  832. void RackWidget::resetSelectionAction() {
  833. history::ComplexAction* complexAction = new history::ComplexAction;
  834. complexAction->name = "reset modules";
  835. for (ModuleWidget* mw : getSelected()) {
  836. assert(mw->module);
  837. // history::ModuleChange
  838. history::ModuleChange* h = new history::ModuleChange;
  839. h->moduleId = mw->module->id;
  840. h->oldModuleJ = mw->toJson();
  841. APP->engine->resetModule(mw->module);
  842. h->newModuleJ = mw->toJson();
  843. complexAction->push(h);
  844. }
  845. APP->history->push(complexAction);
  846. }
  847. void RackWidget::randomizeSelectionAction() {
  848. history::ComplexAction* complexAction = new history::ComplexAction;
  849. complexAction->name = "randomize modules";
  850. for (ModuleWidget* mw : getSelected()) {
  851. assert(mw->module);
  852. // history::ModuleChange
  853. history::ModuleChange* h = new history::ModuleChange;
  854. h->moduleId = mw->module->id;
  855. h->oldModuleJ = mw->toJson();
  856. APP->engine->randomizeModule(mw->module);
  857. h->newModuleJ = mw->toJson();
  858. complexAction->push(h);
  859. }
  860. APP->history->push(complexAction);
  861. }
  862. void RackWidget::disconnectSelectionAction() {
  863. history::ComplexAction* complexAction = new history::ComplexAction;
  864. complexAction->name = "disconnect cables";
  865. for (ModuleWidget* mw : getSelected()) {
  866. mw->appendDisconnectActions(complexAction);
  867. }
  868. if (!complexAction->isEmpty())
  869. APP->history->push(complexAction);
  870. else
  871. delete complexAction;
  872. }
  873. void RackWidget::cloneSelectionAction(bool cloneCables) {
  874. json_t* rootJ = selectionToJson(cloneCables);
  875. DEFER({json_decref(rootJ);});
  876. history::ComplexAction* complexAction = new history::ComplexAction;
  877. complexAction->name = "duplicate modules";
  878. DEFER({
  879. if (!complexAction->isEmpty())
  880. APP->history->push(complexAction);
  881. else
  882. delete complexAction;
  883. });
  884. auto p = RackWidget_pasteJson(this, rootJ, complexAction);
  885. // Clone cables attached to inputs of selected modules but outputs of non-selected modules
  886. if (cloneCables) {
  887. for (CableWidget* cw : getCompleteCables()) {
  888. auto inputIt = p.newModuleIds.find(cw->getCable()->inputModule->id);
  889. if (inputIt == p.newModuleIds.end())
  890. continue;
  891. auto outputIt = p.newModuleIds.find(cw->getCable()->outputModule->id);
  892. if (outputIt != p.newModuleIds.end())
  893. continue;
  894. int64_t clonedInputModuleId = inputIt->second;
  895. engine::Module* clonedInputModule = APP->engine->getModule(clonedInputModuleId);
  896. // Create cable attached to cloned ModuleWidget's input
  897. engine::Cable* clonedCable = new engine::Cable;
  898. clonedCable->inputModule = clonedInputModule;
  899. clonedCable->inputId = cw->cable->inputId;
  900. clonedCable->outputModule = cw->cable->outputModule;
  901. clonedCable->outputId = cw->cable->outputId;
  902. APP->engine->addCable(clonedCable);
  903. app::CableWidget* clonedCw = new app::CableWidget;
  904. clonedCw->setCable(clonedCable);
  905. clonedCw->color = cw->color;
  906. APP->scene->rack->addCable(clonedCw);
  907. // history::CableAdd
  908. history::CableAdd* hca = new history::CableAdd;
  909. hca->setCable(clonedCw);
  910. complexAction->push(hca);
  911. }
  912. }
  913. }
  914. void RackWidget::bypassSelectionAction(bool bypassed) {
  915. history::ComplexAction* complexAction = new history::ComplexAction;
  916. complexAction->name = bypassed ? "bypass modules" : "un-bypass modules";
  917. for (ModuleWidget* mw : getSelected()) {
  918. assert(mw->module);
  919. if (mw->module->isBypassed() == bypassed)
  920. continue;
  921. // history::ModuleBypass
  922. history::ModuleBypass* h = new history::ModuleBypass;
  923. h->moduleId = mw->module->id;
  924. h->bypassed = bypassed;
  925. complexAction->push(h);
  926. APP->engine->bypassModule(mw->module, bypassed);
  927. }
  928. if (!complexAction->isEmpty())
  929. APP->history->push(complexAction);
  930. else
  931. delete complexAction;
  932. }
  933. bool RackWidget::isSelectionBypassed() {
  934. for (ModuleWidget* mw : getSelected()) {
  935. if (!mw->getModule()->isBypassed())
  936. return false;
  937. }
  938. return true;
  939. }
  940. void RackWidget::deleteSelectionAction() {
  941. history::ComplexAction* complexAction = new history::ComplexAction;
  942. complexAction->name = "remove modules";
  943. // Copy selected set since removing ModuleWidgets modifies it.
  944. std::set<ModuleWidget*> selectedModules = getSelected();
  945. for (ModuleWidget* mw : selectedModules) {
  946. mw->appendDisconnectActions(complexAction);
  947. // history::ModuleRemove
  948. history::ModuleRemove* moduleRemove = new history::ModuleRemove;
  949. moduleRemove->setModule(mw);
  950. complexAction->push(moduleRemove);
  951. removeModule(mw);
  952. delete mw;
  953. }
  954. APP->history->push(complexAction);
  955. }
  956. bool RackWidget::requestSelectionPos(math::Vec delta) {
  957. // Calculate new positions
  958. std::map<widget::Widget*, math::Rect> mwBoxes;
  959. for (ModuleWidget* mw : getSelected()) {
  960. math::Rect mwBox = mw->box;
  961. mwBox.pos += delta;
  962. mwBoxes[mw] = mwBox;
  963. }
  964. // Check intersection with other modules
  965. for (widget::Widget* w2 : internal->moduleContainer->children) {
  966. // Don't intersect with selected modules
  967. auto it = mwBoxes.find(w2);
  968. if (it != mwBoxes.end())
  969. continue;
  970. math::Rect w2Box = w2->box;
  971. // Check intersection with all selected modules
  972. for (const auto& pair : mwBoxes) {
  973. if (pair.second.intersects(w2Box))
  974. return false;
  975. }
  976. }
  977. // Accept requested position
  978. for (const auto& pair : mwBoxes) {
  979. pair.first->setPosition(pair.second.pos);
  980. }
  981. updateExpanders();
  982. return true;
  983. }
  984. void RackWidget::setSelectionPosNearest(math::Vec delta) {
  985. eachNearestGridPos(delta, [&](math::Vec delta) -> bool {
  986. return requestSelectionPos(delta);
  987. });
  988. }
  989. void RackWidget::appendSelectionContextMenu(ui::Menu* menu) {
  990. int n = getSelected().size();
  991. menu->addChild(createMenuLabel(string::f("%d selected %s", n, n == 1 ? "module" : "modules")));
  992. // Enable alwaysConsume of menu items if the number of selected modules changes
  993. // Select all
  994. menu->addChild(createMenuItem("Select all", RACK_MOD_CTRL_NAME "+A", [=]() {
  995. selectAll();
  996. }, false, true));
  997. // Deselect
  998. menu->addChild(createMenuItem("Deselect", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+A", [=]() {
  999. deselectAll();
  1000. }, n == 0, true));
  1001. // Copy
  1002. menu->addChild(createMenuItem("Copy", RACK_MOD_CTRL_NAME "+C", [=]() {
  1003. copyClipboardSelection();
  1004. }, n == 0));
  1005. // Paste
  1006. menu->addChild(createMenuItem("Paste", RACK_MOD_CTRL_NAME "+V", [=]() {
  1007. pasteClipboardAction();
  1008. }, false, true));
  1009. // Save
  1010. menu->addChild(createMenuItem("Save selection as", "", [=]() {
  1011. saveSelectionDialog();
  1012. }, n == 0));
  1013. // Initialize
  1014. menu->addChild(createMenuItem("Initialize", RACK_MOD_CTRL_NAME "+I", [=]() {
  1015. resetSelectionAction();
  1016. }, n == 0));
  1017. // Randomize
  1018. menu->addChild(createMenuItem("Randomize", RACK_MOD_CTRL_NAME "+R", [=]() {
  1019. randomizeSelectionAction();
  1020. }, n == 0));
  1021. // Disconnect cables
  1022. menu->addChild(createMenuItem("Disconnect cables", RACK_MOD_CTRL_NAME "+U", [=]() {
  1023. disconnectSelectionAction();
  1024. }, n == 0));
  1025. // Bypass
  1026. std::string bypassText = RACK_MOD_CTRL_NAME "+E";
  1027. bool bypassed = (n > 0) && isSelectionBypassed();
  1028. if (bypassed)
  1029. bypassText += " " CHECKMARK_STRING;
  1030. menu->addChild(createMenuItem("Bypass", bypassText, [=]() {
  1031. bypassSelectionAction(!bypassed);
  1032. }, n == 0, true));
  1033. // Duplicate
  1034. menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() {
  1035. cloneSelectionAction(false);
  1036. }, n == 0));
  1037. // Duplicate with cables
  1038. menu->addChild(createMenuItem("└ with cables", RACK_MOD_SHIFT_NAME "+" RACK_MOD_CTRL_NAME "+D", [=]() {
  1039. cloneSelectionAction(true);
  1040. }, n == 0));
  1041. // Delete
  1042. menu->addChild(createMenuItem("Delete", "Backspace/Delete", [=]() {
  1043. deleteSelectionAction();
  1044. }, n == 0, true));
  1045. }
  1046. void RackWidget::clearCables() {
  1047. internal->incompleteCable = NULL;
  1048. internal->cableContainer->clearChildren();
  1049. }
  1050. void RackWidget::clearCablesAction() {
  1051. // Add CableRemove for every cable to a ComplexAction
  1052. history::ComplexAction* complexAction = new history::ComplexAction;
  1053. complexAction->name = "clear cables";
  1054. for (CableWidget* cw : getCompleteCables()) {
  1055. // history::CableRemove
  1056. history::CableRemove* h = new history::CableRemove;
  1057. h->setCable(cw);
  1058. complexAction->push(h);
  1059. }
  1060. if (!complexAction->isEmpty())
  1061. APP->history->push(complexAction);
  1062. else
  1063. delete complexAction;
  1064. clearCables();
  1065. }
  1066. void RackWidget::clearCablesOnPort(PortWidget* port) {
  1067. for (CableWidget* cw : getCablesOnPort(port)) {
  1068. // Check if cable is connected to port
  1069. if (cw == internal->incompleteCable) {
  1070. internal->incompleteCable = NULL;
  1071. internal->cableContainer->removeChild(cw);
  1072. }
  1073. else {
  1074. removeCable(cw);
  1075. }
  1076. delete cw;
  1077. }
  1078. }
  1079. void RackWidget::addCable(CableWidget* cw) {
  1080. assert(cw->isComplete());
  1081. internal->cableContainer->addChild(cw);
  1082. }
  1083. void RackWidget::removeCable(CableWidget* cw) {
  1084. assert(cw->isComplete());
  1085. internal->cableContainer->removeChild(cw);
  1086. }
  1087. CableWidget* RackWidget::getIncompleteCable() {
  1088. return internal->incompleteCable;
  1089. }
  1090. void RackWidget::setIncompleteCable(CableWidget* cw) {
  1091. if (internal->incompleteCable) {
  1092. internal->cableContainer->removeChild(internal->incompleteCable);
  1093. delete internal->incompleteCable;
  1094. internal->incompleteCable = NULL;
  1095. }
  1096. if (cw) {
  1097. internal->cableContainer->addChild(cw);
  1098. internal->incompleteCable = cw;
  1099. }
  1100. }
  1101. CableWidget* RackWidget::releaseIncompleteCable() {
  1102. if (!internal->incompleteCable)
  1103. return NULL;
  1104. CableWidget* cw = internal->incompleteCable;
  1105. internal->cableContainer->removeChild(internal->incompleteCable);
  1106. internal->incompleteCable = NULL;
  1107. return cw;
  1108. }
  1109. CableWidget* RackWidget::getTopCable(PortWidget* port) {
  1110. for (auto it = internal->cableContainer->children.rbegin(); it != internal->cableContainer->children.rend(); it++) {
  1111. CableWidget* cw = dynamic_cast<CableWidget*>(*it);
  1112. assert(cw);
  1113. if (cw->inputPort == port || cw->outputPort == port)
  1114. return cw;
  1115. }
  1116. return NULL;
  1117. }
  1118. CableWidget* RackWidget::getCable(int64_t cableId) {
  1119. for (widget::Widget* w : internal->cableContainer->children) {
  1120. CableWidget* cw = dynamic_cast<CableWidget*>(w);
  1121. assert(cw);
  1122. if (!cw->cable)
  1123. continue;
  1124. if (cw->cable->id == cableId)
  1125. return cw;
  1126. }
  1127. return NULL;
  1128. }
  1129. std::vector<CableWidget*> RackWidget::getCompleteCables() {
  1130. std::vector<CableWidget*> cws;
  1131. cws.reserve(internal->cableContainer->children.size());
  1132. for (widget::Widget* w : internal->cableContainer->children) {
  1133. CableWidget* cw = dynamic_cast<CableWidget*>(w);
  1134. assert(cw);
  1135. if (cw->isComplete())
  1136. cws.push_back(cw);
  1137. }
  1138. cws.shrink_to_fit();
  1139. return cws;
  1140. }
  1141. std::vector<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) {
  1142. assert(port);
  1143. std::vector<CableWidget*> cws;
  1144. for (widget::Widget* w : internal->cableContainer->children) {
  1145. CableWidget* cw = dynamic_cast<CableWidget*>(w);
  1146. assert(cw);
  1147. if (cw->inputPort == port || cw->outputPort == port) {
  1148. cws.push_back(cw);
  1149. }
  1150. }
  1151. return cws;
  1152. }
  1153. std::vector<CableWidget*> RackWidget::getCompleteCablesOnPort(PortWidget* port) {
  1154. assert(port);
  1155. std::vector<CableWidget*> cws;
  1156. for (widget::Widget* w : internal->cableContainer->children) {
  1157. CableWidget* cw = dynamic_cast<CableWidget*>(w);
  1158. assert(cw);
  1159. if (!cw->isComplete())
  1160. continue;
  1161. if (cw->inputPort == port || cw->outputPort == port) {
  1162. cws.push_back(cw);
  1163. }
  1164. }
  1165. return cws;
  1166. }
  1167. int RackWidget::getNextCableColorId() {
  1168. return internal->nextCableColorId;
  1169. }
  1170. void RackWidget::setNextCableColorId(int id) {
  1171. internal->nextCableColorId = id;
  1172. }
  1173. NVGcolor RackWidget::getNextCableColor() {
  1174. if (settings::cableColors.empty())
  1175. return color::WHITE;
  1176. int id = internal->nextCableColorId++;
  1177. if (id >= (int) settings::cableColors.size())
  1178. id = 0;
  1179. if (internal->nextCableColorId >= (int) settings::cableColors.size())
  1180. internal->nextCableColorId = 0;
  1181. return settings::cableColors[id];
  1182. }
  1183. ParamWidget* RackWidget::getTouchedParam() {
  1184. return touchedParam;
  1185. }
  1186. void RackWidget::setTouchedParam(ParamWidget* pw) {
  1187. touchedParam = pw;
  1188. }
  1189. void RackWidget::updateExpanders() {
  1190. for (widget::Widget* w : internal->moduleContainer->children) {
  1191. math::Vec pLeft = w->box.pos.div(RACK_GRID_SIZE).round();
  1192. math::Vec pRight = w->box.getTopRight().div(RACK_GRID_SIZE).round();
  1193. ModuleWidget* mwLeft = NULL;
  1194. ModuleWidget* mwRight = NULL;
  1195. // Find adjacent modules
  1196. for (widget::Widget* w2 : internal->moduleContainer->children) {
  1197. if (w2 == w)
  1198. continue;
  1199. math::Vec p2Left = w2->box.pos.div(RACK_GRID_SIZE).round();
  1200. math::Vec p2Right = w2->box.getTopRight().div(RACK_GRID_SIZE).round();
  1201. // Check if this is a left module
  1202. if (p2Right.equals(pLeft)) {
  1203. mwLeft = dynamic_cast<ModuleWidget*>(w2);
  1204. }
  1205. // Check if this is a right module
  1206. if (p2Left.equals(pRight)) {
  1207. mwRight = dynamic_cast<ModuleWidget*>(w2);
  1208. }
  1209. }
  1210. ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
  1211. mw->module->leftExpander.moduleId = mwLeft ? mwLeft->module->id : -1;
  1212. mw->module->rightExpander.moduleId = mwRight ? mwRight->module->id : -1;
  1213. }
  1214. }
  1215. } // namespace app
  1216. } // namespace rack