File indexing completed on 2024-12-08 11:06:52

0001 //
0002 // C++ Implementation: flowicndocument
0003 //
0004 // Description:
0005 //
0006 //
0007 // Author: Zoltan P <zoltan.padrah@gmail.com>, (C) 2008
0008 //
0009 // Copyright: See COPYING file that comes with this distribution
0010 //
0011 //
0012 #include "flowicndocument.h"
0013 
0014 #include "cnitemgroup.h"
0015 #include "connector.h"
0016 #include "conrouter.h"
0017 #include "flowcontainer.h"
0018 #include "fpnode.h"
0019 #include "item.h"
0020 #include "junctionflownode.h"
0021 #include "nodegroup.h"
0022 
0023 #include <ktechlab_debug.h>
0024 
0025 FlowICNDocument::FlowICNDocument(const QString &caption)
0026     : ICNDocument(caption)
0027 {
0028 }
0029 
0030 FlowICNDocument::~FlowICNDocument()
0031 {
0032     // Go to hell, KtlQCanvas. I'm in charge of what gets deleted.
0033     KtlQCanvasItemList all = m_canvas->allItems();
0034     const KtlQCanvasItemList::Iterator end = all.end();
0035     for (KtlQCanvasItemList::Iterator it = all.begin(); it != end; ++it)
0036         (*it)->setCanvas(nullptr);
0037 
0038     // Remove all items from the canvas
0039     selectAll();
0040     deleteSelection();
0041 
0042     // Delete anything that got through the above couple of lines
0043     ConnectorList connectorsToDelete = m_connectorList;
0044     connectorsToDelete.clear();
0045     qDeleteAll(connectorsToDelete);
0046 
0047     deleteAllNodes();
0048 }
0049 
0050 void FlowICNDocument::deleteAllNodes()
0051 {
0052     FPNodeMap nodesToDelete = m_flowNodeList;
0053     m_flowNodeList.clear();
0054     qDeleteAll(nodesToDelete);
0055 }
0056 
0057 bool FlowICNDocument::canConnect(KtlQCanvasItem *qcanvasItem1, KtlQCanvasItem *qcanvasItem2) const
0058 {
0059     // Rough outline of what can and can't connect:
0060     // * At most three connectors to a node
0061     // * Can't have connectors going between different levels (e.g. can't have
0062     //   a connector coming outside a FlowContainer from inside).
0063     // * Can't have more than one route between any two nodes
0064     // * In all connections between nodes, must have at least one input and one
0065     //   output node at the ends.
0066 
0067     FPNode *startNode = dynamic_cast<FPNode *>(qcanvasItem1);
0068     FPNode *endNode = dynamic_cast<FPNode *>(qcanvasItem2);
0069 
0070     if ((startNode && startNode->numCon(true, false) > 2) || (endNode && endNode->numCon(true, false) > 2))
0071         return false;
0072 
0073     Connector *startConnector = dynamic_cast<Connector *>(qcanvasItem1);
0074     Connector *endConnector = dynamic_cast<Connector *>(qcanvasItem2);
0075 
0076     // Can't have I-junction in flowcode document
0077     if (startConnector && endConnector)
0078         return false;
0079 
0080     // BEGIN Change connectors to nodes
0081     FPNode *startNode1 = nullptr;
0082     FPNode *startNode2 = nullptr;
0083     if (startConnector) {
0084         startNode1 = dynamic_cast<FPNode *>(startConnector->startNode());
0085         startNode2 = dynamic_cast<FPNode *>(startConnector->endNode());
0086 
0087         if (!startNode1 || !startNode2)
0088             return false;
0089     } else if (!startNode)
0090         return false;
0091 
0092     FPNode *endNode1 = nullptr;
0093     FPNode *endNode2 = nullptr;
0094     if (endConnector) {
0095         endNode1 = dynamic_cast<FPNode *>(endConnector->startNode());
0096         endNode2 = dynamic_cast<FPNode *>(endConnector->endNode());
0097 
0098         if (!endNode1 || !endNode2)
0099             return false;
0100     } else if (!endNode)
0101         return false;
0102 
0103     // END Change connectors to nodes
0104 
0105     // BEGIN Check we have appropriate input and output allowance
0106     if (type() == Document::dt_flowcode) // this is obviouse
0107     {
0108         if (startNode1 && startNode2 && endNode1 && endNode2) {
0109             // Can't have I-configuration
0110             return false;
0111         }
0112 
0113         if (startNode && endNode) {
0114             // Nice and easy straight line to check
0115 
0116             if (!startNode->acceptInput() && !endNode->acceptInput())
0117                 return false;
0118 
0119             if (!startNode->acceptOutput() && !endNode->acceptOutput())
0120                 return false;
0121         }
0122 
0123         else {
0124             // We're in a T-configuration, we can only make this if the base of
0125             // the T is an output
0126             FPNode *base = startNode ? startNode : endNode;
0127             if (!base->acceptOutput())
0128                 return false;
0129         }
0130     } else
0131         qCCritical(KTL_LOG) << "BUG: document type is not dt_flowcode";
0132     // END Check we have appropriate input and output allowance
0133 
0134     return ICNDocument::canConnect(qcanvasItem1, qcanvasItem2);
0135 }
0136 
0137 Connector *FlowICNDocument::createConnector(Connector *con1, Connector *con2, const QPoint & /*pos1*/, const QPoint & /*pos2*/, QPointList * /*pointList*/)
0138 {
0139     // FIXME isn't all this dead code?
0140     /*
0141     if ( !canConnect( con1, con2 ) )
0142         return nullptr;
0143 
0144 
0145     const bool con1UsedManual = con1->usesManualPoints();
0146     const bool con2UsedManual = con2->usesManualPoints();
0147 
0148     QList<QPointList> oldCon1Points = con1->splitConnectorPoints(pos1);
0149     QList<QPointList> oldCon2Points = con2->splitConnectorPoints(pos2);
0150 
0151 
0152     // FIXME dynamic_cast used because Connector doesn't know about FPNode
0153 
0154     FPNode *node1a = dynamic_cast<FPNode*> ( con1->startNode() );
0155     FPNode *node1b = dynamic_cast<FPNode*> ( con1->endNode() );
0156 
0157     FPNode *node2a = dynamic_cast<FPNode*> ( con2->startNode() );
0158     FPNode *node2b = dynamic_cast<FPNode*> ( con2->endNode() );
0159 
0160     if ( !node1a || !node1b || !node2a || !node2b )
0161         return nullptr;
0162     */
0163     con1->hide();
0164     con2->hide();
0165 
0166     // if ( type() != Document::dt_circuit )
0167     return nullptr;
0168 }
0169 
0170 Connector *FlowICNDocument::createConnector(Node *node, Connector *con, const QPoint &pos2, QPointList *pointList)
0171 {
0172     if (!canConnect(node, con))
0173         return nullptr;
0174 
0175     // FIXME dynamic_cast used, fix it in Connector class
0176 
0177     FPNode *conStartNode = dynamic_cast<FPNode *>(con->startNode());
0178     FPNode *conEndNode = dynamic_cast<FPNode *>(con->endNode());
0179 
0180     FPNode *fpNode = dynamic_cast<FPNode *>(node);
0181 
0182     const bool usedManual = con->usesManualPoints();
0183 
0184     FPNode *newNode = new JunctionFlowNode(this, 0, pos2);
0185 
0186     QPointList autoPoints;
0187     if (!pointList) {
0188         addAllItemConnectorPoints();
0189         ConRouter cr(this);
0190         cr.mapRoute(int(node->x()), int(node->y()), pos2.x(), pos2.y());
0191         autoPoints = cr.pointList(false);
0192         pointList = &autoPoints;
0193     }
0194 
0195     QList<QPointList> oldConPoints = con->splitConnectorPoints(pos2);
0196     con->hide();
0197 
0198     // The actual new connector
0199     Connector *new1 = newNode->createInputConnector(node);
0200     fpNode->addOutputConnector(new1);
0201     new1->setRoutePoints(*pointList, usedManual);
0202 
0203     // The two connectors formed from the original one when split
0204     Connector *new2 = newNode->createInputConnector(conStartNode);
0205     conStartNode->addOutputConnector(new2);
0206     new2->setRoutePoints(oldConPoints.at(0), usedManual);
0207 
0208     Connector *new3 = conEndNode->createInputConnector(newNode);
0209     newNode->addOutputConnector(new3);
0210     new3->setRoutePoints(oldConPoints.at(1), usedManual);
0211 
0212     // Avoid flicker: tell them to update their draw lists now
0213     con->updateConnectorPoints(false);
0214     new1->updateDrawList();
0215     new2->updateDrawList();
0216     new3->updateDrawList();
0217 
0218     // Now it's safe to remove the connector...
0219     con->removeConnectorNoArg();
0220     flushDeleteList();
0221 
0222     deleteNodeGroup(conStartNode);
0223     deleteNodeGroup(conEndNode);
0224     createNodeGroup(newNode)->init();
0225 
0226     return new1;
0227 }
0228 
0229 Connector *FlowICNDocument::createConnector(const QString &startNodeId, const QString &endNodeId, QPointList *pointList)
0230 {
0231     FPNode *startNode = getFPnodeWithID(startNodeId);
0232     FPNode *endNode = getFPnodeWithID(endNodeId);
0233 
0234     if (!startNode || !endNode) {
0235         qCDebug(KTL_LOG) << "Either/both the connector start node and end node could not be found";
0236         return nullptr;
0237     }
0238 
0239     if (!canConnect(startNode, endNode))
0240         return nullptr;
0241 
0242     Connector *connector = endNode->createInputConnector(startNode);
0243     if (!connector) {
0244         qCCritical(KTL_LOG) << "End node did not create the connector";
0245         return nullptr;
0246     }
0247     startNode->addOutputConnector(connector);
0248     flushDeleteList(); // Delete any connectors that might have been removed by the nodes
0249 
0250     // Set the route to the manual created one if the user created such a route
0251     if (pointList)
0252         connector->setRoutePoints(*pointList, true);
0253 
0254     // FIXME WTF is going on here? Redundant/meaningless code?
0255     ConnectorList connectorList;
0256     connectorList.append(connector);
0257 
0258     setModified(true);
0259 
0260     requestRerouteInvalidatedConnectors();
0261     return connector;
0262 }
0263 
0264 Node *FlowICNDocument::nodeWithID(const QString &id)
0265 {
0266     if (m_flowNodeList.contains(id))
0267         return m_flowNodeList[id];
0268     else
0269         return nullptr;
0270 }
0271 
0272 FPNode *FlowICNDocument::getFPnodeWithID(const QString &id)
0273 {
0274     if (m_flowNodeList.contains(id))
0275         return m_flowNodeList[id];
0276     else
0277         return nullptr;
0278 }
0279 
0280 void FlowICNDocument::slotAssignNodeGroups()
0281 {
0282     ICNDocument::slotAssignNodeGroups();
0283 
0284     const FPNodeMap::iterator end = m_flowNodeList.end();
0285     for (FPNodeMap::iterator it = m_flowNodeList.begin(); it != end; ++it) {
0286         NodeGroup *ng = createNodeGroup(*it);
0287         if (ng)
0288             ng->init();
0289     }
0290 
0291     // We've destroyed the old node groups, so any collapsed flowcontainers
0292     // containing new node groups need to update them to make them invisible.
0293     const ItemMap::const_iterator itemListEnd = m_itemList.end();
0294     for (ItemMap::const_iterator it = m_itemList.begin(); it != itemListEnd; ++it) {
0295         if (FlowContainer *fc = dynamic_cast<FlowContainer *>(*it))
0296             fc->updateContainedVisibility();
0297     }
0298 }
0299 
0300 void FlowICNDocument::flushDeleteList()
0301 {
0302     // Remove duplicate items in the delete list
0303     KtlQCanvasItemList::iterator end = m_itemDeleteList.end();
0304     for (KtlQCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it) {
0305         if (*it && (m_itemDeleteList.count(*it) > 1)) {
0306             *it = nullptr;
0307         }
0308     }
0309     m_itemDeleteList.removeAll(nullptr);
0310 
0311     end = m_itemDeleteList.end();
0312     for (KtlQCanvasItemList::iterator it = m_itemDeleteList.begin(); it != end; ++it) {
0313         KtlQCanvasItem *qcanvasItem = *it;
0314         m_selectList->removeQCanvasItem(*it);
0315 
0316         if (Item *item = dynamic_cast<Item *>(qcanvasItem))
0317             m_itemList.remove(item->id());
0318 
0319         else if (FPNode *node = dynamic_cast<FPNode *>(qcanvasItem))
0320             m_flowNodeList.remove(node->id());
0321 
0322         else if (Connector *con = dynamic_cast<Connector *>(qcanvasItem))
0323             m_connectorList.removeAll(con);
0324 
0325         else
0326             qCCritical(KTL_LOG) << "Unknown qcanvasItem! " << qcanvasItem;
0327 
0328         qcanvasItem->setCanvas(nullptr);
0329 
0330         delete qcanvasItem;
0331         *it = nullptr;
0332     }
0333 
0334     // Check connectors for merging
0335     bool doneJoin = false;
0336     const FPNodeMap::iterator nlEnd = m_flowNodeList.end();
0337     for (FPNodeMap::iterator it = m_flowNodeList.begin(); it != nlEnd; ++it) {
0338         (*it)->removeNullConnectors();
0339         int conCount = (*it)->getAllConnectors().count();
0340         if (conCount == 2 && !(*it)->parentItem()) {
0341             if (joinConnectors(*it))
0342                 doneJoin = true;
0343         }
0344     }
0345 
0346     if (doneJoin)
0347         flushDeleteList();
0348 
0349     requestRerouteInvalidatedConnectors();
0350 }
0351 
0352 bool FlowICNDocument::registerItem(KtlQCanvasItem *qcanvasItem)
0353 {
0354     if (!qcanvasItem)
0355         return false;
0356 
0357     if (!ItemDocument::registerItem(qcanvasItem)) {
0358         if (FPNode *node = dynamic_cast<FPNode *>(qcanvasItem)) {
0359             m_flowNodeList[node->id()] = node;
0360             emit nodeAdded(static_cast<Node *>(node));
0361         } else if (Connector *connector = dynamic_cast<Connector *>(qcanvasItem)) {
0362             m_connectorList.append(connector);
0363             emit connectorAdded(connector);
0364         } else {
0365             qCCritical(KTL_LOG) << "Unrecognised item";
0366             return false;
0367         }
0368     }
0369 
0370     requestRerouteInvalidatedConnectors();
0371 
0372     return true;
0373 }
0374 
0375 void FlowICNDocument::unregisterUID(const QString &uid)
0376 {
0377     m_flowNodeList.remove(uid);
0378     ICNDocument::unregisterUID(uid);
0379 }
0380 
0381 NodeList FlowICNDocument::nodeList() const
0382 {
0383     NodeList l;
0384 
0385     FPNodeMap::const_iterator end = m_flowNodeList.end();
0386     for (FPNodeMap::const_iterator it = m_flowNodeList.begin(); it != end; ++it)
0387         l << it.value();
0388 
0389     return l;
0390 }
0391 
0392 void FlowICNDocument::selectAllNodes()
0393 {
0394     const FPNodeMap::iterator nodeEnd = m_flowNodeList.end();
0395     for (FPNodeMap::iterator nodeIt = m_flowNodeList.begin(); nodeIt != nodeEnd; ++nodeIt) {
0396         if (*nodeIt)
0397             select(*nodeIt);
0398     }
0399 }
0400 
0401 bool FlowICNDocument::joinConnectors(FPNode *node)
0402 {
0403     // We don't want to destroy the node if it has a parent
0404     if (node->parentItem())
0405         return false;
0406 
0407     node->removeNullConnectors();
0408 
0409     int conCount = node->getAllConnectors().count();
0410     if (conCount != 2)
0411         return false;
0412 
0413     Connector *con1, *con2;
0414     Node *startNode, *endNode;
0415     QPointList conPoints;
0416 
0417     if (node->inputConnectorList().count() == 0) {
0418         // Both connectors emerge from node - output - i.e. node is pure start node
0419         con1 = node->outputConnectorList().at(0).data();
0420         con2 = node->outputConnectorList().at(1).data();
0421         if (con1 == con2) {
0422             return false;
0423         }
0424 
0425         startNode = con1->endNode();
0426         endNode = con2->endNode();
0427         conPoints = con1->connectorPoints(true) + con2->connectorPoints(false);
0428     } else if (node->inputConnectorList().count() == 1) {
0429         // Ont input, one output
0430         con1 = node->inputConnectorList().at(0).data();
0431         con2 = node->outputConnectorList().at(0).data();
0432         if (con1 == con2) {
0433             return false;
0434         }
0435 
0436         startNode = con1->startNode();
0437         endNode = con2->endNode();
0438         conPoints = con1->connectorPoints(false) + con2->connectorPoints(false);
0439     } else {
0440         // Both input - i.e. node is pure end node
0441         con1 = node->inputConnectorList().at(0).data();
0442         con2 = node->inputConnectorList().at(1).data();
0443         if (con1 == con2) {
0444             return false;
0445         }
0446 
0447         startNode = con1->startNode();
0448         endNode = con2->startNode();
0449         conPoints = con1->connectorPoints(false) + con2->connectorPoints(true);
0450     }
0451 
0452     if (!startNode || !endNode)
0453         return false;
0454 
0455     // HACK // FIXME // dynamic_cast used
0456     FPNode *startFpNode, *endFpNode;
0457     startFpNode = dynamic_cast<FPNode *>(startNode);
0458     endFpNode = dynamic_cast<FPNode *>(endNode);
0459 
0460     Connector *newCon = endFpNode->createInputConnector(startFpNode);
0461     if (!newCon)
0462         return false;
0463 
0464     startFpNode->addOutputConnector(newCon);
0465     newCon->setRoutePoints(conPoints, con1->usesManualPoints() || con2->usesManualPoints());
0466 
0467     // Avoid flicker: update draw lists now
0468     con1->updateConnectorPoints(false);
0469     con2->updateConnectorPoints(false);
0470     newCon->updateDrawList();
0471 
0472     node->removeNode();
0473     con1->removeConnectorNoArg();
0474     con2->removeConnectorNoArg();
0475 
0476     return true;
0477 }
0478 
0479 #include "moc_flowicndocument.cpp"