/*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include "canvasmanipulator.h" #include "circuitdocument.h" #include "circuiticndocument.h" #include "circuitview.h" #include "component.h" #include "connector.h" #include "cnitemgroup.h" #include "documentiface.h" #include "drawparts/drawpart.h" #include "ecnode.h" #include "itemdocumentdata.h" #include "ktechlab.h" #include "pin.h" #include "simulator.h" #include "subcircuits.h" #include "switch.h" #include #include #include #include #include #include #include #include #include CircuitDocument::CircuitDocument( const QString & caption, const char *name ) : CircuitICNDocument( caption, name ) { m_pOrientationAction = new KActionMenu( QIcon::fromTheme("transform-rotate"), i18n("Orientation"), this ); m_type = Document::dt_circuit; m_pDocumentIface = new CircuitDocumentIface(this); m_fileExtensionInfo = QString("*.circuit|%1(*.circuit)\n*|%2").arg( i18n("Circuit") ).arg( i18n("All Files") ); m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMDraw::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMRightClick::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMRepeatedItemAdd::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMItemResize::manipulatorInfo() ); m_cmManager->addManipulatorInfo( CMItemDrag::manipulatorInfo() ); connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(requestAssignCircuits()) ); connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(connectorAdded(Connector*)) ); m_updateCircuitsTmr = new QTimer(); connect( m_updateCircuitsTmr, SIGNAL(timeout()), this, SLOT(assignCircuits()) ); requestStateSave(); } CircuitDocument::~CircuitDocument() { m_bDeleted = true; disconnect( m_updateCircuitsTmr, SIGNAL(timeout()), this, SLOT(assignCircuits()) ); disconnect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(connectorAdded(Connector*)) ); disconnect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(requestAssignCircuits()) ); for (ConnectorList::Iterator itConn = m_connectorList.begin(); itConn != m_connectorList.end(); ++itConn) { Connector *connector = itConn->data(); disconnect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestAssignCircuits()) ); } for (ItemMap::Iterator itItem = m_itemList.begin(); itItem != m_itemList.end(); ++itItem) { Item *item = itItem.value(); disconnect( item, SIGNAL(removed(Item*)), this, SLOT(componentRemoved(Item*)) ); Component *comp = dynamic_cast( item ); if ( comp ) { disconnect( comp, SIGNAL(elementDestroyed(Element*)), this, SLOT(requestAssignCircuits()) ); } } deleteCircuits(); delete m_updateCircuitsTmr; delete m_pDocumentIface; } void CircuitDocument::slotInitItemActions( ) { CircuitICNDocument::slotInitItemActions(); CircuitView *activeCircuitView = dynamic_cast(activeView()); if ( !KTechlab::self() || !activeCircuitView ) return; Component *item = dynamic_cast( m_selectList->activeItem() ); if ( (!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType() ) return; QAction * orientation_actions[] = { activeCircuitView->actionByName("edit_orientation_0"), activeCircuitView->actionByName("edit_orientation_90"), activeCircuitView->actionByName("edit_orientation_180"), activeCircuitView->actionByName("edit_orientation_270") }; if ( !item ) { for ( unsigned i = 0; i < 4; ++i ) orientation_actions[i]->setEnabled(false); return; } for ( unsigned i = 0; i < 4; ++ i) { orientation_actions[i]->setEnabled(true); m_pOrientationAction->removeAction( orientation_actions[i] ); m_pOrientationAction->addAction( orientation_actions[i] ); } if ( item->angleDegrees() == 0 ) (static_cast( orientation_actions[0] ))->setChecked(true); else if ( item->angleDegrees() == 90 ) (static_cast( orientation_actions[1] ))->setChecked(true); else if ( item->angleDegrees() == 180 ) (static_cast( orientation_actions[2] ))->setChecked(true); else if ( item->angleDegrees() == 270 ) (static_cast( orientation_actions[3] ))->setChecked(true); } void CircuitDocument::rotateCounterClockwise() { m_selectList->slotRotateCCW(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::rotateClockwise() { m_selectList->slotRotateCW(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::flipHorizontally() { m_selectList->flipHorizontally(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::flipVertically() { m_selectList->flipVertically(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::setOrientation0() { m_selectList->slotSetOrientation0(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::setOrientation90() { m_selectList->slotSetOrientation90(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::setOrientation180() { m_selectList->slotSetOrientation180(); requestRerouteInvalidatedConnectors(); } void CircuitDocument::setOrientation270() { m_selectList->slotSetOrientation270(); requestRerouteInvalidatedConnectors(); } View *CircuitDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name ) { View *view = new CircuitView( this, viewContainer, viewAreaId, name ); handleNewView(view); return view; } void CircuitDocument::slotUpdateConfiguration() { CircuitICNDocument::slotUpdateConfiguration(); ECNodeMap::iterator nodeEnd = m_ecNodeList.end(); for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != nodeEnd; ++it ) { ECNode * n = *it; // static_cast(*it); n->setShowVoltageBars( KTLConfig::showVoltageBars() ); n->setShowVoltageColor( KTLConfig::showVoltageColor() ); } ConnectorList::iterator connectorsEnd = m_connectorList.end(); for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorsEnd; ++it ) (*it)->updateConnectorLines(); ComponentList::iterator componentsEnd = m_componentList.end(); for ( ComponentList::iterator it = m_componentList.begin(); it != componentsEnd; ++it ) (*it)->slotUpdateConfiguration(); } void CircuitDocument::update() { CircuitICNDocument::update(); bool animWires = KTLConfig::animateWires(); if ( KTLConfig::showVoltageColor() || animWires ) { if ( animWires ) { // Wire animation is for showing currents, so we need to recalculate the currents // in the wires. calculateConnectorCurrents(); } ConnectorList::iterator end = m_connectorList.end(); for ( ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it ) { (*it)->incrementCurrentAnimation( 1.0 / double(KTLConfig::refreshRate()) ); (*it)->updateConnectorLines( animWires ); } } if ( KTLConfig::showVoltageColor() || KTLConfig::showVoltageBars() ) { ECNodeMap::iterator end = m_ecNodeList.end(); for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != end; ++it ) { // static_cast(*it)->setNodeChanged(); (*it)->setNodeChanged(); } } } void CircuitDocument::fillContextMenu( const QPoint &pos ) { CircuitICNDocument::fillContextMenu(pos); CircuitView *activeCircuitView = dynamic_cast(activeView()); if ( !activeCircuitView ) return; bool canCreateSubcircuit = (m_selectList->count() > 1 && countExtCon(m_selectList->items()) > 0); QAction * subcircuitAction = activeCircuitView->actionByName("circuit_create_subcircuit"); subcircuitAction->setEnabled( canCreateSubcircuit ); if ( m_selectList->count() < 1 ) return; Component *item = dynamic_cast( selectList()->activeItem() ); // NOTE: I negated this whole condition because I couldn't make out quite what the //logic was --electronerd if (!( (!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType() )) { QAction * orientation_actions[] = { activeCircuitView->actionByName("edit_orientation_0"), activeCircuitView->actionByName("edit_orientation_90"), activeCircuitView->actionByName("edit_orientation_180"), activeCircuitView->actionByName("edit_orientation_270") }; if ( !item ) return; for ( unsigned i = 0; i < 4; ++ i) { m_pOrientationAction->removeAction( orientation_actions[i] ); m_pOrientationAction->addAction( orientation_actions[i] ); } QList orientation_actionlist; // orientation_actionlist.prepend( new KActionSeparator() ); orientation_actionlist.append( m_pOrientationAction ); KTechlab::self()->plugActionList( "orientation_actionlist", orientation_actionlist ); } } void CircuitDocument::deleteCircuits() { const CircuitList::iterator end = m_circuitList.end(); for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it ) { if (!Simulator::isDestroyedSim()) { Simulator::self()->detachCircuit(*it); } delete *it; } m_circuitList.clear(); m_pinList.clear(); m_wireList.clear(); } void CircuitDocument::requestAssignCircuits() { // qDebug() << Q_FUNC_INFO << endl; if (m_bDeleted) { return; } deleteCircuits(); m_updateCircuitsTmr->stop(); m_updateCircuitsTmr->setSingleShot( true ); m_updateCircuitsTmr->start( 0 /*, true */ ); } void CircuitDocument::connectorAdded( Connector * connector ) { if (connector) { connect( connector, SIGNAL(numWiresChanged(unsigned )), this, SLOT(requestAssignCircuits()) ); connect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestAssignCircuits()) ); } } void CircuitDocument::itemAdded( Item * item) { CircuitICNDocument::itemAdded( item ); componentAdded( item ); } void CircuitDocument::componentAdded( Item * item ) { Component *component = dynamic_cast(item); if (!component) return; requestAssignCircuits(); connect( component, SIGNAL(elementCreated(Element*)), this, SLOT(requestAssignCircuits()) ); connect( component, SIGNAL(elementDestroyed(Element*)), this, SLOT(requestAssignCircuits()) ); connect( component, SIGNAL(removed(Item*)), this, SLOT(componentRemoved(Item*)) ); // We don't attach the component to the Simulator just yet, as the // Component's vtable is not yet fully constructed, and so Simulator can't // tell whether or not it is a logic component if ( !m_toSimulateList.contains(component) ) m_toSimulateList << component; } void CircuitDocument::componentRemoved( Item * item ) { Component *component = dynamic_cast(item); if (!component) return; m_componentList.removeAll( component ); m_toSimulateList.removeAll( component ); requestAssignCircuits(); if (!Simulator::isDestroyedSim()) { Simulator::self()->detachComponent(component); } } // I think this is where the inf0z from cnodes/branches is moved into the midle-layer // pins/wires. void CircuitDocument::calculateConnectorCurrents() { const CircuitList::iterator circuitEnd = m_circuitList.end(); for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitEnd; ++it ) (*it)->updateCurrents(); PinList groundPins; // Tell the Pins to reset their calculated currents to zero m_pinList.removeAll((Pin*)nullptr); const PinList::iterator pinEnd = m_pinList.end(); for ( PinList::iterator it = m_pinList.begin(); it != pinEnd; ++it ) { if ( Pin *n = dynamic_cast((Pin*)*it) ) { n->resetCurrent(); n->setSwitchCurrentsUnknown(); if ( !n->parentECNode()->isChildNode() ) { n->setCurrentKnown( true ); // (and it has a current of 0 amps) } else if ( n->groundType() == Pin::gt_always ) { groundPins << n; n->setCurrentKnown( false ); } else { // Child node that is non ground n->setCurrentKnown( n->parentECNode()->numPins() < 2 ); } } } // Tell the components to update their ECNode's currents' from the elements // currents are merged into PINS. const ComponentList::iterator componentEnd = m_componentList.end(); for ( ComponentList::iterator it = m_componentList.begin(); it != componentEnd; ++it ) (*it)->setNodalCurrents(); // And now for the wires and switches... m_wireList.removeAll((Wire*)nullptr); const WireList::iterator clEnd = m_wireList.end(); for ( WireList::iterator it = m_wireList.begin(); it != clEnd; ++it ) (*it)->setCurrentKnown(false); SwitchList switches = m_switchList; WireList wires = m_wireList; bool found = true; while ( (!wires.isEmpty() || !switches.isEmpty() || !groundPins.isEmpty()) && found ) { found = false; for ( WireList::iterator itW = wires.begin(); itW != wires.end(); ) { if ( (*itW)->calculateCurrent() ) { found = true; itW = wires.erase(itW); // note: assigning a temporary interator, incrementing and erasing, seems to crash } else { ++itW; } } SwitchList::iterator switchesEnd = switches.end(); for ( SwitchList::iterator it = switches.begin(); it != switchesEnd; ) { if ( (*it)->calculateCurrent() ) { found = true; // note: assigning a temporary interator, incrementing and erasing, seems to crash // it = container.erase( it ) seems to crash other times SwitchList::iterator oldIt = it; ++it; switches.erase(oldIt); } else ++it; } /* make the ground pins work. Current engine doesn't treat ground explicitly. */ PinList::iterator groundPinsEnd = groundPins.end(); for ( PinList::iterator it = groundPins.begin(); it != groundPinsEnd; ) { if ( (*it)->calculateCurrentFromWires() ) { found = true; // note: assigning a temporary interator, incrementing and erasing, seems to crash sometimes; // it = container.erase( it ) seems to crash other times PinList::iterator oldIt = it; ++it; groundPins.erase(oldIt); } else ++it; } } } void CircuitDocument::assignCircuits() { // Now we can finally add the unadded components to the Simulator const ComponentList::iterator toSimulateEnd = m_toSimulateList.end(); for ( ComponentList::iterator it = m_toSimulateList.begin(); it != toSimulateEnd; ++it ) Simulator::self()->attachComponent(*it); m_toSimulateList.clear(); // Stage 0: Build up pin and wire lists m_pinList.clear(); const ECNodeMap::const_iterator nodeListEnd = m_ecNodeList.end(); for ( ECNodeMap::const_iterator it = m_ecNodeList.begin(); it != nodeListEnd; ++it ) { // if ( ECNode * ecnode = dynamic_cast(*it) ) ECNode* ecnode = *it; for ( unsigned i = 0; i < ecnode->numPins(); i++ ) m_pinList << ecnode->pin(i); } m_wireList.clear(); const ConnectorList::const_iterator connectorListEnd = m_connectorList.end(); for ( ConnectorList::const_iterator it = m_connectorList.begin(); it != connectorListEnd; ++it ) { for ( unsigned i = 0; i < (*it)->numWires(); i++ ) m_wireList << (*it)->wire(i); } typedef QList PinListList; // Stage 1: Partition the circuit up into dependent areas (bar splitting // at ground pins) PinList unassignedPins = m_pinList; PinListList pinListList; while( !unassignedPins.isEmpty() ) { PinList pinList; getPartition( *unassignedPins.begin(), & pinList, & unassignedPins ); pinListList.append(pinList); } // qDebug () << "pinListList.size()="<init(); m_switchList.clear(); m_componentList.clear(); const ItemMap::const_iterator cilEnd = m_itemList.end(); for ( ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it ) { Component *component = dynamic_cast(*it); if ( !component ) continue; m_componentList << component; component->initElements(0); m_switchList += component->switchList(); } circuitListEnd = m_circuitList.end(); for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it ) (*it)->createMatrixMap(); for ( ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it ) { Component *component = dynamic_cast(*it); if (component) component->initElements(1); } circuitListEnd = m_circuitList.end(); for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it ) { (*it)->initCache(); Simulator::self()->attachCircuit(*it); } } void CircuitDocument::getPartition( Pin *pin, PinList *pinList, PinList *unassignedPins, bool onlyGroundDependent ) { if (!pin) return; unassignedPins->removeAll(pin); if ( pinList->contains(pin) ) return; pinList->append(pin); const PinList localConnectedPins = pin->localConnectedPins(); const PinList::const_iterator end = localConnectedPins.end(); for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it ) getPartition( *it, pinList, unassignedPins, onlyGroundDependent ); const PinList groundDependentPins = pin->groundDependentPins(); const PinList::const_iterator dEnd = groundDependentPins.end(); for ( PinList::const_iterator it = groundDependentPins.begin(); it != dEnd; ++it ) getPartition( *it, pinList, unassignedPins, onlyGroundDependent ); if (!onlyGroundDependent) { PinList circuitDependentPins = pin->circuitDependentPins(); const PinList::const_iterator dEnd = circuitDependentPins.end(); for ( PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it ) getPartition( *it, pinList, unassignedPins, onlyGroundDependent ); } } void CircuitDocument::splitIntoCircuits( PinList *pinList ) { // First: identify ground PinList unassignedPins = *pinList; typedef QList PinListList; PinListList pinListList; while ( !unassignedPins.isEmpty() ) { PinList tempPinList; getPartition( *unassignedPins.begin(), & tempPinList, & unassignedPins, true ); pinListList.append(tempPinList); } const PinListList::iterator nllEnd = pinListList.end(); for ( PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it ) Circuit::identifyGround(*it); while ( !pinList->isEmpty() ) { PinList::iterator end = pinList->end(); PinList::iterator it = pinList->begin(); while ( it != end && (*it)->eqId() == -1 ) ++it; if ( it == end ) break; else { Circuitoid *circuitoid = new Circuitoid; recursivePinAdd( *it, circuitoid, pinList ); if ( !tryAsLogicCircuit(circuitoid) ) m_circuitList += createCircuit(circuitoid); delete circuitoid; } } // Remaining pins are ground; tell them about it // TODO This is a bit hacky.... const PinList::iterator end = pinList->end(); for ( PinList::iterator it = pinList->begin(); it != end; ++it ) { (*it)->setVoltage(0.0); ElementList elements = (*it)->elements(); const ElementList::iterator eEnd = elements.end(); for ( ElementList::iterator it = elements.begin(); it != eEnd; ++it ) { if ( LogicIn * logicIn = dynamic_cast(*it) ) { logicIn->setLastState(false); logicIn->callCallback(); } } } } void CircuitDocument::recursivePinAdd( Pin *pin, Circuitoid *circuitoid, PinList *unassignedPins ) { if(!pin) return; if(pin->eqId() != -1 ) unassignedPins->removeAll(pin); if(circuitoid->contains(pin) ) return; circuitoid->addPin(pin); if(pin->eqId() == -1 ) return; const PinList localConnectedPins = pin->localConnectedPins(); const PinList::const_iterator end = localConnectedPins.end(); for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it ) recursivePinAdd( *it, circuitoid, unassignedPins ); const PinList groundDependentPins = pin->groundDependentPins(); const PinList::const_iterator gdEnd = groundDependentPins.end(); for ( PinList::const_iterator it = groundDependentPins.begin(); it != gdEnd; ++it ) recursivePinAdd( *it, circuitoid, unassignedPins ); const PinList circuitDependentPins = pin->circuitDependentPins(); const PinList::const_iterator cdEnd = circuitDependentPins.end(); for ( PinList::const_iterator it = circuitDependentPins.begin(); it != cdEnd; ++it ) recursivePinAdd( *it, circuitoid, unassignedPins ); const ElementList elements = pin->elements(); const ElementList::const_iterator eEnd = elements.end(); for ( ElementList::const_iterator it = elements.begin(); it != eEnd; ++it ) circuitoid->addElement(*it); } bool CircuitDocument::tryAsLogicCircuit( Circuitoid *circuitoid ) { if (!circuitoid) return false; if ( circuitoid->elementList.size() == 0 ) { // This doesn't quite belong here...but whatever. Initialize all // pins to voltage zero as they won't get set to zero otherwise const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd(); for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it ) (*it)->setVoltage(0.0); // A logic circuit only requires there to be no non-logic components, // and at most one LogicOut - so this qualifies return true; } LogicInList logicInList; LogicOut *out = nullptr; uint logicInCount = 0; uint logicOutCount = 0; ElementList::const_iterator end = circuitoid->elementList.end(); for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it ) { if ( (*it)->type() == Element::Element_LogicOut ) { logicOutCount++; out = static_cast(*it); } else if ( (*it)->type() == Element::Element_LogicIn ) { logicInCount++; logicInList += static_cast(*it); } else return false; } if ( logicOutCount > 1 ) return false; else if ( logicOutCount == 1 ) Simulator::self()->createLogicChain( out, logicInList, circuitoid->pinList ); else { // We have ourselves stranded LogicIns...so lets set them all to low const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd(); for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it ) (*it)->setVoltage(0.0); for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it ) { LogicIn * logicIn = static_cast(*it); logicIn->setNextLogic(nullptr); logicIn->setElementSet(nullptr); if ( logicIn->isHigh() ) { logicIn->setLastState(false); logicIn->callCallback(); } } } return true; } Circuit *CircuitDocument::createCircuit( Circuitoid *circuitoid ) { if (!circuitoid) return nullptr; Circuit *circuit = new Circuit(); const PinList::const_iterator nEnd = circuitoid->pinList.end(); for ( PinList::const_iterator it = circuitoid->pinList.begin(); it != nEnd; ++it ) circuit->addPin(*it); const ElementList::const_iterator eEnd = circuitoid->elementList.end(); for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != eEnd; ++it ) circuit->addElement(*it); return circuit; } void CircuitDocument::createSubcircuit() { ItemList itemList = m_selectList->items(); const ItemList::iterator itemListEnd = itemList.end(); for ( ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it ) { if ( !dynamic_cast((Item*)*it) ) *it = nullptr; } itemList.removeAll((Item*)nullptr); if ( itemList.isEmpty() ) { KMessageBox::sorry( activeView(), i18n("No components were found in the selection.") ); return; } // Number of external connections const int extConCount = countExtCon(itemList); if ( extConCount == 0 ) { KMessageBox::sorry( activeView(), i18n("No External Connection components were found in the selection.") ); return; } bool ok; const QString name = QInputDialog::getText(activeView(), "Subcircuit", "Name", QLineEdit::Normal, QString(), &ok); if (!ok) return; SubcircuitData subcircuit; subcircuit.addItems(itemList); subcircuit.addNodes( getCommonNodes(itemList) ); subcircuit.addConnectors( getCommonConnectors(itemList) ); Subcircuits::addSubcircuit( name, subcircuit.toXML() ); } int CircuitDocument::countExtCon( const ItemList &itemList ) const { int count = 0; const ItemList::const_iterator end = itemList.end(); for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it ) { Item * item = *it; if ( item && item->type() == "ec/external_connection" ) count++; } return count; } bool CircuitDocument::isValidItem( const QString &itemId ) { return itemId.startsWith("ec/") || itemId.startsWith("dp/") || itemId.startsWith("sc/"); } bool CircuitDocument::isValidItem( Item *item ) { return (dynamic_cast(item) || dynamic_cast(item)); } void CircuitDocument::displayEquations() { qDebug() << "######################################################" << endl; const CircuitList::iterator end = m_circuitList.end(); int i = 1; for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it ) { qDebug() << "Equation set "<displayEquations(); i++; } qDebug() << "######################################################" << endl; }