#include "app/RackWidget.hpp" #include "widget/TransparentWidget.hpp" #include "app/RackRail.hpp" #include "app/Scene.hpp" #include "app/ModuleBrowser.hpp" #include "settings.hpp" #include "plugin.hpp" #include "engine/Engine.hpp" #include "app.hpp" #include "asset.hpp" #include "patch.hpp" #include "osdialog.h" #include #include namespace rack { namespace app { static ModuleWidget *moduleFromJson(json_t *moduleJ) { // Get slugs json_t *pluginSlugJ = json_object_get(moduleJ, "plugin"); if (!pluginSlugJ) return NULL; json_t *modelSlugJ = json_object_get(moduleJ, "model"); if (!modelSlugJ) return NULL; std::string pluginSlug = json_string_value(pluginSlugJ); std::string modelSlug = json_string_value(modelSlugJ); // Get Model plugin::Model *model = plugin::getModel(pluginSlug, modelSlug); if (!model) return NULL; // Create ModuleWidget ModuleWidget *moduleWidget = model->createModuleWidget(); assert(moduleWidget); moduleWidget->fromJson(moduleJ); return moduleWidget; } struct ModuleContainer : widget::Widget { void draw(const DrawArgs &args) override { // Draw shadows behind each ModuleWidget first, so the shadow doesn't overlap the front of other ModuleWidgets. for (widget::Widget *child : children) { ModuleWidget *w = dynamic_cast(child); assert(w); nvgSave(args.vg); nvgTranslate(args.vg, child->box.pos.x, child->box.pos.y); w->drawShadow(args); nvgRestore(args.vg); } Widget::draw(args); } }; struct CableContainer : widget::TransparentWidget { void draw(const DrawArgs &args) override { Widget::draw(args); // Draw cable plugs for (widget::Widget *w : children) { CableWidget *cw = dynamic_cast(w); assert(cw); cw->drawPlugs(args); } } }; RackWidget::RackWidget() { rails = new widget::FramebufferWidget; rails->box.size = math::Vec(); rails->oversample = 1.0; { RackRail *rail = new RackRail; rail->box.size = math::Vec(); rails->addChild(rail); } addChild(rails); moduleContainer = new ModuleContainer; addChild(moduleContainer); cableContainer = new CableContainer; addChild(cableContainer); } RackWidget::~RackWidget() { clear(); } void RackWidget::step() { // Expand size to fit modules math::Vec moduleSize = moduleContainer->getChildrenBoundingBox().getBottomRight(); // We assume that the size is reset by a parent before calling step(). Otherwise it will grow unbounded. box.size = box.size.max(moduleSize); // Adjust size and position of rails widget::Widget *rail = rails->children.front(); math::Rect bound = getViewport(math::Rect(math::Vec(), box.size)); if (!rails->box.isContaining(bound)) { math::Vec cellMargin = math::Vec(20, 1); rails->box.pos = bound.pos.div(RACK_GRID_SIZE).floor().minus(cellMargin).mult(RACK_GRID_SIZE); rails->box.size = bound.size.plus(cellMargin.mult(RACK_GRID_SIZE).mult(2)); rails->dirty = true; rail->box.size = rails->box.size; } Widget::step(); } void RackWidget::draw(const DrawArgs &args) { Widget::draw(args); } void RackWidget::onHover(const widget::HoverEvent &e) { mousePos = e.pos; OpaqueWidget::onHover(e); } void RackWidget::onHoverKey(const widget::HoverKeyEvent &e) { OpaqueWidget::onHoverKey(e); if (e.getConsumed() != this) return; if (e.action == GLFW_PRESS || e.action == GLFW_REPEAT) { switch (e.key) { case GLFW_KEY_V: { if ((e.mods & WINDOW_MOD_MASK) == WINDOW_MOD_CTRL) { pastePresetClipboardAction(); } } break; } } } void RackWidget::onDragHover(const widget::DragHoverEvent &e) { OpaqueWidget::onDragHover(e); mousePos = e.pos; } void RackWidget::onButton(const widget::ButtonEvent &e) { OpaqueWidget::onButton(e); if (e.getConsumed() == this) { if (e.action == GLFW_PRESS && e.button == GLFW_MOUSE_BUTTON_RIGHT) { APP->scene->moduleBrowser->show(); } } } void RackWidget::onZoom(const widget::ZoomEvent &e) { rails->box.size = math::Vec(); OpaqueWidget::onZoom(e); } void RackWidget::clear() { // This isn't required because removing all ModuleWidgets should remove all cables, but do it just in case. clearCables(); // Remove ModuleWidgets std::list widgets = moduleContainer->children; for (widget::Widget *w : widgets) { ModuleWidget *moduleWidget = dynamic_cast(w); assert(moduleWidget); removeModule(moduleWidget); delete moduleWidget; } } json_t *RackWidget::toJson() { // root json_t *rootJ = json_object(); // modules json_t *modulesJ = json_array(); for (widget::Widget *w : moduleContainer->children) { ModuleWidget *moduleWidget = dynamic_cast(w); assert(moduleWidget); // module json_t *moduleJ = moduleWidget->toJson(); { // id json_object_set_new(moduleJ, "id", json_integer(moduleWidget->module->id)); // pos math::Vec pos = moduleWidget->box.pos.div(RACK_GRID_SIZE).round(); json_t *posJ = json_pack("[i, i]", (int) pos.x, (int) pos.y); json_object_set_new(moduleJ, "pos", posJ); } json_array_append_new(modulesJ, moduleJ); } json_object_set_new(rootJ, "modules", modulesJ); // cables json_t *cablesJ = json_array(); for (widget::Widget *w : cableContainer->children) { CableWidget *cw = dynamic_cast(w); assert(cw); // Only serialize complete cables if (!cw->isComplete()) continue; json_t *cableJ = cw->toJson(); { // id json_object_set_new(rootJ, "id", json_integer(cw->cable->id)); } json_array_append_new(cablesJ, cableJ); } json_object_set_new(rootJ, "cables", cablesJ); return rootJ; } void RackWidget::fromJson(json_t *rootJ) { // modules json_t *modulesJ = json_object_get(rootJ, "modules"); if (!modulesJ) return; size_t moduleIndex; json_t *moduleJ; json_array_foreach(modulesJ, moduleIndex, moduleJ) { ModuleWidget *moduleWidget = moduleFromJson(moduleJ); if (moduleWidget) { // Before 1.0, the module ID was the index in the "modules" array if (APP->patch->isLegacy(2)) { moduleWidget->module->id = moduleIndex; } // id json_t *idJ = json_object_get(moduleJ, "id"); if (idJ) moduleWidget->module->id = json_integer_value(idJ); // pos json_t *posJ = json_object_get(moduleJ, "pos"); double x, y; json_unpack(posJ, "[F, F]", &x, &y); math::Vec pos = math::Vec(x, y); if (APP->patch->isLegacy(1)) { // Before 0.6, positions were in pixel units moduleWidget->box.pos = pos; } else { moduleWidget->box.pos = pos.mult(RACK_GRID_SIZE); } addModule(moduleWidget); } else { json_t *pluginSlugJ = json_object_get(moduleJ, "plugin"); json_t *modelSlugJ = json_object_get(moduleJ, "model"); std::string pluginSlug = json_string_value(pluginSlugJ); std::string modelSlug = json_string_value(modelSlugJ); APP->patch->warningLog += string::f("Could not find module \"%s\" of plugin \"%s\"\n", modelSlug.c_str(), pluginSlug.c_str()); } } // cables json_t *cablesJ = json_object_get(rootJ, "cables"); // Before 1.0, cables were called wires if (!cablesJ) cablesJ = json_object_get(rootJ, "wires"); assert(cablesJ); size_t cableIndex; json_t *cableJ; json_array_foreach(cablesJ, cableIndex, cableJ) { // Create a unserialize cable CableWidget *cw = new CableWidget; cw->fromJson(cableJ); if (!cw->isComplete()) { delete cw; continue; } // Before 1.0, cables IDs were not used, so just use the index of the "cables" array. if (APP->patch->isLegacy(2)) { cw->cable->id = cableIndex; } // id json_t *idJ = json_object_get(cableJ, "id"); if (idJ) cw->cable->id = json_integer_value(idJ); addCable(cw); } } void RackWidget::pastePresetClipboardAction() { const char *moduleJson = glfwGetClipboardString(APP->window->win); if (!moduleJson) { WARN("Could not get text from clipboard."); return; } json_error_t error; json_t *moduleJ = json_loads(moduleJson, 0, &error); if (moduleJ) { ModuleWidget *mw = moduleFromJson(moduleJ); json_decref(moduleJ); addModuleAtMouse(mw); // history::ModuleAdd history::ModuleAdd *h = new history::ModuleAdd; h->setModule(mw); APP->history->push(h); } else { WARN("JSON parsing error at %s %d:%d %s", error.source, error.line, error.column, error.text); } } static void RackWidget_updateAdjacent(RackWidget *that) { // TODO This can be better than O(n^2) for (widget::Widget *w : that->moduleContainer->children) { ModuleWidget *m = dynamic_cast(w); math::Vec pRight = m->box.getTopRight().div(RACK_GRID_SIZE).round(); bool found = false; for (widget::Widget *w2 : that->moduleContainer->children) { ModuleWidget *m2 = dynamic_cast(w2); if (m == m2) continue; math::Vec p2 = m2->box.pos.div(RACK_GRID_SIZE).round(); // Check if m is to the left of m2 if (pRight.isEqual(p2)) { m->module->rightModuleId = m2->module->id; m2->module->leftModuleId = m->module->id; found = true; break; } } if (!found) { m->module->rightModuleId = -1; } } } void RackWidget::addModule(ModuleWidget *m) { // Add module to ModuleContainer assert(m); moduleContainer->addChild(m); if (m->module) { // Add module to Engine APP->engine->addModule(m->module); } RackWidget_updateAdjacent(this); } void RackWidget::addModuleAtMouse(ModuleWidget *m) { assert(m); // Move module nearest to the mouse position m->box.pos = mousePos.minus(m->box.size.div(2)); requestModuleBoxNearest(m, m->box); addModule(m); } void RackWidget::removeModule(ModuleWidget *m) { // Unset touchedParamWidget if (touchedParam) { ModuleWidget *touchedModule = touchedParam->getAncestorOfType(); if (touchedModule == m) touchedParam = NULL; } // Disconnect cables m->disconnect(); if (m->module) { // Remove module from Engine APP->engine->removeModule(m->module); } // Remove module from ModuleContainer moduleContainer->removeChild(m); } bool RackWidget::requestModuleBox(ModuleWidget *m, math::Rect requestedBox) { // Check bounds if (requestedBox.pos.x < 0 || requestedBox.pos.y < 0) return false; // Check intersection with other modules for (widget::Widget *m2 : moduleContainer->children) { // Don't intersect with self if (m == m2) continue; if (requestedBox.isIntersecting(m2->box)) { return false; } } // Accept requested position m->box = requestedBox; RackWidget_updateAdjacent(this); return true; } bool RackWidget::requestModuleBoxNearest(ModuleWidget *m, math::Rect requestedBox) { // Create possible positions int x0 = std::round(requestedBox.pos.x / RACK_GRID_WIDTH); int y0 = std::round(requestedBox.pos.y / RACK_GRID_HEIGHT); std::vector positions; for (int y = std::max(0, y0 - 8); y < y0 + 8; y++) { for (int x = std::max(0, x0 - 400); x < x0 + 400; x++) { positions.push_back(math::Vec(x * RACK_GRID_WIDTH, y * RACK_GRID_HEIGHT)); } } // Sort possible positions by distance to the requested position std::sort(positions.begin(), positions.end(), [requestedBox](math::Vec a, math::Vec b) { return a.minus(requestedBox.pos).norm() < b.minus(requestedBox.pos).norm(); }); // Find a position that does not collide for (math::Vec position : positions) { math::Rect newBox = requestedBox; newBox.pos = position; if (requestModuleBox(m, newBox)) return true; } // We failed to find a box with this brute force algorithm. return false; } ModuleWidget *RackWidget::getModule(int moduleId) { for (widget::Widget *w : moduleContainer->children) { ModuleWidget *moduleWidget = dynamic_cast(w); assert(moduleWidget); if (moduleWidget->module->id == moduleId) return moduleWidget; } return NULL; } bool RackWidget::isEmpty() { return moduleContainer->children.empty(); } void RackWidget::clearCables() { for (widget::Widget *w : cableContainer->children) { CableWidget *cw = dynamic_cast(w); assert(cw); if (!cw->isComplete()) continue; APP->engine->removeCable(cw->cable); } incompleteCable = NULL; cableContainer->clearChildren(); } void RackWidget::clearCablesAction() { // Add CableRemove for every cable to a ComplexAction history::ComplexAction *complexAction = new history::ComplexAction; complexAction->name = "clear cables"; for (widget::Widget *w : cableContainer->children) { CableWidget *cw = dynamic_cast(w); assert(cw); if (!cw->isComplete()) continue; // history::CableRemove history::CableRemove *h = new history::CableRemove; h->setCable(cw); complexAction->push(h); } APP->history->push(complexAction); clearCables(); } void RackWidget::clearCablesOnPort(PortWidget *port) { for (CableWidget *cw : getCablesOnPort(port)) { // Check if cable is connected to port if (cw == incompleteCable) { incompleteCable = NULL; cableContainer->removeChild(cw); } else { removeCable(cw); } delete cw; } } void RackWidget::addCable(CableWidget *w) { assert(w->isComplete()); APP->engine->addCable(w->cable); cableContainer->addChild(w); } void RackWidget::removeCable(CableWidget *w) { assert(w->isComplete()); APP->engine->removeCable(w->cable); cableContainer->removeChild(w); } void RackWidget::setIncompleteCable(CableWidget *w) { if (incompleteCable) { cableContainer->removeChild(incompleteCable); delete incompleteCable; incompleteCable = NULL; } if (w) { cableContainer->addChild(w); incompleteCable = w; } } CableWidget *RackWidget::releaseIncompleteCable() { CableWidget *cw = incompleteCable; cableContainer->removeChild(incompleteCable); incompleteCable = NULL; return cw; } CableWidget *RackWidget::getTopCable(PortWidget *port) { for (auto it = cableContainer->children.rbegin(); it != cableContainer->children.rend(); it++) { CableWidget *cw = dynamic_cast(*it); assert(cw); // Ignore incomplete cables if (!cw->isComplete()) continue; if (cw->inputPort == port || cw->outputPort == port) return cw; } return NULL; } CableWidget *RackWidget::getCable(int cableId) { for (widget::Widget *w : cableContainer->children) { CableWidget *cw = dynamic_cast(w); assert(cw); if (cw->cable->id == cableId) return cw; } return NULL; } std::list RackWidget::getCablesOnPort(PortWidget *port) { assert(port); std::list cables; for (widget::Widget *w : cableContainer->children) { CableWidget *cw = dynamic_cast(w); assert(cw); if (cw->inputPort == port || cw->outputPort == port) { cables.push_back(cw); } } return cables; } } // namespace app } // namespace rack