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