File indexing completed on 2024-05-19 13:32:59

0001 /***************************************************************************
0002  *   Copyright (C) 2003-2004 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 "cnitemgroup.h"
0012 #include "component.h"
0013 #include "connector.h"
0014 #include "flowpart.h"
0015 #include "icndocument.h"
0016 #include "node.h"
0017 #include "nodegroup.h"
0018 
0019 CNItemGroup::CNItemGroup(ICNDocument *icnDocument)
0020     : ItemGroup(icnDocument)
0021 {
0022     p_icnDocument = icnDocument;
0023     m_connectorCount = 0;
0024     m_nodeCount = 0;
0025     m_currentLevel = -1;
0026 }
0027 
0028 CNItemGroup::~CNItemGroup()
0029 {
0030 }
0031 
0032 bool CNItemGroup::addItem(Item *item)
0033 {
0034     // Note, we must prepend the item to the list so that
0035     // activeCNItem() can return the item at the start
0036     // of the list as the most recently added item if some
0037     // the previous activeCNItem is removed
0038 
0039     if (!item || !item->canvas() || m_itemList.contains(item) || !item->isMovable())
0040         return false;
0041 
0042     if (m_currentLevel != -1 && item->level() > m_currentLevel)
0043         return false;
0044 
0045     if (item && m_currentLevel > item->level())
0046         removeAllItems();
0047 
0048     registerItem(item);
0049     m_currentLevel = item->level();
0050     setActiveItem(item);
0051     item->setSelected(true);
0052     updateInfo();
0053     emit itemAdded(item);
0054     return true;
0055 }
0056 
0057 bool CNItemGroup::addNode(Node *node)
0058 {
0059     if (!node || m_nodeList.contains(node) || node->isChildNode())
0060         return false;
0061     m_nodeList.prepend(node);
0062     node->setSelected(true);
0063     updateInfo();
0064     emit nodeAdded(node);
0065     return true;
0066 }
0067 
0068 bool CNItemGroup::addConnector(Connector *con)
0069 {
0070     if (!con || m_connectorList.contains(con))
0071         return false;
0072     m_connectorList.prepend(con);
0073     con->setSelected(true);
0074     updateInfo();
0075     emit connectorAdded(con);
0076     return true;
0077 }
0078 
0079 bool CNItemGroup::addQCanvasItem(KtlQCanvasItem *qcanvasItem)
0080 {
0081     if (!qcanvasItem)
0082         return false;
0083 
0084     Item *item = dynamic_cast<Item *>(qcanvasItem);
0085     if (item)
0086         return addItem(item);
0087 
0088     Node *node = dynamic_cast<Node *>(qcanvasItem);
0089     if (node)
0090         return addNode(node);
0091 
0092     Connector *connector = dynamic_cast<Connector *>(qcanvasItem);
0093     if (!connector) {
0094         ConnectorLine *connectorLine = dynamic_cast<ConnectorLine *>(qcanvasItem);
0095         if (connectorLine)
0096             connector = connectorLine->parent();
0097     }
0098     if (connector)
0099         return addConnector(connector);
0100 
0101     return false;
0102 }
0103 
0104 void CNItemGroup::setItems(KtlQCanvasItemList list)
0105 {
0106     ItemList itemRemoveList = m_itemList;
0107     ConnectorList connectorRemoveList = m_connectorList;
0108     NodeList nodeRemoveList = m_nodeList;
0109 
0110     const KtlQCanvasItemList::const_iterator end = list.end();
0111     for (KtlQCanvasItemList::const_iterator it = list.begin(); it != end; ++it) {
0112         if (Item *item = dynamic_cast<Item *>(*it))
0113             itemRemoveList.removeAll(item);
0114 
0115         else if (Node *node = dynamic_cast<Node *>(*it))
0116             nodeRemoveList.removeAll(node);
0117 
0118         else if (Connector *con = dynamic_cast<Connector *>(*it))
0119             connectorRemoveList.removeAll(con);
0120 
0121         else if (ConnectorLine *conLine = dynamic_cast<ConnectorLine *>(*it))
0122             connectorRemoveList.removeAll(conLine->parent());
0123     }
0124 
0125     {
0126         const ItemList::const_iterator end = itemRemoveList.end();
0127         for (ItemList::const_iterator it = itemRemoveList.begin(); it != end; ++it) {
0128             removeItem(*it);
0129             (*it)->setSelected(false);
0130         }
0131     }
0132 
0133     {
0134         const NodeList::const_iterator end = nodeRemoveList.end();
0135         for (NodeList::const_iterator it = nodeRemoveList.begin(); it != end; ++it) {
0136             removeNode(*it);
0137             (*it)->setSelected(false);
0138         }
0139     }
0140 
0141     {
0142         const ConnectorList::const_iterator end = connectorRemoveList.end();
0143         for (ConnectorList::const_iterator it = connectorRemoveList.begin(); it != end; ++it) {
0144             removeConnector(*it);
0145             (*it)->setSelected(false);
0146         }
0147     }
0148 
0149     {
0150         const KtlQCanvasItemList::const_iterator end = list.end();
0151         for (KtlQCanvasItemList::const_iterator it = list.begin(); it != end; ++it) {
0152             // We don't need to check that we've already got the item as it will
0153             // be checked in the function call
0154             addQCanvasItem(*it);
0155         }
0156     }
0157 }
0158 
0159 void CNItemGroup::removeItem(Item *item)
0160 {
0161     if (!item || !m_itemList.contains(item))
0162         return;
0163     unregisterItem(item);
0164     if (m_activeItem == item)
0165         getActiveItem();
0166 
0167     item->setSelected(false);
0168     updateInfo();
0169     emit itemRemoved(item);
0170 }
0171 
0172 void CNItemGroup::removeNode(Node *node)
0173 {
0174     if (!node || !m_nodeList.contains(node))
0175         return;
0176     m_nodeList.removeAll(node);
0177     node->setSelected(false);
0178     updateInfo();
0179     emit nodeRemoved(node);
0180 }
0181 
0182 void CNItemGroup::removeConnector(Connector *con)
0183 {
0184     if (!con || !m_connectorList.contains(con))
0185         return;
0186     m_connectorList.removeAll(con);
0187     con->setSelected(false);
0188     updateInfo();
0189     emit connectorRemoved(con);
0190 }
0191 
0192 void CNItemGroup::removeQCanvasItem(KtlQCanvasItem *qcanvasItem)
0193 {
0194     if (!qcanvasItem)
0195         return;
0196 
0197     Item *item = dynamic_cast<Item *>(qcanvasItem);
0198     if (item)
0199         return removeItem(item);
0200 
0201     Node *node = dynamic_cast<Node *>(qcanvasItem);
0202     if (node)
0203         return removeNode(node);
0204 
0205     Connector *connector = dynamic_cast<Connector *>(qcanvasItem);
0206     if (!connector) {
0207         ConnectorLine *connectorLine = dynamic_cast<ConnectorLine *>(qcanvasItem);
0208         if (connectorLine)
0209             connector = connectorLine->parent();
0210     }
0211     if (connector)
0212         return removeConnector(connector);
0213 }
0214 
0215 NodeList CNItemGroup::nodes(bool excludeParented) const
0216 {
0217     NodeList nodeList = m_nodeList;
0218     if (excludeParented)
0219         return nodeList;
0220 
0221     NodeGroupList translatableNodeGroups;
0222     p_icnDocument->getTranslatable(items(false), nullptr, nullptr, &translatableNodeGroups);
0223 
0224     NodeGroupList::iterator end = translatableNodeGroups.end();
0225     for (NodeGroupList::iterator it = translatableNodeGroups.begin(); it != end; ++it) {
0226         const NodeList internal = (*it)->internalNodeList();
0227         NodeList::const_iterator internalEnd = internal.end();
0228         for (NodeList::const_iterator intIt = internal.begin(); intIt != internalEnd; ++intIt) {
0229             if (*intIt && !nodeList.contains(*intIt))
0230                 nodeList << *intIt;
0231         }
0232     }
0233 
0234     return nodeList;
0235 }
0236 
0237 ConnectorList CNItemGroup::connectors(bool excludeParented) const
0238 {
0239     ConnectorList connectorList = m_connectorList;
0240     if (excludeParented)
0241         return connectorList;
0242 
0243     ConnectorList translatableConnectors;
0244     NodeGroupList translatableNodeGroups;
0245     p_icnDocument->getTranslatable(items(false), nullptr, &translatableConnectors, &translatableNodeGroups);
0246 
0247     ConnectorList::iterator tcEnd = translatableConnectors.end();
0248     for (ConnectorList::iterator it = translatableConnectors.begin(); it != tcEnd; ++it) {
0249         if (*it && !connectorList.contains(*it))
0250             connectorList << *it;
0251     }
0252 
0253     NodeGroupList::iterator end = translatableNodeGroups.end();
0254     for (NodeGroupList::iterator it = translatableNodeGroups.begin(); it != end; ++it) {
0255         const NodeList internal = (*it)->internalNodeList();
0256         NodeList::const_iterator internalEnd = internal.end();
0257         for (NodeList::const_iterator intIt = internal.begin(); intIt != internalEnd; ++intIt) {
0258             const ConnectorList connected = (*intIt)->getAllConnectors();
0259             ConnectorList::const_iterator connectedEnd = connected.end();
0260             for (ConnectorList::const_iterator conIt = connected.begin(); conIt != connectedEnd; ++conIt) {
0261                 if (*conIt && !connectorList.contains(*conIt))
0262                     connectorList << *conIt;
0263             }
0264         }
0265     }
0266 
0267     return connectorList;
0268 }
0269 
0270 bool CNItemGroup::contains(KtlQCanvasItem *qcanvasItem) const
0271 {
0272     if (!qcanvasItem)
0273         return false;
0274 
0275     const ItemList::const_iterator ciEnd = m_itemList.end();
0276     for (ItemList::const_iterator it = m_itemList.begin(); it != ciEnd; ++it) {
0277         if (*it == qcanvasItem)
0278             return true;
0279     }
0280     const ConnectorList::const_iterator conEnd = m_connectorList.end();
0281     for (ConnectorList::const_iterator it = m_connectorList.begin(); it != conEnd; ++it) {
0282         if (*it == qcanvasItem)
0283             return true;
0284     }
0285     const NodeList::const_iterator nodeEnd = m_nodeList.end();
0286     for (NodeList::const_iterator it = m_nodeList.begin(); it != nodeEnd; ++it) {
0287         if (*it == qcanvasItem)
0288             return true;
0289     }
0290 
0291     return false;
0292 }
0293 
0294 void CNItemGroup::setSelected(bool sel)
0295 {
0296     const ItemList::iterator ciEnd = m_itemList.end();
0297     for (ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it) {
0298         if (*it && (*it)->isSelected() != sel)
0299             (*it)->setSelected(sel);
0300     }
0301     const ConnectorList::iterator conEnd = m_connectorList.end();
0302     for (ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it) {
0303         if (*it && (*it)->isSelected() != sel)
0304             (*it)->setSelected(sel);
0305     }
0306     const NodeList::iterator nodeEnd = m_nodeList.end();
0307     for (NodeList::iterator it = m_nodeList.begin(); it != nodeEnd; ++it) {
0308         if (*it && (*it)->isSelected() != sel)
0309             (*it)->setSelected(sel);
0310     }
0311 }
0312 
0313 bool CNItemGroup::canRotate() const
0314 {
0315     const ItemList::const_iterator end = m_itemList.end();
0316     for (ItemList::const_iterator it = m_itemList.begin(); it != end; ++it) {
0317         // Components can rotate
0318         if (dynamic_cast<Component *>(static_cast<Item *>(*it)))
0319             return true;
0320     }
0321     return false;
0322 }
0323 
0324 bool CNItemGroup::canFlip() const
0325 {
0326     const ItemList::const_iterator end = m_itemList.end();
0327     for (ItemList::const_iterator it = m_itemList.begin(); it != end; ++it) {
0328         // Components can flip
0329         if (dynamic_cast<Component *>(static_cast<Item *>(*it)))
0330             return true;
0331     }
0332     return false;
0333 }
0334 
0335 void CNItemGroup::slotRotateCW()
0336 {
0337     const ItemList::iterator end = m_itemList.end();
0338     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it) {
0339         Component *component = dynamic_cast<Component *>(static_cast<Item *>(*it));
0340         if (component && component->isMovable()) {
0341             int oldAngle = component->angleDegrees();
0342             component->setAngleDegrees(oldAngle + 90);
0343         }
0344     }
0345     p_icnDocument->requestStateSave();
0346 }
0347 
0348 void CNItemGroup::slotRotateCCW()
0349 {
0350     const ItemList::iterator end = m_itemList.end();
0351     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it) {
0352         Component *component = dynamic_cast<Component *>(static_cast<Item *>(*it));
0353         if (component && component->isMovable()) {
0354             int oldAngle = component->angleDegrees();
0355             component->setAngleDegrees(oldAngle - 90);
0356         }
0357     }
0358     p_icnDocument->requestStateSave();
0359 }
0360 
0361 void CNItemGroup::flipHorizontally()
0362 {
0363     const ItemList::iterator end = m_itemList.end();
0364     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it) {
0365         Component *component = dynamic_cast<Component *>(static_cast<Item *>(*it));
0366         if (component && component->isMovable()) {
0367             bool oldFlipped = component->flipped();
0368             component->setFlipped(!oldFlipped);
0369         }
0370     }
0371     p_icnDocument->requestStateSave();
0372 }
0373 
0374 void CNItemGroup::flipVertically()
0375 {
0376     const ItemList::iterator end = m_itemList.end();
0377     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it) {
0378         Component *component = dynamic_cast<Component *>(static_cast<Item *>(*it));
0379         if (component && component->isMovable()) {
0380             bool oldFlipped = component->flipped();
0381 
0382             int oldAngle = component->angleDegrees();
0383             component->setAngleDegrees(oldAngle + 180);
0384             component->setFlipped(!oldFlipped);
0385             component->setAngleDegrees(oldAngle + 180);
0386         }
0387     }
0388     p_icnDocument->requestStateSave();
0389 }
0390 
0391 bool CNItemGroup::haveSameOrientation() const
0392 {
0393     // set true once determined what is in this itemgroup
0394     bool areFlowparts = false;
0395     bool areComponents = false;
0396 
0397     // for components
0398     int angleDegrees = 0;
0399     bool flipped = false;
0400 
0401     // for flowparts
0402     unsigned orientation = 0;
0403 
0404     const ItemList::const_iterator end = m_itemList.end();
0405     for (ItemList::const_iterator it = m_itemList.begin(); it != end; ++it) {
0406         Component *component = dynamic_cast<Component *>(static_cast<Item *>(*it));
0407         FlowPart *flowpart = dynamic_cast<FlowPart *>(static_cast<Item *>(*it));
0408 
0409         if (component && flowpart)
0410             return false;
0411 
0412         if (!component && !flowpart)
0413             return false;
0414 
0415         if (component) {
0416             if (areFlowparts)
0417                 return false;
0418 
0419             if (!areComponents) {
0420                 // It's the first component we've come across
0421                 angleDegrees = component->angleDegrees();
0422                 flipped = component->flipped();
0423                 areComponents = true;
0424             } else {
0425                 if (angleDegrees != component->angleDegrees())
0426                     return false;
0427 
0428                 if (flipped != component->flipped())
0429                     return false;
0430             }
0431         } else {
0432             if (areComponents)
0433                 return false;
0434 
0435             if (!areFlowparts) {
0436                 // It's the first flowpart we've come across
0437                 orientation = flowpart->orientation();
0438                 areFlowparts = true;
0439             } else {
0440                 if (orientation != flowpart->orientation())
0441                     return false;
0442             }
0443         }
0444     }
0445 
0446     return true;
0447 }
0448 
0449 void CNItemGroup::setOrientationAngle(int _angle)
0450 {
0451     const ItemList::iterator end = m_itemList.end();
0452     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it) {
0453         Component *component = dynamic_cast<Component *>(static_cast<Item *>(*it));
0454         if (component && component->isMovable()) {
0455             int oldAngle = component->angleDegrees();
0456             if (oldAngle != _angle) {
0457                 component->setAngleDegrees(_angle);
0458             }
0459         }
0460     }
0461     p_icnDocument->requestStateSave();
0462 }
0463 
0464 void CNItemGroup::setComponentOrientation(int angleDegrees, bool flipped)
0465 {
0466     const ItemList::iterator end = m_itemList.end();
0467     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it) {
0468         Component *component = dynamic_cast<Component *>(static_cast<Item *>(*it));
0469         if (component && component->isMovable()) {
0470             int oldAngle = component->angleDegrees();
0471             int oldFlipped = component->flipped();
0472             if ((oldAngle != angleDegrees) || (oldFlipped != flipped)) {
0473                 component->setFlipped(flipped);
0474                 component->setAngleDegrees(angleDegrees);
0475             }
0476         }
0477     }
0478     p_icnDocument->requestStateSave();
0479 }
0480 
0481 void CNItemGroup::setFlowPartOrientation(unsigned orientation)
0482 {
0483     const ItemList::iterator end = m_itemList.end();
0484     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it) {
0485         FlowPart *flowPart = dynamic_cast<FlowPart *>(static_cast<Item *>(*it));
0486         if (flowPart && flowPart->isMovable())
0487             flowPart->setOrientation(orientation);
0488     }
0489     p_icnDocument->requestStateSave();
0490 }
0491 
0492 void CNItemGroup::mergeGroup(ItemGroup *itemGroup)
0493 {
0494     CNItemGroup *group = dynamic_cast<CNItemGroup *>(itemGroup);
0495     if (!group)
0496         return;
0497 
0498     const ItemList items = group->items();
0499     const ConnectorList connectors = group->connectors();
0500     const NodeList nodes = group->nodes();
0501 
0502     const ItemList::const_iterator ciEnd = items.end();
0503     for (ItemList::const_iterator it = items.begin(); it != ciEnd; ++it) {
0504         addItem(*it);
0505     }
0506     const ConnectorList::const_iterator conEnd = connectors.end();
0507     for (ConnectorList::const_iterator it = connectors.begin(); it != conEnd; ++it) {
0508         addConnector(*it);
0509     }
0510     const NodeList::const_iterator nodeEnd = nodes.end();
0511     for (NodeList::const_iterator it = nodes.begin(); it != nodeEnd; ++it) {
0512         addNode(*it);
0513     }
0514 }
0515 
0516 void CNItemGroup::removeAllItems()
0517 {
0518     while (!m_itemList.isEmpty())
0519         removeItem(*m_itemList.begin());
0520 
0521     while (!m_connectorList.isEmpty())
0522         removeConnector(*m_connectorList.begin());
0523 
0524     while (!m_nodeList.isEmpty())
0525         removeNode(*m_nodeList.begin());
0526 }
0527 
0528 void CNItemGroup::deleteAllItems()
0529 {
0530     const ItemList::iterator ciEnd = m_itemList.end();
0531     for (ItemList::iterator it = m_itemList.begin(); it != ciEnd; ++it) {
0532         if (*it)
0533             (*it)->removeItem();
0534     }
0535     const NodeList::iterator nodeEnd = m_nodeList.end();
0536     for (NodeList::iterator it = m_nodeList.begin(); it != nodeEnd; ++it) {
0537         if (*it && !(*it)->isChildNode()) {
0538             (*it)->removeNode();
0539         }
0540     }
0541     const ConnectorList::iterator conEnd = m_connectorList.end();
0542     for (ConnectorList::iterator it = m_connectorList.begin(); it != conEnd; ++it) {
0543         if (*it) {
0544             (*it)->removeConnectorNoArg();
0545         }
0546     }
0547 
0548     // Clear the lists
0549     removeAllItems();
0550 }
0551 
0552 void CNItemGroup::updateInfo()
0553 {
0554     m_connectorCount = m_connectorList.count();
0555     m_nodeCount = m_nodeList.count();
0556 
0557     if (m_itemList.isEmpty())
0558         m_currentLevel = -1;
0559 }
0560 
0561 void CNItemGroup::getActiveItem()
0562 {
0563     if (m_itemList.isEmpty())
0564         setActiveItem(nullptr);
0565     else
0566         setActiveItem(*m_itemList.begin());
0567 }
0568 
0569 void CNItemGroup::setActiveItem(Item *item)
0570 {
0571     if (item == m_activeItem)
0572         return;
0573     m_activeItem = item;
0574 }
0575 
0576 QStringList CNItemGroup::itemIDs()
0577 {
0578     QStringList list;
0579     ItemList::iterator end = m_itemList.end();
0580     for (ItemList::iterator it = m_itemList.begin(); it != end; ++it) {
0581         if (*it) {
0582             list += (*it)->id();
0583         }
0584     }
0585     return list;
0586 }
0587 
0588 #include "moc_cnitemgroup.cpp"