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.

1466 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 center X coordinates
  583. auto cmp = [&](const widget::Widget* a, const widget::Widget* b) {
  584. return a->box.pos.x + a->box.size.x / 2 < b->box.pos.x + b->box.size.x / 2;
  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. if (w2 == mw)
  591. continue;
  592. // Set position to old position
  593. auto it = internal->moduleOldPositions.find(w2);
  594. if (it != internal->moduleOldPositions.end()) {
  595. w2->box.pos = it->second;
  596. }
  597. // Modules must be on the same row as `mw`
  598. if (w2->box.getTop() != mw->box.getTop())
  599. continue;
  600. // Insert into leftModules or rightModules
  601. if (cmp(w2, mw))
  602. leftModules.insert(w2);
  603. else
  604. rightModules.insert(w2);
  605. }
  606. if (!leftModules.empty()) {
  607. widget::Widget* leftModule = *leftModules.rbegin();
  608. // Make sure module is to the right of the last leftModule.
  609. if (leftModule->box.getRight() > mw->box.getLeft()) {
  610. mw->box.pos.x = leftModule->box.getRight();
  611. }
  612. // If there isn't enough space between the last leftModule and first rightModule, place module to the right of the leftModule.
  613. if (!rightModules.empty()) {
  614. widget::Widget* rightModule = *rightModules.begin();
  615. if (rightModule->box.getLeft() - leftModule->box.getRight() < mw->box.getWidth()) {
  616. mw->box.pos.x = leftModule->box.getRight();
  617. }
  618. }
  619. }
  620. // Shove right modules
  621. float xLimit = mw->box.getRight();
  622. for (auto it = rightModules.begin(); it != rightModules.end(); it++) {
  623. widget::Widget* w2 = *it;
  624. math::Vec newPos = w2->box.pos;
  625. newPos.x = xLimit;
  626. newPos.x = std::round(newPos.x / RACK_GRID_WIDTH) * RACK_GRID_WIDTH;
  627. if (w2->box.pos.x > newPos.x)
  628. break;
  629. w2->box.pos = newPos;
  630. xLimit = w2->box.getRight();
  631. }
  632. updateExpanders();
  633. }
  634. ModuleWidget* RackWidget::getModule(int64_t moduleId) {
  635. for (widget::Widget* w : internal->moduleContainer->children) {
  636. ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
  637. assert(mw);
  638. if (mw->module->id == moduleId)
  639. return mw;
  640. }
  641. return NULL;
  642. }
  643. std::vector<ModuleWidget*> RackWidget::getModules() {
  644. std::vector<ModuleWidget*> mws;
  645. mws.reserve(internal->moduleContainer->children.size());
  646. for (widget::Widget* w : internal->moduleContainer->children) {
  647. ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
  648. assert(mw);
  649. mws.push_back(mw);
  650. }
  651. mws.shrink_to_fit();
  652. return mws;
  653. }
  654. bool RackWidget::hasModules() {
  655. return internal->moduleContainer->children.empty();
  656. }
  657. void RackWidget::updateModuleOldPositions() {
  658. internal->moduleOldPositions.clear();
  659. for (ModuleWidget* mw : getModules()) {
  660. internal->moduleOldPositions[mw] = mw->getPosition();
  661. }
  662. }
  663. history::ComplexAction* RackWidget::getModuleDragAction() {
  664. history::ComplexAction* h = new history::ComplexAction;
  665. h->name = "move modules";
  666. for (ModuleWidget* mw : getModules()) {
  667. // Create ModuleMove action if the module was moved.
  668. auto it = internal->moduleOldPositions.find(mw);
  669. if (it == internal->moduleOldPositions.end())
  670. continue;
  671. math::Vec oldPos = it->second;
  672. if (!oldPos.equals(mw->box.pos)) {
  673. history::ModuleMove* mmh = new history::ModuleMove;
  674. mmh->moduleId = mw->module->id;
  675. mmh->oldPos = oldPos;
  676. mmh->newPos = mw->box.pos;
  677. h->push(mmh);
  678. }
  679. }
  680. return h;
  681. }
  682. void RackWidget::updateSelectionFromRect() {
  683. math::Rect selectionBox = math::Rect::fromCorners(internal->selectionStart, internal->selectionEnd);
  684. deselectAll();
  685. for (ModuleWidget* mw : getModules()) {
  686. bool selected = internal->selecting && selectionBox.intersects(mw->box);
  687. if (selected)
  688. select(mw);
  689. }
  690. }
  691. void RackWidget::selectAll() {
  692. internal->selectedModules.clear();
  693. for (widget::Widget* w : internal->moduleContainer->children) {
  694. ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
  695. assert(mw);
  696. internal->selectedModules.insert(mw);
  697. }
  698. }
  699. void RackWidget::deselectAll() {
  700. internal->selectedModules.clear();
  701. }
  702. void RackWidget::select(ModuleWidget* mw, bool selected) {
  703. if (selected) {
  704. internal->selectedModules.insert(mw);
  705. }
  706. else {
  707. auto it = internal->selectedModules.find(mw);
  708. if (it != internal->selectedModules.end())
  709. internal->selectedModules.erase(it);
  710. }
  711. }
  712. bool RackWidget::hasSelection() {
  713. return !internal->selectedModules.empty();
  714. }
  715. const std::set<ModuleWidget*>& RackWidget::getSelected() {
  716. return internal->selectedModules;
  717. }
  718. bool RackWidget::isSelected(ModuleWidget* mw) {
  719. auto it = internal->selectedModules.find(mw);
  720. return (it != internal->selectedModules.end());
  721. }
  722. json_t* RackWidget::selectionToJson(bool cables) {
  723. json_t* rootJ = json_object();
  724. std::set<engine::Module*> modules;
  725. // modules
  726. json_t* modulesJ = json_array();
  727. for (ModuleWidget* mw : getSelected()) {
  728. json_t* moduleJ = mw->toJson();
  729. // pos
  730. math::Vec pos = mw->box.pos.minus(RACK_OFFSET);
  731. pos = pos.div(RACK_GRID_SIZE).round();
  732. json_t* posJ = json_pack("[i, i]", (int) pos.x, (int) pos.y);
  733. json_object_set_new(moduleJ, "pos", posJ);
  734. json_array_append_new(modulesJ, moduleJ);
  735. modules.insert(mw->getModule());
  736. }
  737. json_object_set_new(rootJ, "modules", modulesJ);
  738. if (cables) {
  739. // cables
  740. json_t* cablesJ = json_array();
  741. for (CableWidget* cw : getCompleteCables()) {
  742. // Only add cables attached on both ends to selected modules
  743. engine::Cable* cable = cw->getCable();
  744. if (!cable || !cable->inputModule || !cable->outputModule)
  745. continue;
  746. const auto inputIt = modules.find(cable->inputModule);
  747. if (inputIt == modules.end())
  748. continue;
  749. const auto outputIt = modules.find(cable->outputModule);
  750. if (outputIt == modules.end())
  751. continue;
  752. json_t* cableJ = cable->toJson();
  753. cw->mergeJson(cableJ);
  754. json_array_append_new(cablesJ, cableJ);
  755. }
  756. json_object_set_new(rootJ, "cables", cablesJ);
  757. }
  758. return rootJ;
  759. }
  760. static const char SELECTION_FILTERS[] = "VCV Rack module selection (.vcvs):vcvs";
  761. void RackWidget::loadSelection(std::string path) {
  762. FILE* file = std::fopen(path.c_str(), "r");
  763. if (!file)
  764. throw Exception("Could not load selection file %s", path.c_str());
  765. DEFER({std::fclose(file);});
  766. INFO("Loading selection %s", path.c_str());
  767. json_error_t error;
  768. json_t* rootJ = json_loadf(file, 0, &error);
  769. if (!rootJ)
  770. 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);
  771. DEFER({json_decref(rootJ);});
  772. pasteJsonAction(rootJ);
  773. }
  774. void RackWidget::loadSelectionDialog() {
  775. std::string selectionDir = asset::user("selections");
  776. system::createDirectories(selectionDir);
  777. osdialog_filters* filters = osdialog_filters_parse(SELECTION_FILTERS);
  778. DEFER({osdialog_filters_free(filters);});
  779. char* pathC = osdialog_file(OSDIALOG_OPEN, selectionDir.c_str(), NULL, filters);
  780. if (!pathC) {
  781. // No path selected
  782. return;
  783. }
  784. DEFER({std::free(pathC);});
  785. try {
  786. loadSelection(pathC);
  787. }
  788. catch (Exception& e) {
  789. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, e.what());
  790. }
  791. }
  792. void RackWidget::saveSelection(std::string path) {
  793. INFO("Saving selection %s", path.c_str());
  794. json_t* rootJ = selectionToJson();
  795. assert(rootJ);
  796. DEFER({json_decref(rootJ);});
  797. engine::Module::jsonStripIds(rootJ);
  798. FILE* file = std::fopen(path.c_str(), "w");
  799. if (!file) {
  800. std::string message = string::f("Could not save selection to file %s", path.c_str());
  801. osdialog_message(OSDIALOG_WARNING, OSDIALOG_OK, message.c_str());
  802. return;
  803. }
  804. DEFER({std::fclose(file);});
  805. json_dumpf(rootJ, file, JSON_INDENT(2));
  806. }
  807. void RackWidget::saveSelectionDialog() {
  808. std::string selectionDir = asset::user("selections");
  809. system::createDirectories(selectionDir);
  810. osdialog_filters* filters = osdialog_filters_parse(SELECTION_FILTERS);
  811. DEFER({osdialog_filters_free(filters);});
  812. char* pathC = osdialog_file(OSDIALOG_SAVE, selectionDir.c_str(), "Untitled.vcvs", filters);
  813. if (!pathC) {
  814. // No path selected
  815. return;
  816. }
  817. DEFER({std::free(pathC);});
  818. std::string path = pathC;
  819. // Automatically append .vcvs extension
  820. if (system::getExtension(path) != ".vcvs")
  821. path += ".vcvs";
  822. saveSelection(path);
  823. }
  824. void RackWidget::copyClipboardSelection() {
  825. json_t* rootJ = selectionToJson();
  826. DEFER({json_decref(rootJ);});
  827. char* moduleJson = json_dumps(rootJ, JSON_INDENT(2));
  828. DEFER({std::free(moduleJson);});
  829. glfwSetClipboardString(APP->window->win, moduleJson);
  830. }
  831. void RackWidget::resetSelectionAction() {
  832. history::ComplexAction* complexAction = new history::ComplexAction;
  833. complexAction->name = "reset modules";
  834. for (ModuleWidget* mw : getSelected()) {
  835. assert(mw->module);
  836. // history::ModuleChange
  837. history::ModuleChange* h = new history::ModuleChange;
  838. h->moduleId = mw->module->id;
  839. h->oldModuleJ = mw->toJson();
  840. APP->engine->resetModule(mw->module);
  841. h->newModuleJ = mw->toJson();
  842. complexAction->push(h);
  843. }
  844. APP->history->push(complexAction);
  845. }
  846. void RackWidget::randomizeSelectionAction() {
  847. history::ComplexAction* complexAction = new history::ComplexAction;
  848. complexAction->name = "randomize modules";
  849. for (ModuleWidget* mw : getSelected()) {
  850. assert(mw->module);
  851. // history::ModuleChange
  852. history::ModuleChange* h = new history::ModuleChange;
  853. h->moduleId = mw->module->id;
  854. h->oldModuleJ = mw->toJson();
  855. APP->engine->randomizeModule(mw->module);
  856. h->newModuleJ = mw->toJson();
  857. complexAction->push(h);
  858. }
  859. APP->history->push(complexAction);
  860. }
  861. void RackWidget::disconnectSelectionAction() {
  862. history::ComplexAction* complexAction = new history::ComplexAction;
  863. complexAction->name = "disconnect cables";
  864. for (ModuleWidget* mw : getSelected()) {
  865. mw->appendDisconnectActions(complexAction);
  866. }
  867. if (!complexAction->isEmpty())
  868. APP->history->push(complexAction);
  869. else
  870. delete complexAction;
  871. }
  872. void RackWidget::cloneSelectionAction(bool cloneCables) {
  873. json_t* rootJ = selectionToJson(cloneCables);
  874. DEFER({json_decref(rootJ);});
  875. history::ComplexAction* complexAction = new history::ComplexAction;
  876. complexAction->name = "duplicate modules";
  877. DEFER({
  878. if (!complexAction->isEmpty())
  879. APP->history->push(complexAction);
  880. else
  881. delete complexAction;
  882. });
  883. auto p = RackWidget_pasteJson(this, rootJ, complexAction);
  884. // Clone cables attached to inputs of selected modules but outputs of non-selected modules
  885. if (cloneCables) {
  886. for (CableWidget* cw : getCompleteCables()) {
  887. auto inputIt = p.newModuleIds.find(cw->getCable()->inputModule->id);
  888. if (inputIt == p.newModuleIds.end())
  889. continue;
  890. auto outputIt = p.newModuleIds.find(cw->getCable()->outputModule->id);
  891. if (outputIt != p.newModuleIds.end())
  892. continue;
  893. int64_t clonedInputModuleId = inputIt->second;
  894. engine::Module* clonedInputModule = APP->engine->getModule(clonedInputModuleId);
  895. // Create cable attached to cloned ModuleWidget's input
  896. engine::Cable* clonedCable = new engine::Cable;
  897. clonedCable->inputModule = clonedInputModule;
  898. clonedCable->inputId = cw->cable->inputId;
  899. clonedCable->outputModule = cw->cable->outputModule;
  900. clonedCable->outputId = cw->cable->outputId;
  901. APP->engine->addCable(clonedCable);
  902. app::CableWidget* clonedCw = new app::CableWidget;
  903. clonedCw->setCable(clonedCable);
  904. clonedCw->color = cw->color;
  905. APP->scene->rack->addCable(clonedCw);
  906. // history::CableAdd
  907. history::CableAdd* hca = new history::CableAdd;
  908. hca->setCable(clonedCw);
  909. complexAction->push(hca);
  910. }
  911. }
  912. }
  913. void RackWidget::bypassSelectionAction(bool bypassed) {
  914. history::ComplexAction* complexAction = new history::ComplexAction;
  915. complexAction->name = bypassed ? "bypass modules" : "un-bypass modules";
  916. for (ModuleWidget* mw : getSelected()) {
  917. assert(mw->module);
  918. if (mw->module->isBypassed() == bypassed)
  919. continue;
  920. // history::ModuleBypass
  921. history::ModuleBypass* h = new history::ModuleBypass;
  922. h->moduleId = mw->module->id;
  923. h->bypassed = bypassed;
  924. complexAction->push(h);
  925. APP->engine->bypassModule(mw->module, bypassed);
  926. }
  927. if (!complexAction->isEmpty())
  928. APP->history->push(complexAction);
  929. else
  930. delete complexAction;
  931. }
  932. bool RackWidget::isSelectionBypassed() {
  933. for (ModuleWidget* mw : getSelected()) {
  934. if (!mw->getModule()->isBypassed())
  935. return false;
  936. }
  937. return true;
  938. }
  939. void RackWidget::deleteSelectionAction() {
  940. history::ComplexAction* complexAction = new history::ComplexAction;
  941. complexAction->name = "remove modules";
  942. // Copy selected set since removing ModuleWidgets modifies it.
  943. std::set<ModuleWidget*> selectedModules = getSelected();
  944. for (ModuleWidget* mw : selectedModules) {
  945. mw->appendDisconnectActions(complexAction);
  946. // history::ModuleRemove
  947. history::ModuleRemove* moduleRemove = new history::ModuleRemove;
  948. moduleRemove->setModule(mw);
  949. complexAction->push(moduleRemove);
  950. removeModule(mw);
  951. delete mw;
  952. }
  953. APP->history->push(complexAction);
  954. }
  955. bool RackWidget::requestSelectionPos(math::Vec delta) {
  956. // Calculate new positions
  957. std::map<widget::Widget*, math::Rect> mwBoxes;
  958. for (ModuleWidget* mw : getSelected()) {
  959. math::Rect mwBox = mw->box;
  960. mwBox.pos += delta;
  961. mwBoxes[mw] = mwBox;
  962. }
  963. // Check intersection with other modules
  964. for (widget::Widget* w2 : internal->moduleContainer->children) {
  965. // Don't intersect with selected modules
  966. auto it = mwBoxes.find(w2);
  967. if (it != mwBoxes.end())
  968. continue;
  969. math::Rect w2Box = w2->box;
  970. // Check intersection with all selected modules
  971. for (const auto& pair : mwBoxes) {
  972. if (pair.second.intersects(w2Box))
  973. return false;
  974. }
  975. }
  976. // Accept requested position
  977. for (const auto& pair : mwBoxes) {
  978. pair.first->setPosition(pair.second.pos);
  979. }
  980. updateExpanders();
  981. return true;
  982. }
  983. void RackWidget::setSelectionPosNearest(math::Vec delta) {
  984. eachNearestGridPos(delta, [&](math::Vec delta) -> bool {
  985. return requestSelectionPos(delta);
  986. });
  987. }
  988. void RackWidget::appendSelectionContextMenu(ui::Menu* menu) {
  989. int n = getSelected().size();
  990. menu->addChild(createMenuLabel(string::f("%d selected %s", n, n == 1 ? "module" : "modules")));
  991. // Enable alwaysConsume of menu items if the number of selected modules changes
  992. // Select all
  993. menu->addChild(createMenuItem("Select all", RACK_MOD_CTRL_NAME "+A", [=]() {
  994. selectAll();
  995. }, false, true));
  996. // Deselect
  997. menu->addChild(createMenuItem("Deselect", RACK_MOD_CTRL_NAME "+" RACK_MOD_SHIFT_NAME "+A", [=]() {
  998. deselectAll();
  999. }, n == 0, true));
  1000. // Copy
  1001. menu->addChild(createMenuItem("Copy", RACK_MOD_CTRL_NAME "+C", [=]() {
  1002. copyClipboardSelection();
  1003. }, n == 0));
  1004. // Paste
  1005. menu->addChild(createMenuItem("Paste", RACK_MOD_CTRL_NAME "+V", [=]() {
  1006. pasteClipboardAction();
  1007. }, false, true));
  1008. // Save
  1009. menu->addChild(createMenuItem("Save selection as", "", [=]() {
  1010. saveSelectionDialog();
  1011. }, n == 0));
  1012. // Initialize
  1013. menu->addChild(createMenuItem("Initialize", RACK_MOD_CTRL_NAME "+I", [=]() {
  1014. resetSelectionAction();
  1015. }, n == 0));
  1016. // Randomize
  1017. menu->addChild(createMenuItem("Randomize", RACK_MOD_CTRL_NAME "+R", [=]() {
  1018. randomizeSelectionAction();
  1019. }, n == 0));
  1020. // Disconnect cables
  1021. menu->addChild(createMenuItem("Disconnect cables", RACK_MOD_CTRL_NAME "+U", [=]() {
  1022. disconnectSelectionAction();
  1023. }, n == 0));
  1024. // Bypass
  1025. std::string bypassText = RACK_MOD_CTRL_NAME "+E";
  1026. bool bypassed = (n > 0) && isSelectionBypassed();
  1027. if (bypassed)
  1028. bypassText += " " CHECKMARK_STRING;
  1029. menu->addChild(createMenuItem("Bypass", bypassText, [=]() {
  1030. bypassSelectionAction(!bypassed);
  1031. }, n == 0, true));
  1032. // Duplicate
  1033. menu->addChild(createMenuItem("Duplicate", RACK_MOD_CTRL_NAME "+D", [=]() {
  1034. cloneSelectionAction(false);
  1035. }, n == 0));
  1036. // Duplicate with cables
  1037. menu->addChild(createMenuItem("└ with cables", RACK_MOD_SHIFT_NAME "+" RACK_MOD_CTRL_NAME "+D", [=]() {
  1038. cloneSelectionAction(true);
  1039. }, n == 0));
  1040. // Delete
  1041. menu->addChild(createMenuItem("Delete", "Backspace/Delete", [=]() {
  1042. deleteSelectionAction();
  1043. }, n == 0, true));
  1044. }
  1045. void RackWidget::clearCables() {
  1046. internal->incompleteCable = NULL;
  1047. internal->cableContainer->clearChildren();
  1048. }
  1049. void RackWidget::clearCablesAction() {
  1050. // Add CableRemove for every cable to a ComplexAction
  1051. history::ComplexAction* complexAction = new history::ComplexAction;
  1052. complexAction->name = "clear cables";
  1053. for (CableWidget* cw : getCompleteCables()) {
  1054. // history::CableRemove
  1055. history::CableRemove* h = new history::CableRemove;
  1056. h->setCable(cw);
  1057. complexAction->push(h);
  1058. }
  1059. if (!complexAction->isEmpty())
  1060. APP->history->push(complexAction);
  1061. else
  1062. delete complexAction;
  1063. clearCables();
  1064. }
  1065. void RackWidget::clearCablesOnPort(PortWidget* port) {
  1066. for (CableWidget* cw : getCablesOnPort(port)) {
  1067. // Check if cable is connected to port
  1068. if (cw == internal->incompleteCable) {
  1069. internal->incompleteCable = NULL;
  1070. internal->cableContainer->removeChild(cw);
  1071. }
  1072. else {
  1073. removeCable(cw);
  1074. }
  1075. delete cw;
  1076. }
  1077. }
  1078. void RackWidget::addCable(CableWidget* cw) {
  1079. assert(cw->isComplete());
  1080. internal->cableContainer->addChild(cw);
  1081. }
  1082. void RackWidget::removeCable(CableWidget* cw) {
  1083. assert(cw->isComplete());
  1084. internal->cableContainer->removeChild(cw);
  1085. }
  1086. CableWidget* RackWidget::getIncompleteCable() {
  1087. return internal->incompleteCable;
  1088. }
  1089. void RackWidget::setIncompleteCable(CableWidget* cw) {
  1090. if (internal->incompleteCable) {
  1091. internal->cableContainer->removeChild(internal->incompleteCable);
  1092. delete internal->incompleteCable;
  1093. internal->incompleteCable = NULL;
  1094. }
  1095. if (cw) {
  1096. internal->cableContainer->addChild(cw);
  1097. internal->incompleteCable = cw;
  1098. }
  1099. }
  1100. CableWidget* RackWidget::releaseIncompleteCable() {
  1101. if (!internal->incompleteCable)
  1102. return NULL;
  1103. CableWidget* cw = internal->incompleteCable;
  1104. internal->cableContainer->removeChild(internal->incompleteCable);
  1105. internal->incompleteCable = NULL;
  1106. return cw;
  1107. }
  1108. CableWidget* RackWidget::getTopCable(PortWidget* port) {
  1109. for (auto it = internal->cableContainer->children.rbegin(); it != internal->cableContainer->children.rend(); it++) {
  1110. CableWidget* cw = dynamic_cast<CableWidget*>(*it);
  1111. assert(cw);
  1112. if (cw->inputPort == port || cw->outputPort == port)
  1113. return cw;
  1114. }
  1115. return NULL;
  1116. }
  1117. CableWidget* RackWidget::getCable(int64_t cableId) {
  1118. for (widget::Widget* w : internal->cableContainer->children) {
  1119. CableWidget* cw = dynamic_cast<CableWidget*>(w);
  1120. assert(cw);
  1121. if (!cw->cable)
  1122. continue;
  1123. if (cw->cable->id == cableId)
  1124. return cw;
  1125. }
  1126. return NULL;
  1127. }
  1128. std::vector<CableWidget*> RackWidget::getCompleteCables() {
  1129. std::vector<CableWidget*> cws;
  1130. cws.reserve(internal->cableContainer->children.size());
  1131. for (widget::Widget* w : internal->cableContainer->children) {
  1132. CableWidget* cw = dynamic_cast<CableWidget*>(w);
  1133. assert(cw);
  1134. if (cw->isComplete())
  1135. cws.push_back(cw);
  1136. }
  1137. cws.shrink_to_fit();
  1138. return cws;
  1139. }
  1140. std::vector<CableWidget*> RackWidget::getCablesOnPort(PortWidget* port) {
  1141. assert(port);
  1142. std::vector<CableWidget*> cws;
  1143. for (widget::Widget* w : internal->cableContainer->children) {
  1144. CableWidget* cw = dynamic_cast<CableWidget*>(w);
  1145. assert(cw);
  1146. if (cw->inputPort == port || cw->outputPort == port) {
  1147. cws.push_back(cw);
  1148. }
  1149. }
  1150. return cws;
  1151. }
  1152. std::vector<CableWidget*> RackWidget::getCompleteCablesOnPort(PortWidget* port) {
  1153. assert(port);
  1154. std::vector<CableWidget*> cws;
  1155. for (widget::Widget* w : internal->cableContainer->children) {
  1156. CableWidget* cw = dynamic_cast<CableWidget*>(w);
  1157. assert(cw);
  1158. if (!cw->isComplete())
  1159. continue;
  1160. if (cw->inputPort == port || cw->outputPort == port) {
  1161. cws.push_back(cw);
  1162. }
  1163. }
  1164. return cws;
  1165. }
  1166. int RackWidget::getNextCableColorId() {
  1167. return internal->nextCableColorId;
  1168. }
  1169. void RackWidget::setNextCableColorId(int id) {
  1170. internal->nextCableColorId = id;
  1171. }
  1172. NVGcolor RackWidget::getNextCableColor() {
  1173. if (settings::cableColors.empty())
  1174. return color::WHITE;
  1175. int id = internal->nextCableColorId++;
  1176. if (id >= (int) settings::cableColors.size())
  1177. id = 0;
  1178. if (internal->nextCableColorId >= (int) settings::cableColors.size())
  1179. internal->nextCableColorId = 0;
  1180. return settings::cableColors[id];
  1181. }
  1182. ParamWidget* RackWidget::getTouchedParam() {
  1183. return touchedParam;
  1184. }
  1185. void RackWidget::setTouchedParam(ParamWidget* pw) {
  1186. touchedParam = pw;
  1187. }
  1188. void RackWidget::updateExpanders() {
  1189. for (widget::Widget* w : internal->moduleContainer->children) {
  1190. math::Vec pLeft = w->box.pos.div(RACK_GRID_SIZE).round();
  1191. math::Vec pRight = w->box.getTopRight().div(RACK_GRID_SIZE).round();
  1192. ModuleWidget* mwLeft = NULL;
  1193. ModuleWidget* mwRight = NULL;
  1194. // Find adjacent modules
  1195. for (widget::Widget* w2 : internal->moduleContainer->children) {
  1196. if (w2 == w)
  1197. continue;
  1198. math::Vec p2Left = w2->box.pos.div(RACK_GRID_SIZE).round();
  1199. math::Vec p2Right = w2->box.getTopRight().div(RACK_GRID_SIZE).round();
  1200. // Check if this is a left module
  1201. if (p2Right.equals(pLeft)) {
  1202. mwLeft = dynamic_cast<ModuleWidget*>(w2);
  1203. }
  1204. // Check if this is a right module
  1205. if (p2Left.equals(pRight)) {
  1206. mwRight = dynamic_cast<ModuleWidget*>(w2);
  1207. }
  1208. }
  1209. ModuleWidget* mw = dynamic_cast<ModuleWidget*>(w);
  1210. mw->module->leftExpander.moduleId = mwLeft ? mwLeft->module->id : -1;
  1211. mw->module->rightExpander.moduleId = mwRight ? mwRight->module->id : -1;
  1212. }
  1213. }
  1214. } // namespace app
  1215. } // namespace rack