static GraphBreaker *findInputBreaker(GraphEditor *editor, const GraphConnectionEndpoint &ep) { for (auto obj : editor->getGraphObjects(GRAPH_CONNECTION)) { auto conn = dynamic_cast<GraphConnection *>(obj); assert(conn != nullptr); if (not (conn->getOutputEndpoint().getObj()->scene() == conn->getInputEndpoint().getObj()->scene())) continue; if (not (conn->getOutputEndpoint() == ep)) continue; auto breaker = dynamic_cast<GraphBreaker *>(conn->getInputEndpoint().getObj().data()); if (breaker == nullptr) continue; return breaker; } return nullptr; }
QString GraphDraw::getSelectionDescription(const int selectionFlags) { //generate names based on the selected objects const auto selected = this->getObjectsSelected(selectionFlags); if (selected.isEmpty()) return tr("no selection"); //if a single connection is selected, pretty print its endpoint IDs if (selected.size() == 1) { auto conn = qobject_cast<GraphConnection *>(selected.at(0)); if (conn == nullptr) return selected.at(0)->getId(); return tr("%1[%2] to %3[%4]").arg( conn->getOutputEndpoint().getObj()->getId(), conn->getOutputEndpoint().getKey().id, conn->getInputEndpoint().getObj()->getId(), conn->getInputEndpoint().getKey().id ); } return tr("selected"); }
GraphConnection *GraphEditor::makeConnection(const GraphConnectionEndpoint &ep0, const GraphConnectionEndpoint &ep1) { //direction check if (ep0.getConnectableAttrs().direction == ep1.getConnectableAttrs().direction or (ep0.getConnectableAttrs().direction == GRAPH_CONN_INPUT and ep1.getConnectableAttrs().direction == GRAPH_CONN_SLOT) or (ep0.getConnectableAttrs().direction == GRAPH_CONN_OUTPUT and ep1.getConnectableAttrs().direction == GRAPH_CONN_SIGNAL) or (ep0.getConnectableAttrs().direction == GRAPH_CONN_SLOT and ep1.getConnectableAttrs().direction == GRAPH_CONN_INPUT) or (ep0.getConnectableAttrs().direction == GRAPH_CONN_SIGNAL and ep1.getConnectableAttrs().direction == GRAPH_CONN_OUTPUT)) { throw Pothos::Exception("GraphEditor::makeConnection()", "cant connect endpoints of the same direction"); } //duplicate check for (auto obj : this->getGraphObjects(GRAPH_CONNECTION)) { auto conn = dynamic_cast<GraphConnection *>(obj); assert(conn != nullptr); if ( (conn->getOutputEndpoint() == ep0 and conn->getInputEndpoint() == ep1) or (conn->getOutputEndpoint() == ep1 and conn->getInputEndpoint() == ep0) ) throw Pothos::Exception("GraphEditor::makeConnection()", "connection already exists"); } auto conn = new GraphConnection(ep0.getObj()->draw()); conn->setupEndpoint(ep0); conn->setupEndpoint(ep1); const auto idHint = QString("Connection_%1%2_%3%4").arg( conn->getOutputEndpoint().getObj()->getId(), conn->getOutputEndpoint().getKey().id, conn->getInputEndpoint().getObj()->getId(), conn->getInputEndpoint().getKey().id ); conn->setId(this->newId(idHint)); assert(conn->getInputEndpoint().isValid()); assert(conn->getOutputEndpoint().isValid()); return conn; }
void GraphEditor::handleMoveGraphObjects(const int index) { if (not this->isVisible()) return; if (index >= this->count()) return; auto draw = this->getCurrentGraphDraw(); auto desc = tr("Move %1 to %2").arg(draw->getSelectionDescription(~GRAPH_CONNECTION), this->tabText(index)); //move all selected objects for (auto obj : draw->getObjectsSelected()) { obj->setSelected(false); this->getGraphDraw(index)->scene()->addItem(obj); } //reparent all connections based on endpoints: std::vector<GraphConnection *> boundaryConnections; for (auto obj : this->getGraphObjects(GRAPH_CONNECTION)) { auto conn = dynamic_cast<GraphConnection *>(obj); assert(conn != nullptr); //Connection has the same endpoints, so make sure that the parent is corrected to the endpoint if (conn->getOutputEndpoint().getObj()->scene() == conn->getInputEndpoint().getObj()->scene()) { if (conn->getOutputEndpoint().getObj()->scene() != conn->scene()) { conn->getInputEndpoint().getObj()->scene()->addItem(conn); } } //otherwise stash it for more processing else { boundaryConnections.push_back(conn); } } //create breakers for output endpoints that have to cross for (auto conn : boundaryConnections) { const auto &epOut = conn->getOutputEndpoint(); const auto &epIn = conn->getInputEndpoint(); auto sigSlotPairs = conn->getSigSlotPairs(); if (sigSlotPairs.empty()) sigSlotPairs.resize(1); //add empty for (const auto &sigSlotPair : sigSlotPairs) { auto breaker = findInputBreaker(this, epOut); if (breaker != nullptr) continue; breaker = new GraphBreaker(epOut.getObj()->draw()); breaker->setInput(true); const auto name = QString("%1[%2]").arg(epOut.getObj()->getId(), epOut.getKey().id); breaker->setId(this->newId(name)); breaker->setNodeName(breaker->getId()); //the first of its name breaker->setRotation(epIn.getObj()->rotation()); breaker->setPos(epIn.getObj()->pos()); auto outConn = this->makeConnection(epOut, GraphConnectionEndpoint(breaker, breaker->getConnectableKeys().at(0))); if (not sigSlotPair.first.isEmpty()) outConn->addSigSlotPair(std::make_pair(sigSlotPair.first, breaker->getConnectableKeys().at(0).id)); if (outConn->scene() != breaker->scene()) breaker->scene()->addItem(outConn); //use desired parent } } //create breakers for input endpoints that have to cross for (auto conn : boundaryConnections) { const auto &epOut = conn->getOutputEndpoint(); const auto &epIn = conn->getInputEndpoint(); auto sigSlotPairs = conn->getSigSlotPairs(); if (sigSlotPairs.empty()) sigSlotPairs.resize(1); //add empty for (const auto &sigSlotPair : sigSlotPairs) { //find the existing breaker or make a new one const auto name = findInputBreaker(this, epOut)->getNodeName(); GraphBreaker *breaker = nullptr; for (auto obj : this->getGraphObjects(GRAPH_BREAKER)) { if (obj->draw() != epIn.getObj()->draw()) continue; auto outBreaker = dynamic_cast<GraphBreaker *>(obj); assert(outBreaker != nullptr); if (outBreaker->isInput()) continue; if (outBreaker->getNodeName() != name) continue; breaker = outBreaker; break; } //make a new output breaker if (breaker == nullptr) { breaker = new GraphBreaker(epIn.getObj()->draw()); breaker->setInput(false); breaker->setId(this->newId(name)); breaker->setNodeName(name); breaker->setRotation(epOut.getObj()->rotation()); breaker->setPos(epOut.getObj()->pos()); } //connect to this breaker auto inConn = this->makeConnection(epIn, GraphConnectionEndpoint(breaker, breaker->getConnectableKeys().at(0))); if (not sigSlotPair.second.isEmpty()) inConn->addSigSlotPair(std::make_pair(breaker->getConnectableKeys().at(0).id, sigSlotPair.second)); if (inConn->scene() != breaker->scene()) breaker->scene()->addItem(inConn); //use desired parent } delete conn; } handleStateChange(GraphState("transform-move", desc)); }