1 /*******************************************************************
2 
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5 
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with Fritzing.  If not, see <http://www.gnu.org/licenses/>.
18 
19 ********************************************************************
20 
21 $Revision: 6998 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $
24 
25 ********************************************************************/
26 
27 /*
28 
29 rubberBand TODO:
30 
31 	* show connectors under
32 
33 	* undo moves & connections
34 
35 	* adjust position so that connectorItem is in the center of connected-to connectorItem
36 
37 	* save and load
38 
39 	* alt/meta/ctrl to drag out a wire
40 
41 	* rubberBand drag
42 
43 	* update connections needs to be smarter (has to to with connecting to wires)
44 		look again at attachedMoved()
45 
46 	* arrow key moves
47 
48 	* hover: trigger the usual part hover highlight
49 
50 	* drag selection should work as normal
51 
52 	* rubberBand drag when part is stretched between two or more parts, some not being dragged correctly
53 
54 	* if a part is locked, disable dragging the leg
55 
56 	* fzp  has "legId" (and someday? "maxlength" or is that in the svg) in <p> element
57 		put the leg definition as a line in the svg, with connectorNleg
58 		then on loading, remove the leg, and change the viewbox height and width
59 			this is tricky.  Better if the leg extends outside the viewbox, then can easily tell which end is draggable
60 		then draw the leg as now
61 
62 	* figure out how to make the connector longer or its clickable area bigger, or if you click on the wire within a few pixels..
63 		since it's easy to grab, no need for some kind of fast disconnect
64 
65 	* hover color makes a mess when dragging leg
66 
67 	* put legItem back into connector item
68 
69 	* export: retrieve svg must remove the rubberBand <line> element
70 
71 	* make it a polygon instead of a line
72 
73 	* what to do when line length is zero
74 
75 	* renderToSVG: make sure sceneBoundingRect is including legs
76 
77 	* delete/undo delete
78 
79 	* clean up pixel turds
80 
81 	* rotate/flip
82 		do not disconnect
83 		should transform around center of the itemBase with no legs
84 
85 	* leg cursor feedback
86 
87 	* move behavior: what to do when dragging a leg: bendpoints
88 
89 	* complex bent leg fails after 2nd rotate
90 
91 	* rotate/flip undo failure
92 
93 	* when itembase is rotated leg or bendpoint drag behavior is screwed up
94 
95 	* rotate target is not correct
96 
97 	* bendpoint redo (after adding multiple bendpoints) is failing
98 
99 	* subclass leg connectoritem?  not for the moment
100 
101 	* remove bendpoint: right click, double click
102 
103 	* bendpoints: shift-90 degree?
104 
105 	* copy/paste
106 		connected and not
107 
108 	* bad crash when swapping back to unrubberBand.  probably some kind of boundingRect issue...
109 
110 	* crash: swappable, swappable, undo, redo
111 
112 	* swapping parts with rubberBand legs, can assume pins will always line up (unless legs can have diffent max distances)
113 		* no-no
114 		* no-yes
115 		* yes-no
116 		* yes-yes
117 
118 	* rotate rubberBand, swap rubberBand, undo: crash of the item being undone.  it's a prepareGeometryChange() bug
119 
120 	* swapping when original is rotated
121 
122 	crash swapping 3v battery for 4.8 when 3v is rotated 45
123 
124 	* click selection behavior should be as if selecting the part
125 		click on leg should select part
126 
127 	* update bug when a rubberBand part has all legs connected and the part is dragged
128 		within a particular region, the part body stops updating--
129 		but the legs follow the phantom part until the part jumps into position
130 
131 	* export: resistors and other custom generated parts with legs (retrieve svg)
132 
133 	curve: undo/redo
134 		at mouse release curve is killed
135 
136 	* curve: save/load
137 
138 	* curve: copy/paste
139 
140 	* curve:export
141 
142 	* curve: make straight function
143 
144 	* curve: fix connector indicator
145 
146 	* curve: fix connector click region
147 
148 	* curve: connector region is not following when dragging connector
149 
150 	when dragging to breadboard from parts bin, don't get final alignment to breadboard
151 
152 	survival in parts editor
153 
154 	swapping: keep bends?
155 
156 	bendpoints: align to grid?
157 
158 	resistor: leg y-coordinate is slightly off
159 
160 	parts to modify
161 		** LEDs (obsolete 5 colors and 15 SMD versions)
162 			maintain color when switching from obsolete
163 		** RGB LEDs
164 		** resistors
165 		** tantalum caps
166 		** electrolytic caps
167 		** ceramic caps
168 		** diodes
169 		** tilt sensor
170 		** temperature sensor
171 		** light sensor
172 		** reed switch
173 		** 2aa battery
174 		** 4aaa battery
175 		** 9v battery
176 		** stepper motors
177 		** servo
178 		** dc motor
179 		** piezo
180 		** loudspeaker
181 		** mic
182 		** solenoid
183 		** peltier
184 		** distance sensor
185 		** transistors
186 		** FETs
187 		** voltage regulator
188 		** resonator
189 		** inductor
190 		** crystal
191 
192 		<path
193 			style="fill:none;fill-rule:evenodd;stroke:#c8ab37;stroke-width:3.54330707;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
194 			d="M 256.20728,390.8502 C 256.20728,390.8502 256.20728,381.58878 256.20728,381.58878"
195 			id="FourWire3End" />
196 
197 
198 -------------------------------------------------
199 
200 rubberBand drag with snap-disconnect after a certain length is reached
201 
202 parts editor support
203 
204 */
205 
206 ///////////////////////////////////////////////////////
207 
208 #include "connectoritem.h"
209 
210 #include <QBrush>
211 #include <QPen>
212 #include <QColor>
213 #include <limits>
214 #include <QSet>
215 #include <QToolTip>
216 #include <QBitmap>
217 #include <QApplication>
218 #include <qmath.h>
219 
220 #include "../sketch/infographicsview.h"
221 #include "../debugdialog.h"
222 #include "bus.h"
223 #include "../items/wire.h"
224 #include "../items/virtualwire.h"
225 #include "../model/modelpart.h"
226 #include "../utils/graphicsutils.h"
227 #include "../utils/graphutils.h"
228 #include "../utils/textutils.h"
229 #include "../utils/ratsnestcolors.h"
230 #include "../utils/bezier.h"
231 #include "../utils/bezierdisplay.h"
232 #include "../utils/cursormaster.h"
233 #include "ercdata.h"
234 
235 /////////////////////////////////////////////////////////
236 
237 static Bezier UndoBezier;
238 static BezierDisplay * TheBezierDisplay = NULL;
239 
240 static const double StandardLegConnectorDrawEnabledLength = 5;		// pixels
241 static const double StandardLegConnectorDetectLength = 9;			// pixels
242 
243 QList<ConnectorItem *> ConnectorItem::m_equalPotentialDisplayItems;
244 
245 const QList<ConnectorItem *> ConnectorItem::emptyConnectorItemList;
246 
247 static double MAX_DOUBLE = std::numeric_limits<double>::max();
248 
wireLessThan(ConnectorItem * c1,ConnectorItem * c2)249 bool wireLessThan(ConnectorItem * c1, ConnectorItem * c2)
250 {
251 	if (c1->connectorType() == c2->connectorType()) {
252 		// if they're the same type return the topmost
253 		return c1->zValue() > c2->zValue();
254 	}
255 	if (c1->connectorType() == Connector::Female) {
256 		// choose the female first
257 		return true;
258 	}
259 	if (c2->connectorType() == Connector::Female) {
260 		// choose the female first
261 		return false;
262 	}
263 	if (c1->connectorType() == Connector::Male) {
264 		// choose the male first
265 		return true;
266 	}
267 	if (c2->connectorType() == Connector::Male) {
268 		// choose the male first
269 		return false;
270 	}
271 	if (c1->connectorType() == Connector::Pad) {
272 		// choose the pad first
273 		return true;
274 	}
275 	if (c2->connectorType() == Connector::Pad) {
276 		// choose the pad first
277 		return false;
278 	}
279 
280 	// Connector::Wire last
281 	return c1->zValue() > c2->zValue();
282 }
283 
addColor(QColor & color,int offset)284 QColor addColor(QColor & color, int offset)
285 {
286     QColor rgb = color.toRgb();
287 	rgb.setRgb(qMax(0, qMin(rgb.red() + offset, 255)), qMax(0, qMin(rgb.green() + offset, 255)),qMax(0, qMin(rgb.blue() + offset, 255)));
288 
289     // convert back to same color spec as original color
290     return rgb.convertTo(color.spec());
291 }
292 
293 /////////////////////////////////////////////////////////////
294 
ConnectorItemAction(QAction * action)295 ConnectorItemAction::ConnectorItemAction(QAction * action) : QAction(action) {
296 	m_connectorItem = NULL;
297 	this->setText(action->text());
298 	this->setStatusTip(action->statusTip());
299 	this->setCheckable(action->isCheckable());
300 }
301 
ConnectorItemAction(const QString & title,QObject * parent)302 ConnectorItemAction::ConnectorItemAction(const QString & title, QObject * parent) : QAction(title, parent) {
303 	m_connectorItem = NULL;
304 }
305 
setConnectorItem(ConnectorItem * c)306 void ConnectorItemAction::setConnectorItem(ConnectorItem * c) {
307 	m_connectorItem = c;
308 }
309 
connectorItem()310 ConnectorItem * ConnectorItemAction::connectorItem() {
311 	return m_connectorItem;
312 }
313 
314 /////////////////////////////////////////////////////////
315 
ConnectorItem(Connector * connector,ItemBase * attachedTo)316 ConnectorItem::ConnectorItem( Connector * connector, ItemBase * attachedTo )
317 	: NonConnectorItem(attachedTo)
318 {
319 	// initialize m_connectorT, otherwise will trigger qWarning("QLine::unitVector: New line does not have unit length");
320 	// TODO: figure out why paint is being called with m_connectorT not initialized
321 	m_groundFillSeed = false;
322 	m_connectorDetectT = m_connectorDrawT = 0;
323 	m_draggingCurve = m_draggingLeg = m_rubberBandLeg = m_bigDot = m_hybrid = false;
324 	m_hoverEnterSpaceBarWasPressed = m_spaceBarWasPressed = false;
325 	m_overConnectorItem = NULL;
326 	m_connectorHovering = false;
327 	m_connector = connector;
328 	if (connector != NULL) {
329 		connector->addViewItem(this);
330 	}
331     setAcceptHoverEvents(true);
332     this->setCursor((attachedTo && attachedTo->itemType() == ModelPart::Wire) ? *CursorMaster::BendpointCursor : *CursorMaster::MakeWireCursor);
333 
334 	//DebugDialog::debug(QString("%1 attached to %2")
335 			//.arg(this->connector()->connectorShared()->id())
336 			//.arg(attachedTo->modelPartShared()->title()) );
337 }
338 
~ConnectorItem()339 ConnectorItem::~ConnectorItem() {
340 	m_equalPotentialDisplayItems.removeOne(this);
341 	//DebugDialog::debug(QString("deleting connectorItem %1").arg((long) this, 0, 16));
342 	foreach (ConnectorItem * connectorItem, m_connectedTo) {
343 		if (connectorItem != NULL) {
344 			//DebugDialog::debug(QString("temp remove %1 %2").arg(this->attachedToID()).arg(connectorItem->attachedToID()));
345 			connectorItem->tempRemove(this, this->attachedToID() != connectorItem->attachedToID());
346 		}
347 	}
348 	if (this->connector() != NULL) {
349 		this->connector()->removeViewItem(this);
350 	}
351 	clearCurves();
352 }
353 
hoverEnterEvent(QGraphicsSceneHoverEvent * event)354 void ConnectorItem::hoverEnterEvent ( QGraphicsSceneHoverEvent * event ) {
355 
356     //debugInfo("connector hoverEnter");
357 	/*
358 	QRectF sbr = this->sceneBoundingRect();
359 	QPointF p = event->scenePos();
360 
361 	debugInfo(QString("hover %1, %2 %3 %4 %5, %6 %7")
362 		.arg((long) this, 0, 16)
363 		.arg(sbr.left())
364 		.arg(sbr.top())
365 		.arg(sbr.width())
366 		.arg(sbr.height())
367 		.arg(p.x())
368 		.arg(p.y())
369 		);
370 	*/
371 
372 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
373 	if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) {
374 		m_hoverEnterSpaceBarWasPressed = true;
375 		event->ignore();
376 		return;
377 	}
378 
379 	//DebugDialog::debug("---CI set override cursor");
380 	CursorMaster::instance()->addCursor(this, cursor());
381 	bool setDefaultCursor = true;
382 	m_hoverEnterSpaceBarWasPressed = false;
383 	setHoverColor();
384 	if (infoGraphicsView != NULL) {
385 		infoGraphicsView->hoverEnterConnectorItem(event, this);
386 		if (m_rubberBandLeg) {
387 			updateLegCursor(event->pos(), event->modifiers());
388 			setDefaultCursor = false;
389 		}
390 	}
391 	if (this->m_attachedTo != NULL) {
392 		if (this->attachedToItemType() == ModelPart::Wire) {
393 			updateWireCursor(event->modifiers());
394 			setDefaultCursor = false;
395 		}
396 		m_attachedTo->hoverEnterConnectorItem(event, this);
397 	}
398 
399 	if (setDefaultCursor) CursorMaster::instance()->addCursor(this, *CursorMaster::MakeWireCursor);
400 }
401 
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)402 void ConnectorItem::hoverLeaveEvent ( QGraphicsSceneHoverEvent * event ) {
403 	if (m_hoverEnterSpaceBarWasPressed) {
404 		event->ignore();
405 		return;
406 	}
407 
408     QList<ConnectorItem *> visited;
409 	restoreColor(visited);
410 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
411 	if (infoGraphicsView != NULL) {
412 		infoGraphicsView->hoverLeaveConnectorItem(event, this);
413 	}
414 
415 	CursorMaster::instance()->removeCursor(this);
416 
417 	if (this->m_attachedTo != NULL) {
418 		m_attachedTo->hoverLeaveConnectorItem(event, this);
419 	}
420 
421 	//DebugDialog::debug("------CI restore override cursor");
422 	CursorMaster::instance()->removeCursor(this);
423 }
424 
hoverMoveEvent(QGraphicsSceneHoverEvent * event)425 void ConnectorItem::hoverMoveEvent ( QGraphicsSceneHoverEvent * event ) {
426 	if (m_hoverEnterSpaceBarWasPressed) {
427 		event->ignore();
428 		return;
429 	}
430 
431 	if (this->m_attachedTo != NULL) {
432 		m_attachedTo->hoverMoveConnectorItem(event, this);
433 	}
434 
435 	if (m_rubberBandLeg) {
436 		updateLegCursor(event->pos(), event->modifiers());
437 	}
438 }
439 
connector()440 Connector * ConnectorItem::connector() {
441 	return m_connector;
442 }
443 
clearConnectorHover()444 void ConnectorItem::clearConnectorHover() {
445 	m_connectorHovering = false;
446 }
447 
connectorHover(ItemBase * itemBase,bool hovering)448 void ConnectorItem::connectorHover(ItemBase * itemBase, bool hovering) {
449 	m_connectorHovering = hovering;
450 	if (hovering) {
451 		setHoverColor();			// could make this light up buses as well
452 	}
453 	else {
454         QList<ConnectorItem *> visited;
455 		restoreColor(visited);
456 	}
457 	if (this->m_attachedTo != NULL) {
458 		m_attachedTo->connectorHover(this, itemBase, hovering);
459 	}
460 }
461 
connectorHovering()462 bool ConnectorItem::connectorHovering() {
463 	return m_connectorHovering;
464 }
465 
connectTo(ConnectorItem * connected)466 void ConnectorItem::connectTo(ConnectorItem * connected) {
467 	if (m_connectedTo.contains(connected)) return;
468 
469 	m_connectedTo.append(connected);
470 	//DebugDialog::debug(QString("connect to cc:%4 this:%1 to:%2 %3").arg((long) this, 0, 16).arg((long) connected, 0, 16).arg(connected->attachedTo()->modelPartShared()->title()).arg(m_connectedTo.count()) );
471     QList<ConnectorItem *> visited;
472 	restoreColor(visited);
473 	if (m_attachedTo != NULL) {
474 		m_attachedTo->connectionChange(this, connected, true);
475 	}
476 }
477 
removeConnection(ItemBase * itemBase)478 ConnectorItem * ConnectorItem::removeConnection(ItemBase * itemBase) {
479     QList<ConnectorItem *> visited;
480 	for (int i = 0; i < m_connectedTo.count(); i++) {
481 		if (m_connectedTo[i]->attachedTo() == itemBase) {
482 			ConnectorItem * removed = m_connectedTo[i];
483 			m_connectedTo.removeAt(i);
484 			if (m_attachedTo != NULL) {
485 				m_attachedTo->connectionChange(this, removed, false);
486 			}
487 			restoreColor(visited);
488 			DebugDialog::debug(QString("remove from:%1 to:%2 count%3")
489 				.arg((long) this, 0, 16)
490 				.arg(itemBase->modelPartShared()->title())
491 				.arg(m_connectedTo.count()) );
492 			return removed;
493 		}
494 	}
495 
496 	return NULL;
497 }
498 
removeConnection(ConnectorItem * connectedItem,bool emitChange)499 void ConnectorItem::removeConnection(ConnectorItem * connectedItem, bool emitChange) {
500 	if (connectedItem == NULL) return;
501 
502 	m_connectedTo.removeOne(connectedItem);
503     QList<ConnectorItem *> visited;
504 	restoreColor(visited);
505 	if (emitChange) {
506 		m_attachedTo->connectionChange(this, connectedItem, false);
507 	}
508 }
509 
tempConnectTo(ConnectorItem * item,bool applyColor)510 void ConnectorItem::tempConnectTo(ConnectorItem * item, bool applyColor) {
511 	if (!m_connectedTo.contains(item)) m_connectedTo.append(item);
512 
513 	if(applyColor) {
514         QList<ConnectorItem *> visited;
515         restoreColor(visited);
516     }
517 }
518 
tempRemove(ConnectorItem * item,bool applyColor)519 void ConnectorItem::tempRemove(ConnectorItem * item, bool applyColor) {
520 	m_connectedTo.removeOne(item);
521 
522 	if(applyColor) {
523         QList<ConnectorItem *> visited;
524         restoreColor(visited);
525     }
526 }
527 
restoreColor(QList<ConnectorItem * > & visited)528 void ConnectorItem::restoreColor(QList<ConnectorItem *> & visited)
529 {
530     if (visited.contains(this)) return;
531     visited.append(this);
532 
533 	if (!attachedTo()->isEverVisible()) return;
534 
535     QList<ConnectorItem *> connectorItems;
536     connectorItems.append(this);
537     collectEqualPotential(connectorItems, true, getSkipFlags());
538     visited.append(connectorItems);
539     QSet<ItemBase *> attachedTo;
540     foreach (ConnectorItem * connectorItem, connectorItems) {
541         if (connectorItem->isEverVisible()) {
542             if (connectorItem->attachedToItemType() != ModelPart::Wire) {
543                 attachedTo.insert(connectorItem->attachedTo()->layerKinChief());
544             }
545         }
546     }
547 
548     foreach (ConnectorItem * connectorItem, connectorItems) {
549         if (connectorItem->isEverVisible()) {
550 	        //QString how;
551 	        if (attachedTo.count() <= 1) {
552 		        if (connectorItem->connectorType() == Connector::Female) {
553                     if (connectorItem->connectionsCount() > 0) connectorItem->setUnconnectedColor();
554 			        else connectorItem->setNormalColor();
555 			        //how = "normal";
556 		        }
557 		        else {
558 			        connectorItem->setUnconnectedColor();
559 			        //how = "unconnected";
560 		        }
561 	        }
562 	        else {
563 		        connectorItem->setConnectedColor();
564 		        //how = "connected";
565 	        }
566         }
567     }
568 
569 
570 	/*
571 	DebugDialog::debug(QString("restore color dobus:%1 bccount:%2 docross:%3 cid:'%4' '%5' id:%6 '%7' vid:%8 vlid:%9 %10")
572 		.arg(doBuses)
573 		.arg(busConnectionCount)
574 		.arg(doCross)
575 		.arg(this->connectorSharedID())
576 		.arg(this->connectorSharedName())
577 		.arg(this->attachedToID())
578 		.arg(this->attachedToInstanceTitle())
579 		.arg(this->attachedToViewID())
580 		.arg(this->attachedToViewLayerID())
581 		.arg(how)
582 	);
583 
584 	*/
585 }
586 
setConnectedColor()587 void ConnectorItem::setConnectedColor() {
588 	if (m_attachedTo == NULL) return;
589 
590 	QBrush brush;
591 	QPen pen;
592 	m_attachedTo->getConnectedColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect);
593 	//DebugDialog::debug(QString("set connected %1 %2").arg(attachedToID()).arg(pen->width()));
594 	setColorAux(brush, pen, true);
595 }
596 
setNormalColor()597 void ConnectorItem::setNormalColor() {
598 	if (m_attachedTo == NULL) return;
599 
600 	QBrush brush;
601 	QPen pen;
602 	m_attachedTo->getNormalColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect);
603 	//DebugDialog::debug(QString("set normal %1 %2").arg(attachedToID()).arg(pen->width()));
604 	setColorAux(brush, pen, false);
605 }
606 
setUnconnectedColor()607 void ConnectorItem::setUnconnectedColor() {
608 	if (m_attachedTo == NULL) return;
609 
610 	QBrush brush;
611 	QPen pen;
612 	//DebugDialog::debug(QString("set unconnected %1").arg(attachedToID()) );
613 	m_attachedTo->getUnconnectedColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect);
614 	setColorAux(brush, pen, true);
615 }
616 
setHoverColor()617 void ConnectorItem::setHoverColor() {
618 	if (m_attachedTo == NULL) return;
619 
620 	QBrush brush;
621 	QPen pen;
622 	m_attachedTo->getHoverColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect);
623 	setColorAux(brush, pen, true);
624 }
625 
setColorAux(const QBrush & brush,const QPen & pen,bool paint)626 void ConnectorItem::setColorAux(const QBrush & brush, const QPen & pen, bool paint) {
627     //debugInfo(QString("setColorAux %1 %2").arg(brush.color().name()).arg(pen.color().name()));
628 	m_paint = paint;
629 	this->setBrush(brush);
630 	this->setPen(pen);
631 	update();
632 }
633 
setColorAux(const QColor & color,bool paint)634 void ConnectorItem::setColorAux(const QColor &color, bool paint) {
635     setColorAux(QBrush(color), QPen(color), paint);
636 }
637 
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)638 void ConnectorItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) {
639 
640 	//DebugDialog::debug("in connectorItem mouseReleaseEvent");
641 	clearEqualPotentialDisplay();
642 
643 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
644 
645 	if (m_rubberBandLeg && m_draggingLeg) {
646 		m_draggingLeg = false;
647 
648 		if (m_insertBendpointPossible) {
649 			// didn't move far enough; bail
650 			return;
651 		}
652 
653 		if (m_moveCount == 0) {
654 			// never moved
655 			return;
656 		}
657 
658 		ConnectorItem * to = releaseDrag();
659 
660 		if (m_draggingCurve) {
661 			m_draggingCurve = false;
662 			if (TheBezierDisplay) {
663 				delete TheBezierDisplay;
664 				TheBezierDisplay = NULL;
665 			}
666 
667 			if (infoGraphicsView != NULL) {
668 				infoGraphicsView->prepLegCurveChange(this, m_draggingLegIndex, &UndoBezier, m_legCurves.at(m_draggingLegIndex), false);
669 			}
670 
671 			return;
672 		}
673 
674 		if (m_oldPolygon.count() < m_legPolygon.count()) {
675 			// we inserted a bendpoint
676 			InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
677 			if (infoGraphicsView != NULL) {
678 				infoGraphicsView->prepLegBendpointChange(this, m_oldPolygon.count(), m_legPolygon.count(), m_draggingLegIndex, m_legPolygon.at(m_draggingLegIndex),
679 									&UndoBezier, m_legCurves.at(m_draggingLegIndex -1), m_legCurves.at(m_draggingLegIndex), false);
680 			}
681 			return;
682 		}
683 
684 		bool changeConnections = m_draggingLegIndex == m_legPolygon.count() - 1;
685 		if (to != NULL && changeConnections) {
686 			// center endpoint in the target connectorItem
687 			reposition(to->sceneAdjustedTerminalPoint(NULL), m_draggingLegIndex);
688 		}
689 		if (infoGraphicsView != NULL) {
690 			infoGraphicsView->prepLegBendpointMove(this, m_draggingLegIndex, mapToScene(m_oldPolygon.at(m_draggingLegIndex)), mapToScene(m_legPolygon.at(m_draggingLegIndex)), to, changeConnections);
691 		}
692 		return;
693 	}
694 
695 	if (this->m_attachedTo != NULL && m_attachedTo->acceptsMouseReleaseConnectorEvent(this, event)) {
696 		m_attachedTo->mouseReleaseConnectorEvent(this, event);
697 		return;
698 	}
699 
700 	QGraphicsRectItem::mouseReleaseEvent(event);
701 }
702 
mouseDoubleClickEvent(QGraphicsSceneMouseEvent * event)703 void ConnectorItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) {
704 	if (m_rubberBandLeg) {
705 		int bendpointIndex;
706 		CursorLocation cursorLocation = findLocation(event->pos(), bendpointIndex);
707 		switch (cursorLocation) {
708 			case InBendpoint:
709 				if (bendpointIndex < m_legPolygon.count() - 1) {
710 					removeBendpoint(bendpointIndex);
711 				}
712 				break;
713 			case InSegment:
714 				insertBendpoint(event->pos(), bendpointIndex);
715 				break;
716 			default:
717 				break;
718 		}
719 
720 		return;
721 	}
722 
723 	if (this->m_attachedTo != NULL && m_attachedTo->acceptsMouseDoubleClickConnectorEvent(this, event)) {
724 		m_attachedTo->mouseDoubleClickConnectorEvent(this);
725 		return;
726 	}
727 
728 	QGraphicsRectItem::mouseDoubleClickEvent(event);
729 }
730 
mouseMoveEvent(QGraphicsSceneMouseEvent * event)731 void ConnectorItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) {
732 	m_moveCount++;
733 	if (m_rubberBandLeg && m_draggingLeg) {
734 		if (m_draggingCurve) {
735 			Bezier * bezier = m_legCurves.at(m_draggingLegIndex);
736 			if (bezier != NULL && !bezier->isEmpty()) {
737 				prepareGeometryChange();
738 				bezier->recalc(event->pos());
739 				calcConnectorEnd();
740 				update();
741 				if (TheBezierDisplay) TheBezierDisplay->updateDisplay(this, bezier);
742 				return;
743 			}
744 		}
745 
746 		QPointF currentPos = event->scenePos();
747         QPointF buttonDownPos = event->buttonDownScenePos(Qt::LeftButton);
748 
749 		if (m_insertBendpointPossible) {
750 			if (qSqrt(GraphicsUtils::distanceSqd(currentPos, buttonDownPos)) >= QApplication::startDragDistance()) {
751 				insertBendpointAux(m_holdPos, m_draggingLegIndex);
752 				m_insertBendpointPossible = false;
753 			}
754 			else {
755 				return;
756 			}
757 		}
758 
759 		if (event->modifiers() & Qt::ShiftModifier && m_draggingLegIndex > 0 && m_draggingLegIndex < m_legPolygon.count() - 1) {
760 			bool bendpoint = false;
761 			QPointF initialPos = mapToScene(m_legPolygon.at(m_draggingLegIndex - 1));
762 			QPointF otherInitialPos = mapToScene(m_legPolygon.at(m_draggingLegIndex + 1));
763 			QPointF p1(initialPos.x(), otherInitialPos.y());
764 			double d = GraphicsUtils::distanceSqd(p1, currentPos);
765 			if (d <= 144) {
766 				bendpoint = true;
767 				currentPos = p1;
768 			}
769 			else {
770 				p1.setX(otherInitialPos.x());
771 				p1.setY(initialPos.y());
772 				d = GraphicsUtils::distanceSqd(p1, currentPos);
773 				if (d <= 144) {
774 					bendpoint = true;
775 					currentPos = p1;
776 				}
777 			}
778 
779 			if (!bendpoint) {
780 				currentPos = GraphicsUtils::calcConstraint(initialPos, currentPos);
781 			}
782 		}
783 
784 		reposition(m_holdPos + currentPos - buttonDownPos, m_draggingLegIndex);
785 
786 		QList<ConnectorItem *> exclude;
787 		findConnectorUnder(true, true, exclude, true, this);
788 
789 		return;
790 	}
791 
792 	if (this->m_attachedTo != NULL && m_attachedTo->acceptsMouseMoveConnectorEvent(this, event)) {
793 		m_attachedTo->mouseMoveConnectorEvent(this, event);
794 		return;
795 	}
796 
797 	QGraphicsRectItem::mouseMoveEvent(event);
798 }
799 
mousePressEvent(QGraphicsSceneMouseEvent * event)800 void ConnectorItem::mousePressEvent(QGraphicsSceneMouseEvent *event) {
801 	m_draggingCurve = m_draggingLeg = false;
802 	m_moveCount = 0;
803 
804 	if (event->button() != Qt::LeftButton) {
805 		QGraphicsRectItem::mousePressEvent(event);
806 		return;
807 	}
808 
809 	if (m_attachedTo->filterMousePressConnectorEvent(this, event)) {
810 		event->ignore();
811 		return;
812 	}
813 
814 	clearEqualPotentialDisplay();
815 
816 	InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this);
817 	if (infographics != NULL && infographics->spaceBarIsPressed()) {
818 		event->ignore();
819 		return;
820 	}
821 
822 	m_equalPotentialDisplayItems.append(this);
823 	collectEqualPotential(m_equalPotentialDisplayItems, true, ViewGeometry::NoFlag);
824 	//m_equalPotentialDisplayItems.removeAt(0);									// not sure whether to leave the clicked one in or out of the list
825 	QList<ConnectorItem *> visited;
826     //DebugDialog::debug("_______________________");
827     foreach (ConnectorItem * connectorItem, m_equalPotentialDisplayItems) {
828 		connectorItem->showEqualPotential(true, visited);
829         //connectorItem->debugInfo("display eqp");
830 	}
831 
832 	if (m_rubberBandLeg && this->m_attachedTo != NULL && m_attachedTo->acceptsMousePressLegEvent(this, event)) {
833 		if (legMousePressEvent(event)) return;
834 	}
835 
836 	if (this->m_attachedTo != NULL && m_attachedTo->acceptsMousePressConnectorEvent(this, event)) {
837 		m_attachedTo->mousePressConnectorEvent(this, event);
838 		return;
839 	}
840 
841 	QGraphicsRectItem::mousePressEvent(event);
842 }
843 
connectionsCount()844 int ConnectorItem::connectionsCount() {
845 	return m_connectedTo.count();
846 }
847 
attachedMoved(bool includeRatsnest,QList<ConnectorItem * > & already)848 void ConnectorItem::attachedMoved(bool includeRatsnest, QList<ConnectorItem *> & already) {
849 	//DebugDialog::debug("attached moved");
850     if (!this->isVisible()) return;
851 
852     QSet<ConnectorItem *> allTo;
853     allTo.insert(this);
854 	foreach (ConnectorItem * toConnectorItem, m_connectedTo) {
855         allTo.insert(toConnectorItem);
856         foreach (ConnectorItem * subTo, toConnectorItem->connectedToItems()) {
857             allTo.insert(subTo);
858         }
859 	}
860     allTo.remove(this);
861     foreach (ConnectorItem * toConnectorItem, allTo) {
862         ItemBase * itemBase = toConnectorItem->attachedTo();
863 		if (itemBase == NULL) continue;
864 		if (!itemBase->isVisible()) {
865 			//this->debugInfo("continue");
866 			//itemBase->debugInfo("    ");
867 			continue;
868 		}
869         if (itemBase->getRatsnest() && !includeRatsnest) {
870             continue;
871         }
872 
873         toConnectorItem->attachedTo()->connectedMoved(this, toConnectorItem, already);
874     }
875 }
876 
firstConnectedToIsh()877 ConnectorItem * ConnectorItem::firstConnectedToIsh() {
878 	if (m_connectedTo.count() <= 0) return NULL;
879 
880 	foreach (ConnectorItem * connectorItem, m_connectedTo) {
881 		if (connectorItem->attachedTo()->getRatsnest()) continue;
882 		if (!connectorItem->isVisible()) continue;
883 
884         return connectorItem;
885 	}
886 
887 	// TODO: not sure whether to return invisible connectors
888 	// TODO: get rid of this function?
889 
890 	foreach (ConnectorItem * connectorItem, m_connectedTo) {
891 		if (connectorItem->attachedTo()->getRatsnest()) continue;
892 
893 		return connectorItem;
894 	}
895 
896 	return NULL;
897 }
898 
setTerminalPoint(QPointF p)899 void ConnectorItem::setTerminalPoint(QPointF p) {
900 	m_terminalPoint = p;
901 }
902 
terminalPoint()903 QPointF ConnectorItem::terminalPoint() {
904 	return m_terminalPoint;
905 }
906 
adjustedTerminalPoint()907 QPointF ConnectorItem::adjustedTerminalPoint() {
908 	if (m_legPolygon.count() < 2) {
909 		return m_terminalPoint + this->rect().topLeft();
910 	}
911 
912 	return m_legPolygon.last();
913 }
914 
sceneAdjustedTerminalPoint(ConnectorItem * connectee)915 QPointF ConnectorItem::sceneAdjustedTerminalPoint(ConnectorItem * connectee) {
916 
917 	if ((connectee != NULL) && !m_circular && !m_shape.isEmpty() && (connectee->attachedToItemType() == ModelPart::Wire)) {
918 		Wire * wire = qobject_cast<Wire *>(connectee->attachedTo());
919 		if ((wire != NULL) && !wire->getRatsnest()) {
920 			QPointF anchor = wire->otherConnector(connectee)->sceneAdjustedTerminalPoint(NULL);
921 			double newX = 0, newY = 0, newDistance = MAX_DOUBLE;
922 			int count = m_shape.elementCount();
923 
924 			QPointF prev;
925 			for (int i = 0; i < count; i++) {
926 				QPainterPath::Element el = m_shape.elementAt(i);
927 				if (el.isMoveTo()) {
928 					prev = this->mapToScene(QPointF(el));
929 				}
930 				else {
931 					QPointF current = this->mapToScene(QPointF(el));
932 					double candidateX, candidateY, candidateDistance;
933 					bool atEndpoint;
934 					GraphicsUtils::distanceFromLine(anchor.x(), anchor.y(), prev.x(), prev.y(), current.x(), current.y(),
935 										candidateX, candidateY, candidateDistance, atEndpoint);
936 					if (candidateDistance < newDistance) {
937 						newX = candidateX;
938 						newY = candidateY;
939 						newDistance = candidateDistance;
940 						//DebugDialog::debug(QString("anchor:%1,%2; new:%3,%4; %5").arg(anchor.x()).arg(anchor.y()).arg(newX).arg(newY).arg(newDistance));
941 					}
942 
943 					prev = current;
944 				}
945 			}
946 
947 			//DebugDialog::debug(QString("anchor:%1,%2; new:%3,%4; %5\n\n").arg(anchor.x()).arg(anchor.y()).arg(newX).arg(newY).arg(newDistance));
948 			return QPointF(newX, newY);
949 		}
950 	}
951 
952 	return this->mapToScene(adjustedTerminalPoint());
953 }
954 
connectedTo(ConnectorItem * connectorItem)955 bool ConnectorItem::connectedTo(ConnectorItem * connectorItem) {
956 	return this->m_connectedTo.contains(connectorItem);
957 }
958 
connectedToItems()959 const QList< QPointer<ConnectorItem> > & ConnectorItem::connectedToItems() {
960 	return m_connectedTo;
961 }
962 
setHidden(bool hide)963 void ConnectorItem::setHidden(bool hide) {
964 	m_hidden = hide;
965 
966 	setHiddenOrInactive();
967 }
968 
setHybrid(bool h)969 void ConnectorItem::setHybrid(bool h) {
970 	m_hybrid = h;
971 	setHiddenOrInactive();
972 }
973 
isHybrid()974 bool ConnectorItem::isHybrid() {
975 	return m_hybrid;
976 }
977 
978 
setBigDot(bool bd)979 void ConnectorItem::setBigDot(bool bd) {
980 	m_bigDot = bd;
981 	//if (bd) {
982 	//	this->debugInfo("big dot");
983 	//}
984 }
985 
isBigDot()986 bool ConnectorItem::isBigDot() {
987 	return m_bigDot;
988 }
989 
setInactive(bool inactivate)990 void ConnectorItem::setInactive(bool inactivate) {
991 	m_inactive = inactivate;
992 	setHiddenOrInactive();
993 }
994 
setHiddenOrInactive()995 void ConnectorItem::setHiddenOrInactive() {
996 	if (m_hidden || m_inactive || m_hybrid || m_layerHidden) {
997 		this->setAcceptedMouseButtons(Qt::NoButton);
998 		this->unsetCursor();
999 		setAcceptHoverEvents(false);
1000 	}
1001 	else {
1002 		this->setAcceptedMouseButtons(ALLMOUSEBUTTONS);
1003 		this->setCursor(attachedToItemType() == ModelPart::Wire ? *CursorMaster::BendpointCursor : *CursorMaster::MakeWireCursor);
1004 		setAcceptHoverEvents(true);
1005 	}
1006 	this->update();
1007 }
1008 
overConnectorItem()1009 ConnectorItem * ConnectorItem::overConnectorItem() {
1010 	return m_overConnectorItem;
1011 }
1012 
setOverConnectorItem(ConnectorItem * connectorItem)1013 void ConnectorItem::setOverConnectorItem(ConnectorItem * connectorItem) {
1014 	m_overConnectorItem = connectorItem;
1015 }
1016 
1017 
connectorSharedID()1018 const QString & ConnectorItem::connectorSharedID() {
1019 	if (m_connector == NULL) return ___emptyString___;
1020 
1021 	return m_connector->connectorSharedID();
1022 }
1023 
connectorSharedReplacedby()1024 const QString & ConnectorItem::connectorSharedReplacedby() {
1025 	if (m_connector == NULL) return ___emptyString___;
1026 
1027 	return m_connector->connectorSharedReplacedby();
1028 }
1029 
connectorSharedErcData()1030 ErcData * ConnectorItem::connectorSharedErcData() {
1031 	if (m_connector == NULL) return NULL;
1032 
1033 	return m_connector->connectorSharedErcData();
1034 }
1035 
connectorSharedName()1036 const QString & ConnectorItem::connectorSharedName() {
1037 	if (m_connector == NULL) return ___emptyString___;
1038 
1039 	return m_connector->connectorSharedName();
1040 }
1041 
connectorSharedDescription()1042 const QString & ConnectorItem::connectorSharedDescription() {
1043 	if (m_connector == NULL) return ___emptyString___;
1044 
1045 	return m_connector->connectorSharedDescription();
1046 }
1047 
busID()1048 const QString & ConnectorItem::busID() {
1049 	if (m_connector == NULL) return ___emptyString___;
1050 
1051 	return m_connector->busID();
1052 }
1053 
modelPartShared()1054 ModelPartShared * ConnectorItem::modelPartShared() {
1055 	if (m_attachedTo == NULL) return NULL;
1056 
1057 	return m_attachedTo->modelPartShared();
1058 }
1059 
modelPart()1060 ModelPart * ConnectorItem::modelPart() {
1061 	if (m_attachedTo == NULL) return NULL;
1062 
1063 	return m_attachedTo->modelPart();
1064 }
1065 
bus()1066 Bus * ConnectorItem::bus() {
1067 	if (m_connector == NULL) return NULL;
1068 
1069 	return m_connector->bus();
1070 }
1071 
attachedToViewLayerID()1072 ViewLayer::ViewLayerID ConnectorItem::attachedToViewLayerID() {
1073 	if (m_attachedTo == NULL) return ViewLayer::UnknownLayer;
1074 
1075 	return m_attachedTo->viewLayerID();
1076 }
1077 
attachedToViewLayerPlacement()1078 ViewLayer::ViewLayerPlacement ConnectorItem::attachedToViewLayerPlacement() {
1079 	if (m_attachedTo == NULL) return ViewLayer::UnknownPlacement;
1080 
1081 	return m_attachedTo->viewLayerPlacement();
1082 }
1083 
attachedToViewID()1084 ViewLayer::ViewID ConnectorItem::attachedToViewID() {
1085 	if (m_attachedTo == NULL) return ViewLayer::UnknownView;
1086 
1087 	return m_attachedTo->viewID();
1088 }
1089 
connectorType()1090 Connector::ConnectorType ConnectorItem::connectorType() {
1091 	if (m_connector == NULL) return Connector::Unknown;
1092 
1093 	return m_connector->connectorType();
1094 }
1095 
chained()1096 bool ConnectorItem::chained() {
1097 	foreach (ConnectorItem * toConnectorItem, m_connectedTo) {
1098 		if (toConnectorItem->attachedToItemType() == ModelPart::Wire) {
1099 			return true;
1100 		}
1101 	}
1102 
1103 	return false;
1104 }
1105 
saveInstance(QXmlStreamWriter & writer)1106 void ConnectorItem::saveInstance(QXmlStreamWriter & writer) {
1107 	if (m_connectedTo.count() <= 0 && !m_rubberBandLeg && !m_groundFillSeed) {
1108 		// no need to save if there's no connection
1109 		return;
1110 	}
1111 
1112 	writer.writeStartElement("connector");
1113 	writer.writeAttribute("connectorId", connectorSharedID());
1114 	writer.writeAttribute("layer", ViewLayer::viewLayerXmlNameFromID(attachedToViewLayerID()));
1115 	if (m_groundFillSeed) {
1116 		writer.writeAttribute("groundFillSeed", "true");
1117 	}
1118 
1119 	writer.writeStartElement("geometry");
1120 	QPointF p = this->pos();
1121 	writer.writeAttribute("x", QString::number(p.x()));
1122 	writer.writeAttribute("y", QString::number(p.y()));
1123 	writer.writeEndElement();
1124 
1125 	if (m_rubberBandLeg && m_legPolygon.count() > 1) {
1126 		writer.writeStartElement("leg");
1127 		for (int i = 0; i < m_legPolygon.count(); i++) {
1128 			QPointF p = m_legPolygon.at(i);
1129 			writer.writeStartElement("point");
1130 			writer.writeAttribute("x", QString::number(p.x()));
1131 			writer.writeAttribute("y", QString::number(p.y()));
1132 			writer.writeEndElement();
1133 
1134 			Bezier * bezier = m_legCurves.at(i);
1135 			if (bezier == NULL) {
1136 				writer.writeStartElement("bezier");
1137 				writer.writeEndElement();
1138 			}
1139 			else {
1140 				bezier->write(writer);
1141 			}
1142 		}
1143 		writer.writeEndElement();
1144 	}
1145 
1146 	if (m_connectedTo.count() > 0) {
1147 		writer.writeStartElement("connects");
1148 		foreach (ConnectorItem * connectorItem, this->m_connectedTo) {
1149 			if (connectorItem->attachedTo()->getRatsnest()) continue;
1150 
1151 			connectorItem->writeConnector(writer, "connect");
1152 		}
1153 		writer.writeEndElement();
1154 	}
1155 
1156 	writeOtherElements(writer);
1157 
1158 	writer.writeEndElement();
1159 }
1160 
1161 
writeConnector(QXmlStreamWriter & writer,const QString & elementName)1162 void ConnectorItem::writeConnector(QXmlStreamWriter & writer, const QString & elementName)
1163 {
1164 	//DebugDialog::debug(QString("write connector %1").arg(this->attachedToID()));
1165 	writer.writeStartElement(elementName);
1166 	writer.writeAttribute("connectorId", connectorSharedID());
1167 	writer.writeAttribute("modelIndex", QString::number(connector()->modelIndex()));
1168 	writer.writeAttribute("layer", ViewLayer::viewLayerXmlNameFromID(attachedToViewLayerID()));
1169 	writer.writeEndElement();
1170 }
1171 
writeOtherElements(QXmlStreamWriter & writer)1172 void ConnectorItem::writeOtherElements(QXmlStreamWriter & writer) {
1173 	Q_UNUSED(writer);
1174 }
1175 
wiredTo(ConnectorItem * target,ViewGeometry::WireFlags skipFlags)1176 bool ConnectorItem::wiredTo(ConnectorItem * target, ViewGeometry::WireFlags skipFlags) {
1177 	QList<ConnectorItem *> connectorItems;
1178 	connectorItems.append(this);
1179 	collectEqualPotential(connectorItems, true, skipFlags);
1180 	return connectorItems.contains(target);
1181 }
1182 
directlyWiredTo(ConnectorItem * source,ConnectorItem * target,ViewGeometry::WireFlags flags)1183 Wire * ConnectorItem::directlyWiredTo(ConnectorItem * source, ConnectorItem * target, ViewGeometry::WireFlags flags) {
1184 	 QList<ConnectorItem *> visited;
1185 	 return directlyWiredToAux(source, target, flags, visited);
1186 }
1187 
directlyWiredToAux(ConnectorItem * source,ConnectorItem * target,ViewGeometry::WireFlags flags,QList<ConnectorItem * > & visited)1188 Wire * ConnectorItem::directlyWiredToAux(ConnectorItem * source, ConnectorItem * target, ViewGeometry::WireFlags flags, QList<ConnectorItem *> & visited) {
1189 	if (visited.contains(source)) return NULL;
1190 
1191 	QList<ConnectorItem *> equals;
1192 	equals << source;
1193 
1194 	ConnectorItem * cross = source->getCrossLayerConnectorItem();
1195 	if (cross) {
1196 		if (!visited.contains(cross)) {
1197 			equals << cross;
1198 		}
1199 	}
1200 
1201 	visited.append(equals);
1202 
1203 	foreach (ConnectorItem * fromItem, equals) {
1204 		foreach (ConnectorItem * toConnectorItem, fromItem->m_connectedTo) {
1205 			ItemBase * toItem = toConnectorItem->attachedTo();
1206 			if (toItem == NULL) {
1207 				continue;			// shouldn't happen
1208 			}
1209 
1210 			if (toItem->itemType() != ModelPart::Wire) continue;
1211 
1212 			Wire * wire = qobject_cast<Wire *>(toItem);
1213 			if (!wire->hasAnyFlag(flags)) continue;
1214 
1215 			ConnectorItem * otherEnd = wire->otherConnector(toConnectorItem);
1216 			bool isChained = false;
1217 			foreach (ConnectorItem * otherConnectorItem, otherEnd->m_connectedTo) {
1218 				if (target == otherConnectorItem) {
1219 					return wire;
1220 				}
1221 				if (target->getCrossLayerConnectorItem() == otherConnectorItem) {
1222 					return wire;
1223 				}
1224 				if (otherConnectorItem->attachedToItemType() == ModelPart::Wire) {
1225 					//DebugDialog::debug(QString("wired from %1 to %2").arg(wire->id()).arg(otherConnectorItem->attachedToID()));
1226 					isChained = true;
1227 				}
1228 			}
1229 
1230 			if (isChained) {
1231 				if (ConnectorItem::directlyWiredToAux(otherEnd, target, flags, visited)) {
1232 					return wire;
1233 				}
1234 			}
1235 		}
1236 	}
1237 
1238     return NULL;
1239 }
1240 
isConnectedToPart()1241 bool ConnectorItem::isConnectedToPart() {
1242 
1243 	QList<ConnectorItem *> tempItems;
1244 	tempItems << this;
1245 
1246 	ConnectorItem * thisCrossConnectorItem = this->getCrossLayerConnectorItem();
1247 	QList<ConnectorItem *> busConnectedItems;
1248 	Bus * b = bus();
1249 	if (b != NULL) {
1250 		attachedTo()->busConnectorItems(b, this, busConnectedItems);
1251 	}
1252 
1253 	for (int i = 0; i < tempItems.count(); i++) {
1254 		ConnectorItem * connectorItem = tempItems[i];
1255 
1256 		if ((connectorItem != this) &&
1257 			(connectorItem != thisCrossConnectorItem) &&
1258 			!busConnectedItems.contains(connectorItem))
1259 		{
1260 			switch (connectorItem->attachedToItemType()) {
1261 				case ModelPart::Symbol:
1262 				case ModelPart::SchematicSubpart:
1263 				case ModelPart::Jumper:
1264 				case ModelPart::Part:
1265 				case ModelPart::CopperFill:
1266 				case ModelPart::Board:
1267 				case ModelPart::Breadboard:
1268 				case ModelPart::ResizableBoard:
1269 				case ModelPart::Via:
1270 					if (connectorItem->attachedTo()->isEverVisible()) {
1271 						return true;
1272 					}
1273 					break;
1274 				default:
1275 					break;
1276 			}
1277 		}
1278 
1279 		ConnectorItem * crossConnectorItem = connectorItem->getCrossLayerConnectorItem();
1280 		if (crossConnectorItem != NULL) {
1281 			if (!tempItems.contains(crossConnectorItem)) {
1282 				tempItems.append(crossConnectorItem);
1283 			}
1284 		}
1285 
1286 		foreach (ConnectorItem * cto, connectorItem->connectedToItems()) {
1287 			if (tempItems.contains(cto)) continue;
1288 
1289 			tempItems.append(cto);
1290 		}
1291 
1292 		Bus * bus = connectorItem->bus();
1293 		if (bus != NULL) {
1294 			QList<ConnectorItem *> busConnectedItems;
1295 			connectorItem->attachedTo()->busConnectorItems(bus, connectorItem, busConnectedItems);
1296 			foreach (ConnectorItem * busConnectedItem, busConnectedItems) {
1297 				if (!tempItems.contains(busConnectedItem)) {
1298 					tempItems.append(busConnectedItem);
1299 				}
1300 			}
1301 		}
1302 	}
1303 
1304 	return false;
1305 }
1306 
collectEqualPotential(QList<ConnectorItem * > & connectorItems,bool crossLayers,ViewGeometry::WireFlags skipFlags)1307 void ConnectorItem::collectEqualPotential(QList<ConnectorItem *> & connectorItems, bool crossLayers, ViewGeometry::WireFlags skipFlags) {
1308 	// collects all the connectors at the same potential
1309 	// allows direct connections or wired connections
1310 
1311 	//DebugDialog::debug("__________________");
1312 
1313 	QList<ConnectorItem *> tempItems = connectorItems;
1314 	connectorItems.clear();
1315 
1316 	for (int i = 0; i < tempItems.count(); i++) {
1317 		ConnectorItem * connectorItem = tempItems[i];
1318 		//connectorItem->debugInfo("testing eqp");
1319 
1320 		Wire * fromWire = (connectorItem->attachedToItemType() == ModelPart::Wire) ? qobject_cast<Wire *>(connectorItem->attachedTo()) : NULL;
1321 		if (fromWire != NULL) {
1322 			if (fromWire->hasAnyFlag(skipFlags)) {
1323 				// don't add this kind of wire
1324 				continue;
1325 			}
1326 		}
1327 		else {
1328 			if (crossLayers) {
1329 				ConnectorItem * crossConnectorItem = connectorItem->getCrossLayerConnectorItem();
1330 				if (crossConnectorItem != NULL) {
1331 					if (!tempItems.contains(crossConnectorItem)) {
1332 						tempItems.append(crossConnectorItem);
1333 					}
1334 				}
1335 			}
1336 		}
1337 
1338 		// this one's a keeper
1339 		connectorItems.append(connectorItem);
1340         //connectorItem->debugInfo("collect");
1341 
1342 		foreach (ConnectorItem * cto, connectorItem->connectedToItems()) {
1343 			if (tempItems.contains(cto)) continue;
1344 
1345 			if ((skipFlags & ViewGeometry::NormalFlag) && (fromWire == NULL) && (cto->attachedToItemType() != ModelPart::Wire)) {
1346 				// direct (part-to-part) connections not allowed
1347 				continue;
1348 			}
1349 
1350 			tempItems.append(cto);
1351 		}
1352 
1353 		Bus * bus = connectorItem->bus();
1354 		if (bus != NULL) {
1355 			QList<ConnectorItem *> busConnectedItems;
1356 			connectorItem->attachedTo()->busConnectorItems(bus, connectorItem, busConnectedItems);
1357 #ifndef QT_NO_DEBUG
1358 			if (connectorItem->attachedToItemType() == ModelPart::Wire && busConnectedItems.count() != 2) {
1359 				connectorItem->debugInfo("bus is missing");
1360 				//busConnectedItems.clear();
1361 				//connectorItem->attachedTo()->busConnectorItems(bus, busConnectedItems);
1362 			}
1363 #endif
1364 			foreach (ConnectorItem * busConnectedItem, busConnectedItems) {
1365 				if (!tempItems.contains(busConnectedItem)) {
1366 					tempItems.append(busConnectedItem);
1367 				}
1368 			}
1369 		}
1370 	}
1371 }
1372 
collectParts(QList<ConnectorItem * > & connectorItems,QList<ConnectorItem * > & partsConnectors,bool includeSymbols,ViewLayer::ViewLayerPlacement viewLayerPlacement)1373 void ConnectorItem::collectParts(QList<ConnectorItem *> & connectorItems, QList<ConnectorItem *> & partsConnectors, bool includeSymbols, ViewLayer::ViewLayerPlacement viewLayerPlacement)
1374 {
1375 	if (connectorItems.count() == 0) return;
1376 
1377 	//DebugDialog::debug("___________________________");
1378 	switch (viewLayerPlacement) {
1379 		case ViewLayer::NewTop:
1380 		case ViewLayer::NewBottom:
1381 		case ViewLayer::NewTopAndBottom:
1382 			break;
1383 		default:
1384 			DebugDialog::debug(QString("collect parts unknown spec %1").arg(viewLayerPlacement));
1385 			viewLayerPlacement = ViewLayer::NewTopAndBottom;
1386 			break;
1387 	}
1388 
1389 	foreach (ConnectorItem * connectorItem, connectorItems) {
1390 		if (connectorItem->isHybrid()) {
1391 			continue;
1392 		}
1393 
1394 		ItemBase * candidate = connectorItem->attachedTo();
1395 		switch (candidate->itemType()) {
1396 			case ModelPart::Symbol:
1397 			case ModelPart::SchematicSubpart:
1398 				if (!includeSymbols) break;
1399 			case ModelPart::Jumper:
1400 			case ModelPart::Part:
1401 			case ModelPart::CopperFill:
1402 			case ModelPart::Board:
1403 			case ModelPart::ResizableBoard:
1404 			case ModelPart::Via:
1405 				collectPart(connectorItem, partsConnectors, viewLayerPlacement);
1406 				break;
1407 			default:
1408 				break;
1409 		}
1410 	}
1411 }
1412 
collectPart(ConnectorItem * connectorItem,QList<ConnectorItem * > & partsConnectors,ViewLayer::ViewLayerPlacement viewLayerPlacement)1413 void ConnectorItem::collectPart(ConnectorItem * connectorItem, QList<ConnectorItem *> & partsConnectors, ViewLayer::ViewLayerPlacement viewLayerPlacement) {
1414 	if (partsConnectors.contains(connectorItem)) return;
1415 
1416 	ConnectorItem * crossConnectorItem = connectorItem->getCrossLayerConnectorItem();
1417 	if (crossConnectorItem != NULL) {
1418 		if (partsConnectors.contains(crossConnectorItem)) {
1419 			return;
1420 		}
1421 
1422 		if (viewLayerPlacement == ViewLayer::NewTopAndBottom) {
1423 			partsConnectors.append(crossConnectorItem);
1424 
1425 			/*
1426 			DebugDialog::debug(QString("collecting both: %1 %2 %3 %4")
1427 				.arg(crossConnectorItem->attachedToID())
1428 				.arg(crossConnectorItem->connectorSharedID())
1429 				.arg(crossConnectorItem->attachedToViewLayerID())
1430 				.arg((long)crossConnectorItem->attachedTo(), 0, 16) );
1431 			*/
1432 
1433 		}
1434 		else if (viewLayerPlacement == ViewLayer::NewTop) {
1435 			if (connectorItem->attachedToViewLayerID() == ViewLayer::Copper1) {
1436 			}
1437 			else {
1438 				connectorItem = crossConnectorItem;
1439 			}
1440 		}
1441 		else if (viewLayerPlacement == ViewLayer::NewBottom) {
1442 			if (connectorItem->attachedToViewLayerID() == ViewLayer::Copper0) {
1443 			}
1444 			else {
1445 				connectorItem = crossConnectorItem;
1446 			}
1447 		}
1448 	}
1449 
1450 	/*
1451 	DebugDialog::debug(QString("collecting part: %1 %2 %3 %4")
1452 		.arg(connectorItem->attachedToID())
1453 		.arg(connectorItem->connectorSharedID())
1454 		.arg(connectorItem->attachedToViewLayerID())
1455 		.arg((long) connectorItem->attachedTo(), 0, 16) );
1456 	*/
1457 
1458 	partsConnectors.append(connectorItem);
1459 }
1460 
updateTooltip()1461 void ConnectorItem::updateTooltip() {
1462 	if (attachedToItemType() != ModelPart::Wire) {
1463 		QString name = connectorSharedName();
1464         bool isInt = false;
1465         name.toInt(&isInt);
1466 		QString descr = connectorSharedDescription();
1467         if (name.compare(descr, Qt::CaseInsensitive) == 0) {
1468             descr = "";
1469         }
1470         else {
1471             descr = ":" + descr;
1472         }
1473         QString id = connectorSharedID();
1474         int ix = IntegerFinder.indexIn(id);
1475         if (ix < 0 || isInt) {
1476             id = "";
1477         }
1478         else {
1479             if (attachedTo()->modelPart()->hasZeroConnector()) {
1480                 id = QString::number(IntegerFinder.cap(0).toInt() + 1);
1481             }
1482             else {
1483                 id = IntegerFinder.cap(0);
1484             }
1485             if (!id.isEmpty()) {
1486                 id = " <span style='color:#909090;'>(" + id + ")</span>";
1487             }
1488         }
1489 
1490         QString tt = QString("<b>%2</b>%3%1<br /><span style='font-size:small;'>%4</span>")
1491                 .arg(id)
1492                 .arg(name)
1493                 .arg(descr)
1494                 .arg(attachedToTitle());
1495 		setToolTip(tt);
1496 		return;
1497 	}
1498 
1499 	QList<ConnectorItem *> connectors;
1500 	foreach(ConnectorItem * toConnectorItem, m_connectedTo) {
1501         if (toConnectorItem->attachedToItemType() != ModelPart::Wire) {
1502 			connectors.append(toConnectorItem);
1503 		}
1504 	}
1505 
1506 	if (connectors.count() == 0) {
1507 		setToolTip("");
1508 		return;
1509 	}
1510 
1511 
1512 	QString connections = QString("<ul style='margin-left:0;padding-left:0;'>");
1513 	foreach(ConnectorItem * connectorItem, connectors) {
1514 		connections += QString("<li style='margin-left:0;padding-left:0;'>") + "<b>" + connectorItem->attachedTo()->label() + "</b> " + connectorItem->connectorSharedName() + "</li>";
1515 	}
1516 	connections += "</ul>";
1517 
1518     setToolTip(ItemBase::ITEMBASE_FONT_PREFIX + connections + ItemBase::ITEMBASE_FONT_SUFFIX);
1519 
1520 }
1521 
clearConnector()1522 void ConnectorItem::clearConnector() {
1523 	m_connector = NULL;
1524 }
1525 
1526 
connectionIsAllowed(ConnectorItem * other)1527 bool ConnectorItem::connectionIsAllowed(ConnectorItem * other) {
1528 	if (!connector()->connectionIsAllowed(other->connector())) return false;
1529 	if (!m_attachedTo->connectionIsAllowed(other)) return false;
1530 	foreach (ConnectorItem * toConnectorItem, connectedToItems()) {
1531 		if (!toConnectorItem->attachedTo()->connectionIsAllowed(other)) {
1532 			return false;
1533 		}
1534 	}
1535 
1536 	return true;
1537 }
1538 
showEqualPotential(bool show,QList<ConnectorItem * > & visited)1539 void ConnectorItem::showEqualPotential(bool show, QList<ConnectorItem *> & visited) {
1540 	if (!show) {
1541 		restoreColor(visited);
1542 		return;
1543 	}
1544 
1545 	QBrush brush;
1546 	QPen pen;
1547 	m_attachedTo->getEqualPotentialColor(this, brush, pen, m_opacity, m_negativePenWidth, m_negativeOffsetRect);
1548 	//DebugDialog::debug(QString("set eqp %1 %2 %3").arg(attachedToID()).arg(pen->width()).arg(pen->color().name()));
1549 	setColorAux(brush, pen, true);
1550 
1551 }
1552 
clearEqualPotentialDisplay()1553 void ConnectorItem::clearEqualPotentialDisplay() {
1554 	//DebugDialog::debug(QString("clear eqp3"));
1555     QList<ConnectorItem *> visited;
1556 	foreach (ConnectorItem * connectorItem, m_equalPotentialDisplayItems) {
1557 		connectorItem->restoreColor(visited);
1558 	}
1559 	m_equalPotentialDisplayItems.clear();
1560 }
1561 
isEverVisible()1562 bool ConnectorItem::isEverVisible() {
1563 	return m_attachedTo->isEverVisible();
1564 }
1565 
isGrounded(ConnectorItem * c1,ConnectorItem * c2)1566 bool ConnectorItem::isGrounded(ConnectorItem * c1, ConnectorItem * c2) {
1567 	QList<ConnectorItem *> connectorItems;
1568 	if (c1 != NULL) {
1569 		connectorItems.append(c1);
1570 	}
1571 	if (c2 != NULL) {
1572 		connectorItems.append(c2);
1573 	}
1574 	collectEqualPotential(connectorItems, true, ViewGeometry::NoFlag);
1575 
1576 	foreach (ConnectorItem * end, connectorItems) {
1577 		if (end->isGrounded()) return true;
1578 
1579 	}
1580 
1581 	return false;
1582 }
1583 
isGrounded()1584 bool ConnectorItem::isGrounded() {
1585 	QString name = connectorSharedName();
1586 	return ((name.compare("gnd", Qt::CaseInsensitive) == 0) ||
1587 			// (name.compare("-", Qt::CaseInsensitive) == 0) ||
1588 			(name.compare("vss", Qt::CaseInsensitive) == 0) ||
1589 			(name.compare("ground", Qt::CaseInsensitive) == 0)
1590 			);
1591 }
1592 
getCrossLayerConnectorItem()1593 ConnectorItem * ConnectorItem::getCrossLayerConnectorItem() {
1594 	if (m_connector == NULL) return NULL;
1595 	if (m_attachedTo == NULL) return NULL;
1596 	if (m_attachedTo->viewID() != ViewLayer::PCBView) return NULL;
1597 
1598 	ViewLayer::ViewLayerID viewLayerID = attachedToViewLayerID();
1599 	if (viewLayerID == ViewLayer::Copper0) {
1600 		return m_connector->connectorItemByViewLayerID(this->attachedToViewID(), ViewLayer::Copper1);
1601 	}
1602 	if (viewLayerID == ViewLayer::Copper1) {
1603 		return m_connector->connectorItemByViewLayerID(this->attachedToViewID(), ViewLayer::Copper0);
1604 	}
1605 
1606 	return NULL;
1607 }
1608 
isInLayers(ViewLayer::ViewLayerPlacement viewLayerPlacement)1609 bool ConnectorItem::isInLayers(ViewLayer::ViewLayerPlacement viewLayerPlacement) {
1610 	return ViewLayer::copperLayers(viewLayerPlacement).contains(attachedToViewLayerID());
1611 }
1612 
isCrossLayerConnectorItem(ConnectorItem * candidate)1613 bool ConnectorItem::isCrossLayerConnectorItem(ConnectorItem * candidate) {
1614 	if (candidate == NULL) return false;
1615 
1616 	ConnectorItem * cross = getCrossLayerConnectorItem();
1617 	return cross == candidate;
1618 }
1619 
isCrossLayerFrom(ConnectorItem * candidate)1620 bool ConnectorItem::isCrossLayerFrom(ConnectorItem * candidate) {
1621 	return !ViewLayer::canConnect(this->attachedToViewLayerID(), candidate->attachedToViewLayerID());
1622 }
1623 
1624 
isGrey(QColor color)1625 bool isGrey(QColor color) {
1626 	if (qAbs(color.red() - color.green()) > 16) return false;
1627 	if (qAbs(color.red() - color.blue()) > 16) return false;
1628 	if (qAbs(color.green() - color.blue()) > 16) return false;
1629 	if (color.red() < 0x60) return false;
1630 	if (color.red() > 0xA0) return false;
1631 	return true;
1632 }
1633 
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)1634 void ConnectorItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
1635 {
1636 	if (m_hybrid) return;
1637 	if (doNotPaint()) return;
1638 
1639 	if (m_legPolygon.count() > 1) {
1640 		paintLeg(painter);
1641 		return;
1642 	}
1643 
1644 	if (m_effectively == EffectivelyUnknown) {
1645 		if (!m_circular && m_shape.isEmpty()) {
1646 			if (this->attachedTo()->viewID() == ViewLayer::PCBView) {
1647 				QRectF r = rect();
1648 				if (qAbs(r.width() - r.height()) < 0.01) m_effectively = EffectivelyCircular;
1649 				else m_effectively = EffectivelyRectangular;
1650 			}
1651 		}
1652 	}
1653 
1654 	NonConnectorItem::paint(painter, option, widget);
1655 }
1656 
paintLeg(QPainter * painter)1657 void ConnectorItem::paintLeg(QPainter * painter)
1658 {
1659 	QPen lpen = legPen();
1660 	painter->setPen(lpen);
1661 
1662 	bool hasCurves = false;
1663 	foreach (Bezier * bezier, m_legCurves) {
1664 		if (bezier != NULL && !bezier->isEmpty()) {
1665 			hasCurves = true;
1666 			break;
1667 		}
1668 	}
1669 
1670 	// draw the leg first
1671 	paintLeg(painter, hasCurves);
1672 
1673 	if (m_legPolygon.count() > 2) {
1674 		// draw bendpoint indicators
1675 		double halfWidth = lpen.widthF() / 2;
1676 		painter->setPen(Qt::NoPen);
1677 		QColor c =  addColor(m_legColor, (qGray(m_legColor.rgb()) < 64) ? 80 : -64);
1678 		painter->setBrush(c);
1679 		for (int i = 1; i < m_legPolygon.count() - 1; i++) {
1680 			painter->drawEllipse(m_legPolygon.at(i), halfWidth, halfWidth);
1681 		}
1682 		painter->setBrush(Qt::NoBrush);
1683 	}
1684 
1685 	if (m_attachedTo->inHover()) {
1686 		// hover highlight
1687 		lpen.setColor((qGray(m_legColor.rgb()) < 48) ? QColor(255, 255, 255) : QColor(0, 0, 0));
1688 		painter->setOpacity(ItemBase::HoverOpacity);
1689 		painter->setPen(lpen);
1690 
1691 		paintLeg(painter, hasCurves);
1692 	}
1693 
1694 	// now draw the connector
1695 	Bezier * bezier = m_legCurves.at(m_legCurves.count() - 2);
1696 	bool connectorIsCurved = (bezier != NULL && !bezier->isEmpty());
1697 	Bezier left, right;
1698 	QPainterPath path;
1699 	if (connectorIsCurved) {
1700 		bezier->split(m_connectorDrawT, left, right);
1701 		path.moveTo(right.endpoint0());
1702 		path.cubicTo(right.cp0(), right.cp1(), right.endpoint1());
1703 	}
1704 
1705 	if (!isGrey(m_legColor)) {
1706 		// draw an undercolor so the connectorColor will be visible on top of the leg color
1707 		lpen.setColor(0x8c8c8c);			// TODO: don't hardcode color
1708 		painter->setOpacity(1);
1709 		painter->setPen(lpen);
1710 		if (connectorIsCurved) {
1711 			painter->drawPath(path);
1712 		}
1713 		else {
1714 			painter->drawLine(m_connectorDrawEnd, m_legPolygon.last());
1715 		}
1716 	}
1717 
1718 	QPen pen = this->pen();
1719 	pen.setWidthF(m_legStrokeWidth);
1720 	pen.setCapStyle(Qt::RoundCap);
1721 	painter->setOpacity(m_opacity);
1722 	painter->setPen(pen);
1723 	if (connectorIsCurved) {
1724 		painter->drawPath(path);
1725 	}
1726 	else {
1727 		painter->drawLine(m_connectorDrawEnd, m_legPolygon.last());
1728 	}
1729 }
1730 
paintLeg(QPainter * painter,bool hasCurves)1731 void ConnectorItem::paintLeg(QPainter * painter, bool hasCurves)
1732 {
1733 	if (hasCurves) {
1734 		for (int i = 0; i < m_legPolygon.count() -1; i++) {
1735 			Bezier * bezier = m_legCurves.at(i);
1736 			if (bezier && !bezier->isEmpty()) {
1737 				QPainterPath path;
1738 				path.moveTo(m_legPolygon.at(i));
1739 				path.cubicTo(bezier->cp0(), bezier->cp1(), m_legPolygon.at(i + 1));
1740 				painter->drawPath(path);
1741 			}
1742 			else {
1743 				painter->drawLine(m_legPolygon.at(i), m_legPolygon.at(i + 1));
1744 			}
1745 		}
1746 	}
1747 	else {
1748 		painter->drawPolyline(m_legPolygon);
1749 	}
1750 }
1751 
1752 
chooseFromSpec(ViewLayer::ViewLayerPlacement viewLayerPlacement)1753 ConnectorItem * ConnectorItem::chooseFromSpec(ViewLayer::ViewLayerPlacement viewLayerPlacement) {
1754 	ConnectorItem * crossConnectorItem = getCrossLayerConnectorItem();
1755 	if (crossConnectorItem == NULL) return this;
1756 
1757 	ViewLayer::ViewLayerID basis = ViewLayer::Copper0;
1758 	switch (viewLayerPlacement) {
1759 		case ViewLayer::NewTop:
1760 			basis = ViewLayer::Copper1;
1761 			break;
1762 		case ViewLayer::NewBottom:
1763 			basis = ViewLayer::Copper0;
1764 			break;
1765 		default:
1766 			DebugDialog::debug(QString("unusual viewLayerPlacement %1").arg(viewLayerPlacement));
1767 			basis = ViewLayer::Copper0;
1768 			break;
1769 	}
1770 
1771 	if (this->attachedToViewLayerID() == basis) {
1772 		return this;
1773 	}
1774 	if (crossConnectorItem->attachedToViewLayerID() == basis) {
1775 		return crossConnectorItem;
1776 	}
1777 	return this;
1778 }
1779 
connectedToWires()1780 bool ConnectorItem::connectedToWires() {
1781 	foreach (ConnectorItem * toConnectorItem, connectedToItems()) {
1782 		if (toConnectorItem->attachedToItemType() == ModelPart::Wire) {
1783 			return true;
1784 		}
1785 	}
1786 
1787 	ConnectorItem * crossConnectorItem = getCrossLayerConnectorItem();
1788 	if (crossConnectorItem == NULL) return false;
1789 
1790 	foreach (ConnectorItem * toConnectorItem, crossConnectorItem->connectedToItems()) {
1791 		if (toConnectorItem->attachedToItemType() == ModelPart::Wire) {
1792 			return true;
1793 		}
1794 	}
1795 
1796 	return false;
1797 }
1798 
displayRatsnest(QList<ConnectorItem * > & partConnectorItems,ViewGeometry::WireFlags myFlag)1799 void ConnectorItem::displayRatsnest(QList<ConnectorItem *> & partConnectorItems, ViewGeometry::WireFlags myFlag) {
1800 	bool formerColorWasNamed = false;
1801 	bool gotFormerColor = false;
1802 	QColor formerColor;
1803 
1804 	VirtualWire * vw = NULL;
1805 	foreach (ConnectorItem * fromConnectorItem, partConnectorItems) {
1806 		foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
1807 			vw = qobject_cast<VirtualWire *>(toConnectorItem->attachedTo());
1808 			if (vw != NULL) break;
1809 		}
1810 		if (vw != NULL) break;
1811 	}
1812 
1813 	if (vw != NULL) {
1814 		formerColorWasNamed = vw->colorWasNamed();
1815 		formerColor = vw->color();
1816 		gotFormerColor = true;
1817 		clearRatsnestDisplay(partConnectorItems);
1818 	}
1819 
1820 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
1821 	if (infoGraphicsView == NULL) return;
1822 
1823 	if (partConnectorItems.count() < 2) return;
1824 
1825 	QStringList connectorNames;
1826 	ConnectorItem::collectConnectorNames(partConnectorItems, connectorNames);
1827 	QColor color;
1828 	bool colorWasNamed = RatsnestColors::findConnectorColor(connectorNames, color);
1829 	if (!colorWasNamed) {
1830 		if (!formerColorWasNamed && gotFormerColor) {
1831 			color = formerColor;
1832 		}
1833 		else {
1834 			infoGraphicsView->getRatsnestColor(color);
1835 		}
1836 	}
1837 
1838 	ConnectorPairHash result;
1839 	GraphUtils::chooseRatsnestGraph(&partConnectorItems, (ViewGeometry::RatsnestFlag | ViewGeometry::NormalFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag) ^ myFlag, result);
1840 
1841 	foreach (ConnectorItem * key, result.uniqueKeys()) {
1842 		foreach (ConnectorItem * value, result.values(key)) {
1843 			VirtualWire * vw = infoGraphicsView->makeOneRatsnestWire(key, value, false, color, false);
1844 			if (vw) {
1845 				vw->setColorWasNamed(colorWasNamed);
1846 			}
1847 		}
1848 	}
1849 }
1850 
clearRatsnestDisplay(QList<ConnectorItem * > & connectorItems)1851 void ConnectorItem::clearRatsnestDisplay(QList<ConnectorItem *> & connectorItems) {
1852 
1853 	QSet<VirtualWire *> ratsnests;
1854 	foreach (ConnectorItem * fromConnectorItem, connectorItems) {
1855 		if (fromConnectorItem == NULL) continue;
1856 
1857 		foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
1858 			VirtualWire * vw = qobject_cast<VirtualWire *>(toConnectorItem->attachedTo());
1859 			if (vw != NULL) {
1860 				ratsnests.insert(vw);
1861 			}
1862 		}
1863 	}
1864 
1865 	foreach (VirtualWire * vw, ratsnests.values()) {
1866 		ConnectorItem * c1 = vw->connector0()->firstConnectedToIsh();
1867 		if (c1 != NULL) {
1868 			vw->connector0()->tempRemove(c1, false);
1869 			c1->tempRemove(vw->connector0(), false);
1870 		}
1871 
1872 		ConnectorItem * c2 = vw->connector1()->firstConnectedToIsh();
1873 		if (c2 != NULL) {
1874 			vw->connector1()->tempRemove(c2, false);
1875 			c2->tempRemove(vw->connector1(), false);
1876 		}
1877 
1878 		//vw->debugInfo("removing rat 1");
1879 		vw->scene()->removeItem(vw);
1880 		delete vw;
1881 	}
1882 }
1883 
1884 
collectConnectorNames(QList<ConnectorItem * > & connectorItems,QStringList & connectorNames)1885 void ConnectorItem::collectConnectorNames(QList<ConnectorItem *> & connectorItems, QStringList & connectorNames)
1886 {
1887 	foreach(ConnectorItem * connectorItem, connectorItems) {
1888 		if (!connectorNames.contains(connectorItem->connectorSharedName())) {
1889 			connectorNames.append(connectorItem->connectorSharedName());
1890 			//DebugDialog::debug("name " + connectorItem->connectorSharedName());
1891 		}
1892 	}
1893 }
1894 
calcClipRadius()1895 double ConnectorItem::calcClipRadius() {
1896 	if (m_circular) {
1897 		return radius() - (strokeWidth() / 2.0);
1898 	}
1899 
1900 	if (m_effectively == EffectivelyCircular) {
1901 		double rad = rect().width() / 2;
1902 		return rad - (rad / 5);
1903 	}
1904 
1905 	return 0;
1906 }
1907 
isEffectivelyCircular()1908 bool ConnectorItem::isEffectivelyCircular() {
1909 	return m_circular || m_effectively == EffectivelyCircular;
1910 }
1911 
debugInfo(const QString & msg)1912 void ConnectorItem::debugInfo(const QString & msg)
1913 {
1914 
1915 #ifndef QT_NO_DEBUG
1916     QPointF p = sceneAdjustedTerminalPoint(NULL);
1917 	QString s = QString("%1 cid:%2 cname:%3 title:%4 id:%5 type:%6 inst:%7 vlid:%8 vid:%9 spec:%10 flg:%11 hy:%12 bus:%13 r:%14 sw:%15 pos:(%16 %17)")
1918 			.arg(msg)
1919 			.arg(this->connectorSharedID())
1920 			.arg(this->connectorSharedName())
1921 			.arg(this->attachedToTitle())
1922 			.arg(this->attachedToID())
1923 			.arg(this->attachedToItemType())
1924 			.arg(this->attachedToInstanceTitle())
1925 			.arg(this->attachedToViewLayerID())
1926 			.arg(this->attachedToViewID())
1927 			.arg(this->attachedToViewLayerPlacement())
1928 			.arg(this->attachedTo()->wireFlags())
1929 			.arg(this->m_hybrid)
1930 			.arg((long) this->bus(), 0, 16)
1931             .arg(this->m_radius)
1932             .arg(this->m_strokeWidth)
1933             .arg(p.x())
1934             .arg(p.y())
1935             ;
1936 	//s.replace(" ", "_");
1937 	DebugDialog::debug(s);
1938 #else
1939 	Q_UNUSED(msg);
1940 #endif
1941 }
1942 
minDimension()1943 double ConnectorItem::minDimension() {
1944 	QRectF r = this->boundingRect();
1945 	return qMin(r.width(), r.height());
1946 }
1947 
findConnectorUnder(bool useTerminalPoint,bool allowAlready,const QList<ConnectorItem * > & exclude,bool displayDragTooltip,ConnectorItem * other)1948 ConnectorItem * ConnectorItem::findConnectorUnder(bool useTerminalPoint, bool allowAlready, const QList<ConnectorItem *> & exclude, bool displayDragTooltip, ConnectorItem * other)
1949 {
1950 	QList<QGraphicsItem *> items = useTerminalPoint
1951 		? this->scene()->items(this->sceneAdjustedTerminalPoint(NULL))
1952 		: this->scene()->items(mapToScene(this->rect()));			// only wires use rect
1953 	QList<ConnectorItem *> candidates;
1954 	// for the moment, take the topmost ConnectorItem that doesn't belong to me
1955 	foreach (QGraphicsItem * item, items) {
1956 		ConnectorItem * connectorItemUnder = dynamic_cast<ConnectorItem *>(item);
1957 		if (connectorItemUnder == NULL) continue;
1958 		if (connectorItemUnder->connector() == NULL) continue;			// shouldn't happen
1959 		if (attachedTo()->childItems().contains(connectorItemUnder)) continue;		// don't use own connectors
1960 		if (!this->connectionIsAllowed(connectorItemUnder)) {
1961 			continue;
1962 		}
1963 		if (!allowAlready) {
1964 			if (connectorItemUnder->connectedToItems().contains(this)) {
1965 				continue;		// already connected
1966 			}
1967 		}
1968 		if (exclude.contains(connectorItemUnder)) continue;
1969 
1970 
1971 		candidates.append(connectorItemUnder);
1972 	}
1973 
1974 	ConnectorItem * candidate = NULL;
1975 	if (candidates.count() == 1) {
1976 		candidate = candidates[0];
1977 	}
1978 	else if (candidates.count() > 0) {
1979 		qSort(candidates.begin(), candidates.end(), wireLessThan);
1980 		candidate = candidates[0];
1981 	}
1982 
1983 	if (m_overConnectorItem != NULL && candidate != m_overConnectorItem) {
1984 		m_overConnectorItem->connectorHover(NULL, false);
1985 	}
1986 	if (candidate != NULL && candidate != m_overConnectorItem) {
1987 		candidate->connectorHover(NULL, true);
1988 	}
1989 
1990 	m_overConnectorItem = candidate;
1991 
1992 	if (candidate == NULL) {
1993 		if (this->connectorHovering()) {
1994 			this->connectorHover(NULL, false);
1995 		}
1996 	}
1997 	else {
1998 		if (!this->connectorHovering()) {
1999 			this->connectorHover(NULL, true);
2000 		}
2001 	}
2002 
2003 	if (displayDragTooltip) {
2004 		displayTooltip(m_overConnectorItem, other);
2005 	}
2006 
2007 	return m_overConnectorItem;
2008 }
2009 
displayTooltip(ConnectorItem * ci,ConnectorItem * other)2010 void ConnectorItem::displayTooltip(ConnectorItem * ci, ConnectorItem * other)
2011 {
2012 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
2013 	if (infoGraphicsView == NULL) return;
2014 
2015 	// Activate tooltip for destination connector. based on a patch submitted by bryant.mairs
2016 	QString text;
2017 	if (ci && ci->connectorHovering()) {
2018 		if (other) {
2019 			text = QString("%1: %2\n%3: %4")
2020 						.arg(other->attachedToInstanceTitle())
2021 						.arg(other->connectorSharedName())
2022 						.arg(ci->attachedToInstanceTitle())
2023 						.arg(ci->connectorSharedName());
2024 		}
2025 		else {
2026 			text = QString("%1: %2").arg(ci->attachedToInstanceTitle()).arg(ci->connectorSharedName());
2027 		}
2028 	}
2029 	else {
2030 		if (other) {
2031 			text = QString("%1: %2")
2032 				.arg(other->attachedToInstanceTitle())
2033 				.arg(other->connectorSharedName());
2034 		}
2035 	}
2036     // Now use Qt's tooltip functionality to display our tooltip.
2037     // The tooltip text is first cleared as only a change in tooltip
2038     // text will update its position.
2039     // A rect is generated to smooth out position updates.
2040     // NOTE: Increasing this rect will cause the tooltip to disappear
2041     // and not reappear until another pixel move after the move that
2042     // disabled it.
2043     QPoint sp = QCursor::pos();
2044     QToolTip::showText(sp, "", infoGraphicsView);
2045 	if (!text.isEmpty()) {
2046 		QPoint q = infoGraphicsView->mapFromGlobal(sp);
2047 		QRect r(q.x(), q.y(), 1, 1);
2048 		QToolTip::showText(sp, text, infoGraphicsView, r);
2049 	}
2050 }
2051 
releaseDrag()2052 ConnectorItem * ConnectorItem::releaseDrag() {
2053 	ConnectorItem * result = m_overConnectorItem;
2054 	if (m_overConnectorItem != NULL) {
2055 		m_overConnectorItem->connectorHover(NULL, false);
2056 
2057 		// clean up
2058 		setOverConnectorItem(NULL);
2059 		clearConnectorHover();
2060         QList<ConnectorItem *> visited;
2061 		restoreColor(visited);
2062 	}
2063 	attachedTo()->clearConnectorHover();
2064 	return result;
2065 }
2066 
rotateLeg(const QPolygonF & poly,bool active)2067 void ConnectorItem::rotateLeg(const QPolygonF & poly, bool active)
2068 {
2069 	resetLeg(poly, false, active, "rotate");
2070 }
2071 
resetLeg(const QPolygonF & poly,bool relative,bool active,const QString & why)2072 void ConnectorItem::resetLeg(const QPolygonF & poly, bool relative, bool active, const QString & why)
2073 {
2074 	if (!m_rubberBandLeg) return;
2075 
2076 	ConnectorItem * target = NULL;
2077 	foreach (ConnectorItem * connectorItem, this->m_connectedTo) {
2078 		if (connectorItem->connectorType() == Connector::Female) {
2079 			target = connectorItem;
2080 			break;
2081 		}
2082 	}
2083 
2084 	if (target == NULL) {
2085 		setLeg(poly, relative, why);
2086 		return;
2087 	}
2088 
2089 	if (!active) {
2090 		repositionTarget();
2091 		return;
2092 	}
2093 
2094 	if (why.compare("swap") == 0) {
2095 		setLeg(poly, relative, why);
2096 		repositionTarget();
2097 		return;
2098 	}
2099 
2100 	//DebugDialog::debug("connectorItem prepareGeometryChange 1");
2101 	prepareGeometryChange();
2102 	QPointF sceneNewLast = target->sceneAdjustedTerminalPoint(NULL);
2103 	QPointF sceneOldLast = poly.last();
2104 
2105 	for (int i = 1; i < m_legPolygon.count(); i++) {
2106 		m_legPolygon.replace(i, mapFromScene(poly.at(i) - sceneOldLast + sceneNewLast));
2107 	}
2108 
2109 	calcConnectorEnd();
2110 	update();
2111 }
2112 
setLeg(const QPolygonF & poly,bool relative,const QString & why)2113 void ConnectorItem::setLeg(const QPolygonF & poly, bool relative, const QString & why)
2114 {
2115 	Q_UNUSED(why);
2116 
2117 	if (!m_rubberBandLeg) return;
2118 
2119 	repoly(poly, relative);
2120 	update();
2121 }
2122 
leg()2123 const QPolygonF & ConnectorItem::leg() {
2124 	static QPolygonF emptyPoly;
2125 
2126 	if (!m_rubberBandLeg) return emptyPoly;
2127 
2128 	return m_legPolygon;
2129 }
2130 
isDraggingLeg()2131 bool ConnectorItem::isDraggingLeg() {
2132 	return m_draggingLeg;
2133 }
2134 
makeLegSvg(QPointF offset,double dpi,double printerScale,bool blackOnly)2135 QString ConnectorItem::makeLegSvg(QPointF offset, double dpi, double printerScale, bool blackOnly) {
2136 	if (!m_rubberBandLeg) return "";
2137 
2138 	QString data("M");
2139 	QPointF p = m_legPolygon.at(0);
2140 	data += TextUtils::pointToSvgString(mapToScene(p), offset, dpi, printerScale);
2141 	for (int i = 1; i < m_legPolygon.count(); i++) {
2142 		QPointF p = m_legPolygon.at(i);
2143 		Bezier * bezier = m_legCurves.at(i - 1);
2144 		if (bezier == NULL || bezier->isEmpty()) {
2145 			data += "L";
2146 			data += TextUtils::pointToSvgString(mapToScene(p), offset, dpi, printerScale);
2147 		}
2148 		else {
2149 			data += "C";
2150 			data += TextUtils::pointToSvgString(mapToScene(bezier->cp0()), offset, dpi, printerScale);
2151 			data += " ";
2152 			data += TextUtils::pointToSvgString(mapToScene(bezier->cp1()), offset, dpi, printerScale);
2153 			data += " ";
2154 			data += TextUtils::pointToSvgString(mapToScene(p), offset, dpi, printerScale);
2155 		}
2156 	}
2157 
2158 	QString path = QString("<path stroke='%1' stroke-width='%2' stroke-linecap='round' stroke-linejoin='round' fill='none' d='%3' />\n")
2159 						.arg(blackOnly ? "black" :  m_legColor.name())
2160 						.arg(m_legStrokeWidth * dpi / printerScale)
2161 						.arg(data);
2162 	return path;
2163 }
2164 
sceneAdjustedLeg()2165 QPolygonF ConnectorItem::sceneAdjustedLeg() {
2166 	if (!m_rubberBandLeg) return QPolygonF();
2167 
2168 	QPolygonF poly;
2169 	foreach (QPointF p, m_legPolygon) {
2170 		poly.append(mapToScene(p));
2171 	}
2172 
2173 	return poly;
2174 }
2175 
prepareToStretch(bool activeStretch)2176 void ConnectorItem::prepareToStretch(bool activeStretch) {
2177 	m_activeStretch = activeStretch;
2178 	m_oldPolygon = sceneAdjustedLeg();
2179 }
2180 
stretchBy(QPointF howMuch)2181 void ConnectorItem::stretchBy(QPointF howMuch) {
2182 	if (!m_rubberBandLeg) return;
2183 
2184 	Q_UNUSED(howMuch);
2185 
2186 	resetLeg(m_oldPolygon, false, m_activeStretch, "move");
2187 
2188 	// if update isn't called here then legs repaint, but body doesn't
2189 	// so you get a weird "headless" effect
2190 	m_attachedTo->update();
2191 }
2192 
stretchDone(QPolygonF & oldLeg,QPolygonF & newLeg,bool & active)2193 void ConnectorItem::stretchDone(QPolygonF & oldLeg, QPolygonF & newLeg, bool & active) {
2194 	oldLeg = m_oldPolygon;
2195 	newLeg = sceneAdjustedLeg();
2196 	active = m_activeStretch;
2197 }
2198 
moveDone(int & index0,QPointF & oldPos0,QPointF & newPos0,int & index1,QPointF & oldPos1,QPointF & newPos1)2199 void ConnectorItem::moveDone(int & index0, QPointF & oldPos0, QPointF & newPos0, int & index1, QPointF & oldPos1, QPointF & newPos1) {
2200 	index0 = (m_activeStretch) ? 1 : m_legPolygon.count() - 1;
2201 	oldPos0 = m_oldPolygon.at(index0);
2202 	newPos0 = mapToScene(m_legPolygon.at(index0));
2203 	index1 = m_legPolygon.count() - 1;
2204 	oldPos1 = m_oldPolygon.at(index1);
2205 	newPos1 = mapToScene(m_legPolygon.at(index1));
2206 }
2207 
boundingRect() const2208 QRectF ConnectorItem::boundingRect() const
2209 {
2210 	if (m_legPolygon.count() < 2) return NonConnectorItem::boundingRect();
2211 
2212 	return shape().controlPointRect();
2213 }
2214 
hoverShape() const2215 QPainterPath ConnectorItem::hoverShape() const
2216 {
2217 	return shapeAux(2 * m_legStrokeWidth);
2218 }
2219 
shape() const2220 QPainterPath ConnectorItem::shape() const
2221 {
2222 	return shapeAux(m_legStrokeWidth);
2223 }
2224 
shapeAux(double width) const2225 QPainterPath ConnectorItem::shapeAux(double width) const
2226 {
2227 	if (m_legPolygon.count() < 2) return NonConnectorItem::shape();
2228 
2229 	QPainterPath path;
2230 	path.moveTo(m_legPolygon.at(0));
2231 	for (int i = 1; i < m_legPolygon.count(); i++) {
2232 		Bezier * bezier = m_legCurves.at(i - 1);
2233 		if (bezier != NULL && !bezier->isEmpty()) {
2234 			path.cubicTo(bezier->cp0(), bezier->cp1(), m_legPolygon.at(i));
2235 		}
2236 		else {
2237 			path.lineTo(m_legPolygon.at(i));
2238 		}
2239 	}
2240 
2241 	QPen pen = legPen();
2242 
2243 	return GraphicsUtils::shapeFromPath(path, pen, width, false);
2244 }
2245 
repositionTarget()2246 void ConnectorItem::repositionTarget()
2247 {
2248 	// this connector is connected to another part which is being dragged
2249 	foreach (ConnectorItem * connectorItem, this->m_connectedTo) {
2250 		if (connectorItem->connectorType() == Connector::Female) {
2251 			reposition(connectorItem->sceneAdjustedTerminalPoint(NULL), m_legPolygon.count() - 1);
2252 			break;
2253 		}
2254 	}
2255 }
2256 
reposition(QPointF sceneDestPos,int draggingIndex)2257 void ConnectorItem::reposition(QPointF sceneDestPos, int draggingIndex)
2258 {
2259 	//DebugDialog::debug("connectorItem prepareGeometryChange 2");
2260 	prepareGeometryChange();
2261 	//foreach (QPointF p, m_legPolygon) DebugDialog::debug(QString("point b %1 %2").arg(p.x()).arg(p.y()));
2262 	QPointF dest = mapFromScene(sceneDestPos);
2263 	m_legPolygon.replace(draggingIndex, dest);
2264 	//foreach (QPointF p, m_legPolygon) DebugDialog::debug(QString("point a %1 %2").arg(p.x()).arg(p.y()));
2265 	calcConnectorEnd();
2266 }
2267 
repoly(const QPolygonF & poly,bool relative)2268 void ConnectorItem::repoly(const QPolygonF & poly, bool relative)
2269 {
2270 	//DebugDialog::debug("connectorItem prepareGeometryChange 3");
2271 	prepareGeometryChange();
2272 
2273 	//foreach (QPointF p, m_legPolygon) DebugDialog::debug(QString("point b %1 %2").arg(p.x()).arg(p.y()));
2274 	m_legPolygon.clear();
2275 	clearCurves();
2276 
2277 	foreach (QPointF p, poly) {
2278 		m_legPolygon.append(relative ? p : mapFromScene(p));
2279 		m_legCurves.append(NULL);
2280 	}
2281 	//foreach (QPointF p, m_legPolygon) DebugDialog::debug(QString("point a %1 %2").arg(p.x()).arg(p.y()));
2282 	calcConnectorEnd();
2283 }
2284 
calcConnectorEnd()2285 void ConnectorItem::calcConnectorEnd()
2286 {
2287 	if (m_legPolygon.count() < 2) {
2288 		m_connectorDrawEnd = m_connectorDetectEnd = QPointF(0,0);
2289 		return;
2290 	}
2291 
2292 	QPointF p1 = m_legPolygon.last();
2293 	QPointF p0 = m_legPolygon.at(m_legPolygon.count() - 2);
2294 	double dx = p1.x() - p0.x();
2295 	double dy = p1.y() - p0.y();
2296 	double lineLen = qSqrt((dx * dx) + (dy * dy));
2297 	double drawlen = qMax(0.5, qMin(lineLen, StandardLegConnectorDrawEnabledLength));
2298 	double detectlen = qMax(0.5, qMin(lineLen, StandardLegConnectorDetectLength));
2299 
2300 	Bezier * bezier = m_legCurves.at(m_legCurves.count() - 2);
2301 	if (bezier == NULL || bezier->isEmpty()) {
2302 		m_connectorDrawEnd = QPointF(p1 - QPointF(dx * drawlen / lineLen, dy * drawlen / lineLen));
2303 		m_connectorDetectEnd = QPointF(p1 - QPointF(dx * detectlen / lineLen, dy * detectlen / lineLen));
2304 		return;
2305 	}
2306 
2307 	bezier->set_endpoints(p0, p1);
2308 	m_connectorDetectT = m_connectorDrawT = 0;
2309 	double blen = bezier->computeCubicCurveLength(1.0, 24);
2310 	if (blen < StandardLegConnectorDetectLength) {
2311 		return;
2312 	}
2313 
2314 	m_connectorDetectT = findT(bezier, blen, StandardLegConnectorDetectLength);
2315 	m_connectorDrawT = findT(bezier, blen, StandardLegConnectorDrawEnabledLength);
2316 }
2317 
findT(Bezier * bezier,double blen,double length)2318 double ConnectorItem::findT(Bezier * bezier, double blen, double length)
2319 {
2320 	// use binary search to find a value for t
2321 	double tmax = 1.0;
2322 	double tmin = 0;
2323 	double t = 1.0 - (length / blen);
2324 	while (true) {
2325 		double l = bezier->computeCubicCurveLength(t, 24);
2326 		if (qAbs(blen - length - l) < .0001) {
2327 			return t;
2328 		}
2329 
2330 		if (blen - length - l > 0) {
2331 			// too short
2332 			tmin = t;
2333 			t = (t + tmax) / 2;
2334 		}
2335 		else {
2336 			// too long
2337 			tmax = t;
2338 			t = (t + tmin) / 2;
2339 		}
2340 	}
2341 	return t;
2342 }
2343 
legID(ViewLayer::ViewID viewID,ViewLayer::ViewLayerID viewLayerID)2344 const QString & ConnectorItem::legID(ViewLayer::ViewID viewID, ViewLayer::ViewLayerID viewLayerID) {
2345 	if (m_connector) return m_connector->legID(viewID, viewLayerID);
2346 
2347 	return ___emptyString___;
2348 }
2349 
setRubberBandLeg(QColor color,double strokeWidth,QLineF parentLine)2350 void ConnectorItem::setRubberBandLeg(QColor color, double strokeWidth, QLineF parentLine) {
2351 	// assumes this is only called once, when the connector is first set up
2352 
2353 	/*
2354 	this->debugInfo(QString("set rubber band leg %1 %2 %3 %4")
2355 		.arg(parentLine.p1().x())
2356 		.arg(parentLine.p1().y())
2357 		.arg(parentLine.p2().x())
2358 		.arg(parentLine.p2().y())
2359 		);
2360 	*/
2361 
2362 	m_rubberBandLeg = true;
2363 	setFlag(QGraphicsItem::ItemIsMovable, true);
2364 	setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
2365 	setAcceptedMouseButtons(ALLMOUSEBUTTONS);
2366 
2367 	// p1 is always the start point closest to the body.
2368 	setPos(parentLine.p1());
2369 	m_legPolygon.append(QPointF(0,0));
2370 	m_legPolygon.append(parentLine.p2() - parentLine.p1());
2371 	m_legCurves.append(NULL);
2372 	m_legCurves.append(NULL);
2373 	m_legStrokeWidth = strokeWidth;
2374 	m_legColor = color;
2375 	reposition(m_attachedTo->mapToScene(parentLine.p2()), 1);
2376 
2377 	this->setCircular(false);
2378 }
2379 
hasRubberBandLeg() const2380 bool ConnectorItem::hasRubberBandLeg() const {
2381 	return m_rubberBandLeg;
2382 }
2383 
killRubberBandLeg()2384 void ConnectorItem::killRubberBandLeg() {
2385 	// this is a hack; see the caller for explanation
2386 	prepareGeometryChange();
2387 	m_rubberBandLeg = false;
2388 	m_legPolygon.clear();
2389 	clearCurves();
2390 }
2391 
legPen() const2392 QPen ConnectorItem::legPen() const
2393 {
2394 	if (!m_rubberBandLeg) return QPen();
2395 
2396 	QPen pen;
2397 	pen.setWidthF(m_legStrokeWidth);
2398 	pen.setColor(m_legColor);
2399 	pen.setCapStyle(Qt::RoundCap);
2400 	pen.setJoinStyle(Qt::RoundJoin);
2401 	return pen;
2402 }
2403 
legMousePressEvent(QGraphicsSceneMouseEvent * event)2404 bool ConnectorItem::legMousePressEvent(QGraphicsSceneMouseEvent *event) {
2405 	m_insertBendpointPossible = false;
2406 
2407 	if (attachedTo()->moveLock()) {
2408 		event->ignore();
2409 		return true;
2410 	}
2411 
2412 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
2413 	if (infoGraphicsView != NULL) {
2414 		infoGraphicsView->prepLegSelection(this->attachedTo());
2415 	}
2416 
2417 	int bendpointIndex;
2418 	CursorLocation cursorLocation = findLocation(event->pos(), bendpointIndex);
2419 	switch (cursorLocation) {
2420 
2421 		case InConnector:
2422 			if (event->modifiers() & altOrMetaModifier()) return false;
2423 
2424 			m_holdPos = mapToScene(m_legPolygon.last());
2425 			m_draggingLeg = true;
2426 			m_draggingLegIndex = m_legPolygon.count() - 1;
2427 			m_oldPolygon = m_legPolygon;
2428 			QGraphicsRectItem::mousePressEvent(event);
2429 			return true;
2430 
2431 		case InSegment:
2432 			if (curvyWiresIndicated(event->modifiers())) {
2433 				m_draggingLegIndex = bendpointIndex - 1;
2434 				Bezier * bezier = m_legCurves.at(m_draggingLegIndex);
2435 				if (bezier == NULL) {
2436 					bezier = new Bezier();
2437 					m_legCurves.replace(m_draggingLegIndex, bezier);
2438 				}
2439 
2440 				UndoBezier.copy(bezier);
2441 				if (bezier->isEmpty()) {
2442 					QPointF p0 = m_legPolygon.at(m_draggingLegIndex);
2443 					QPointF p1 = m_legPolygon.at(m_draggingLegIndex + 1);
2444 					bezier->initToEnds(p0, p1);
2445 				}
2446 
2447 				bezier->initControlIndex(event->pos(), m_legStrokeWidth);
2448 				m_draggingCurve = m_draggingLeg = true;
2449 				TheBezierDisplay = new BezierDisplay;
2450 				TheBezierDisplay->initDisplay(this, bezier);
2451 				return true;
2452 			}
2453 			else {
2454 				m_insertBendpointPossible = true;
2455 			}
2456 			// must continue on to InBendpoint
2457 
2458 		case InBendpoint:
2459 			m_draggingLegIndex = bendpointIndex;
2460 			m_holdPos = event->scenePos();
2461 			m_oldPolygon = m_legPolygon;
2462 			m_draggingLeg = true;
2463 			QGraphicsRectItem::mousePressEvent(event);
2464 			return true;
2465 
2466 		case InOrigin:
2467 		case InNotFound:
2468 		default:
2469 			event->ignore();
2470 			return true;
2471 	}
2472 }
2473 
findLocation(QPointF location,int & bendpointIndex)2474 ConnectorItem::CursorLocation ConnectorItem::findLocation(QPointF location, int & bendpointIndex) {
2475 	QPainterPath path;
2476 	Bezier * bezier = m_legCurves.at(m_legCurves.count() - 2);
2477 	if (bezier == NULL || bezier->isEmpty()) {
2478 		path.moveTo(m_connectorDetectEnd);
2479 		path.lineTo(m_legPolygon.last());
2480 	}
2481 	else {
2482 		Bezier left, right;
2483 		bezier->split(m_connectorDetectT, left, right);
2484 		path.moveTo(right.endpoint0());
2485 		path.cubicTo(right.cp0(), right.cp1(), right.endpoint1());
2486 	}
2487 
2488 	QPen pen = legPen();
2489 	path = GraphicsUtils::shapeFromPath(path, pen, m_legStrokeWidth, false);
2490 	if (path.contains(location)) {
2491 		return InConnector;
2492 	}
2493 
2494 	double wSqd = 4 * m_legStrokeWidth * m_legStrokeWidth;			// hover distance
2495 	for (int i = 0; i < m_legPolygon.count() - 1; i++) {
2496 		QPainterPath path;
2497 		path.moveTo(m_legPolygon.at(i));
2498 		Bezier * bezier = m_legCurves.at(i);
2499 		if (bezier != NULL && !bezier->isEmpty()) {
2500 			path.cubicTo(bezier->cp0(), bezier->cp1(), m_legPolygon.at(i + 1));
2501 		}
2502 		else {
2503 			path.lineTo(m_legPolygon.at(i + 1));
2504 		}
2505 		path = GraphicsUtils::shapeFromPath(path, pen, m_legStrokeWidth, false);
2506 		if (path.contains(location)) {
2507 			double d = GraphicsUtils::distanceSqd(m_legPolygon.at(i), location);
2508 			if (d <= wSqd) {
2509 				bendpointIndex = i;
2510 				if (i == 0) {
2511 					return InOrigin;
2512 				}
2513 
2514 				return InBendpoint;
2515 			}
2516 			else {
2517 				d = GraphicsUtils::distanceSqd(m_legPolygon.at(i + 1), location);
2518 				if (d <= wSqd) {
2519 					bendpointIndex = i + 1;
2520 					return InBendpoint;
2521 				}
2522 			}
2523 
2524 			bendpointIndex = i + 1;
2525 			return InSegment;
2526 		}
2527 	}
2528 
2529 	return InNotFound;
2530 }
2531 
contextMenuEvent(QGraphicsSceneContextMenuEvent * event)2532 void ConnectorItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
2533 {
2534 	if (m_hidden || m_inactive || m_hybrid) {
2535 		event->ignore();
2536 		return;
2537 	}
2538 
2539 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
2540 	if (infoGraphicsView != NULL) {
2541 		infoGraphicsView->setActiveConnectorItem(this);
2542 	}
2543 
2544 	if ((acceptedMouseButtons() & Qt::RightButton) == 0) {
2545 		event->ignore();
2546 		return;
2547 	}
2548 
2549 	if (!m_rubberBandLeg) {
2550 		event->ignore();
2551 		return;
2552 	}
2553 
2554 	int bendpointIndex;
2555 	CursorLocation cursorLocation = findLocation(event->pos(), bendpointIndex);
2556 	switch (cursorLocation) {
2557 		case InSegment:
2558 		{
2559 			QMenu menu;
2560 			QAction * addAction = menu.addAction(tr("Add bendpoint"));
2561 			addAction->setData(1);
2562 			Bezier * bezier = m_legCurves.at(bendpointIndex - 1);
2563 			if (bezier != NULL && !bezier->isEmpty()) {
2564 				QAction * straightenAction = menu.addAction(tr("Straighten curve"));
2565 				straightenAction->setData(2);
2566 			}
2567 			QAction *selectedAction = menu.exec(event->screenPos());
2568 			if (selectedAction) {
2569 				if (selectedAction->data().toInt() == 1) {
2570 					insertBendpoint(event->pos(), bendpointIndex);
2571 				}
2572 				else if (selectedAction->data().toInt() == 2) {
2573 					InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
2574 					if (infoGraphicsView != NULL) {
2575 						Bezier newBezier;
2576 						infoGraphicsView->prepLegCurveChange(this, bendpointIndex - 1,bezier, &newBezier, true);
2577 					}
2578 				}
2579 			}
2580 		}
2581 		return;
2582 
2583 		case InBendpoint:
2584 			if (bendpointIndex < m_legPolygon.count() - 1) {
2585 				QMenu menu;
2586 				menu.addAction(tr("Remove bendpoint"));
2587 				QAction *selectedAction = menu.exec(event->screenPos());
2588 				if (selectedAction) {
2589 					removeBendpoint(bendpointIndex);
2590 				}
2591 				return;
2592 			}
2593 
2594 		default:
2595 			break;
2596 	}
2597 
2598 	event->ignore();
2599 	return;
2600 }
2601 
insertBendpoint(QPointF p,int bendpointIndex)2602 void ConnectorItem::insertBendpoint(QPointF p, int bendpointIndex)
2603 {
2604 	prepareGeometryChange();
2605 
2606 	m_oldPolygon = m_legPolygon;
2607 	insertBendpointAux(p, bendpointIndex);
2608 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
2609 	if (infoGraphicsView != NULL) {
2610 		infoGraphicsView->prepLegBendpointChange(this, m_oldPolygon.count(), m_legPolygon.count(), bendpointIndex, p,
2611 							&UndoBezier, m_legCurves.at(bendpointIndex - 1), m_legCurves.at(bendpointIndex), false);
2612 	}
2613 	calcConnectorEnd();
2614 	update();
2615 }
2616 
insertBendpointAux(QPointF p,int bendpointIndex)2617 Bezier * ConnectorItem::insertBendpointAux(QPointF p, int bendpointIndex)
2618 {
2619 	UndoBezier.clear();
2620 	m_legPolygon.insert(bendpointIndex, p);
2621 	m_legCurves.insert(bendpointIndex, NULL);
2622 	Bezier * bezier = m_legCurves.at(bendpointIndex - 1);
2623 	if (bezier == NULL || bezier->isEmpty()) return NULL;
2624 
2625 	QPointF p0 = m_legPolygon.at(bendpointIndex - 1);
2626 	QPointF p1 = m_legPolygon.at(bendpointIndex + 1);
2627 	bezier->set_endpoints(p0, p1);
2628 	UndoBezier.copy(bezier);
2629 
2630 	double t = bezier->findSplit(p, m_legStrokeWidth);
2631 	Bezier left, right;
2632 	bezier->split(t, left, right);
2633 	replaceBezier(bendpointIndex - 1, &left);
2634 	replaceBezier(bendpointIndex, &right);
2635 	return NULL;
2636 }
2637 
removeBendpoint(int bendpointIndex)2638 void ConnectorItem::removeBendpoint(int bendpointIndex)
2639 {
2640 	prepareGeometryChange();
2641 
2642 	Bezier b0, b1;
2643 	b0.copy(m_legCurves.at(bendpointIndex - 1));
2644 	QPointF p0 = m_legPolygon.at(bendpointIndex - 1);
2645 	QPointF p1 = m_legPolygon.at(bendpointIndex);
2646 	b0.set_endpoints(p0, p1);
2647 	b1.copy(m_legCurves.at(bendpointIndex));
2648 	p0 = m_legPolygon.at(bendpointIndex);
2649 	p1 = m_legPolygon.at(bendpointIndex + 1);
2650 	b0.set_endpoints(p0, p1);
2651 
2652 	m_oldPolygon = m_legPolygon;
2653 	QPointF p = m_legPolygon.at(bendpointIndex);
2654 	m_legPolygon.remove(bendpointIndex);
2655 
2656 	Bezier b2 = b0.join(&b1);
2657 	replaceBezier(bendpointIndex - 1, &b2);
2658 
2659 	Bezier * bezier = m_legCurves.at(bendpointIndex);
2660 	m_legCurves.remove(bendpointIndex);
2661 	if (bezier) delete bezier;
2662 
2663 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
2664 	if (infoGraphicsView != NULL) {
2665 		infoGraphicsView->prepLegBendpointChange(this, m_oldPolygon.count(), m_legPolygon.count(), bendpointIndex, p, &b0, &b1, &b2, false);
2666 	}
2667 
2668 
2669 	calcConnectorEnd();
2670 	update();
2671 }
2672 
clearCurves()2673 void ConnectorItem::clearCurves()
2674 {
2675 	foreach (Bezier * bezier, m_legCurves) {
2676 		if (bezier != NULL) delete bezier;
2677 	}
2678 	m_legCurves.clear();
2679 }
2680 
changeLegCurve(int index,const Bezier * newBezier)2681 void ConnectorItem::changeLegCurve(int index, const Bezier *newBezier)
2682 {
2683 	prepareGeometryChange();
2684 
2685 	replaceBezier(index, newBezier);
2686 	calcConnectorEnd();
2687 	update();
2688 }
2689 
addLegBendpoint(int index,QPointF p,const Bezier * bezierLeft,const Bezier * bezierRight)2690 void ConnectorItem::addLegBendpoint(int index, QPointF p, const Bezier * bezierLeft, const Bezier * bezierRight)
2691 {
2692 	prepareGeometryChange();
2693 
2694 	m_legPolygon.insert(index, p);
2695 	m_legCurves.insert(index, NULL);
2696 	replaceBezier(index - 1, bezierLeft);
2697 	replaceBezier(index, bezierRight);
2698 	calcConnectorEnd();
2699 	update();
2700 
2701 }
2702 
removeLegBendpoint(int index,const Bezier * newBezier)2703 void ConnectorItem::removeLegBendpoint(int index, const Bezier * newBezier)
2704 {
2705 	prepareGeometryChange();
2706 
2707 	m_legPolygon.remove(index);
2708 	Bezier * bezier = m_legCurves.at(index);
2709 	if (bezier) delete bezier;
2710 	m_legCurves.remove(index);
2711 	replaceBezier(index - 1, newBezier);
2712 	calcConnectorEnd();
2713 	update();
2714 }
2715 
moveLegBendpoint(int index,QPointF p)2716 void ConnectorItem::moveLegBendpoint(int index, QPointF p)
2717 {
2718 	m_legPolygon.replace(index, mapFromScene(p));
2719 	calcConnectorEnd();
2720 	update();
2721 }
2722 
beziers()2723 const QVector<Bezier *> & ConnectorItem::beziers()
2724 {
2725 	return m_legCurves;
2726 }
2727 
replaceBezier(int index,const Bezier * newBezier)2728 void ConnectorItem::replaceBezier(int index, const Bezier * newBezier)
2729 {
2730 	Bezier * bezier = m_legCurves.at(index);
2731 	if (bezier == NULL && newBezier == NULL) {
2732 	}
2733 	else if (bezier && newBezier) {
2734 		bezier->copy(newBezier);
2735 	}
2736 	else if (newBezier) {
2737 		bezier = new Bezier;
2738 		m_legCurves.replace(index , bezier);
2739 		bezier->copy(newBezier);
2740 	}
2741 	else if (bezier) {
2742 		bezier->clear();
2743 	}
2744 }
2745 
cursorKeyEvent(Qt::KeyboardModifiers modifiers)2746 void ConnectorItem::cursorKeyEvent(Qt::KeyboardModifiers modifiers)
2747 {
2748 	if (m_rubberBandLeg) {
2749 		if (m_draggingLeg) return;
2750 
2751 		InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);;
2752 		if (infoGraphicsView) {
2753 			QPoint p = infoGraphicsView->mapFromGlobal(QCursor::pos());
2754 			QPointF r = infoGraphicsView->mapToScene(p);
2755 			// DebugDialog::debug(QString("got key event %1").arg(keyEvent->modifiers()));
2756 			updateLegCursor(mapFromScene(r), modifiers);
2757 		}
2758 	}
2759 	else if (attachedToItemType() == ModelPart::Wire) {
2760 		updateWireCursor(modifiers);
2761 	}
2762 }
2763 
updateWireCursor(Qt::KeyboardModifiers modifiers)2764 void ConnectorItem::updateWireCursor(Qt::KeyboardModifiers modifiers)
2765 {
2766 	//DebugDialog::debug("uwc");
2767 	QCursor cursor = *CursorMaster::BendpointCursor;
2768 	if (isBendpoint()) {
2769 		//DebugDialog::debug("uwc bend");
2770 		if (modifiers & altOrMetaModifier()) {
2771 			//DebugDialog::debug("uwc alt");
2772 			Wire * wire = qobject_cast<Wire *>(attachedTo());
2773 			if (wire != NULL && wire->canChainMultiple()) {
2774 				//DebugDialog::debug("uwc make wire");
2775 				cursor = *CursorMaster::MakeWireCursor;
2776 			}
2777 		}
2778 	}
2779 
2780 	CursorMaster::instance()->addCursor(this, cursor);
2781 }
2782 
updateLegCursor(QPointF p,Qt::KeyboardModifiers modifiers)2783 void ConnectorItem::updateLegCursor(QPointF p, Qt::KeyboardModifiers modifiers)
2784 {
2785 	int bendpointIndex;
2786 	CursorLocation cursorLocation = findLocation(p, bendpointIndex);
2787 	QCursor cursor;
2788 	switch (cursorLocation) {
2789 		case InOrigin:
2790 			cursor = *attachedTo()->getCursor(modifiers);
2791 			break;
2792 		case InBendpoint:
2793 			cursor = *CursorMaster::BendpointCursor;
2794 			break;
2795 		case InSegment:
2796 			cursor = curvyWiresIndicated(modifiers) ? *CursorMaster::MakeCurveCursor : *CursorMaster::NewBendpointCursor;
2797 			break;
2798 		case InConnector:
2799 			cursor = (modifiers & altOrMetaModifier()) ? *CursorMaster::MakeWireCursor : *CursorMaster::BendlegCursor;
2800 			break;
2801 		default:
2802 			cursor = Qt::ArrowCursor;
2803 			break;
2804 	}
2805 	CursorMaster::instance()->addCursor(this, cursor);
2806 }
2807 
curvyWiresIndicated(Qt::KeyboardModifiers modifiers)2808 bool ConnectorItem::curvyWiresIndicated(Qt::KeyboardModifiers modifiers)
2809 {
2810 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
2811 	if (infoGraphicsView == NULL) return true;
2812 
2813 	return infoGraphicsView->curvyWiresIndicated(modifiers);
2814 }
2815 
isBendpoint()2816 bool ConnectorItem::isBendpoint()
2817 {
2818 	if (connectionsCount() == 0) return false;
2819 
2820 	foreach (ConnectorItem * ci, connectedToItems()) {
2821 		if (ci->attachedToItemType() != ModelPart::Wire) {
2822 			return false;
2823 		}
2824 	}
2825 
2826 	return true;
2827 }
2828 
setConnectorLocalName(const QString & name)2829 void ConnectorItem::setConnectorLocalName(const QString & name)
2830 {
2831 	if (m_connector) {
2832 		m_connector->setConnectorLocalName(name);
2833 	}
2834 }
2835 
isGroundFillSeed()2836 bool ConnectorItem::isGroundFillSeed()
2837 {
2838 	return m_groundFillSeed;
2839 }
2840 
setGroundFillSeed(bool seed)2841 void ConnectorItem::setGroundFillSeed(bool seed)
2842 {
2843 	m_groundFillSeed = seed;
2844 	ConnectorItem * cross = this->getCrossLayerConnectorItem();
2845 	if (cross) cross->m_groundFillSeed = seed;
2846 }
2847 
getSkipFlags()2848 ViewGeometry::WireFlags ConnectorItem::getSkipFlags() {
2849     InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
2850     if (infoGraphicsView == NULL) return ViewGeometry::RatsnestFlag;
2851 
2852     return (ViewGeometry::RatsnestFlag | ViewGeometry::NormalFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag) ^ infoGraphicsView->getTraceFlag();
2853 }
2854