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

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 "circuitdocument.h"
0012 #include "canvasmanipulator.h"
0013 #include "circuiticndocument.h"
0014 #include "circuitview.h"
0015 #include "cnitemgroup.h"
0016 #include "component.h"
0017 #include "connector.h"
0018 #include "documentiface.h"
0019 #include "drawparts/drawpart.h"
0020 #include "ecnode.h"
0021 #include "itemdocumentdata.h"
0022 #include "ktechlab.h"
0023 #include "pin.h"
0024 #include "simulator.h"
0025 #include "subcircuits.h"
0026 #include "switch.h"
0027 
0028 #include <KActionMenu>
0029 #include <KLocalizedString>
0030 #include <KMessageBox>
0031 #include <KToggleAction>
0032 
0033 #include <QInputDialog>
0034 #include <QRegExp>
0035 #include <QTimer>
0036 
0037 #include <ktlconfig.h>
0038 #include <ktechlab_debug.h>
0039 
0040 CircuitDocument::CircuitDocument(const QString &caption)
0041     : CircuitICNDocument(caption)
0042 {
0043     m_pOrientationAction = new KActionMenu(QIcon::fromTheme("transform-rotate"), i18n("Orientation"), this);
0044 
0045     m_type = Document::dt_circuit;
0046     m_pDocumentIface = new CircuitDocumentIface(this);
0047     m_fileExtensionInfo = QString("*.circuit|%1(*.circuit)\n*|%2").arg(i18n("Circuit")).arg(i18n("All Files"));
0048     m_fileExtensionValue = QString(".circuit");
0049 
0050     m_cmManager->addManipulatorInfo(CMSelect::manipulatorInfo());
0051     m_cmManager->addManipulatorInfo(CMDraw::manipulatorInfo());
0052     m_cmManager->addManipulatorInfo(CMRightClick::manipulatorInfo());
0053     m_cmManager->addManipulatorInfo(CMRepeatedItemAdd::manipulatorInfo());
0054     m_cmManager->addManipulatorInfo(CMItemResize::manipulatorInfo());
0055     m_cmManager->addManipulatorInfo(CMItemDrag::manipulatorInfo());
0056 
0057     connect(this, &CircuitDocument::connectorAdded, this, &CircuitDocument::requestAssignCircuits);
0058     connect(this, &CircuitDocument::connectorAdded, this, &CircuitDocument::connectorAddedSlot);
0059 
0060     m_updateCircuitsTmr = new QTimer();
0061     connect(m_updateCircuitsTmr, &QTimer::timeout, this, &CircuitDocument::assignCircuits);
0062 
0063     requestStateSave();
0064 }
0065 
0066 CircuitDocument::~CircuitDocument()
0067 {
0068     m_bDeleted = true;
0069 
0070     disconnect(m_updateCircuitsTmr, &QTimer::timeout, this, &CircuitDocument::assignCircuits);
0071     disconnect(this, &CircuitDocument::connectorAdded, this, &CircuitDocument::connectorAddedSlot);
0072     disconnect(this, &CircuitDocument::connectorAdded, this, &CircuitDocument::requestAssignCircuits);
0073 
0074     for (ConnectorList::Iterator itConn = m_connectorList.begin(); itConn != m_connectorList.end(); ++itConn) {
0075         Connector *connector = itConn->data();
0076         disconnect(connector, &Connector::removed, this, &CircuitDocument::requestAssignCircuits);
0077     }
0078     for (ItemMap::Iterator itItem = m_itemList.begin(); itItem != m_itemList.end(); ++itItem) {
0079         Item *item = itItem.value();
0080         disconnect(item, &Item::removed, this, &CircuitDocument::componentRemoved);
0081         Component *comp = dynamic_cast<Component *>(item);
0082         if (comp) {
0083             disconnect(comp, &Component::elementDestroyed, this, &CircuitDocument::requestAssignCircuits);
0084         }
0085     }
0086 
0087     deleteCircuits();
0088 
0089     delete m_updateCircuitsTmr;
0090     delete m_pDocumentIface;
0091 }
0092 
0093 void CircuitDocument::slotInitItemActions()
0094 {
0095     CircuitICNDocument::slotInitItemActions();
0096 
0097     CircuitView *activeCircuitView = dynamic_cast<CircuitView *>(activeView());
0098 
0099     if (!KTechlab::self() || !activeCircuitView)
0100         return;
0101 
0102     Component *item = dynamic_cast<Component *>(m_selectList->activeItem());
0103 
0104     if ((!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType())
0105         return;
0106 
0107     QAction *orientation_actions[] = {
0108         activeCircuitView->actionByName("edit_orientation_0"), activeCircuitView->actionByName("edit_orientation_90"), activeCircuitView->actionByName("edit_orientation_180"), activeCircuitView->actionByName("edit_orientation_270")};
0109 
0110     if (!item) {
0111         for (unsigned i = 0; i < 4; ++i)
0112             orientation_actions[i]->setEnabled(false);
0113         return;
0114     }
0115 
0116     for (unsigned i = 0; i < 4; ++i) {
0117         orientation_actions[i]->setEnabled(true);
0118         m_pOrientationAction->removeAction(orientation_actions[i]);
0119         m_pOrientationAction->addAction(orientation_actions[i]);
0120     }
0121 
0122     if (item->angleDegrees() == 0)
0123         (static_cast<KToggleAction *>(orientation_actions[0]))->setChecked(true);
0124     else if (item->angleDegrees() == 90)
0125         (static_cast<KToggleAction *>(orientation_actions[1]))->setChecked(true);
0126     else if (item->angleDegrees() == 180)
0127         (static_cast<KToggleAction *>(orientation_actions[2]))->setChecked(true);
0128     else if (item->angleDegrees() == 270)
0129         (static_cast<KToggleAction *>(orientation_actions[3]))->setChecked(true);
0130 }
0131 
0132 void CircuitDocument::rotateCounterClockwise()
0133 {
0134     m_selectList->slotRotateCCW();
0135     requestRerouteInvalidatedConnectors();
0136 }
0137 
0138 void CircuitDocument::rotateClockwise()
0139 {
0140     m_selectList->slotRotateCW();
0141     requestRerouteInvalidatedConnectors();
0142 }
0143 
0144 void CircuitDocument::flipHorizontally()
0145 {
0146     m_selectList->flipHorizontally();
0147     requestRerouteInvalidatedConnectors();
0148 }
0149 
0150 void CircuitDocument::flipVertically()
0151 {
0152     m_selectList->flipVertically();
0153     requestRerouteInvalidatedConnectors();
0154 }
0155 
0156 void CircuitDocument::setOrientation0()
0157 {
0158     m_selectList->slotSetOrientation0();
0159     requestRerouteInvalidatedConnectors();
0160 }
0161 
0162 void CircuitDocument::setOrientation90()
0163 {
0164     m_selectList->slotSetOrientation90();
0165     requestRerouteInvalidatedConnectors();
0166 }
0167 
0168 void CircuitDocument::setOrientation180()
0169 {
0170     m_selectList->slotSetOrientation180();
0171     requestRerouteInvalidatedConnectors();
0172 }
0173 
0174 void CircuitDocument::setOrientation270()
0175 {
0176     m_selectList->slotSetOrientation270();
0177     requestRerouteInvalidatedConnectors();
0178 }
0179 
0180 View *CircuitDocument::createView(ViewContainer *viewContainer, uint viewAreaId)
0181 {
0182     View *view = new CircuitView(this, viewContainer, viewAreaId);
0183     handleNewView(view);
0184     return view;
0185 }
0186 
0187 void CircuitDocument::slotUpdateConfiguration()
0188 {
0189     CircuitICNDocument::slotUpdateConfiguration();
0190 
0191     ECNodeMap::iterator nodeEnd = m_ecNodeList.end();
0192     for (ECNodeMap::iterator it = m_ecNodeList.begin(); it != nodeEnd; ++it) {
0193         ECNode *n = *it; // static_cast<ECNode*>(*it);
0194         n->setShowVoltageBars(KTLConfig::showVoltageBars());
0195         n->setShowVoltageColor(KTLConfig::showVoltageColor());
0196     }
0197 
0198     ConnectorList::iterator connectorsEnd = m_connectorList.end();
0199     for (ConnectorList::iterator it = m_connectorList.begin(); it != connectorsEnd; ++it)
0200         (*it)->updateConnectorLines();
0201 
0202     ComponentList::iterator componentsEnd = m_componentList.end();
0203     for (ComponentList::iterator it = m_componentList.begin(); it != componentsEnd; ++it)
0204         (*it)->slotUpdateConfiguration();
0205 }
0206 
0207 void CircuitDocument::update()
0208 {
0209     CircuitICNDocument::update();
0210 
0211     bool animWires = KTLConfig::animateWires();
0212 
0213     if (KTLConfig::showVoltageColor() || animWires) {
0214         if (animWires) {
0215             // Wire animation is for showing currents, so we need to recalculate the currents
0216             // in the wires.
0217             calculateConnectorCurrents();
0218         }
0219 
0220         ConnectorList::iterator end = m_connectorList.end();
0221         for (ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it) {
0222             (*it)->incrementCurrentAnimation(1.0 / double(KTLConfig::refreshRate()));
0223             (*it)->updateConnectorLines(animWires);
0224         }
0225     }
0226 
0227     if (KTLConfig::showVoltageColor() || KTLConfig::showVoltageBars()) {
0228         ECNodeMap::iterator end = m_ecNodeList.end();
0229         for (ECNodeMap::iterator it = m_ecNodeList.begin(); it != end; ++it) {
0230             // static_cast<ECNode*>(*it)->setNodeChanged();
0231             (*it)->setNodeChanged();
0232         }
0233     }
0234 }
0235 
0236 void CircuitDocument::fillContextMenu(const QPoint &pos)
0237 {
0238     CircuitICNDocument::fillContextMenu(pos);
0239 
0240     CircuitView *activeCircuitView = dynamic_cast<CircuitView *>(activeView());
0241 
0242     if (!activeCircuitView)
0243         return;
0244 
0245     bool canCreateSubcircuit = (m_selectList->count() > 1 && countExtCon(m_selectList->items()) > 0);
0246     QAction *subcircuitAction = activeCircuitView->actionByName("circuit_create_subcircuit");
0247     subcircuitAction->setEnabled(canCreateSubcircuit);
0248 
0249     if (m_selectList->count() < 1)
0250         return;
0251 
0252     Component *item = dynamic_cast<Component *>(selectList()->activeItem());
0253 
0254     // NOTE: I negated this whole condition because I couldn't make out quite what the
0255     // logic was --electronerd
0256     if (!((!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType())) {
0257         QAction *orientation_actions[] = {
0258             activeCircuitView->actionByName("edit_orientation_0"), activeCircuitView->actionByName("edit_orientation_90"), activeCircuitView->actionByName("edit_orientation_180"), activeCircuitView->actionByName("edit_orientation_270")};
0259 
0260         if (!item)
0261             return;
0262 
0263         for (unsigned i = 0; i < 4; ++i) {
0264             m_pOrientationAction->removeAction(orientation_actions[i]);
0265             m_pOrientationAction->addAction(orientation_actions[i]);
0266         }
0267 
0268         QList<QAction *> orientation_actionlist;
0269         //  orientation_actionlist.prepend( new KActionSeparator() );
0270         orientation_actionlist.append(m_pOrientationAction);
0271         KTechlab::self()->plugActionList("orientation_actionlist", orientation_actionlist);
0272     }
0273 }
0274 
0275 void CircuitDocument::deleteCircuits()
0276 {
0277     const CircuitList::iterator end = m_circuitList.end();
0278     for (CircuitList::iterator it = m_circuitList.begin(); it != end; ++it) {
0279         if (!Simulator::isDestroyedSim()) {
0280             Simulator::self()->detachCircuit(*it);
0281         }
0282         delete *it;
0283     }
0284     m_circuitList.clear();
0285     m_pinList.clear();
0286     m_wireList.clear();
0287 }
0288 
0289 void CircuitDocument::requestAssignCircuits()
0290 {
0291     //  qCDebug(KTL_LOG);
0292     if (m_bDeleted) {
0293         return;
0294     }
0295     deleteCircuits();
0296     m_updateCircuitsTmr->stop();
0297     m_updateCircuitsTmr->setSingleShot(true);
0298     m_updateCircuitsTmr->start(0 /*, true */);
0299 }
0300 
0301 void CircuitDocument::connectorAddedSlot(Connector *connector)
0302 {
0303     if (connector) {
0304         connect(connector, &Connector::numWiresChanged, this, &CircuitDocument::requestAssignCircuits);
0305         connect(connector, &Connector::removed, this, &CircuitDocument::requestAssignCircuits);
0306     }
0307 }
0308 
0309 void CircuitDocument::itemAdded(Item *item)
0310 {
0311     CircuitICNDocument::itemAdded(item);
0312     componentAdded(item);
0313 }
0314 
0315 void CircuitDocument::componentAdded(Item *item)
0316 {
0317     Component *component = dynamic_cast<Component *>(item);
0318 
0319     if (!component)
0320         return;
0321 
0322     requestAssignCircuits();
0323 
0324     connect(component, &Component::elementCreated, this, &CircuitDocument::requestAssignCircuits);
0325     connect(component, &Component::elementDestroyed, this, &CircuitDocument::requestAssignCircuits);
0326     connect(component, &Component::removed, this, &CircuitDocument::componentRemoved);
0327 
0328     // We don't attach the component to the Simulator just yet, as the
0329     // Component's vtable is not yet fully constructed, and so Simulator can't
0330     // tell whether or not it is a logic component
0331     if (!m_toSimulateList.contains(component))
0332         m_toSimulateList << component;
0333 }
0334 
0335 void CircuitDocument::componentRemoved(Item *item)
0336 {
0337     Component *component = dynamic_cast<Component *>(item);
0338 
0339     if (!component)
0340         return;
0341 
0342     m_componentList.removeAll(component);
0343     m_toSimulateList.removeAll(component);
0344 
0345     requestAssignCircuits();
0346 
0347     if (!Simulator::isDestroyedSim()) {
0348         Simulator::self()->detachComponent(component);
0349     }
0350 }
0351 
0352 // I think this is where the inf0z from cnodes/branches is moved into the midle-layer
0353 // pins/wires.
0354 
0355 void CircuitDocument::calculateConnectorCurrents()
0356 {
0357     const CircuitList::iterator circuitEnd = m_circuitList.end();
0358     for (CircuitList::iterator it = m_circuitList.begin(); it != circuitEnd; ++it)
0359         (*it)->updateCurrents();
0360 
0361     PinList groundPins;
0362 
0363     // Tell the Pins to reset their calculated currents to zero
0364     m_pinList.removeAll(static_cast<Pin *>(nullptr));
0365 
0366     const PinList::iterator pinEnd = m_pinList.end();
0367     for (PinList::iterator it = m_pinList.begin(); it != pinEnd; ++it) {
0368         if (Pin *n = dynamic_cast<Pin *>(static_cast<Pin *>(*it))) {
0369             n->resetCurrent();
0370             n->setSwitchCurrentsUnknown();
0371 
0372             if (!n->parentECNode()->isChildNode()) {
0373                 n->setCurrentKnown(true);
0374                 // (and it has a current of 0 amps)
0375             } else if (n->groundType() == Pin::gt_always) {
0376                 groundPins << n;
0377                 n->setCurrentKnown(false);
0378             } else {
0379                 // Child node that is non ground
0380                 n->setCurrentKnown(n->parentECNode()->numPins() < 2);
0381             }
0382         }
0383     }
0384 
0385     // Tell the components to update their ECNode's currents' from the elements
0386     // currents are merged into PINS.
0387     const ComponentList::iterator componentEnd = m_componentList.end();
0388     for (ComponentList::iterator it = m_componentList.begin(); it != componentEnd; ++it)
0389         (*it)->setNodalCurrents();
0390 
0391     // And now for the wires and switches...
0392     m_wireList.removeAll(static_cast<Wire *>(nullptr));
0393     const WireList::iterator clEnd = m_wireList.end();
0394     for (WireList::iterator it = m_wireList.begin(); it != clEnd; ++it)
0395         (*it)->setCurrentKnown(false);
0396 
0397     SwitchList switches = m_switchList;
0398     WireList wires = m_wireList;
0399     bool found = true;
0400     while ((!wires.isEmpty() || !switches.isEmpty() || !groundPins.isEmpty()) && found) {
0401         found = false;
0402 
0403         for (WireList::iterator itW = wires.begin(); itW != wires.end();) {
0404             if ((*itW)->calculateCurrent()) {
0405                 found = true;
0406                 itW = wires.erase(itW);
0407                 // note: assigning a temporary interator, incrementing and erasing, seems to crash
0408             } else {
0409                 ++itW;
0410             }
0411         }
0412 
0413         SwitchList::iterator switchesEnd = switches.end();
0414         for (SwitchList::iterator it = switches.begin(); it != switchesEnd;) {
0415             if ((*it)->calculateCurrent()) {
0416                 found = true;
0417                 // note: assigning a temporary interator, incrementing and erasing, seems to crash
0418                 // it = container.erase( it ) seems to crash other times
0419                 SwitchList::iterator oldIt = it;
0420                 ++it;
0421                 switches.erase(oldIt);
0422             } else
0423                 ++it;
0424         }
0425 
0426         /*
0427         make the ground pins work. Current engine doesn't treat ground explicitly.
0428         */
0429 
0430         PinList::iterator groundPinsEnd = groundPins.end();
0431         for (PinList::iterator it = groundPins.begin(); it != groundPinsEnd;) {
0432             if ((*it)->calculateCurrentFromWires()) {
0433                 found = true;
0434                 // note: assigning a temporary interator, incrementing and erasing, seems to crash sometimes;
0435                 // it = container.erase( it ) seems to crash other times
0436                 PinList::iterator oldIt = it;
0437                 ++it;
0438                 groundPins.erase(oldIt);
0439             } else
0440                 ++it;
0441         }
0442     }
0443 }
0444 
0445 void CircuitDocument::assignCircuits()
0446 {
0447     // Now we can finally add the unadded components to the Simulator
0448     const ComponentList::iterator toSimulateEnd = m_toSimulateList.end();
0449     for (ComponentList::iterator it = m_toSimulateList.begin(); it != toSimulateEnd; ++it)
0450         Simulator::self()->attachComponent(*it);
0451 
0452     m_toSimulateList.clear();
0453 
0454     // Stage 0: Build up pin and wire lists
0455     m_pinList.clear();
0456 
0457     const ECNodeMap::const_iterator nodeListEnd = m_ecNodeList.end();
0458     for (ECNodeMap::const_iterator it = m_ecNodeList.begin(); it != nodeListEnd; ++it) {
0459         // if ( ECNode * ecnode = dynamic_cast<ECNode*>(*it) )
0460         ECNode *ecnode = *it;
0461 
0462         for (unsigned i = 0; i < ecnode->numPins(); i++)
0463             m_pinList << ecnode->pin(i);
0464     }
0465 
0466     m_wireList.clear();
0467 
0468     const ConnectorList::const_iterator connectorListEnd = m_connectorList.end();
0469     for (ConnectorList::const_iterator it = m_connectorList.begin(); it != connectorListEnd; ++it) {
0470         for (unsigned i = 0; i < (*it)->numWires(); i++)
0471             m_wireList << (*it)->wire(i);
0472     }
0473 
0474     typedef QList<PinList> PinListList;
0475 
0476     // Stage 1: Partition the circuit up into dependent areas (bar splitting
0477     // at ground pins)
0478     PinList unassignedPins = m_pinList;
0479     PinListList pinListList;
0480 
0481     while (!unassignedPins.isEmpty()) {
0482         PinList pinList;
0483         getPartition(*unassignedPins.begin(), &pinList, &unassignedPins);
0484         pinListList.append(pinList);
0485     }
0486 
0487     //  qCDebug(KTL_LOG) << "pinListList.size()="<<pinListList.size();
0488 
0489     // Stage 2: Split up each partition into circuits by ground pins
0490     const PinListList::iterator nllEnd = pinListList.end();
0491     for (PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it)
0492         splitIntoCircuits(&*it);
0493 
0494     // Stage 3: Initialize the circuits
0495     m_circuitList.removeAll(nullptr);
0496     CircuitList::iterator circuitListEnd = m_circuitList.end();
0497     for (CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it)
0498         (*it)->init();
0499 
0500     m_switchList.clear();
0501     m_componentList.clear();
0502     const ItemMap::const_iterator cilEnd = m_itemList.end();
0503     for (ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it) {
0504         Component *component = dynamic_cast<Component *>(*it);
0505 
0506         if (!component)
0507             continue;
0508 
0509         m_componentList << component;
0510         component->initElements(0);
0511         m_switchList += component->switchList();
0512     }
0513 
0514     circuitListEnd = m_circuitList.end();
0515     for (CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it)
0516         (*it)->createMatrixMap();
0517 
0518     for (ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it) {
0519         Component *component = dynamic_cast<Component *>(*it);
0520 
0521         if (component)
0522             component->initElements(1);
0523     }
0524 
0525     circuitListEnd = m_circuitList.end();
0526     for (CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it) {
0527         (*it)->initCache();
0528         Simulator::self()->attachCircuit(*it);
0529     }
0530 }
0531 
0532 void CircuitDocument::getPartition(Pin *pin, PinList *pinList, PinList *unassignedPins, bool onlyGroundDependent)
0533 {
0534     if (!pin)
0535         return;
0536 
0537     unassignedPins->removeAll(pin);
0538 
0539     if (pinList->contains(pin))
0540         return;
0541 
0542     pinList->append(pin);
0543 
0544     const PinList localConnectedPins = pin->localConnectedPins();
0545     const PinList::const_iterator end = localConnectedPins.end();
0546     for (PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it)
0547         getPartition(*it, pinList, unassignedPins, onlyGroundDependent);
0548 
0549     const PinList groundDependentPins = pin->groundDependentPins();
0550     const PinList::const_iterator dEnd = groundDependentPins.end();
0551     for (PinList::const_iterator it = groundDependentPins.begin(); it != dEnd; ++it)
0552         getPartition(*it, pinList, unassignedPins, onlyGroundDependent);
0553 
0554     if (!onlyGroundDependent) {
0555         PinList circuitDependentPins = pin->circuitDependentPins();
0556         const PinList::const_iterator dEnd = circuitDependentPins.end();
0557         for (PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it)
0558             getPartition(*it, pinList, unassignedPins, onlyGroundDependent);
0559     }
0560 }
0561 
0562 void CircuitDocument::splitIntoCircuits(PinList *pinList)
0563 {
0564     // First: identify ground
0565     PinList unassignedPins = *pinList;
0566     typedef QList<PinList> PinListList;
0567     PinListList pinListList;
0568 
0569     while (!unassignedPins.isEmpty()) {
0570         PinList tempPinList;
0571         getPartition(*unassignedPins.begin(), &tempPinList, &unassignedPins, true);
0572         pinListList.append(tempPinList);
0573     }
0574 
0575     const PinListList::iterator nllEnd = pinListList.end();
0576     for (PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it)
0577         Circuit::identifyGround(*it);
0578 
0579     while (!pinList->isEmpty()) {
0580         PinList::iterator end = pinList->end();
0581         PinList::iterator it = pinList->begin();
0582 
0583         while (it != end && (*it)->eqId() == -1)
0584             ++it;
0585 
0586         if (it == end)
0587             break;
0588         else {
0589             Circuitoid *circuitoid = new Circuitoid;
0590             recursivePinAdd(*it, circuitoid, pinList);
0591 
0592             if (!tryAsLogicCircuit(circuitoid))
0593                 m_circuitList += createCircuit(circuitoid);
0594 
0595             delete circuitoid;
0596         }
0597     }
0598 
0599     // Remaining pins are ground; tell them about it
0600     // TODO This is a bit hacky....
0601     const PinList::iterator end = pinList->end();
0602     for (PinList::iterator it = pinList->begin(); it != end; ++it) {
0603         (*it)->setVoltage(0.0);
0604         ElementList elements = (*it)->elements();
0605         const ElementList::iterator eEnd = elements.end();
0606         for (ElementList::iterator it = elements.begin(); it != eEnd; ++it) {
0607             if (LogicIn *logicIn = dynamic_cast<LogicIn *>(*it)) {
0608                 logicIn->setLastState(false);
0609                 logicIn->callCallback();
0610             }
0611         }
0612     }
0613 }
0614 
0615 void CircuitDocument::recursivePinAdd(Pin *pin, Circuitoid *circuitoid, PinList *unassignedPins)
0616 {
0617     if (!pin)
0618         return;
0619 
0620     if (pin->eqId() != -1)
0621         unassignedPins->removeAll(pin);
0622 
0623     if (circuitoid->contains(pin))
0624         return;
0625 
0626     circuitoid->addPin(pin);
0627 
0628     if (pin->eqId() == -1)
0629         return;
0630 
0631     const PinList localConnectedPins = pin->localConnectedPins();
0632     const PinList::const_iterator end = localConnectedPins.end();
0633     for (PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it)
0634         recursivePinAdd(*it, circuitoid, unassignedPins);
0635 
0636     const PinList groundDependentPins = pin->groundDependentPins();
0637     const PinList::const_iterator gdEnd = groundDependentPins.end();
0638     for (PinList::const_iterator it = groundDependentPins.begin(); it != gdEnd; ++it)
0639         recursivePinAdd(*it, circuitoid, unassignedPins);
0640 
0641     const PinList circuitDependentPins = pin->circuitDependentPins();
0642     const PinList::const_iterator cdEnd = circuitDependentPins.end();
0643     for (PinList::const_iterator it = circuitDependentPins.begin(); it != cdEnd; ++it)
0644         recursivePinAdd(*it, circuitoid, unassignedPins);
0645 
0646     const ElementList elements = pin->elements();
0647     const ElementList::const_iterator eEnd = elements.end();
0648     for (ElementList::const_iterator it = elements.begin(); it != eEnd; ++it)
0649         circuitoid->addElement(*it);
0650 }
0651 
0652 bool CircuitDocument::tryAsLogicCircuit(Circuitoid *circuitoid)
0653 {
0654     if (!circuitoid)
0655         return false;
0656 
0657     if (circuitoid->elementList.size() == 0) {
0658         // This doesn't quite belong here...but whatever. Initialize all
0659         // pins to voltage zero as they won't get set to zero otherwise
0660         const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd();
0661         for (PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it)
0662             (*it)->setVoltage(0.0);
0663 
0664         // A logic circuit only requires there to be no non-logic components,
0665         // and at most one LogicOut - so this qualifies
0666         return true;
0667     }
0668 
0669     LogicInList logicInList;
0670     LogicOut *out = nullptr;
0671 
0672     uint logicInCount = 0;
0673     uint logicOutCount = 0;
0674     ElementList::const_iterator end = circuitoid->elementList.end();
0675     for (ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it) {
0676         if ((*it)->type() == Element::Element_LogicOut) {
0677             logicOutCount++;
0678             out = static_cast<LogicOut *>(*it);
0679         } else if ((*it)->type() == Element::Element_LogicIn) {
0680             logicInCount++;
0681             logicInList += static_cast<LogicIn *>(*it);
0682         } else
0683             return false;
0684     }
0685 
0686     if (logicOutCount > 1)
0687         return false;
0688     else if (logicOutCount == 1)
0689         Simulator::self()->createLogicChain(out, logicInList, circuitoid->pinList);
0690     else {
0691         // We have ourselves stranded LogicIns...so lets set them all to low
0692 
0693         const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd();
0694         for (PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it)
0695             (*it)->setVoltage(0.0);
0696 
0697         for (ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it) {
0698             LogicIn *logicIn = static_cast<LogicIn *>(*it);
0699             logicIn->setNextLogic(nullptr);
0700             logicIn->setElementSet(nullptr);
0701             if (logicIn->isHigh()) {
0702                 logicIn->setLastState(false);
0703                 logicIn->callCallback();
0704             }
0705         }
0706     }
0707 
0708     return true;
0709 }
0710 
0711 Circuit *CircuitDocument::createCircuit(Circuitoid *circuitoid)
0712 {
0713     if (!circuitoid)
0714         return nullptr;
0715 
0716     Circuit *circuit = new Circuit();
0717 
0718     const PinList::const_iterator nEnd = circuitoid->pinList.end();
0719     for (PinList::const_iterator it = circuitoid->pinList.begin(); it != nEnd; ++it)
0720         circuit->addPin(*it);
0721 
0722     const ElementList::const_iterator eEnd = circuitoid->elementList.end();
0723     for (ElementList::const_iterator it = circuitoid->elementList.begin(); it != eEnd; ++it)
0724         circuit->addElement(*it);
0725 
0726     return circuit;
0727 }
0728 
0729 void CircuitDocument::createSubcircuit()
0730 {
0731     ItemList itemList = m_selectList->items();
0732     const ItemList::iterator itemListEnd = itemList.end();
0733     for (ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it) {
0734         if (!dynamic_cast<Component *>(static_cast<Item *>(*it)))
0735             *it = nullptr;
0736     }
0737 
0738     itemList.removeAll(static_cast<Item *>(nullptr));
0739 
0740     if (itemList.isEmpty()) {
0741         KMessageBox::error(activeView(), i18n("No components were found in the selection."));
0742         return;
0743     }
0744 
0745     // Number of external connections
0746     const int extConCount = countExtCon(itemList);
0747     if (extConCount == 0) {
0748         KMessageBox::error(activeView(), i18n("No External Connection components were found in the selection."));
0749         return;
0750     }
0751 
0752     bool ok;
0753     const QString name = QInputDialog::getText(activeView(), "Subcircuit", "Name", QLineEdit::Normal, QString(), &ok);
0754     if (!ok)
0755         return;
0756 
0757     SubcircuitData subcircuit;
0758     subcircuit.addItems(itemList);
0759     subcircuit.addNodes(getCommonNodes(itemList));
0760     subcircuit.addConnectors(getCommonConnectors(itemList));
0761 
0762     Subcircuits::addSubcircuit(name, subcircuit.toXML());
0763 }
0764 
0765 int CircuitDocument::countExtCon(const ItemList &itemList) const
0766 {
0767     int count = 0;
0768     const ItemList::const_iterator end = itemList.end();
0769     for (ItemList::const_iterator it = itemList.begin(); it != end; ++it) {
0770         Item *item = *it;
0771 
0772         if (item && item->type() == "ec/external_connection")
0773             count++;
0774     }
0775     return count;
0776 }
0777 
0778 bool CircuitDocument::isValidItem(const QString &itemId)
0779 {
0780     return itemId.startsWith("ec/") || itemId.startsWith("dp/") || itemId.startsWith("sc/");
0781 }
0782 
0783 bool CircuitDocument::isValidItem(Item *item)
0784 {
0785     return (dynamic_cast<Component *>(item) || dynamic_cast<DrawPart *>(item));
0786 }
0787 
0788 void CircuitDocument::displayEquations()
0789 {
0790     qCDebug(KTL_LOG) << "######################################################";
0791     const CircuitList::iterator end = m_circuitList.end();
0792     int i = 1;
0793     for (CircuitList::iterator it = m_circuitList.begin(); it != end; ++it) {
0794         qCDebug(KTL_LOG) << "Equation set " << i << ":";
0795         (*it)->displayEquations();
0796         i++;
0797     }
0798     qCDebug(KTL_LOG) << "######################################################";
0799 }
0800 
0801 #include "moc_circuitdocument.cpp"