File indexing completed on 2024-04-21 09:34:45

0001 /***************************************************************************
0002  *   Copyright (C) 2005 by David Saxton                                    *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "icndocument.h"
0012 #include "canvasmanipulator.h"
0013 #include "cnitemgroup.h"
0014 #include "component.h"
0015 #include "connector.h"
0016 #include "conrouter.h"
0017 #include "ecnode.h"
0018 #include "flowcontainer.h"
0019 #include "fpnode.h"
0020 #include "icnview.h"
0021 #include "itemdocumentdata.h"
0022 #include "itemlibrary.h"
0023 #include "junctionflownode.h"
0024 #include "junctionnode.h"
0025 #include "ktechlab.h"
0026 #include "nodegroup.h"
0027 #include "outputflownode.h"
0028 #include "utils.h"
0029 
0030 #include <QApplication>
0031 #include <QClipboard>
0032 #include <QTimer>
0033 
0034 #include <ktechlab_debug.h>
0035 
0036 // BEGIN class ICNDocument
0037 ICNDocument::ICNDocument(const QString &caption)
0038     : ItemDocument(caption)
0039     , m_cells(nullptr)
0040 {
0041     m_canvas->retune(48);
0042     m_selectList = new CNItemGroup(this);
0043 
0044     createCellMap();
0045 
0046     m_cmManager->addManipulatorInfo(CMItemMove::manipulatorInfo());
0047     m_cmManager->addManipulatorInfo(CMAutoConnector::manipulatorInfo());
0048     m_cmManager->addManipulatorInfo(CMManualConnector::manipulatorInfo());
0049 }
0050 
0051 ICNDocument::~ICNDocument()
0052 {
0053     m_bDeleted = true;
0054 
0055     GuardedNodeGroupList ngToDelete = m_nodeGroupList;
0056     m_nodeGroupList.clear();
0057     qDeleteAll(ngToDelete);
0058 
0059     delete m_cells;
0060     delete m_selectList;
0061 }
0062 
0063 View *ICNDocument::createView(ViewContainer *viewContainer, uint viewAreaId)
0064 {
0065     ICNView *icnView = new ICNView(this, viewContainer, viewAreaId);
0066     handleNewView(icnView);
0067     return icnView;
0068 }
0069 
0070 ItemGroup *ICNDocument::selectList() const
0071 {
0072     return m_selectList;
0073 }
0074 
0075 void ICNDocument::fillContextMenu(const QPoint &pos)
0076 {
0077     ItemDocument::fillContextMenu(pos);
0078     slotInitItemActions();
0079 }
0080 
0081 CNItem *ICNDocument::cnItemWithID(const QString &id)
0082 {
0083     return dynamic_cast<CNItem *>(itemWithID(id));
0084 }
0085 
0086 Connector *ICNDocument::connectorWithID(const QString &id)
0087 {
0088     const ConnectorList::iterator end = m_connectorList.end();
0089     for (ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it) {
0090         if ((*it)->id() == id)
0091             return *it;
0092     }
0093     return nullptr;
0094 }
0095 
0096 FlowContainer *ICNDocument::flowContainer(const QPoint &pos)
0097 {
0098     KtlQCanvasItemList collisions = m_canvas->collisions(pos);
0099     FlowContainer *flowContainer = nullptr;
0100     int currentLevel = -1;
0101     const KtlQCanvasItemList::iterator end = collisions.end();
0102     for (KtlQCanvasItemList::iterator it = collisions.begin(); it != end; ++it) {
0103         if (FlowContainer *container = dynamic_cast<FlowContainer *>(*it)) {
0104             if (container->level() > currentLevel && !m_selectList->contains(container)) {
0105                 currentLevel = container->level();
0106                 flowContainer = container;
0107             }
0108         }
0109     }
0110 
0111     return flowContainer;
0112 }
0113 
0114 bool ICNDocument::canConnect(KtlQCanvasItem *qcanvasItem1, KtlQCanvasItem *qcanvasItem2) const
0115 {
0116     // Rough outline of what can and can't connect:
0117     // * At most three connectors to a node
0118     // * Can't have connectors going between different levels (e.g. can't have
0119     //   a connector coming outside a FlowContainer from inside).
0120     // * Can't have more than one route between any two nodes
0121     // * In all connections between nodes, must have at least one input and one
0122     //   output node at the ends.
0123 
0124     Node *startNode = dynamic_cast<Node *>(qcanvasItem1);
0125     Node *endNode = dynamic_cast<Node *>(qcanvasItem2);
0126 
0127     if ((startNode && startNode->numCon(true, false) > 2) || (endNode && endNode->numCon(true, false) > 2))
0128         return false;
0129 
0130     Connector *startConnector = dynamic_cast<Connector *>(qcanvasItem1);
0131     Connector *endConnector = dynamic_cast<Connector *>(qcanvasItem2);
0132 
0133     // FIXME: overload this instead of calling type().
0134     // Can't have T- or I- junction in PinMapEditor document
0135     if (type() == Document::dt_pinMapEditor && (startConnector || endConnector))
0136         return false;
0137 
0138     // Can't have I-junction in flowcode document
0139     if (type() == Document::dt_flowcode && startConnector && endConnector)
0140         return false;
0141 
0142     // BEGIN Change connectors to nodes
0143     Node *startNode1 = nullptr;
0144     Node *startNode2 = nullptr;
0145     if (startConnector) {
0146         startNode1 = startConnector->startNode();
0147         startNode2 = startConnector->endNode();
0148 
0149         if (!startNode1 || !startNode2)
0150             return false;
0151     } else if (!startNode)
0152         return false;
0153 
0154     Node *endNode1 = nullptr;
0155     Node *endNode2 = nullptr;
0156     if (endConnector) {
0157         endNode1 = endConnector->startNode();
0158         endNode2 = endConnector->endNode();
0159 
0160         if (!endNode1 || !endNode2)
0161             return false;
0162     } else if (!endNode)
0163         return false;
0164 
0165     Node *start[3];
0166     start[0] = startNode;
0167     start[1] = startNode1;
0168     start[2] = startNode2;
0169 
0170     Node *end[3];
0171     end[0] = endNode;
0172     end[1] = endNode1;
0173     end[2] = endNode2;
0174     // END Change connectors to nodes
0175 
0176     // BEGIN Check nodes aren't already connected
0177     for (unsigned i = 0; i < 3; i++) {
0178         for (unsigned j = 0; j < 3; j++) {
0179             if (start[i] && end[j] && start[i]->isConnected(end[j]))
0180                 return false;
0181         }
0182     }
0183     // END Check nodes aren't already connected together
0184 
0185     // BEGIN Simple level check
0186     for (unsigned i = 0; i < 3; i++) {
0187         for (unsigned j = 0; j < 3; j++) {
0188             if (start[i] && end[j] && start[i]->level() != end[j]->level())
0189                 return false;
0190         }
0191     }
0192     // END Simple level check
0193 
0194     // BEGIN Advanced level check
0195     CNItem *startParentItem[3];
0196     for (unsigned i = 0; i < 3; i++)
0197         startParentItem[i] = start[i] ? start[i]->parentItem() : nullptr;
0198 
0199     CNItem *endParentItem[3];
0200     for (unsigned i = 0; i < 3; i++)
0201         endParentItem[i] = end[i] ? end[i]->parentItem() : nullptr;
0202 
0203     Item *container[6] = {nullptr};
0204 
0205     for (unsigned i = 0; i < 3; i++) {
0206         if (startParentItem[i]) {
0207             int dl = start[i]->level() - startParentItem[i]->level();
0208             if (dl == 0)
0209                 container[i] = startParentItem[i]->parentItem();
0210             else if (dl == 1)
0211                 container[i] = startParentItem[i];
0212             else
0213                 qCCritical(KTL_LOG) << " start, i=" << i << " dl=" << dl;
0214         }
0215 
0216         if (endParentItem[i]) {
0217             int dl = end[i]->level() - endParentItem[i]->level();
0218             if (dl == 0)
0219                 container[i + 3] = endParentItem[i]->parentItem();
0220             else if (dl == 1)
0221                 container[i + 3] = endParentItem[i];
0222             else
0223                 qCCritical(KTL_LOG) << " end, i=" << i << " dl=" << dl;
0224         }
0225     }
0226 
0227     // Everything better well have the same container...
0228     for (unsigned i = 0; i < 6; ++i) {
0229         for (unsigned j = 0; j < i; ++j) {
0230             Node *n1 = i < 3 ? start[i] : end[i - 3];
0231             Node *n2 = j < 3 ? start[j] : end[j - 3];
0232             if (n1 && n2 && (container[i] != container[j]))
0233                 return false;
0234         }
0235     }
0236     // END Advanced level check
0237 
0238     // Well, it looks like we can, afterall, connect them...
0239     return true;
0240 }
0241 
0242 Connector *ICNDocument::createConnector(Node *startNode, Node *endNode, QPointList *pointList)
0243 {
0244     if (!canConnect(startNode, endNode))
0245         return nullptr;
0246 
0247     QPointList autoPoints;
0248     if (!pointList) {
0249         addAllItemConnectorPoints();
0250         ConRouter cr(this);
0251         cr.mapRoute(int(startNode->x()), int(startNode->y()), int(endNode->x()), int(endNode->y()));
0252         autoPoints = cr.pointList(false);
0253         pointList = &autoPoints;
0254     }
0255 
0256     Connector *con = nullptr;
0257 
0258     // Check if we need to swap the ends around, and create the connector
0259     // FIXME: dynamic_cast used
0260     if (dynamic_cast<OutputFlowNode *>(endNode) != nullptr)
0261         con = createConnector(endNode->id(), startNode->id(), pointList);
0262     else
0263         con = createConnector(startNode->id(), endNode->id(), pointList);
0264 
0265     bool startInGroup = deleteNodeGroup(startNode);
0266     bool endInGroup = deleteNodeGroup(endNode);
0267     if (startInGroup || endInGroup) {
0268         NodeGroup *ng = createNodeGroup(startNode);
0269         ng->addNode(endNode, true);
0270         ng->init();
0271     }
0272 
0273     flushDeleteList();
0274     return con;
0275 }
0276 
0277 NodeGroup *ICNDocument::createNodeGroup(Node *node)
0278 {
0279     if (!node || node->isChildNode())
0280         return nullptr;
0281 
0282     const GuardedNodeGroupList::iterator end = m_nodeGroupList.end();
0283     for (GuardedNodeGroupList::iterator it = m_nodeGroupList.begin(); it != end; ++it) {
0284         if (*it && (*it)->contains(node)) {
0285             return *it;
0286         }
0287     }
0288 
0289     NodeGroup *group = new NodeGroup(this);
0290     m_nodeGroupList += group;
0291     group->addNode(node, true);
0292 
0293     return group;
0294 }
0295 
0296 bool ICNDocument::deleteNodeGroup(Node *node)
0297 {
0298     if (!node || node->isChildNode())
0299         return false;
0300 
0301     const GuardedNodeGroupList::iterator end = m_nodeGroupList.end();
0302     for (GuardedNodeGroupList::iterator it = m_nodeGroupList.begin(); it != end; ++it) {
0303         if (*it && (*it)->contains(node)) {
0304             delete *it;
0305             m_nodeGroupList.erase(it);
0306             return true;
0307         }
0308     }
0309 
0310     return false;
0311 }
0312 
0313 void ICNDocument::slotRequestAssignNG()
0314 {
0315     requestEvent(ItemDocumentEvent::UpdateNodeGroups);
0316 }
0317 
0318 void ICNDocument::slotAssignNodeGroups()
0319 {
0320     const GuardedNodeGroupList::iterator nglEnd = m_nodeGroupList.end();
0321     for (GuardedNodeGroupList::iterator it = m_nodeGroupList.begin(); it != nglEnd; ++it)
0322         delete *it;
0323     m_nodeGroupList.clear();
0324 }
0325 
0326 void ICNDocument::getTranslatable(const ItemList &itemList, ConnectorList *fixedConnectors, ConnectorList *translatableConnectors, NodeGroupList *translatableNodeGroups)
0327 {
0328     ConnectorList tempCL1;
0329     if (!fixedConnectors)
0330         fixedConnectors = &tempCL1;
0331 
0332     ConnectorList tempCL2;
0333     if (!translatableConnectors)
0334         translatableConnectors = &tempCL2;
0335 
0336     NodeGroupList tempNGL;
0337     if (!translatableNodeGroups)
0338         translatableNodeGroups = &tempNGL;
0339 
0340     // We record the connectors attached to the items, and
0341     // the number of times an item in the list is connected to
0342     // it - i.e. 1 or 2. For those with 2, it is safe to update their
0343     // route as it simply involves shifting the route
0344     typedef QMap<Connector *, int> ConnectorMap;
0345     ConnectorMap fixedConnectorMap;
0346 
0347     // This list of nodes is built up, used for later in determining fixed NodeGroups
0348     NodeList itemNodeList;
0349 
0350     {
0351         const ItemList::const_iterator itemListEnd = itemList.end();
0352         for (ItemList::const_iterator it = itemList.begin(); it != itemListEnd; ++it) {
0353             CNItem *cnItem = dynamic_cast<CNItem *>(static_cast<Item *>(*it));
0354             if (!cnItem || !cnItem->canvas())
0355                 continue;
0356 
0357             NodeInfoMap nodeMap = cnItem->nodeMap();
0358 
0359             const NodeInfoMap::iterator nlEnd = nodeMap.end();
0360             for (NodeInfoMap::iterator nlIt = nodeMap.begin(); nlIt != nlEnd; ++nlIt) {
0361                 itemNodeList.append(nlIt.value().node);
0362             }
0363 
0364             ConnectorList conList = cnItem->connectorList();
0365             conList.removeAll(static_cast<Connector *>(nullptr));
0366 
0367             const ConnectorList::iterator clEnd = conList.end();
0368             for (ConnectorList::iterator clit = conList.begin(); clit != clEnd; ++clit) {
0369                 ConnectorMap::iterator cit = fixedConnectorMap.find(*clit);
0370                 if (cit != fixedConnectorMap.end()) {
0371                     cit.value()++;
0372                 } else
0373                     fixedConnectorMap[*clit] = 1;
0374             }
0375         }
0376     }
0377 
0378     // We now look through the NodeGroups to see if we have all the external
0379     // nodes for a given nodeGroup - if so, then the connectors in the fixed
0380     // connectors are ok to be moved
0381     ConnectorList fixedNGConnectors;
0382     {
0383         translatableNodeGroups->clear();
0384 
0385         const GuardedNodeGroupList::const_iterator end = m_nodeGroupList.end();
0386         for (GuardedNodeGroupList::const_iterator it = m_nodeGroupList.begin(); it != end; ++it) {
0387             NodeGroup *ng = *it;
0388             if (!ng)
0389                 continue;
0390 
0391             NodeList externalNodeList = ng->externalNodeList();
0392             const NodeList::iterator itemNodeListEnd = itemNodeList.end();
0393             for (NodeList::iterator inlIt = itemNodeList.begin(); inlIt != itemNodeListEnd; ++inlIt)
0394                 externalNodeList.removeAll(*inlIt);
0395 
0396             if (externalNodeList.isEmpty()) {
0397                 translatableNodeGroups->append(ng);
0398 
0399                 const ConnectorList ngConnectorList = ng->connectorList();
0400                 const ConnectorList::const_iterator ngConnectorListEnd = ngConnectorList.end();
0401                 for (ConnectorList::const_iterator ngclIt = ngConnectorList.begin(); ngclIt != ngConnectorListEnd; ++ngclIt) {
0402                     if (*ngclIt)
0403                         fixedNGConnectors += *ngclIt;
0404                 }
0405             }
0406         }
0407     }
0408 
0409     translatableConnectors->clear();
0410 
0411     const ConnectorMap::iterator fcEnd = fixedConnectorMap.end();
0412     for (ConnectorMap::iterator it = fixedConnectorMap.begin(); it != fcEnd; ++it) {
0413         // We allow it to be fixed if it is connected to two of the CNItems in the
0414         // select list, or is connected to itself (hence only appears to be connected to one,
0415         // but is fixed anyway
0416         Node *startNode = it.key()->endNode();
0417         Node *endNode = it.key()->startNode();
0418 
0419         if ((it.value() > 1) || (startNode && endNode && startNode->parentItem() == endNode->parentItem())) {
0420             translatableConnectors->append(const_cast<Connector *>(it.key()));
0421         } else if (!fixedNGConnectors.contains(it.key()) && !fixedConnectors->contains(it.key())) {
0422             fixedConnectors->append(it.key());
0423         }
0424     }
0425 }
0426 
0427 void ICNDocument::addCPenalty(int x, int y, int score)
0428 {
0429     if (m_cells->haveCell(x, y))
0430         m_cells->cell(x, y).Cpenalty += score;
0431 }
0432 
0433 void ICNDocument::createCellMap()
0434 {
0435     const ItemMap::iterator ciEnd = m_itemList.end();
0436     for (ItemMap::iterator it = m_itemList.begin(); it != ciEnd; ++it) {
0437         if (CNItem *cnItem = dynamic_cast<CNItem *>(*it))
0438             cnItem->updateConnectorPoints(false);
0439     }
0440 
0441     const ConnectorList::iterator conEnd = m_connectorList.end();
0442     for (ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it) {
0443         (*it)->updateConnectorPoints(false);
0444     }
0445 
0446     delete m_cells;
0447 
0448     m_cells = new Cells(canvas()->rect());
0449 
0450     for (ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it)
0451         (*it)->updateConnectorPoints(true);
0452 }
0453 
0454 int ICNDocument::gridSnap(int pos)
0455 {
0456     return snapToCanvas(pos);
0457 }
0458 
0459 QPoint ICNDocument::gridSnap(const QPoint &pos)
0460 {
0461     return QPoint(snapToCanvas(pos.x()), snapToCanvas(pos.y()));
0462 }
0463 
0464 void ICNDocument::appendDeleteList(KtlQCanvasItem *qcanvasItem)
0465 {
0466     if (!qcanvasItem || m_itemDeleteList.indexOf(qcanvasItem) != -1)
0467         return;
0468 
0469     m_itemDeleteList.append(qcanvasItem);
0470 
0471     /* the issue here is that we don't seem to have a generic call for all of these so we have to
0472     spend time figuring out which method to call...
0473     */
0474 
0475     if (Node *node = dynamic_cast<Node *>(qcanvasItem))
0476         node->removeNode();
0477     else if (Item *item = dynamic_cast<Item *>(qcanvasItem))
0478         item->removeItem();
0479     else {
0480         Connector *connector = dynamic_cast<Connector *>(qcanvasItem);
0481         if (!connector) {
0482             if (ConnectorLine *cl = dynamic_cast<ConnectorLine *>(qcanvasItem))
0483                 connector = cl->parent();
0484         }
0485 
0486         if (connector)
0487             connector->removeConnectorNoArg();
0488         else
0489             qCWarning(KTL_LOG) << "unrecognised KtlQCanvasItem " << qcanvasItem;
0490     }
0491 }
0492 
0493 bool ICNDocument::registerItem(KtlQCanvasItem *qcanvasItem)
0494 {
0495     if (!qcanvasItem)
0496         return false;
0497 
0498     if (!ItemDocument::registerItem(qcanvasItem)) {
0499         if (dynamic_cast<Node *>(qcanvasItem)) {
0500             /*
0501             m_nodeList[ node->id() ] = node;
0502             emit nodeAdded(node);
0503             */
0504             qCCritical(KTL_LOG) << "BUG: this member should have been overridden!";
0505 
0506         } else if (Connector *connector = dynamic_cast<Connector *>(qcanvasItem)) {
0507             m_connectorList.append(connector);
0508             emit connectorAdded(connector);
0509         } else {
0510             qCCritical(KTL_LOG) << "Unrecognised item";
0511             return false;
0512         }
0513     }
0514 
0515     requestRerouteInvalidatedConnectors();
0516 
0517     return true;
0518 }
0519 
0520 void ICNDocument::copy()
0521 {
0522     if (m_selectList->isEmpty())
0523         return;
0524 
0525     ItemDocumentData data(type());
0526 
0527     // We only want to copy the connectors who have all ends attached to something in the selection
0528     ConnectorList connectorList = m_selectList->connectors(false);
0529 
0530     typedef QMap<Node *, ConnectorList> NCLMap;
0531     NCLMap nclMap;
0532 
0533     ConnectorList::iterator end = connectorList.end();
0534     for (ConnectorList::iterator it = connectorList.begin(); it != end; ++it) {
0535         Node *startNode = (*it)->startNode();
0536         if (startNode && !startNode->isChildNode())
0537             nclMap[startNode].append(*it);
0538 
0539         Node *endNode = (*it)->endNode();
0540         if (endNode && !endNode->isChildNode())
0541             nclMap[endNode].append(*it);
0542     }
0543 
0544     NodeList nodeList;
0545     // Remove those connectors (and nodes) which are dangling on an orphan node
0546     NCLMap::iterator nclEnd = nclMap.end();
0547     for (NCLMap::iterator it = nclMap.begin(); it != nclEnd; ++it) {
0548         if (it.value().size() > 1)
0549             nodeList.append(it.key());
0550         else if (it.value().size() > 0)
0551             connectorList.removeAll(it.value().at(0));
0552     }
0553 
0554     data.addItems(m_selectList->items(false));
0555     data.addNodes(nodeList);
0556     data.addConnectors(connectorList);
0557 
0558     QApplication::clipboard()->setText(data.toXML(), QClipboard::Clipboard);
0559 }
0560 
0561 void ICNDocument::selectAll()
0562 {
0563     selectAllNodes();
0564 
0565     const ItemMap::iterator itemEnd = m_itemList.end();
0566     for (ItemMap::iterator itemIt = m_itemList.begin(); itemIt != itemEnd; ++itemIt) {
0567         if (*itemIt)
0568             select(*itemIt);
0569     }
0570 
0571     const ConnectorList::iterator conEnd = m_connectorList.end();
0572     for (ConnectorList::iterator connectorIt = m_connectorList.begin(); connectorIt != conEnd; ++connectorIt) {
0573         if (*connectorIt)
0574             select(*connectorIt);
0575     }
0576 }
0577 
0578 Item *ICNDocument::addItem(const QString &id, const QPoint &p, bool newItem)
0579 {
0580     if (!isValidItem(id))
0581         return nullptr;
0582 
0583     // First, we need to tell all containers to go to full bounding so that
0584     // we can detect a "collision" with them
0585     const ItemMap::iterator end = m_itemList.end();
0586     for (ItemMap::iterator it = m_itemList.begin(); it != end; ++it) {
0587         if (FlowContainer *flowContainer = dynamic_cast<FlowContainer *>(*it))
0588             flowContainer->setFullBounds(true);
0589     }
0590 
0591     KtlQCanvasItemList preCollisions = canvas()->collisions(p);
0592     for (ItemMap::iterator it = m_itemList.begin(); it != end; ++it) {
0593         if (FlowContainer *flowContainer = dynamic_cast<FlowContainer *>(*it))
0594             flowContainer->setFullBounds(false);
0595     }
0596 
0597     Item *item = itemLibrary()->createItem(id, this, newItem);
0598     if (!item)
0599         return nullptr;
0600 
0601     // Look through the CNItems at the given point (sorted by z-coordinate) for
0602     // a container item.
0603     FlowContainer *container = nullptr;
0604     const KtlQCanvasItemList::iterator pcEnd = preCollisions.end();
0605     for (KtlQCanvasItemList::iterator it = preCollisions.begin(); it != pcEnd && !container; ++it) {
0606         if (FlowContainer *flowContainer = dynamic_cast<FlowContainer *>(*it))
0607             container = flowContainer;
0608     }
0609 
0610     // We want to check it is not a special item first as
0611     // isValidItem may prompt the user about his bad choice
0612     if (!isValidItem(item)) {
0613         item->removeItem();
0614         flushDeleteList();
0615         return nullptr;
0616     }
0617 
0618     int x = int(p.x());
0619     int y = int(p.y());
0620 
0621     if (x < 16 || x > (canvas()->width()))
0622         x = 16;
0623     if (y < 16 || y > (canvas()->height()))
0624         y = 16;
0625 
0626     if (CNItem *cnItem = dynamic_cast<CNItem *>(item)) {
0627         cnItem->move(snapToCanvas(p.x()), snapToCanvas(p.y()));
0628 
0629         if (container)
0630             container->addChild(cnItem);
0631 
0632     } else
0633         item->move(x, y);
0634 
0635     item->show();
0636     requestStateSave();
0637     return item;
0638 }
0639 
0640 void ICNDocument::addAllItemConnectorPoints()
0641 {
0642     // FIXME The next line crashes sometimes??!
0643     const ItemMap::iterator ciEnd = m_itemList.end();
0644     for (ItemMap::iterator it = m_itemList.begin(); it != ciEnd; ++it) {
0645         if (CNItem *cnItem = dynamic_cast<CNItem *>(*it))
0646             cnItem->updateConnectorPoints(true);
0647     }
0648 }
0649 
0650 void ICNDocument::requestRerouteInvalidatedConnectors()
0651 {
0652     requestEvent(ItemDocumentEvent::RerouteInvalidatedConnectors);
0653 }
0654 
0655 void ICNDocument::rerouteInvalidatedConnectors()
0656 {
0657     // qApp->processEvents(QEventLoop::AllEvents, 300); // 2015.07.07 - do not process events, if it is not urgently needed; might generate crashes?
0658 
0659     // We only ever need to add the connector points for CNItem's when we're about to reroute...
0660     addAllItemConnectorPoints();
0661 
0662     // List of connectors which are to be determined to need rerouting (and whose routes aren't controlled by NodeGroups)
0663     ConnectorList connectorRerouteList;
0664 
0665     // For those connectors that are controlled by node groups
0666     NodeGroupList nodeGroupRerouteList;
0667 
0668     const ConnectorList::iterator connectorListEnd = m_connectorList.end();
0669     for (ConnectorList::iterator it = m_connectorList.begin(); it != connectorListEnd; ++it) {
0670         Connector *connector = *it;
0671         if (connector && connector->isVisible() && connector->startNode() && connector->endNode()) {
0672             // Perform a series of tests to see if the connector needs rerouting
0673             bool needsRerouting = false;
0674 
0675             // Test to see if we actually have any points
0676             const QPointList pointList = connector->connectorPoints();
0677 
0678             if (pointList.isEmpty())
0679                 needsRerouting = true;
0680 
0681             // Test to see if the route doesn't match up with the node positions at either end
0682             if (!needsRerouting) {
0683                 const QPoint listStart = pointList.first();
0684                 const QPoint listEnd = pointList.last();
0685                 const QPoint nodeStart = QPoint(int(connector->startNode()->x()), int(connector->startNode()->y()));
0686                 const QPoint nodeEnd = QPoint(int(connector->endNode()->x()), int(connector->endNode()->y()));
0687 
0688                 if (((listStart != nodeStart) || (listEnd != nodeEnd)) && ((listStart != nodeEnd) || (listEnd != nodeStart))) {
0689                     needsRerouting = true;
0690                 }
0691             }
0692 
0693             // Test to see if the route intersects any Items (we ignore if it is a manual route)
0694             if (!needsRerouting && !connector->usesManualPoints()) {
0695                 const KtlQCanvasItemList collisions = connector->collisions(true);
0696                 const KtlQCanvasItemList::const_iterator collisionsEnd = collisions.end();
0697                 for (KtlQCanvasItemList::const_iterator collisionsIt = collisions.begin(); (collisionsIt != collisionsEnd) && !needsRerouting; ++collisionsIt) {
0698                     if (dynamic_cast<Item *>(*collisionsIt))
0699                         needsRerouting = true;
0700                 }
0701             }
0702 
0703             if (needsRerouting) {
0704                 NodeGroup *nodeGroup = connector->nodeGroup();
0705 
0706                 if (!nodeGroup && !connectorRerouteList.contains(connector))
0707                     connectorRerouteList.append(connector);
0708                 else if (nodeGroup && !nodeGroupRerouteList.contains(nodeGroup))
0709                     nodeGroupRerouteList.append(nodeGroup);
0710             }
0711         }
0712     }
0713 
0714     // To allow proper rerouting, we want to start with clean routes for all of the invalidated connectors
0715     const NodeGroupList::iterator nodeGroupRerouteEnd = nodeGroupRerouteList.end();
0716     for (NodeGroupList::iterator it = nodeGroupRerouteList.begin(); it != nodeGroupRerouteEnd; ++it) {
0717         const ConnectorList contained = (*it)->connectorList();
0718         const ConnectorList::const_iterator end = contained.end();
0719         for (ConnectorList::const_iterator it = contained.begin(); it != end; ++it)
0720             (*it)->updateConnectorPoints(false);
0721     }
0722 
0723     const ConnectorList::iterator connectorRerouteEnd = connectorRerouteList.end();
0724     for (ConnectorList::iterator it = connectorRerouteList.begin(); it != connectorRerouteEnd; ++it)
0725         (*it)->updateConnectorPoints(false);
0726 
0727     // And finally, reroute the connectors
0728     for (NodeGroupList::iterator it = nodeGroupRerouteList.begin(); it != nodeGroupRerouteEnd; ++it)
0729         (*it)->updateRoutes();
0730 
0731     for (ConnectorList::iterator it = connectorRerouteList.begin(); it != connectorRerouteEnd; ++it)
0732         (*it)->rerouteConnector();
0733 
0734     for (ConnectorList::iterator it = m_connectorList.begin(); it != connectorListEnd; ++it) {
0735         if (*it)
0736             (*it)->updateDrawList();
0737     }
0738 }
0739 
0740 void ICNDocument::deleteSelection()
0741 {
0742     // End whatever editing mode we are in, as we don't want to start editing
0743     // something that is about to no longer exist...
0744     m_cmManager->cancelCurrentManipulation();
0745 
0746     if (m_selectList->isEmpty())
0747         return;
0748 
0749     m_selectList->deleteAllItems();
0750     flushDeleteList();
0751     setModified(true);
0752 
0753     // We need to emit this so that property widgets etc...
0754     // can clear themselves.
0755     emit selectionChanged();
0756 
0757     requestRerouteInvalidatedConnectors();
0758     requestStateSave();
0759 }
0760 
0761 ConnectorList ICNDocument::getCommonConnectors(const ItemList &list)
0762 {
0763     NodeList nodeList = getCommonNodes(list);
0764 
0765     // Now, get all the connectors, and remove the ones that don't have both end
0766     // nodes in the above generated list
0767     ConnectorList connectorList = m_connectorList;
0768     const ConnectorList::iterator connectorListEnd = connectorList.end();
0769     for (ConnectorList::iterator it = connectorList.begin(); it != connectorListEnd; ++it) {
0770         Connector *con = *it;
0771         if (!con || !nodeList.contains(con->startNode()) || !nodeList.contains(con->endNode())) {
0772             *it = nullptr;
0773         }
0774     }
0775     connectorList.removeAll(static_cast<Connector *>(nullptr));
0776     return connectorList;
0777 }
0778 
0779 NodeList ICNDocument::getCommonNodes(const ItemList &list)
0780 {
0781     NodeList nodeList;
0782 
0783     const ItemList::const_iterator listEnd = list.end();
0784     for (ItemList::const_iterator it = list.begin(); it != listEnd; ++it) {
0785         NodeInfoMap nodeMap;
0786         CNItem *cnItem = dynamic_cast<CNItem *>(static_cast<Item *>(*it));
0787 
0788         if (cnItem)
0789             nodeMap = cnItem->nodeMap();
0790 
0791         const NodeInfoMap::iterator nodeMapEnd = nodeMap.end();
0792         for (NodeInfoMap::iterator it = nodeMap.begin(); it != nodeMapEnd; ++it) {
0793             Node *node = it.value().node;
0794 
0795             if (!nodeList.contains(node))
0796                 nodeList += node;
0797 
0798             NodeGroup *ng = node->nodeGroup();
0799             if (ng) {
0800                 NodeList intNodeList = ng->internalNodeList();
0801                 const NodeList::iterator intNodeListEnd = intNodeList.end();
0802                 for (NodeList::iterator it = intNodeList.begin(); it != intNodeListEnd; ++it) {
0803                     Node *intNode = *it;
0804                     if (!nodeList.contains(intNode))
0805                         nodeList += intNode;
0806                 }
0807             }
0808         }
0809     }
0810 
0811     return nodeList;
0812 }
0813 
0814 void ICNDocument::unregisterUID(const QString &uid)
0815 {
0816     ItemDocument::unregisterUID(uid);
0817 }
0818 
0819 // END class ICNDocument
0820 
0821 DirCursor *DirCursor::m_self = nullptr;
0822 
0823 DirCursor::DirCursor()
0824 {
0825     initCursors();
0826 }
0827 
0828 DirCursor::~DirCursor()
0829 {
0830 }
0831 
0832 DirCursor *DirCursor::self()
0833 {
0834     if (!m_self)
0835         m_self = new DirCursor;
0836     return m_self;
0837 }
0838 
0839 void DirCursor::initCursors()
0840 {
0841     //  QCursor c(Qt::ArrowCursor);
0842     //  QBitmap bitmap = *c.bitmap();
0843     //  QBitmap mask = *c.mask();
0844     //  QPixmap pm( bitmap->width(), bitmap->height() );
0845     //  pm.setMask(mask);
0846     //  pm = c.pi
0847 }
0848 
0849 #include "moc_icndocument.cpp"