1 /***************************************************************************
2  *   Copyright (C) 2003-2005 by David Saxton                               *
3  *   david@bluehaze.org                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  ***************************************************************************/
10 
11 #include "canvasmanipulator.h"
12 #include "circuitdocument.h"
13 #include "circuiticndocument.h"
14 #include "circuitview.h"
15 #include "component.h"
16 #include "connector.h"
17 #include "cnitemgroup.h"
18 #include "documentiface.h"
19 #include "drawparts/drawpart.h"
20 #include "ecnode.h"
21 #include "itemdocumentdata.h"
22 #include "ktechlab.h"
23 #include "pin.h"
24 #include "simulator.h"
25 #include "subcircuits.h"
26 #include "switch.h"
27 
28 #include <KLocalizedString>
29 #include <KMessageBox>
30 #include <KActionMenu>
31 #include <KToggleAction>
32 
33 #include <QDebug>
34 #include <QInputDialog>
35 #include <QRegExp>
36 #include <QTimer>
37 
38 #include <ktlconfig.h>
39 
40 
CircuitDocument(const QString & caption,const char * name)41 CircuitDocument::CircuitDocument( const QString & caption, const char *name )
42 	: CircuitICNDocument( caption, name )
43 {
44 	m_pOrientationAction = new KActionMenu( QIcon::fromTheme("transform-rotate"), i18n("Orientation"), this );
45 
46 	m_type = Document::dt_circuit;
47 	m_pDocumentIface = new CircuitDocumentIface(this);
48 	m_fileExtensionInfo = QString("*.circuit|%1(*.circuit)\n*|%2").arg( i18n("Circuit") ).arg( i18n("All Files") );
49 
50 	m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() );
51 	m_cmManager->addManipulatorInfo( CMDraw::manipulatorInfo() );
52 	m_cmManager->addManipulatorInfo( CMRightClick::manipulatorInfo() );
53 	m_cmManager->addManipulatorInfo( CMRepeatedItemAdd::manipulatorInfo() );
54 	m_cmManager->addManipulatorInfo( CMItemResize::manipulatorInfo() );
55 	m_cmManager->addManipulatorInfo( CMItemDrag::manipulatorInfo() );
56 
57 	connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(requestAssignCircuits()) );
58 	connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(connectorAdded(Connector*)) );
59 
60 	m_updateCircuitsTmr = new QTimer();
61 	connect( m_updateCircuitsTmr, SIGNAL(timeout()), this, SLOT(assignCircuits()) );
62 
63 	requestStateSave();
64 }
65 
~CircuitDocument()66 CircuitDocument::~CircuitDocument()
67 {
68 	m_bDeleted = true;
69 
70     disconnect( m_updateCircuitsTmr, SIGNAL(timeout()), this, SLOT(assignCircuits()) );
71     disconnect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(connectorAdded(Connector*)) );
72     disconnect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(requestAssignCircuits()) );
73 
74     for (ConnectorList::Iterator itConn = m_connectorList.begin(); itConn != m_connectorList.end(); ++itConn) {
75         Connector *connector = itConn->data();
76         disconnect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestAssignCircuits()) );
77     }
78     for (ItemMap::Iterator itItem = m_itemList.begin(); itItem != m_itemList.end(); ++itItem) {
79         Item *item = itItem.value();
80         disconnect( item, SIGNAL(removed(Item*)), this, SLOT(componentRemoved(Item*)) );
81         Component *comp = dynamic_cast<Component*>( item );
82         if ( comp ) {
83             disconnect( comp, SIGNAL(elementDestroyed(Element*)), this, SLOT(requestAssignCircuits()) );
84         }
85     }
86 
87     deleteCircuits();
88 
89 	delete m_updateCircuitsTmr;
90 	delete m_pDocumentIface;
91 }
92 
93 
slotInitItemActions()94 void CircuitDocument::slotInitItemActions( )
95 {
96 	CircuitICNDocument::slotInitItemActions();
97 
98 	CircuitView *activeCircuitView = dynamic_cast<CircuitView*>(activeView());
99 
100 	if ( !KTechlab::self() || !activeCircuitView ) return;
101 
102 	Component *item = dynamic_cast<Component*>( m_selectList->activeItem() );
103 
104 	if ( (!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType() )
105 		return;
106 
107 	QAction * orientation_actions[] = {
108 		activeCircuitView->actionByName("edit_orientation_0"),
109 		activeCircuitView->actionByName("edit_orientation_90"),
110 		activeCircuitView->actionByName("edit_orientation_180"),
111 		activeCircuitView->actionByName("edit_orientation_270") };
112 
113 	if ( !item )
114 	{
115 		for ( unsigned i = 0; i < 4; ++i )
116 			orientation_actions[i]->setEnabled(false);
117 		return;
118 	}
119 
120 	for ( unsigned i = 0; i < 4; ++ i)
121 	{
122 		orientation_actions[i]->setEnabled(true);
123 		m_pOrientationAction->removeAction( orientation_actions[i] );
124 		m_pOrientationAction->addAction( orientation_actions[i] );
125 	}
126 
127 	if ( item->angleDegrees() == 0 )
128 		(static_cast<KToggleAction*>( orientation_actions[0] ))->setChecked(true);
129 	else if ( item->angleDegrees() == 90 )
130 		(static_cast<KToggleAction*>( orientation_actions[1] ))->setChecked(true);
131 	else if ( item->angleDegrees() == 180 )
132 		(static_cast<KToggleAction*>( orientation_actions[2] ))->setChecked(true);
133 	else if ( item->angleDegrees() == 270 )
134 		(static_cast<KToggleAction*>( orientation_actions[3] ))->setChecked(true);
135 }
136 
137 
rotateCounterClockwise()138 void CircuitDocument::rotateCounterClockwise()
139 {
140 	m_selectList->slotRotateCCW();
141 	requestRerouteInvalidatedConnectors();
142 }
143 
rotateClockwise()144 void CircuitDocument::rotateClockwise()
145 {
146 	m_selectList->slotRotateCW();
147 	requestRerouteInvalidatedConnectors();
148 }
149 
flipHorizontally()150 void CircuitDocument::flipHorizontally()
151 {
152 	m_selectList->flipHorizontally();
153 	requestRerouteInvalidatedConnectors();
154 }
155 
flipVertically()156 void CircuitDocument::flipVertically()
157 {
158 	m_selectList->flipVertically();
159 	requestRerouteInvalidatedConnectors();
160 }
161 
setOrientation0()162 void CircuitDocument::setOrientation0()
163 {
164 	m_selectList->slotSetOrientation0();
165 	requestRerouteInvalidatedConnectors();
166 }
167 
setOrientation90()168 void CircuitDocument::setOrientation90()
169 {
170 	m_selectList->slotSetOrientation90();
171 	requestRerouteInvalidatedConnectors();
172 }
173 
setOrientation180()174 void CircuitDocument::setOrientation180()
175 {
176 	m_selectList->slotSetOrientation180();
177 	requestRerouteInvalidatedConnectors();
178 }
179 
setOrientation270()180 void CircuitDocument::setOrientation270()
181 {
182 	m_selectList->slotSetOrientation270();
183 	requestRerouteInvalidatedConnectors();
184 }
185 
186 
createView(ViewContainer * viewContainer,uint viewAreaId,const char * name)187 View *CircuitDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name )
188 {
189 	View *view = new CircuitView( this, viewContainer, viewAreaId, name );
190 	handleNewView(view);
191 	return view;
192 }
193 
194 
slotUpdateConfiguration()195 void CircuitDocument::slotUpdateConfiguration()
196 {
197 	CircuitICNDocument::slotUpdateConfiguration();
198 
199 	ECNodeMap::iterator nodeEnd = m_ecNodeList.end();
200 	for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != nodeEnd; ++it )
201 	{
202 		ECNode * n = *it; // static_cast<ECNode*>(*it);
203 		n->setShowVoltageBars( KTLConfig::showVoltageBars() );
204 		n->setShowVoltageColor( KTLConfig::showVoltageColor() );
205 	}
206 
207 	ConnectorList::iterator connectorsEnd = m_connectorList.end();
208 	for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorsEnd; ++it )
209 		(*it)->updateConnectorLines();
210 
211 	ComponentList::iterator componentsEnd = m_componentList.end();
212 	for ( ComponentList::iterator it = m_componentList.begin(); it != componentsEnd; ++it )
213 		(*it)->slotUpdateConfiguration();
214 }
215 
216 
update()217 void CircuitDocument::update()
218 {
219 	CircuitICNDocument::update();
220 
221 	bool animWires = KTLConfig::animateWires();
222 
223 	if ( KTLConfig::showVoltageColor() || animWires )
224 	{
225 		if ( animWires )
226 		{
227 			// Wire animation is for showing currents, so we need to recalculate the currents
228 			// in the wires.
229 			calculateConnectorCurrents();
230 		}
231 
232 		ConnectorList::iterator end = m_connectorList.end();
233 		for ( ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it )
234 		{
235 			(*it)->incrementCurrentAnimation( 1.0 / double(KTLConfig::refreshRate()) );
236 			(*it)->updateConnectorLines( animWires );
237 		}
238 	}
239 
240 	if ( KTLConfig::showVoltageColor() || KTLConfig::showVoltageBars() )
241 	{
242 		ECNodeMap::iterator end = m_ecNodeList.end();
243 		for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != end; ++it )
244 		{
245 			// static_cast<ECNode*>(*it)->setNodeChanged();
246 			(*it)->setNodeChanged();
247 		}
248 	}
249 }
250 
251 
fillContextMenu(const QPoint & pos)252 void CircuitDocument::fillContextMenu( const QPoint &pos )
253 {
254 	CircuitICNDocument::fillContextMenu(pos);
255 
256 	CircuitView *activeCircuitView = dynamic_cast<CircuitView*>(activeView());
257 
258 	if ( !activeCircuitView ) return;
259 
260 	bool canCreateSubcircuit = (m_selectList->count() > 1 && countExtCon(m_selectList->items()) > 0);
261 	QAction * subcircuitAction = activeCircuitView->actionByName("circuit_create_subcircuit");
262 	subcircuitAction->setEnabled( canCreateSubcircuit );
263 
264 	if ( m_selectList->count() < 1 ) return;
265 
266 	Component *item = dynamic_cast<Component*>( selectList()->activeItem() );
267 
268 	// NOTE: I negated this whole condition because I couldn't make out quite what the
269 	//logic was --electronerd
270 	if (!( (!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType() ))
271 	{
272 		QAction * orientation_actions[] = {
273 			activeCircuitView->actionByName("edit_orientation_0"),
274 			activeCircuitView->actionByName("edit_orientation_90"),
275 			activeCircuitView->actionByName("edit_orientation_180"),
276 			activeCircuitView->actionByName("edit_orientation_270") };
277 
278 		if ( !item ) return;
279 
280 		for ( unsigned i = 0; i < 4; ++ i)
281 		{
282 			m_pOrientationAction->removeAction( orientation_actions[i] );
283 			m_pOrientationAction->addAction( orientation_actions[i] );
284 		}
285 
286 		QList<QAction*> orientation_actionlist;
287 	// 	orientation_actionlist.prepend( new KActionSeparator() );
288 		orientation_actionlist.append( m_pOrientationAction );
289 		KTechlab::self()->plugActionList( "orientation_actionlist", orientation_actionlist );
290 	}
291 }
292 
293 
deleteCircuits()294 void CircuitDocument::deleteCircuits()
295 {
296 	const CircuitList::iterator end = m_circuitList.end();
297 	for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it )
298 	{
299         if (!Simulator::isDestroyedSim()) {
300             Simulator::self()->detachCircuit(*it);
301         }
302 		delete *it;
303 	}
304 	m_circuitList.clear();
305 	m_pinList.clear();
306 	m_wireList.clear();
307 }
308 
309 
requestAssignCircuits()310 void CircuitDocument::requestAssignCircuits()
311 {
312 // 	qDebug() << Q_FUNC_INFO << endl;
313     if (m_bDeleted) {
314         return;
315     }
316 	deleteCircuits();
317 	m_updateCircuitsTmr->stop();
318     m_updateCircuitsTmr->setSingleShot( true );
319 	m_updateCircuitsTmr->start( 0 /*, true */ );
320 }
321 
322 
connectorAdded(Connector * connector)323 void CircuitDocument::connectorAdded( Connector * connector )
324 {
325 	if (connector) {
326 		connect( connector, SIGNAL(numWiresChanged(unsigned )), this, SLOT(requestAssignCircuits()) );
327 		connect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestAssignCircuits()) );
328 	}
329 }
330 
331 
itemAdded(Item * item)332 void CircuitDocument::itemAdded( Item * item)
333 {
334 	CircuitICNDocument::itemAdded( item );
335 	componentAdded( item );
336 }
337 
338 
componentAdded(Item * item)339 void CircuitDocument::componentAdded( Item * item )
340 {
341 	Component *component = dynamic_cast<Component*>(item);
342 
343 	if (!component) return;
344 
345 	requestAssignCircuits();
346 
347 	connect( component, SIGNAL(elementCreated(Element*)), this, SLOT(requestAssignCircuits()) );
348 	connect( component, SIGNAL(elementDestroyed(Element*)), this, SLOT(requestAssignCircuits()) );
349 	connect( component, SIGNAL(removed(Item*)), this, SLOT(componentRemoved(Item*)) );
350 
351 	// We don't attach the component to the Simulator just yet, as the
352 	// Component's vtable is not yet fully constructed, and so Simulator can't
353 	// tell whether or not it is a logic component
354 	if ( !m_toSimulateList.contains(component) )
355 		m_toSimulateList << component;
356 }
357 
358 
componentRemoved(Item * item)359 void CircuitDocument::componentRemoved( Item * item )
360 {
361 	Component *component = dynamic_cast<Component*>(item);
362 
363 	if (!component) return;
364 
365 	m_componentList.removeAll( component );
366 	m_toSimulateList.removeAll( component );
367 
368 	requestAssignCircuits();
369 
370     if (!Simulator::isDestroyedSim()) {
371         Simulator::self()->detachComponent(component);
372     }
373 }
374 
375 // I think this is where the inf0z from cnodes/branches is moved into the midle-layer
376 // pins/wires.
377 
calculateConnectorCurrents()378 void CircuitDocument::calculateConnectorCurrents()
379 {
380 	const CircuitList::iterator circuitEnd = m_circuitList.end();
381 	for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitEnd; ++it )
382 		(*it)->updateCurrents();
383 
384 	PinList groundPins;
385 
386 	// Tell the Pins to reset their calculated currents to zero
387 	m_pinList.removeAll((Pin*)nullptr);
388 
389 	const PinList::iterator pinEnd = m_pinList.end();
390 	for ( PinList::iterator it = m_pinList.begin(); it != pinEnd; ++it )
391 	{
392 		if ( Pin *n = dynamic_cast<Pin*>((Pin*)*it) ) {
393 			n->resetCurrent();
394 			n->setSwitchCurrentsUnknown();
395 
396 			if ( !n->parentECNode()->isChildNode() ) {
397 				n->setCurrentKnown( true );
398 				// (and it has a current of 0 amps)
399 			} else if ( n->groundType() == Pin::gt_always ) {
400 				groundPins << n;
401 				n->setCurrentKnown( false );
402 			} else {
403 				// Child node that is non ground
404 				n->setCurrentKnown( n->parentECNode()->numPins() < 2 );
405 			}
406 		}
407 	}
408 
409 
410 	// Tell the components to update their ECNode's currents' from the elements
411 // currents are merged into PINS.
412 	const ComponentList::iterator componentEnd = m_componentList.end();
413 	for ( ComponentList::iterator it = m_componentList.begin(); it != componentEnd; ++it )
414 		(*it)->setNodalCurrents();
415 
416 
417 	// And now for the wires and switches...
418 	m_wireList.removeAll((Wire*)nullptr);
419 	const WireList::iterator clEnd = m_wireList.end();
420 	for ( WireList::iterator it = m_wireList.begin(); it != clEnd; ++it )
421 		(*it)->setCurrentKnown(false);
422 
423 	SwitchList switches = m_switchList;
424 	WireList wires = m_wireList;
425 	bool found = true;
426 	while ( (!wires.isEmpty() || !switches.isEmpty() || !groundPins.isEmpty()) && found )
427 	{
428 		found = false;
429 
430 		for ( WireList::iterator itW = wires.begin(); itW != wires.end(); )
431 		{
432 			if ( (*itW)->calculateCurrent() )
433 			{
434 				found = true;
435 				itW = wires.erase(itW);
436                 // note: assigning a temporary interator, incrementing and erasing, seems to crash
437 			} else {
438                 ++itW;
439             }
440 		}
441 
442 		SwitchList::iterator switchesEnd = switches.end();
443 		for ( SwitchList::iterator it = switches.begin(); it != switchesEnd; )
444 		{
445 			if ( (*it)->calculateCurrent() )
446 			{
447 				found = true;
448                 // note: assigning a temporary interator, incrementing and erasing, seems to crash
449                 // it = container.erase( it ) seems to crash other times
450 				SwitchList::iterator oldIt = it;
451 				++it;
452 				switches.erase(oldIt);
453 			} else ++it;
454 		}
455 
456 /*
457 make the ground pins work. Current engine doesn't treat ground explicitly.
458 */
459 
460 		PinList::iterator groundPinsEnd = groundPins.end();
461 		for ( PinList::iterator it = groundPins.begin(); it != groundPinsEnd; ) {
462 			if ( (*it)->calculateCurrentFromWires() ) {
463 				found = true;
464                 // note: assigning a temporary interator, incrementing and erasing, seems to crash sometimes;
465                 // it = container.erase( it ) seems to crash other times
466 				PinList::iterator oldIt = it;
467 				++it;
468 				groundPins.erase(oldIt);
469 			} else ++it;
470 		}
471 	}
472 }
473 
474 
assignCircuits()475 void CircuitDocument::assignCircuits()
476 {
477 	// Now we can finally add the unadded components to the Simulator
478 	const ComponentList::iterator toSimulateEnd = m_toSimulateList.end();
479 	for ( ComponentList::iterator it = m_toSimulateList.begin(); it != toSimulateEnd; ++it )
480 		Simulator::self()->attachComponent(*it);
481 
482 	m_toSimulateList.clear();
483 
484 	// Stage 0: Build up pin and wire lists
485 	m_pinList.clear();
486 
487 	const ECNodeMap::const_iterator nodeListEnd = m_ecNodeList.end();
488 	for ( ECNodeMap::const_iterator it = m_ecNodeList.begin(); it != nodeListEnd; ++it )
489 	{
490 		// if ( ECNode * ecnode = dynamic_cast<ECNode*>(*it) )
491 		ECNode* ecnode = *it;
492 
493 		for ( unsigned i = 0; i < ecnode->numPins(); i++ )
494 			m_pinList << ecnode->pin(i);
495 
496 	}
497 
498 	m_wireList.clear();
499 
500 	const ConnectorList::const_iterator connectorListEnd = m_connectorList.end();
501 	for ( ConnectorList::const_iterator it = m_connectorList.begin(); it != connectorListEnd; ++it )
502 	{
503 		for ( unsigned i = 0; i < (*it)->numWires(); i++ )
504 			m_wireList << (*it)->wire(i);
505 	}
506 
507 	typedef QList<PinList> PinListList;
508 
509 	// Stage 1: Partition the circuit up into dependent areas (bar splitting
510 	// at ground pins)
511 	PinList unassignedPins = m_pinList;
512 	PinListList pinListList;
513 
514 	while( !unassignedPins.isEmpty() ) {
515 		PinList pinList;
516 		getPartition( *unassignedPins.begin(), & pinList, & unassignedPins );
517 		pinListList.append(pinList);
518 	}
519 
520 // 	qDebug () << "pinListList.size()="<<pinListList.size()<<endl;
521 
522 	// Stage 2: Split up each partition into circuits by ground pins
523 	const PinListList::iterator nllEnd = pinListList.end();
524 	for ( PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it )
525 		splitIntoCircuits(&*it);
526 
527 	// Stage 3: Initialize the circuits
528 	m_circuitList.removeAll(nullptr);
529 	CircuitList::iterator circuitListEnd = m_circuitList.end();
530 	for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it )
531 		(*it)->init();
532 
533 	m_switchList.clear();
534 	m_componentList.clear();
535 	const ItemMap::const_iterator cilEnd = m_itemList.end();
536 	for ( ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it )
537 	{
538 		Component *component = dynamic_cast<Component*>(*it);
539 
540 		if ( !component ) continue;
541 
542 		m_componentList << component;
543 		component->initElements(0);
544 		m_switchList += component->switchList();
545 	}
546 
547 	circuitListEnd = m_circuitList.end();
548 	for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it )
549 		(*it)->createMatrixMap();
550 
551 	for ( ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it )
552 	{
553 		Component *component = dynamic_cast<Component*>(*it);
554 
555 		if (component) component->initElements(1);
556 	}
557 
558 	circuitListEnd = m_circuitList.end();
559 	for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it )
560 	{
561 		(*it)->initCache();
562 		Simulator::self()->attachCircuit(*it);
563 	}
564 }
565 
566 
getPartition(Pin * pin,PinList * pinList,PinList * unassignedPins,bool onlyGroundDependent)567 void CircuitDocument::getPartition( Pin *pin, PinList *pinList, PinList *unassignedPins, bool onlyGroundDependent )
568 {
569 	if (!pin) return;
570 
571 	unassignedPins->removeAll(pin);
572 
573 	if ( pinList->contains(pin) ) return;
574 
575 	pinList->append(pin);
576 
577 	const PinList localConnectedPins = pin->localConnectedPins();
578 	const PinList::const_iterator end = localConnectedPins.end();
579 	for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it )
580 		getPartition( *it, pinList, unassignedPins, onlyGroundDependent );
581 
582 	const PinList groundDependentPins = pin->groundDependentPins();
583 	const PinList::const_iterator dEnd = groundDependentPins.end();
584 	for ( PinList::const_iterator it = groundDependentPins.begin(); it != dEnd; ++it )
585 		getPartition( *it, pinList, unassignedPins, onlyGroundDependent );
586 
587 	if (!onlyGroundDependent) {
588 		PinList circuitDependentPins = pin->circuitDependentPins();
589 		const PinList::const_iterator dEnd = circuitDependentPins.end();
590 		for ( PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it )
591 			getPartition( *it, pinList, unassignedPins, onlyGroundDependent );
592 	}
593 }
594 
595 
splitIntoCircuits(PinList * pinList)596 void CircuitDocument::splitIntoCircuits( PinList *pinList )
597 {
598 	// First: identify ground
599 	PinList unassignedPins = *pinList;
600 	typedef QList<PinList> PinListList;
601 	PinListList pinListList;
602 
603 	while ( !unassignedPins.isEmpty() ) {
604 		PinList tempPinList;
605 		getPartition( *unassignedPins.begin(), & tempPinList, & unassignedPins, true );
606 		pinListList.append(tempPinList);
607 	}
608 
609 	const PinListList::iterator nllEnd = pinListList.end();
610 	for ( PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it )
611 		Circuit::identifyGround(*it);
612 
613 	while ( !pinList->isEmpty() ) {
614 		PinList::iterator end = pinList->end();
615 		PinList::iterator it = pinList->begin();
616 
617 		while ( it != end && (*it)->eqId() == -1 )
618 			++it;
619 
620 		if ( it == end ) break;
621 		else {
622 			Circuitoid *circuitoid = new Circuitoid;
623 			recursivePinAdd( *it, circuitoid, pinList );
624 
625 			if ( !tryAsLogicCircuit(circuitoid) )
626 				m_circuitList += createCircuit(circuitoid);
627 
628 			delete circuitoid;
629 		}
630 	}
631 
632 
633 	// Remaining pins are ground; tell them about it
634 	// TODO This is a bit hacky....
635 	const PinList::iterator end = pinList->end();
636 	for ( PinList::iterator it = pinList->begin(); it != end; ++it ) {
637 		(*it)->setVoltage(0.0);
638 		ElementList elements = (*it)->elements();
639 		const ElementList::iterator eEnd = elements.end();
640 		for ( ElementList::iterator it = elements.begin(); it != eEnd; ++it )
641 		{
642 			if ( LogicIn * logicIn = dynamic_cast<LogicIn*>(*it) )
643 			{
644 				logicIn->setLastState(false);
645 				logicIn->callCallback();
646 			}
647 		}
648 	}
649 }
650 
651 
recursivePinAdd(Pin * pin,Circuitoid * circuitoid,PinList * unassignedPins)652 void CircuitDocument::recursivePinAdd( Pin *pin, Circuitoid *circuitoid, PinList *unassignedPins )
653 {
654 	if(!pin) return;
655 
656 	if(pin->eqId() != -1 )
657 		unassignedPins->removeAll(pin);
658 
659 	if(circuitoid->contains(pin) ) return;
660 
661 	circuitoid->addPin(pin);
662 
663 	if(pin->eqId() == -1 ) return;
664 
665 	const PinList localConnectedPins = pin->localConnectedPins();
666 	const PinList::const_iterator end = localConnectedPins.end();
667 	for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it )
668 		recursivePinAdd( *it, circuitoid, unassignedPins );
669 
670 	const PinList groundDependentPins = pin->groundDependentPins();
671 	const PinList::const_iterator gdEnd = groundDependentPins.end();
672 	for ( PinList::const_iterator it = groundDependentPins.begin(); it != gdEnd; ++it )
673 		recursivePinAdd( *it, circuitoid, unassignedPins );
674 
675 	const PinList circuitDependentPins = pin->circuitDependentPins();
676 	const PinList::const_iterator cdEnd = circuitDependentPins.end();
677 	for ( PinList::const_iterator it = circuitDependentPins.begin(); it != cdEnd; ++it )
678 		recursivePinAdd( *it, circuitoid, unassignedPins );
679 
680 	const ElementList elements = pin->elements();
681 	const ElementList::const_iterator eEnd = elements.end();
682 	for ( ElementList::const_iterator it = elements.begin(); it != eEnd; ++it )
683 		circuitoid->addElement(*it);
684 }
685 
686 
tryAsLogicCircuit(Circuitoid * circuitoid)687 bool CircuitDocument::tryAsLogicCircuit( Circuitoid *circuitoid )
688 {
689 	if (!circuitoid) return false;
690 
691 	if ( circuitoid->elementList.size() == 0 )
692 	{
693 		// This doesn't quite belong here...but whatever. Initialize all
694 		// pins to voltage zero as they won't get set to zero otherwise
695 		const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd();
696 		for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it )
697 			(*it)->setVoltage(0.0);
698 
699 		// A logic circuit only requires there to be no non-logic components,
700 		// and at most one LogicOut - so this qualifies
701 		return true;
702 	}
703 
704 	LogicInList logicInList;
705 	LogicOut *out = nullptr;
706 
707 	uint logicInCount = 0;
708 	uint logicOutCount = 0;
709 	ElementList::const_iterator end = circuitoid->elementList.end();
710 	for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it )
711 	{
712 		if ( (*it)->type() == Element::Element_LogicOut )
713 		{
714 			logicOutCount++;
715 			out = static_cast<LogicOut*>(*it);
716 		} else if ( (*it)->type() == Element::Element_LogicIn )
717 		{
718 			logicInCount++;
719 			logicInList += static_cast<LogicIn*>(*it);
720 		} else return false;
721 	}
722 
723 	if ( logicOutCount > 1 ) return false;
724 	else if ( logicOutCount == 1 )
725 		Simulator::self()->createLogicChain( out, logicInList, circuitoid->pinList );
726 	else {
727 		// We have ourselves stranded LogicIns...so lets set them all to low
728 
729 		const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd();
730 		for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it )
731 			(*it)->setVoltage(0.0);
732 
733 		for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it )
734 		{
735 			LogicIn * logicIn = static_cast<LogicIn*>(*it);
736 			logicIn->setNextLogic(nullptr);
737 			logicIn->setElementSet(nullptr);
738 			if ( logicIn->isHigh() )
739 			{
740 				logicIn->setLastState(false);
741 				logicIn->callCallback();
742 			}
743 		}
744 	}
745 
746 	return true;
747 }
748 
749 
createCircuit(Circuitoid * circuitoid)750 Circuit *CircuitDocument::createCircuit( Circuitoid *circuitoid )
751 {
752 	if (!circuitoid) return nullptr;
753 
754 	Circuit *circuit = new Circuit();
755 
756 	const PinList::const_iterator nEnd = circuitoid->pinList.end();
757 	for ( PinList::const_iterator it = circuitoid->pinList.begin(); it != nEnd; ++it )
758 		circuit->addPin(*it);
759 
760 	const ElementList::const_iterator eEnd = circuitoid->elementList.end();
761 	for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != eEnd; ++it )
762 		circuit->addElement(*it);
763 
764 	return circuit;
765 }
766 
767 
createSubcircuit()768 void CircuitDocument::createSubcircuit()
769 {
770 	ItemList itemList = m_selectList->items();
771 	const ItemList::iterator itemListEnd = itemList.end();
772 	for ( ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it )
773 	{
774 		if ( !dynamic_cast<Component*>((Item*)*it) )
775 			*it = nullptr;
776 	}
777 
778 	itemList.removeAll((Item*)nullptr);
779 
780 	if ( itemList.isEmpty() ) {
781 		KMessageBox::sorry( activeView(), i18n("No components were found in the selection.") );
782 		return;
783 	}
784 
785 	// Number of external connections
786 	const int extConCount = countExtCon(itemList);
787 	if ( extConCount == 0 ) {
788 		KMessageBox::sorry( activeView(), i18n("No External Connection components were found in the selection.") );
789 		return;
790 	}
791 
792 	bool ok;
793 	const QString name = QInputDialog::getText(activeView(), "Subcircuit", "Name", QLineEdit::Normal, QString(), &ok);
794 	if (!ok) return;
795 
796 	SubcircuitData subcircuit;
797 	subcircuit.addItems(itemList);
798 	subcircuit.addNodes( getCommonNodes(itemList) );
799 	subcircuit.addConnectors( getCommonConnectors(itemList) );
800 
801 	Subcircuits::addSubcircuit( name, subcircuit.toXML() );
802 }
803 
804 
countExtCon(const ItemList & itemList) const805 int CircuitDocument::countExtCon( const ItemList &itemList ) const
806 {
807 	int count = 0;
808 	const ItemList::const_iterator end = itemList.end();
809 	for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it )
810 	{
811 		Item * item = *it;
812 
813 		if ( item && item->type() == "ec/external_connection" )
814 			count++;
815 	}
816 	return count;
817 }
818 
819 
isValidItem(const QString & itemId)820 bool CircuitDocument::isValidItem( const QString &itemId )
821 {
822 	return itemId.startsWith("ec/") || itemId.startsWith("dp/") || itemId.startsWith("sc/");
823 }
824 
825 
isValidItem(Item * item)826 bool CircuitDocument::isValidItem( Item *item )
827 {
828 	return (dynamic_cast<Component*>(item) || dynamic_cast<DrawPart*>(item));
829 }
830 
831 
displayEquations()832 void CircuitDocument::displayEquations()
833 {
834 	qDebug() << "######################################################" << endl;
835 	const CircuitList::iterator end = m_circuitList.end();
836 	int i = 1;
837 	for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it )
838 	{
839 		qDebug() << "Equation set "<<i<<":\n";
840 		(*it)->displayEquations();
841 		i++;
842 	}
843 	qDebug() << "######################################################" << endl;
844 }
845