File indexing completed on 2024-12-01 05:13:02
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"