File indexing completed on 2024-06-16 11:34:23

0001 /***************************************************************************
0002  *   Copyright (C) 2003-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 "ecnode.h"
0012 #include "circuitdocument.h"
0013 #include "component.h"
0014 #include "connector.h"
0015 #include "electronicconnector.h"
0016 #include "pin.h"
0017 
0018 #include <QPainter>
0019 
0020 #include <ktlconfig.h>
0021 #include <ktechlab_debug.h>
0022 
0023 ECNode::ECNode(ICNDocument *icnDocument, Node::node_type _type, int dir, const QPoint &pos, QString *_id)
0024     : Node(icnDocument, _type, dir, pos, _id)
0025 {
0026     m_prevV = 0;
0027     m_prevI = 0;
0028     m_pinPoint = nullptr;
0029     m_bShowVoltageBars = KTLConfig::showVoltageBars();
0030     m_bShowVoltageColor = KTLConfig::showVoltageColor();
0031 
0032     if (icnDocument)
0033         icnDocument->registerItem(this);
0034 
0035     m_pins.resize(1);
0036     m_pins[0] = new Pin(this);
0037 }
0038 
0039 ECNode::~ECNode()
0040 {
0041     if (m_pinPoint) {
0042         m_pinPoint->setCanvas(nullptr);
0043         delete m_pinPoint;
0044     }
0045 
0046     qDeleteAll(m_pins);
0047     m_pins.resize(0);
0048 }
0049 
0050 void ECNode::setNumPins(unsigned num)
0051 {
0052     unsigned oldNum = m_pins.size();
0053 
0054     if (num == oldNum)
0055         return;
0056 
0057     if (num > oldNum) {
0058         m_pins.resize(num);
0059         for (unsigned i = oldNum; i < num; i++)
0060             m_pins[i] = new Pin(this);
0061     } else {
0062         for (unsigned i = num; i < oldNum; i++)
0063             delete m_pins[i];
0064         m_pins.resize(num);
0065     }
0066 
0067     emit numPinsChanged(num);
0068 }
0069 
0070 Pin *ECNode::pin(unsigned num) const
0071 {
0072     return (int(num) < m_pins.size()) ? m_pins[num] : nullptr;
0073 }
0074 
0075 void ECNode::setNodeChanged()
0076 {
0077     if (!canvas() || numPins() != 1)
0078         return;
0079 
0080     Pin *pin = m_pins[0];
0081 
0082     double v = pin->voltage();
0083     double i = pin->current();
0084 
0085     if (v != m_prevV || i != m_prevI) {
0086         QRect r = boundingRect();
0087         //      r.setCoords( r.left()+(r.width()/2)-1, r.top()+(r.height()/2)-1, r.right()-(r.width()/2)+1, r.bottom()-(r.height()/2)+1 );
0088         canvas()->setChanged(r);
0089         m_prevV = v;
0090         m_prevI = i;
0091     }
0092 }
0093 
0094 void ECNode::setParentItem(CNItem *parentItem)
0095 {
0096     Node::setParentItem(parentItem);
0097 
0098     if (Component *component = dynamic_cast<Component *>(parentItem)) {
0099         connect(component, SIGNAL(elementDestroyed(Element *)), this, SLOT(removeElement(Element *)));
0100         // TODO fix compiler error: invalid application of ‘sizeof’ to incomplete type ‘Element’
0101         // connect(component, &Component::elementDestroyed, this, &ECNode::removeElement);
0102         connect(component, SIGNAL(switchDestroyed(Switch *)), this, SLOT(removeSwitch(Switch *)));
0103         // TODO fix compiler error: invalid application of ‘sizeof’ to incomplete type ‘Switch’
0104         // connect(component, &Component::switchDestroyed, this, &ECNode::removeSwitch);
0105     }
0106 }
0107 
0108 void ECNode::removeElement(Element *e)
0109 {
0110     for (int i = 0; i < m_pins.size(); i++)
0111         m_pins[i]->removeElement(e);
0112 }
0113 
0114 void ECNode::removeSwitch(Switch *sw)
0115 {
0116     for (int i = 0; i < m_pins.size(); i++)
0117         m_pins[i]->removeSwitch(sw);
0118 }
0119 
0120 // -- functionality from node.cpp --
0121 
0122 bool ECNode::isConnected(Node *node, NodeList *checkedNodes)
0123 {
0124     if (this == node)
0125         return true;
0126 
0127     bool firstNode = !checkedNodes;
0128     if (firstNode)
0129         checkedNodes = new NodeList();
0130 
0131     else if (checkedNodes->contains(this))
0132         return false;
0133 
0134     checkedNodes->append(this);
0135 
0136     const ConnectorList::const_iterator inputEnd = m_connectorList.end();
0137     for (ConnectorList::const_iterator it = m_connectorList.begin(); it != inputEnd; ++it) {
0138         Connector *connector = *it;
0139         if (connector) {
0140             Node *startNode = connector->startNode();
0141             if (startNode && startNode->isConnected(node, checkedNodes)) {
0142                 if (firstNode) {
0143                     delete checkedNodes;
0144                 }
0145                 return true;
0146             }
0147         }
0148     }
0149 
0150     if (firstNode) {
0151         delete checkedNodes;
0152     }
0153 
0154     return false;
0155 }
0156 
0157 void ECNode::checkForRemoval(Connector *connector)
0158 {
0159     removeConnector(connector);
0160     setNodeSelected(false);
0161 
0162     removeNullConnectors();
0163 
0164     if (!p_parentItem) {
0165         int conCount = m_connectorList.count();
0166         if (conCount < 1)
0167             removeNode();
0168     }
0169 }
0170 
0171 void ECNode::setVisible(bool yes)
0172 {
0173     if (isVisible() == yes)
0174         return;
0175 
0176     KtlQCanvasPolygon::setVisible(yes);
0177 
0178     const ConnectorList::iterator inputEnd = m_connectorList.end();
0179     for (ConnectorList::iterator it = m_connectorList.begin(); it != inputEnd; ++it) {
0180         Connector *connector = *it;
0181         if (connector) {
0182             if (isVisible())
0183                 connector->setVisible(true);
0184             else {
0185                 Node *node = connector->startNode();
0186                 connector->setVisible(node && node->isVisible());
0187             }
0188         }
0189     }
0190 }
0191 
0192 QPoint ECNode::findConnectorDivergePoint(bool *found)
0193 {
0194     // FIXME someone should check that this function is OK ... I just don't understand what it does
0195     bool temp;
0196     if (!found)
0197         found = &temp;
0198     *found = false;
0199 
0200     if (numCon(false, false) != 2)
0201         return QPoint(0, 0);
0202 
0203     QPointList p1;
0204     QPointList p2;
0205 
0206     int inSize = m_connectorList.count();
0207 
0208     const ConnectorList connectors = m_connectorList;
0209     const ConnectorList::const_iterator end = connectors.end();
0210 
0211     bool gotP1 = false;
0212     bool gotP2 = false;
0213 
0214     int at = -1;
0215     for (ConnectorList::const_iterator it = connectors.begin(); it != end && !gotP2; ++it) {
0216         at++;
0217         if (!(*it) || !(*it)->canvas())
0218             continue;
0219 
0220         if (gotP1) {
0221             p2 = (*it)->connectorPoints(at < inSize);
0222             gotP2 = true;
0223         } else {
0224             p1 = (*it)->connectorPoints(at < inSize);
0225             gotP1 = true;
0226         }
0227     }
0228 
0229     if (!gotP1 || !gotP2)
0230         return QPoint(0, 0);
0231 
0232     // If they are differing lengths, return the end of the shortest
0233     if (p1.size() < p2.size()) {
0234         *found = true;
0235         return p1.last();
0236     } else if (p2.size() < p1.size()) {
0237         *found = true;
0238         return p2.last();
0239     }
0240 
0241     Q_ASSERT(p1.size() == p2.size());
0242 
0243     for (int i = 1; i < qMin(p1.size(), p2.size()); ++i) {
0244         if (p1[i] != p2[i]) {
0245             *found = true;
0246             return p1[i - 1];
0247         }
0248     }
0249     return QPoint(0, 0);
0250 }
0251 
0252 void ECNode::addConnector(Connector *const connector)
0253 {
0254     if (!handleNewConnector(connector))
0255         return;
0256 
0257     m_connectorList.append(connector);
0258 }
0259 
0260 bool ECNode::handleNewConnector(Connector *connector)
0261 {
0262     if (!connector)
0263         return false;
0264 
0265     if (m_connectorList.contains(connector)) {
0266         qCWarning(KTL_LOG) << " Already have connector = " << connector;
0267         return false;
0268     }
0269 
0270     connect(this, &ECNode::removed, connector, &Connector::removeConnectorNodeArg);
0271     connect(connector, &Connector::removed, this, &ECNode::checkForRemoval);
0272     connect(connector, &Connector::selected, this, &ECNode::setNodeSelected);
0273 
0274     if (!isChildNode())
0275         p_icnDocument->slotRequestAssignNG();
0276 
0277     return true;
0278 }
0279 
0280 Connector *ECNode::createConnector(Node *node)
0281 {
0282     // FIXME dynamic_cast used
0283     Connector *connector = new ElectronicConnector(dynamic_cast<ECNode *>(node), dynamic_cast<ECNode *>(this), p_icnDocument);
0284     addConnector(connector);
0285 
0286     return connector;
0287 }
0288 
0289 void ECNode::removeNullConnectors()
0290 {
0291     m_connectorList.removeAll(static_cast<Connector *>(nullptr));
0292 }
0293 
0294 int ECNode::numCon(bool includeParentItem, bool includeHiddenConnectors) const
0295 {
0296     unsigned count = 0;
0297 
0298     const ConnectorList connectors = m_connectorList;
0299 
0300     ConnectorList::const_iterator end = connectors.end();
0301     for (ConnectorList::const_iterator it = connectors.begin(); it != end; ++it) {
0302         if (*it && (includeHiddenConnectors || (*it)->canvas()))
0303             count++;
0304     }
0305 
0306     if (isChildNode() && includeParentItem)
0307         count++;
0308 
0309     return count;
0310 }
0311 
0312 void ECNode::removeConnector(Connector *connector)
0313 {
0314     if (!connector)
0315         return;
0316 
0317     ConnectorList::iterator it;
0318 
0319     // it = m_connectorList.find(connector); // 2018.12.02
0320     int i = m_connectorList.indexOf(connector);
0321     it = (i == -1 ? m_connectorList.end() : (m_connectorList.begin() + i));
0322     if (it != m_connectorList.end()) {
0323         (*it)->removeConnectorNoArg();
0324         (*it) = nullptr;
0325     }
0326 }
0327 
0328 Connector *ECNode::getAConnector() const
0329 {
0330     if (!m_connectorList.isEmpty())
0331         return *m_connectorList.begin();
0332     else
0333         return nullptr;
0334 }
0335 
0336 #include "moc_ecnode.cpp"