1 /*******************************************************************
2 
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2015 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 ofro
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: 7000 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-29 07:24:08 +0200 (Mon, 29 Apr 2013) $
24 
25 ********************************************************************/
26 
27 #include <QtCore>
28 
29 #include <QGraphicsScene>
30 #include <QPoint>
31 #include <QPair>
32 #include <QMatrix>
33 #include <QtAlgorithms>
34 #include <QPen>
35 #include <QColor>
36 #include <QRubberBand>
37 #include <QLine>
38 #include <QHash>
39 #include <QMultiHash>
40 #include <QBrush>
41 #include <QGraphicsItem>
42 #include <QMainWindow>
43 #include <QApplication>
44 #include <QDomElement>
45 #include <QSettings>
46 #include <QClipboard>
47 #include <QScrollBar>
48 #include <QStatusBar>
49 
50 #include <limits>
51 
52 #include "../items/partfactory.h"
53 #include "../items/paletteitem.h"
54 #include "../items/logoitem.h"
55 #include "../items/pad.h"
56 #include "../items/ruler.h"
57 #include "../items/symbolpaletteitem.h"
58 #include "../items/wire.h"
59 #include "../commands.h"
60 #include "../model/modelpart.h"
61 #include "../debugdialog.h"
62 #include "../items/layerkinpaletteitem.h"
63 #include "sketchwidget.h"
64 #include "../connectors/connectoritem.h"
65 #include "../connectors/svgidlayer.h"
66 #include "../items/jumperitem.h"
67 #include "../items/stripboard.h"
68 #include "../items/virtualwire.h"
69 #include "../items/tracewire.h"
70 #include "../itemdrag.h"
71 #include "../layerattributes.h"
72 #include "../waitpushundostack.h"
73 #include "fgraphicsscene.h"
74 #include "../version/version.h"
75 #include "../items/partlabel.h"
76 #include "../items/note.h"
77 #include "../svg/svgfilesplitter.h"
78 #include "../svg/svgflattener.h"
79 #include "../infoview/htmlinfoview.h"
80 #include "../items/resizableboard.h"
81 #include "../utils/graphicsutils.h"
82 #include "../utils/textutils.h"
83 #include "../utils/bezier.h"
84 #include "../utils/cursormaster.h"
85 #include "../utils/fmessagebox.h"
86 #include "../fsvgrenderer.h"
87 #include "../items/resistor.h"
88 #include "../items/mysterypart.h"
89 #include "../items/pinheader.h"
90 #include "../items/dip.h"
91 #include "../items/groundplane.h"
92 #include "../items/moduleidnames.h"
93 #include "../items/hole.h"
94 #include "../items/capacitor.h"
95 #include "../items/schematicframe.h"
96 #include "../utils/graphutils.h"
97 #include "../utils/ratsnestcolors.h"
98 #include "../utils/cursormaster.h"
99 
100 /////////////////////////////////////////////////////////////////////
101 
hideTerminalID(QDomDocument & doc,const QString & terminalID)102 bool hideTerminalID(QDomDocument & doc, const QString & terminalID) {
103     QDomElement root = doc.documentElement();
104     QDomElement terminal = TextUtils::findElementWithAttribute(root, "id", terminalID);
105     if (terminal.isNull()) return false;
106 
107     terminal.setTagName("g");
108     return true;
109 }
110 
ensureStrokeWidth(QDomDocument & doc,const QString & connectorID,double factor)111 bool ensureStrokeWidth(QDomDocument & doc, const QString & connectorID, double factor) {
112     QDomElement root = doc.documentElement();
113     QDomElement connector = TextUtils::findElementWithAttribute(root, "id", connectorID);
114     if (connector.isNull()) return false;
115 
116     QString stroke = connector.attribute("stroke");
117     if (stroke.isEmpty()) return false;
118 
119     QString strokeWidth = connector.attribute("stroke-width");
120     if (!strokeWidth.isEmpty()) return false;
121 
122     TextUtils::getStrokeWidth(connector, factor);       // default stroke width is 1, multipled by factor
123     return true;
124 }
125 
126 /////////////////////////////////////////////////////////////////////
127 
SizeItem()128 SizeItem::SizeItem()
129 {
130 }
131 
~SizeItem()132 SizeItem::~SizeItem()
133 {
134 }
135 
136 /////////////////////////////////////////////////////////////////////
137 
138 enum ConnectionStatus {
139 	IN_,
140 	OUT_,
141 	FREE_,
142 	UNDETERMINED_
143 };
144 
145 static const double CloseEnough = 0.5;  // in pixels, for swapping into the breadboard
146 
147 const int SketchWidget::MoveAutoScrollThreshold = 5;
148 const int SketchWidget::DragAutoScrollThreshold = 10;
149 static const int AutoRepeatDelay = 750;
150 const int SketchWidget::PropChangeDelay = 100;
151 bool SketchWidget::m_blockUI = false;
152 
153 /////////////////////////////////////////////////////////////////////
154 
zLessThan(QGraphicsItem * & p1,QGraphicsItem * & p2)155 bool zLessThan(QGraphicsItem * & p1, QGraphicsItem * & p2)
156 {
157 	return p1->zValue() < p2->zValue();
158 }
159 
160 /////////////////////////////////////////////////////////////////////
161 
SketchWidget(ViewLayer::ViewID viewID,QWidget * parent,int size,int minSize)162 SketchWidget::SketchWidget(ViewLayer::ViewID viewID, QWidget *parent, int size, int minSize)
163     : InfoGraphicsView(parent)
164 {
165     m_everZoomed = false;
166     m_itemMenu = NULL;
167     m_pasting = false;
168     m_rubberBandLegWasEnabled = m_curvyWires = false;
169 	m_middleMouseIsPressed = false;
170 	m_arrowTimer.setParent(this);
171 	m_arrowTimer.setInterval(AutoRepeatDelay);
172 	m_arrowTimer.setSingleShot(true);
173 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
174     m_arrowTimer.setTimerType(Qt::PreciseTimer);
175     m_autoScrollTimer.setTimerType(Qt::PreciseTimer);
176 #endif
177 	connect(&m_arrowTimer, SIGNAL(timeout()), this, SLOT(arrowTimerTimeout()));
178 	m_addDefaultParts = false;
179 	m_addedDefaultPart = NULL;
180 	m_movingItem = NULL;
181 	m_movingSVGRenderer = NULL;
182 	m_clearSceneRect = false;
183 	m_draggingBendpoint = false;
184 	m_zoom = 100;
185 	m_showGrid = m_alignToGrid = true;
186 	m_movingByMouse = m_movingByArrow = false;
187 	m_statusConnectState = StatusConnectNotTried;
188 	m_dragBendpointWire = NULL;
189 	m_lastHoverEnterItem = NULL;
190 	m_lastHoverEnterConnectorItem = NULL;
191 	m_spaceBarWasPressed = m_spaceBarIsPressed = false;
192 	m_current = false;
193 	m_ignoreSelectionChangeEvents = 0;
194 	m_droppingItem = NULL;
195 	m_chainDrag = false;
196 	m_bendpointWire = m_connectorDragWire = NULL;
197 	m_tempDragWireCommand = m_holdingSelectItemCommand = NULL;
198 	m_viewID = viewID;
199 	//setAlignment(Qt::AlignLeft | Qt::AlignTop);
200 	setDragMode(QGraphicsView::RubberBandDrag);
201     setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);
202     setAcceptDrops(true);
203 	setRenderHint(QPainter::Antialiasing, true);
204 
205 	//setCacheMode(QGraphicsView::CacheBackground);
206 	//setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
207 	//setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
208 
209 	setTransformationAnchor(QGraphicsView::AnchorViewCenter);
210 	//setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
211 	//setTransformationAnchor(QGraphicsView::NoAnchor);
212     FGraphicsScene* scene = new FGraphicsScene(this);
213     this->setScene(scene);
214 
215     //this->scene()->setSceneRect(0,0, rect().width(), rect().height());
216 
217     // Setting the scene rect here seems to mean it never resizes when the user drags an object
218     // outside the sceneRect bounds.  So catch some signal and do the resize manually?
219     // this->scene()->setSceneRect(0, 0, 500, 500);
220 
221     // if the sceneRect isn't set, the view seems to grow and scroll gracefully as new items are added
222     // however, it doesn't shrink if items are removed.
223 
224     // a bit of a hack so that, when there is no scenerect set,
225     // the first item dropped into the scene doesn't leap to the top left corner
226     // as the scene resizes to fit the new item
227    	m_sizeItem = new SizeItem();
228     m_sizeItem->setLine(0, 0, rect().width(), rect().height());
229 	//DebugDialog::debug(QString("initial rect %1 %2").arg(rect().width()).arg(rect().height()));
230     this->scene()->addItem(m_sizeItem);
231     m_sizeItem->setVisible(false);
232 
233 	connect(this->scene(), SIGNAL(selectionChanged()), this, SLOT(selectionChangedSlot()));
234 
235 	connect(QApplication::clipboard(),SIGNAL(changed(QClipboard::Mode)),this,SLOT(restartPasteCount()));
236     restartPasteCount(); // the first time
237 
238     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
239     resize(size, size);
240     setMinimumSize(minSize, minSize);
241 
242     setLastPaletteItemSelected(NULL);
243 
244     m_infoViewOnHover = true;
245 
246 	setMouseTracking(true);
247 
248 }
249 
~SketchWidget()250 SketchWidget::~SketchWidget() {
251 	foreach (ViewLayer * viewLayer, m_viewLayers.values()) {
252 		if (viewLayer == NULL) continue;
253 
254 		delete viewLayer;
255 	}
256 	m_viewLayers.clear();
257 }
258 
restartPasteCount()259 void SketchWidget::restartPasteCount() {
260 	m_pasteCount = 0;
261 }
262 
undoStack()263 WaitPushUndoStack* SketchWidget::undoStack() {
264 	return m_undoStack;
265 }
266 
setUndoStack(WaitPushUndoStack * undoStack)267 void SketchWidget::setUndoStack(WaitPushUndoStack * undoStack) {
268 	m_undoStack = undoStack;
269 }
270 
loadFromModelParts(QList<ModelPart * > & modelParts,BaseCommand::CrossViewType crossViewType,QUndoCommand * parentCommand,bool offsetPaste,const QRectF * boundingRect,bool seekOutsideConnections,QList<long> & newIDs)271 void SketchWidget::loadFromModelParts(QList<ModelPart *> & modelParts, BaseCommand::CrossViewType crossViewType, QUndoCommand * parentCommand, bool offsetPaste, const QRectF * boundingRect, bool seekOutsideConnections, QList<long> & newIDs) {
272 	clearHoldingSelectItem();
273 
274 	if (parentCommand) {
275 		SelectItemCommand * selectItemCommand = stackSelectionState(false, parentCommand);
276 		selectItemCommand->setSelectItemType(SelectItemCommand::DeselectAll);
277 		selectItemCommand->setCrossViewType(crossViewType);
278 		new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
279         new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
280 	}
281 
282 	QHash<long, ItemBase *> newItems;
283 	setIgnoreSelectionChangeEvents(true);
284 
285 	QString viewName = ViewLayer::viewIDXmlName(m_viewID);
286 	QMultiMap<double, ItemBase *> zmap;
287 
288 	QPointF sceneCenter = mapToScene(viewport()->rect().center());
289 
290 	QPointF sceneCorner;
291 	if (boundingRect) {
292 		sceneCorner.setX(sceneCenter.x() - (boundingRect->width() / 2));
293 		sceneCorner.setY(sceneCenter.y() - (boundingRect->height() / 2));
294 	}
295 
296     QHash<ItemBase *, long> superparts;
297     QHash<long, long> superparts2;
298     QList<ModelPart *> zeroLength;
299 	// make parts
300 	foreach (ModelPart * mp, modelParts) {
301 		QDomElement instance = mp->instanceDomElement();
302 		if (instance.isNull()) continue;
303 
304 		QDomElement views = instance.firstChildElement("views");
305 		if (views.isNull()) continue;
306 
307 		QDomElement view = views.firstChildElement(viewName);
308 		if (view.isNull()) continue;
309 
310 		bool locked = view.attribute("locked", "").compare("true") == 0;
311         bool superpartOK;
312         long superpartID = view.attribute("superpart", "").toLong(&superpartOK);
313         if (superpartOK) {
314             superpartID = ItemBase::getNextID(superpartID);
315         }
316 
317 		QDomElement geometry = view.firstChildElement("geometry");
318 		if (geometry.isNull()) continue;
319 
320 		ViewGeometry viewGeometry(geometry);
321         if (mp->itemType() == ModelPart::Wire) {
322             if (viewGeometry.hasFlag(getTraceFlag())) {
323                 QLineF l = viewGeometry.line();
324                 if (l.p1().x() == 0 && l.p1().y() == 0 && l.p2().x() == 0 && l.p2().y() == 0) {
325                     if (view.firstChildElement("connectors").isNull() && view.nextSiblingElement().isNull()) {
326                         DebugDialog::debug(QString("wire has zero length %1 in %2").arg(mp->moduleID()).arg(m_viewID));
327                         zeroLength.append(mp);
328                         continue;
329                     }
330                 }
331             }
332         }
333 
334 		QDomElement labelGeometry = view.firstChildElement("titleGeometry");
335 
336         QDomElement layerHidden = view.firstChildElement("layerHidden");
337 
338 		ViewLayer::ViewLayerPlacement viewLayerPlacement = getViewLayerPlacement(mp, instance, view, viewGeometry);
339 
340 		// use a function of the model index to ensure the same parts have the same ID across views
341 		long newID = ItemBase::getNextID(mp->modelIndex());
342 		if (parentCommand == NULL) {
343             viewGeometryConversionHack(viewGeometry, mp);
344 			ItemBase * itemBase = addItemAux(mp, viewLayerPlacement, viewGeometry, newID, true, m_viewID, false);
345 			if (itemBase != NULL) {
346 				if (locked) {
347 					itemBase->setMoveLock(true);
348 				}
349 
350                 if (superpartOK) {
351                     superparts.insert(itemBase, superpartID);
352                 }
353 
354                 while (!layerHidden.isNull()) {
355                     hidePartLayer(itemBase, ViewLayer::viewLayerIDFromXmlString(layerHidden.attribute("layer")), true);
356                     layerHidden = layerHidden.nextSiblingElement("layerHidden");
357                 }
358 
359                 //if (itemBase->itemType() == ModelPart::ResizableBoard) {
360                     //DebugDialog::debug("sticky");
361                 //}
362                 if (itemBase->isBaseSticky() && itemBase->isLocalSticky()) {
363                     // make sure the icon is displayed
364                     itemBase->setLocalSticky(true);
365                 }
366 
367 				zmap.insert(viewGeometry.z() - qFloor(viewGeometry.z()), itemBase);
368 				bool gotOne = false;
369 				if (!gotOne) {
370 					PaletteItem * paletteItem = qobject_cast<PaletteItem *>(itemBase);
371 					if (paletteItem != NULL) {
372 						// wires don't have transforms
373 
374                         //paletteItem->setTransforms();     // jrc 14 july 2013: this call seems redundant--transforms have already been set up by now
375 						gotOne = true;
376 					}
377 				}
378 				if (!gotOne) {
379 					Wire * wire = qobject_cast<Wire *>(itemBase);
380 					if (wire != NULL) {
381 						QDomElement extras = view.firstChildElement("wireExtras");
382 						wire->setExtras(extras, this);
383 						gotOne = true;
384 					}
385 				}
386 				if (!gotOne) {
387 					Note * note = qobject_cast<Note *>(itemBase);
388 					if (note != NULL) {
389 						note->setText(mp->instanceText(), true);
390 						gotOne = true;
391 					}
392 				}
393 
394 				// use the modelIndex from mp, not from the newly created item, because we're mapping from the modelIndex in the xml file
395 				newItems.insert(mp->modelIndex(), itemBase);
396 				if (itemBase->itemType() != ModelPart::Wire) {
397 					itemBase->restorePartLabel(labelGeometry, getLabelViewLayerID(itemBase));
398 				}
399 			}
400 		}
401 		else {
402 			// offset pasted items so we can differentiate them from the originals
403 			if (offsetPaste) {
404 				if (m_pasteOffset.x() != 0 || m_pasteOffset.y() != 0) {
405 					viewGeometry.offset((20 * m_pasteCount) + m_pasteOffset.x(), (20 * m_pasteCount) + m_pasteOffset.y());
406 				}
407 				else if (boundingRect && !boundingRect->isNull()) {
408 					double dx = viewGeometry.loc().x() - boundingRect->left() + sceneCorner.x() + (20 * m_pasteCount);
409 					double dy = viewGeometry.loc().y() - boundingRect->top() + sceneCorner.y() + (20 * m_pasteCount);
410 					viewGeometry.setLoc(QPointF(dx, dy));
411 				}
412 			}
413 			newAddItemCommand(crossViewType, mp, mp->moduleID(), viewLayerPlacement, viewGeometry, newID, false, mp->modelIndex(), false, parentCommand);
414 
415             if (superpartOK) {
416                 superparts2.insert(newID, superpartID);
417             }
418 
419 			// TODO: all this part specific stuff should be in the PartFactory
420 
421 			if (Board::isBoard(mp) || mp->itemType() == ModelPart::Logo) {
422 				bool ok;
423 				double w = mp->localProp("width").toDouble(&ok);
424 				if (ok) {
425 					double h = mp->localProp("height").toDouble(&ok);
426 					if (ok) {
427 						new ResizeBoardCommand(this, newID, w, h, w, h, parentCommand);
428 					}
429 				}
430 			}
431 			else if (mp->itemType() == ModelPart::Note) {
432 				new ChangeNoteTextCommand(this, newID, mp->instanceText(), mp->instanceText(), viewGeometry.rect().size(), viewGeometry.rect().size(), parentCommand);
433 			}
434 			else if (mp->itemType() == ModelPart::Ruler) {
435 				QString w = mp->localProp("width").toString();
436 				QString w2 = w;
437 				w.chop(2);
438 				int units = w2.endsWith("cm") ? 0 : 1;
439 				new ResizeBoardCommand(this, newID, w.toDouble(), units, w.toDouble(), units, parentCommand);
440 				mp->setLocalProp("width", "");		// ResizeBoardCommand won't execute if the width property is already set
441 			}
442 
443 			if (locked) {
444 				new MoveLockCommand(this, newID, true, true, parentCommand);
445 			}
446 
447             while (!layerHidden.isNull()) {
448                 new HidePartLayerCommand(this, newID, ViewLayer::viewLayerIDFromXmlString(layerHidden.attribute("layer")), true, true, parentCommand);
449                 layerHidden = layerHidden.nextSiblingElement("layerHidden");
450             }
451 
452 			if (!labelGeometry.isNull()) {
453 				QDomElement clone = labelGeometry.cloneNode(true).toElement();
454 				bool ok;
455 				double x = clone.attribute("x").toDouble(&ok);
456 				if (ok) {
457 					if (m_pasteOffset.x() == 0 && m_pasteOffset.y() == 0) {
458 						int dx = (boundingRect) ? boundingRect->left() : 0;
459 						x = x - dx + sceneCorner.x() + (20 * m_pasteCount);
460 					}
461 					else {
462 						x += (20 * m_pasteCount) + m_pasteOffset.x();
463 					}
464 					clone.setAttribute("x", QString::number(x));
465 				}
466 				double y = clone.attribute("y").toDouble(&ok);
467 				if (ok) {
468 					if (m_pasteOffset.x() == 0 && m_pasteOffset.y() == 0) {
469 						int dy = boundingRect ? boundingRect->top() : 0;
470 						y = y - dy + sceneCorner.y() + (20 * m_pasteCount);
471 					}
472 					else {
473 						y += (20 * m_pasteCount) + m_pasteOffset.y();
474 					}
475 					clone.setAttribute("y", QString::number(y));
476 				}
477 				new RestoreLabelCommand(this, newID, clone, parentCommand);
478 			}
479 
480 			newIDs << newID;
481 			if (mp->moduleID() == ModuleIDNames::WireModuleIDName) {
482 				addWireExtras(newID, view, parentCommand);
483 			}
484 		}
485 	}
486 
487     foreach (ModelPart * mp, zeroLength) {
488         modelParts.removeOne(mp);
489         mp->killViewItems();
490         m_sketchModel->removeModelPart(mp);
491         delete mp;
492     }
493 
494     foreach (ItemBase * sub, superparts.keys()) {
495         ItemBase * super = findItem(superparts.value(sub));
496         if (super) {
497             super->addSubpart(sub);
498         }
499     }
500 
501     foreach (long newID, superparts2.keys()) {
502         AddSubpartCommand * asc = new AddSubpartCommand(this, crossViewType, superparts2.value(newID), newID, parentCommand);
503         asc->setRedoOnly();
504     }
505 
506     if (parentCommand) {
507 	    foreach (long id, newIDs) {
508 		    new CheckStickyCommand(this, crossViewType, id, false, CheckStickyCommand::RemoveOnly, parentCommand);
509 	    }
510     }
511 
512 	if (zmap.count() > 0) {
513 		double z = 0.5;
514 		foreach (ItemBase * itemBase, zmap.values()) {
515 			itemBase->slamZ(z);
516 			z += ViewLayer::getZIncrement();
517 		}
518 		foreach (ViewLayer * viewLayer, m_viewLayers) {
519 			if (viewLayer != NULL) viewLayer->resetNextZ(z);
520 		}
521 	}
522 
523 	QStringList alreadyConnected;
524 
525 	QHash<QString, QDomElement> legs;
526 
527 	// now restore connections
528 	foreach (ModelPart * mp, modelParts) {
529 		QDomElement instance = mp->instanceDomElement();
530 		if (instance.isNull()) continue;
531 
532 		QDomElement views = instance.firstChildElement("views");
533 		if (views.isNull()) continue;
534 
535 		QDomElement view = views.firstChildElement(viewName);
536 		if (view.isNull()) continue;
537 
538 		QDomElement connectors = view.firstChildElement("connectors");
539 		if (connectors.isNull()) {
540             continue;
541         }
542 
543 		QDomElement connector = connectors.firstChildElement("connector");
544 		while (!connector.isNull()) {
545 			QString fromConnectorID = connector.attribute("connectorId");
546 			ViewLayer::ViewLayerID connectorViewLayerID = ViewLayer::viewLayerIDFromXmlString(connector.attribute("layer"));
547 			bool gfs = connector.attribute("groundFillSeed").compare("true") == 0;
548 			if (gfs) {
549 				ItemBase * fromBase = newItems.value(mp->modelIndex(), NULL);
550 				if (fromBase) {
551 					ConnectorItem * fromConnectorItem = fromBase->findConnectorItemWithSharedID(fromConnectorID, ViewLayer::specFromID(connectorViewLayerID));
552 					if (fromConnectorItem) {
553 						fromConnectorItem->setGroundFillSeed(true);
554 					}
555 				}
556 			}
557 			QDomElement connects = connector.firstChildElement("connects");
558 			if (!connects.isNull()) {
559 				QDomElement connect = connects.firstChildElement("connect");
560 				while (!connect.isNull()) {
561 					handleConnect(connect, mp, fromConnectorID, connectorViewLayerID, alreadyConnected, newItems, parentCommand, seekOutsideConnections);
562 					connect = connect.nextSiblingElement("connect");
563 				}
564 			}
565 
566 			QDomElement leg = connector.firstChildElement("leg");
567 			if (!leg.isNull() && !leg.firstChildElement("point").isNull()) {
568 				if (parentCommand) {
569 					legs.insert(QString::number(ItemBase::getNextID(mp->modelIndex())) + "." + fromConnectorID, leg);
570 				}
571 				else {
572 					ItemBase * fromBase = newItems.value(mp->modelIndex(), NULL);
573 					if (fromBase) {
574 						legs.insert(QString::number(fromBase->id()) + "." + fromConnectorID, leg);
575 					}
576 				}
577 			}
578 
579 			connector = connector.nextSiblingElement("connector");
580 		}
581 	}
582 
583 	// must do legs after all connections are set up
584 	foreach (QString key, legs.keys()) {
585 		int ix = key.indexOf(".");
586 		if (ix <= 0) continue;
587 
588 		QDomElement leg = legs.value(key);
589 		long id = key.left(ix).toInt();
590 		QString fromConnectorID = key.remove(0, ix + 1);
591 
592 		QPolygonF poly = TextUtils::polygonFromElement(leg);
593 		if (poly.count() < 2) continue;
594 
595 		if (parentCommand) {
596 			ChangeLegCommand * clc = new ChangeLegCommand(this, id, fromConnectorID, poly, poly, true, true, "copy", parentCommand);
597 			clc->setSimple();
598 		}
599 		else {
600 			changeLeg(id, fromConnectorID, poly, true, "load");
601 		}
602 
603 		QDomElement bElement = leg.firstChildElement("bezier");
604 		int bIndex = 0;
605 		while (!bElement.isNull()) {
606 			Bezier bezier = Bezier::fromElement(bElement);
607 			if (!bezier.isEmpty()) {
608 				if (parentCommand) {
609 					new ChangeLegCurveCommand(this, id, fromConnectorID, bIndex, &bezier, &bezier, parentCommand);
610 				}
611 				else {
612 					changeLegCurve(id, fromConnectorID, bIndex, &bezier);
613 				}
614 			}
615 			bElement = bElement.nextSiblingElement("bezier");
616 			bIndex++;
617 		}
618 	}
619 
620 
621 
622 	if (parentCommand == NULL) {
623 		foreach (ItemBase * item, newItems) {
624 			item->doneLoading();
625 			if (item->isBaseSticky()) {
626 				stickyScoop(item, false, NULL);
627 			}
628 		}
629 
630 		m_pasteCount = 0;
631 		this->scene()->clearSelection();
632 		cleanUpWires(false, NULL);
633 
634 	}
635 	else {
636 		if (offsetPaste) {
637 			// m_pasteCount used for offsetting paste items, not a count of how many items are pasted
638 			m_pasteCount++;
639 		}
640         new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
641 		new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
642 	}
643 
644 	setIgnoreSelectionChangeEvents(false);
645 	m_pasteOffset = QPointF(0,0);
646 }
647 
handleConnect(QDomElement & connect,ModelPart * mp,const QString & fromConnectorID,ViewLayer::ViewLayerID fromViewLayerID,QStringList & alreadyConnected,QHash<long,ItemBase * > & newItems,QUndoCommand * parentCommand,bool seekOutsideConnections)648 void SketchWidget::handleConnect(QDomElement & connect, ModelPart * mp, const QString & fromConnectorID, ViewLayer::ViewLayerID fromViewLayerID,
649 									QStringList & alreadyConnected, QHash<long, ItemBase *> & newItems, QUndoCommand * parentCommand,
650 									bool seekOutsideConnections)
651 {
652 	bool ok;
653 	QHash<long, ItemBase *> otherNewItems;
654 	long modelIndex = connect.attribute("modelIndex").toLong(&ok);
655 	QString toConnectorID = connect.attribute("connectorId");
656 	ViewLayer::ViewLayerID toViewLayerID = ViewLayer::viewLayerIDFromXmlString(connect.attribute("layer"));
657 	QString already = ((mp->modelIndex() <= modelIndex) ? QString("%1.%2.%3.%4.%5.%6") : QString("%4.%5.%6.%1.%2.%3"))
658 						.arg(mp->modelIndex()).arg(fromConnectorID).arg(fromViewLayerID)
659 						.arg(modelIndex).arg(toConnectorID).arg(toViewLayerID);
660 	if (alreadyConnected.contains(already)) return;
661 
662 	alreadyConnected.append(already);
663 
664 	if (parentCommand == NULL) {
665 		ItemBase * fromBase = newItems.value(mp->modelIndex(), NULL);
666 		ItemBase * toBase = newItems.value(modelIndex, NULL);
667 		if (toBase == NULL) {
668 			toBase = otherNewItems.value(modelIndex, NULL);
669 		}
670 		if (fromBase == NULL || toBase == NULL) {
671 			if (!seekOutsideConnections) return;
672 
673 			if (fromBase == NULL) {
674 				fromBase = findItem(mp->modelIndex() * ModelPart::indexMultiplier);
675 				if (fromBase == NULL) return;
676 			}
677 
678 			if (toBase == NULL) {
679 				toBase = findItem(modelIndex * ModelPart::indexMultiplier);
680 				if (toBase == NULL) return;
681 			}
682 		}
683 
684 		ConnectorItem * fromConnectorItem = fromBase->findConnectorItemWithSharedID(fromConnectorID, ViewLayer::specFromID(fromViewLayerID));
685 		ConnectorItem * toConnectorItem = toBase->findConnectorItemWithSharedID(toConnectorID, ViewLayer::specFromID(toViewLayerID));
686 		if (fromConnectorItem == NULL || toConnectorItem == NULL) {
687 			return;
688 		}
689 
690 		fromConnectorItem->connectTo(toConnectorItem);
691 		toConnectorItem->connectTo(fromConnectorItem);
692 		fromConnectorItem->connector()->connectTo(toConnectorItem->connector());
693 		if (fromConnectorItem->attachedToItemType() == ModelPart::Wire && toConnectorItem->attachedToItemType() == ModelPart::Wire) {
694 			fromConnectorItem->setHidden(false);
695 			toConnectorItem->setHidden(false);
696 		}
697 		ratsnestConnect(fromConnectorItem, true);
698 		ratsnestConnect(toConnectorItem, true);
699 		return;
700 	}
701 
702 	// single view because handle connect is called from loadModelPart which gets called for each view
703 	new ChangeConnectionCommand(this, BaseCommand::SingleView,
704 								ItemBase::getNextID(mp->modelIndex()), fromConnectorID,
705 								ItemBase::getNextID(modelIndex), toConnectorID,
706 								ViewLayer::specFromID(fromViewLayerID),
707 								true, parentCommand);
708 }
709 
addWireExtras(long newID,QDomElement & view,QUndoCommand * parentCommand)710 void SketchWidget::addWireExtras(long newID, QDomElement & view, QUndoCommand * parentCommand)
711 {
712 	QDomElement extras = view.firstChildElement("wireExtras");
713 	if (extras.isNull()) return;
714 
715 	QDomElement copy(extras);
716 
717 	new WireExtrasCommand(this, newID, copy, copy, parentCommand);
718 }
719 
setWireExtras(long newID,QDomElement & extras)720 void SketchWidget::setWireExtras(long newID, QDomElement & extras)
721 {
722 	Wire * wire = qobject_cast<Wire *>(findItem(newID));
723 	if (wire == NULL) return;
724 
725 	wire->setExtras(extras, this);
726 }
727 
addItem(const QString & moduleID,ViewLayer::ViewLayerPlacement viewLayerPlacement,BaseCommand::CrossViewType crossViewType,const ViewGeometry & viewGeometry,long id,long modelIndex,AddDeleteItemCommand * originatingCommand)728 ItemBase * SketchWidget::addItem(const QString & moduleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, BaseCommand::CrossViewType crossViewType, const ViewGeometry & viewGeometry, long id, long modelIndex,  AddDeleteItemCommand * originatingCommand) {
729 	if (m_referenceModel == NULL) return NULL;
730 
731 	ItemBase * itemBase = NULL;
732 	ModelPart * modelPart = m_referenceModel->retrieveModelPart(moduleID);
733 
734 	if (modelPart != NULL) {
735         if (!m_blockUI) {
736 		    QApplication::setOverrideCursor(Qt::WaitCursor);
737 		    statusMessage(tr("loading part"));
738         }
739 		itemBase = addItem(modelPart, viewLayerPlacement, crossViewType, viewGeometry, id, modelIndex, originatingCommand);
740         if (!m_blockUI) {
741 		    statusMessage(tr("done loading"), 2000);
742 		    QApplication::restoreOverrideCursor();
743         }
744 	}
745 
746 	return itemBase;
747 }
748 
749 
addItem(ModelPart * modelPart,ViewLayer::ViewLayerPlacement viewLayerPlacement,BaseCommand::CrossViewType crossViewType,const ViewGeometry & viewGeometry,long id,long modelIndex,AddDeleteItemCommand * originatingCommand)750 ItemBase * SketchWidget::addItem(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, BaseCommand::CrossViewType crossViewType, const ViewGeometry & viewGeometry, long id, long modelIndex, AddDeleteItemCommand * originatingCommand)
751 {
752 
753 	ItemBase * newItem = NULL;
754 	//if (checkAlreadyExists) {
755 	//	newItem = findItem(id);
756 	//	if (newItem != NULL) {
757 	//		newItem->debugInfo("already exists");
758 	//	}
759 	//}
760 	if (newItem == NULL) {
761 		ModelPart * mp = NULL;
762 		if (modelIndex >= 0) {
763 			// used only with Paste, so far--this assures that parts created across views will share the same ModelPart
764 			mp = m_sketchModel->findModelPart(modelPart->moduleID(), id);
765 		}
766 		if (mp == NULL) {
767 			modelPart = m_sketchModel->addModelPart(m_sketchModel->root(), modelPart);
768 		}
769 		else {
770 			modelPart = mp;
771 		}
772 		if (modelPart == NULL) return NULL;
773 
774 		newItem = addItemAux(modelPart, viewLayerPlacement, viewGeometry, id, true, m_viewID, false);
775 	}
776 
777 	if (crossViewType == BaseCommand::CrossView) {
778 		//DebugDialog::debug(QString("emit item added"));
779 		emit itemAddedSignal(modelPart, newItem, viewLayerPlacement, viewGeometry, id, originatingCommand ? originatingCommand->dropOrigin() : NULL);
780 		//DebugDialog::debug(QString("after emit item added"));
781 	}
782 
783 	return newItem;
784 }
785 
addItemAuxTemp(ModelPart * modelPart,ViewLayer::ViewLayerPlacement viewLayerPlacement,const ViewGeometry & viewGeometry,long id,bool doConnectors,ViewLayer::ViewID viewID,bool temporary)786 ItemBase * SketchWidget::addItemAuxTemp(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, const ViewGeometry & viewGeometry, long id,  bool doConnectors, ViewLayer::ViewID viewID, bool temporary)
787 {
788 	modelPart = m_sketchModel->addModelPart(m_sketchModel->root(), modelPart);
789 	if (modelPart == NULL) return NULL;   // this is very fucked up
790 
791 	return addItemAux(modelPart, viewLayerPlacement, viewGeometry, id, doConnectors, viewID, temporary);
792 }
793 
addItemAux(ModelPart * modelPart,ViewLayer::ViewLayerPlacement viewLayerPlacement,const ViewGeometry & viewGeometry,long id,bool doConnectors,ViewLayer::ViewID viewID,bool temporary)794 ItemBase * SketchWidget::addItemAux(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, const ViewGeometry & viewGeometry, long id, bool doConnectors, ViewLayer::ViewID viewID, bool temporary)
795 {
796 	if (viewID == ViewLayer::UnknownView) {
797 		viewID = m_viewID;
798 	}
799 
800 	if (doConnectors) {
801 		modelPart->initConnectors();    // is a no-op if connectors already in place
802 	}
803 
804 	ItemBase * newItem = PartFactory::createPart(modelPart, viewLayerPlacement, viewID, viewGeometry, id, m_itemMenu, m_wireMenu, true);
805 	Wire * wire = qobject_cast<Wire *>(newItem);
806 	if (wire) {
807 
808 		QString descr;
809 		bool ratsnest = viewGeometry.getRatsnest();
810 		if (ratsnest) {
811 			setClipEnds((ClipableWire *) wire, true);
812 			descr = "ratsnest";
813 		}
814 		else if (viewGeometry.getAnyTrace() ) {
815 			setClipEnds((ClipableWire *) wire, true);
816 			descr = "trace";
817 		}
818 		else {
819 			wire->setNormal(true);
820 			descr = "wire";
821 		}
822 
823 		wire->setUp(getWireViewLayerID(viewGeometry, wire->viewLayerPlacement()), m_viewLayers, this);
824 		setWireVisible(wire);
825 		wire->updateConnectors();
826 
827 		addToScene(wire, wire->viewLayerID());
828 		wire->addedToScene(temporary);
829 		wire->debugInfo("add " + descr);
830 
831 		return wire;
832 	}
833 
834 	if (modelPart->itemType() == ModelPart::Note) {
835 		newItem->setViewLayerID(getNoteViewLayerID(), m_viewLayers);
836 		newItem->setZValue(newItem->z());
837 		newItem->setVisible(true);
838 		addToScene(newItem, getNoteViewLayerID());
839         newItem->addedToScene(temporary);
840 		return newItem;
841 	}
842 
843 	bool ok;
844 	addPartItem(modelPart, viewLayerPlacement, (PaletteItem *) newItem, doConnectors, ok, viewID, temporary);
845 	newItem->debugInfo("add part");
846 	setNewPartVisible(newItem);
847 	newItem->updateConnectors();
848 	return newItem;
849 }
850 
851 
setNewPartVisible(ItemBase * itemBase)852 void SketchWidget::setNewPartVisible(ItemBase * itemBase) {
853 	Q_UNUSED(itemBase);
854 	// defaults to visible, so do nothing
855 }
856 
checkSticky(long id,bool doEmit,bool checkCurrent,CheckStickyCommand * checkStickyCommand)857 void SketchWidget::checkSticky(long id, bool doEmit, bool checkCurrent, CheckStickyCommand * checkStickyCommand)
858 {
859 	ItemBase * itemBase = findItem(id);
860 	if (itemBase == NULL) return;
861 
862     if (itemBase->hidden() || itemBase->layerHidden()) {
863     }
864 	else if (itemBase->isBaseSticky()) {
865 		stickyScoop(itemBase, checkCurrent, checkStickyCommand);
866 	}
867 	else {
868 		ItemBase * stickyOne = overSticky(itemBase);
869 		ItemBase * wasStickyOne = itemBase->stickingTo();
870 		if (stickyOne != wasStickyOne) {
871 			if (wasStickyOne != NULL) {
872 				wasStickyOne->addSticky(itemBase, false);
873 				itemBase->addSticky(wasStickyOne, false);
874 				if (checkStickyCommand) {
875 					checkStickyCommand->stick(this, wasStickyOne->id(), itemBase->id(), false);
876 				}
877 			}
878 			if (stickyOne != NULL) {
879 				stickyOne->addSticky(itemBase, true);
880 				itemBase->addSticky(stickyOne, true);
881 				if (checkStickyCommand) {
882 					checkStickyCommand->stick(this, stickyOne->id(), itemBase->id(), true);
883 				}
884 			}
885 		}
886 	}
887 
888 	if (doEmit) {
889 		checkStickySignal(id, false, false, checkStickyCommand);
890 	}
891 }
892 
addPartItem(ModelPart * modelPart,ViewLayer::ViewLayerPlacement viewLayerPlacement,PaletteItem * paletteItem,bool doConnectors,bool & ok,ViewLayer::ViewID viewID,bool temporary)893 PaletteItem* SketchWidget::addPartItem(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, PaletteItem * paletteItem, bool doConnectors, bool & ok, ViewLayer::ViewID viewID, bool temporary) {
894 
895 	ok = false;
896 	ViewLayer::ViewLayerID viewLayerID = getViewLayerID(modelPart, viewID, viewLayerPlacement);
897 	if (viewLayerID == ViewLayer::UnknownLayer) {
898 	    // render it only if the layer is defined in the fzp file
899 	    // if the view is not defined in the part file, without this condition
900 	    // fritzing crashes
901         return paletteItem;
902     }
903 
904 	QString error;
905 	bool result = paletteItem->renderImage(modelPart, viewID, m_viewLayers, viewLayerID, doConnectors, error);
906 	if (!result) {
907 		bool retry = false;
908 		switch (viewLayerID) {
909 			case ViewLayer::Copper0:
910 				viewLayerID = ViewLayer::Copper1;
911 				retry = true;
912 				break;
913 			case ViewLayer::Copper1:
914 				viewLayerID = ViewLayer::Copper0;
915 				retry = true;
916 				break;
917 			default:
918 				break;
919 		}
920 		if (retry) {
921 			result = paletteItem->renderImage(modelPart, viewID, m_viewLayers, viewLayerID, doConnectors, error);
922 		}
923 	}
924 
925     bool hideSuper = modelPart->hasSubparts() && !temporary && viewID == ViewLayer::SchematicView;
926 	if (result) {
927 		//DebugDialog::debug(QString("addPartItem %1").arg(viewID));
928 		addToScene(paletteItem, paletteItem->viewLayerID());
929         if (hideSuper) {
930             paletteItem->setEverVisible(false);
931             paletteItem->setVisible(false);
932         }
933 		paletteItem->loadLayerKin(m_viewLayers, viewLayerPlacement);
934 		foreach (ItemBase * lkpi, paletteItem->layerKin()) {
935 			this->scene()->addItem(lkpi);
936 			lkpi->setHidden(!layerIsVisible(lkpi->viewLayerID()));
937 			lkpi->setInactive(!layerIsActive(lkpi->viewLayerID()));
938             if (hideSuper) {
939                 lkpi->setEverVisible(false);
940                 lkpi->setVisible(false);
941             }
942 		}
943 		//DebugDialog::debug(QString("after layerkin %1").arg(viewID));
944 		ok = true;
945 	}
946 	else {
947 		// nobody falls through to here now?
948 
949 		FMessageBox::information(NULL, QObject::tr("Fritzing"),
950 			QObject::tr("Error reading file %1: %2.").arg(modelPart->path()).arg(error) );
951 
952 
953 		DebugDialog::debug(QString("addPartItem renderImage failed %1 %2").arg(modelPart->moduleID()).arg(error));
954 
955 		//paletteItem->modelPart()->removeViewItem(paletteItem);
956 		//delete paletteItem;
957 		//return NULL;
958 		scene()->addItem(paletteItem);
959 		//paletteItem->setVisible(false);
960 	}
961 	paletteItem->addedToScene(temporary);
962 	return paletteItem;
963 }
964 
addToScene(ItemBase * item,ViewLayer::ViewLayerID viewLayerID)965 void SketchWidget::addToScene(ItemBase * item, ViewLayer::ViewLayerID viewLayerID) {
966 	scene()->addItem(item);
967  	item->setSelected(true);
968  	item->setHidden(!layerIsVisible(viewLayerID));
969  	item->setInactive(!layerIsActive(viewLayerID));
970 }
971 
findItem(long id)972 ItemBase * SketchWidget::findItem(long id) {
973 	// TODO:  this needs to be optimized: could make a hash table
974 
975 	long baseid = id / ModelPart::indexMultiplier;
976 
977 	foreach (QGraphicsItem * item, this->scene()->items()) {
978 		ItemBase* base = dynamic_cast<ItemBase *>(item);
979 		if (base == NULL) continue;
980 
981 		if (base->id() == id) {
982 			return base;
983 		}
984 
985 		if (base->id() / ModelPart::indexMultiplier == baseid) {
986 			// found chief or layerkin
987 			ItemBase * chief = base->layerKinChief();
988 			if (chief->id() == id) return chief;
989 
990 			foreach (ItemBase * lk, chief->layerKin()) {
991 				if (lk->id() == id) return lk;
992 			}
993 
994            return chief;
995 
996 		}
997 	}
998 
999 	return NULL;
1000 }
1001 
deleteItem(long id,bool deleteModelPart,bool doEmit,bool later)1002 void SketchWidget::deleteItem(long id, bool deleteModelPart, bool doEmit, bool later) {
1003 	ItemBase * pitem = findItem(id);
1004 	DebugDialog::debug(QString("delete item (1) %1 %2 %3 %4").arg(id).arg(doEmit).arg(m_viewID).arg((long) pitem, 0, 16) );
1005 	if (pitem != NULL) {
1006 		deleteItem(pitem, deleteModelPart, doEmit, later);
1007 	}
1008 	else {
1009 		if (doEmit) {
1010 			emit itemDeletedSignal(id);
1011 		}
1012 	}
1013 }
1014 
deleteItem(ItemBase * itemBase,bool deleteModelPart,bool doEmit,bool later)1015 void SketchWidget::deleteItem(ItemBase * itemBase, bool deleteModelPart, bool doEmit, bool later)
1016 {
1017 	long id = itemBase->id();
1018 	DebugDialog::debug(QString("delete item (2) %1 %2 %3 %4").arg(id).arg(itemBase->title()).arg(m_viewID).arg((long) itemBase, 0, 16) );
1019 
1020 	// this is a hack to try to workaround a Qt 4.7 crash in QGraphicsSceneFindItemBspTreeVisitor::visit
1021 	// when using a custom boundingRect, after deleting an item, it still appears on the visit list.
1022 	//
1023 	// the problem arises because the legItems are used to calculate the boundingRect() of the item.
1024 	// But in the destructor, the childItems are deleted first, then the BSP tree is updated
1025 	// at that point, the boundingRect() will return a different value than what's in the BSP tree,
1026 	// which is the old value of the boundingRect before the legs were deleted.
1027 
1028 	if (itemBase->hasRubberBandLeg()) {
1029 		DebugDialog::debug("kill rubberBand");
1030 		itemBase->killRubberBandLeg();
1031 	}
1032 
1033 	if (m_infoView != NULL) {
1034 		m_infoView->unregisterCurrentItemIf(itemBase->id());
1035 	}
1036 	if (itemBase == this->m_lastPaletteItemSelected) {
1037 		setLastPaletteItemSelected(NULL);
1038 	}
1039 	// m_lastSelected.removeOne(itemBase); hack for 4.5.something
1040 
1041 	if (deleteModelPart) {
1042 		ModelPart * modelPart = itemBase->modelPart();
1043 		if (modelPart != NULL) {
1044 			m_sketchModel->removeModelPart(modelPart);
1045 			delete modelPart;
1046 		}
1047 	}
1048 
1049 	itemBase->removeLayerKin();
1050 	this->scene()->removeItem(itemBase);
1051 
1052 	if (later) {
1053 		itemBase->deleteLater();
1054 	}
1055 	else {
1056 		delete itemBase;
1057 	}
1058 
1059 	if (doEmit) {
1060 		emit itemDeletedSignal(id);
1061 	}
1062 
1063 }
1064 
deleteSelected(Wire * wire,bool minus)1065 void SketchWidget::deleteSelected(Wire * wire, bool minus) {
1066 	QSet<ItemBase *> itemBases;
1067 	if (wire) {
1068 		itemBases << wire;
1069 	}
1070 	else {
1071 		foreach (QGraphicsItem * item, scene()->selectedItems()) {
1072 			ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
1073 			if (itemBase == NULL) continue;
1074 			if (itemBase->moveLock()) continue;
1075 
1076 			itemBase = itemBase->layerKinChief();
1077 			if (itemBase->moveLock()) continue;
1078 
1079 			itemBases.insert(itemBase);
1080 		}
1081 	}
1082 
1083 	if (itemBases.count() == 0) return;
1084 
1085 	// assumes ratsnest is not mixed with other itembases
1086 	bool rats = true;
1087 	foreach (ItemBase * itemBase, itemBases) {
1088 		Wire * wire = qobject_cast<Wire *>(itemBase);
1089 		if (wire == NULL) {
1090 			rats = false;
1091 			break;
1092 		}
1093 		if (!wire->getRatsnest()) {
1094 			rats = false;
1095 			break;
1096 		}
1097 	}
1098 
1099 	if (!rats) {
1100 		cutDeleteAux("Delete", minus, wire);			// wire is selected in this case, so don't bother sending it along
1101 		return;
1102 	}
1103 
1104 	QUndoCommand * parentCommand = new QUndoCommand(tr("Delete ratsnest"));
1105     new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
1106 	deleteRatsnest(wire, parentCommand);
1107     new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
1108 	m_undoStack->waitPush(parentCommand, PropChangeDelay);
1109 }
1110 
cutDeleteAux(QString undoStackMessage,bool minus,Wire * wire)1111 void SketchWidget::cutDeleteAux(QString undoStackMessage, bool minus, Wire * wire) {
1112 
1113 	//DebugDialog::debug("before delete");
1114 
1115     // get sitems first, before calling stackSelectionState
1116     // because selectedItems will return an empty list
1117 	const QList<QGraphicsItem *> sitems = scene()->selectedItems();
1118 
1119 	QSet<ItemBase *> deletedItems;
1120     if (minus && wire != NULL) {
1121         // called from wire context menu "delete to bendpoint"
1122         deletedItems.insert(wire);
1123     }
1124     else {
1125 	    foreach (QGraphicsItem * sitem, sitems) {
1126 		    if (!canDeleteItem(sitem, sitems.count())) continue;
1127 
1128 		    // canDeleteItem insures dynamic_cast<ItemBase *>(sitem)->layerKinChief() won't break
1129             ItemBase * itemBase = dynamic_cast<ItemBase *>(sitem)->layerKinChief();
1130             if (itemBase->superpart()) {
1131                 deletedItems.insert(itemBase->superpart());
1132                 foreach (ItemBase * sub, itemBase->superpart()->subparts()) deletedItems.insert(sub);
1133             }
1134             else if (itemBase->subparts().count() > 0) {
1135                 deletedItems.insert(itemBase);
1136                 foreach (ItemBase * sub, itemBase->subparts()) deletedItems.insert(sub);
1137             }
1138             else {
1139 		        deletedItems.insert(itemBase);
1140             }
1141 	    }
1142 
1143         if (!minus) {
1144             QSet<Wire *> allWires;
1145             foreach (ItemBase * itemBase, deletedItems) {
1146                 QList<ConnectorItem *> connectorItems;
1147                 foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
1148                     connectorItems.append(connectorItem);
1149                     ConnectorItem * cross = connectorItem->getCrossLayerConnectorItem();
1150                     if (cross) connectorItems.append(cross);
1151                 }
1152                 foreach (ConnectorItem * connectorItem, connectorItems) {
1153                     foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) {
1154                         if (toConnectorItem->attachedToItemType() != ModelPart::Wire) continue;
1155 
1156                         Wire * wire = qobject_cast<Wire *>(toConnectorItem->attachedTo());
1157                         if (wire->getRatsnest()) continue;
1158                         //if (!wire->isTraceType(getTraceFlag())) continue;         // removes connected wires across views when commented out
1159 
1160                         QList<Wire *> wires;
1161                         wire->collectDirectWires(wires);
1162                         foreach (Wire * w, wires) {
1163                             allWires.insert(w);
1164                         }
1165                     }
1166                 }
1167             }
1168             if (allWires.count() > 0) {
1169                 QList<Wire *> wires = allWires.toList();
1170                 wires.at(0)->collectDirectWires(wires);
1171                 foreach (Wire * w, wires) {
1172                     deletedItems.insert(w);
1173                 }
1174             }
1175         }
1176     }
1177 
1178 	if (deletedItems.count() <= 0) {
1179 		return;
1180 	}
1181 
1182 	QString string;
1183 	if (deletedItems.count() == 1) {
1184 		ItemBase * firstItem = *(deletedItems.begin());
1185 		string = tr("%1 %2").arg(undoStackMessage).arg(firstItem->title());
1186 	}
1187 	else {
1188 		string = tr("%1 %2 items").arg(undoStackMessage).arg(QString::number(deletedItems.count()));
1189 	}
1190 
1191 	QUndoCommand * parentCommand = new QUndoCommand(string);
1192 	parentCommand->setText(string);
1193 
1194 	deleteAux(deletedItems, parentCommand, true);
1195 }
1196 
deleteAux(QSet<ItemBase * > & deletedItems,QUndoCommand * parentCommand,bool doPush)1197 void SketchWidget::deleteAux(QSet<ItemBase *> & deletedItems, QUndoCommand * parentCommand, bool doPush)
1198 {
1199     stackSelectionState(false, parentCommand);
1200 
1201 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
1202 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
1203 
1204 	deleteMiddle(deletedItems, parentCommand);
1205 
1206 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
1207 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
1208 
1209     foreach (ItemBase * itemBase, deletedItems) {
1210         if (itemBase->superpart() != NULL) {
1211             AddSubpartCommand * asc = new AddSubpartCommand(this, BaseCommand::CrossView, itemBase->superpart()->id(), itemBase->id(), parentCommand);
1212             asc->setUndoOnly();
1213         }
1214     }
1215 
1216 	// actual delete commands must come last for undo to work properly
1217 	foreach (ItemBase * itemBase, deletedItems) {
1218 		this->makeDeleteItemCommand(itemBase, BaseCommand::CrossView, parentCommand);
1219 	}
1220 	if (doPush) {
1221    		m_undoStack->waitPush(parentCommand, PropChangeDelay);
1222 	}
1223 }
1224 
isVirtualWireConnector(ConnectorItem * toConnectorItem)1225 bool isVirtualWireConnector(ConnectorItem * toConnectorItem) {
1226 	return (qobject_cast<VirtualWire *>(toConnectorItem->attachedTo()) != NULL);
1227 }
1228 
deleteMiddle(QSet<ItemBase * > & deletedItems,QUndoCommand * parentCommand)1229 void SketchWidget::deleteMiddle(QSet<ItemBase *> & deletedItems, QUndoCommand * parentCommand) {
1230 	foreach (ItemBase * itemBase, deletedItems) {
1231 		foreach (ConnectorItem * fromConnectorItem, itemBase->cachedConnectorItems()) {
1232 			foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
1233 				extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem,
1234 											  ViewLayer::specFromID(fromConnectorItem->attachedToViewLayerID()),
1235 											  false, parentCommand);
1236 				fromConnectorItem->tempRemove(toConnectorItem, false);
1237 				toConnectorItem->tempRemove(fromConnectorItem, false);
1238 			}
1239 
1240 			fromConnectorItem = fromConnectorItem->getCrossLayerConnectorItem();
1241 			if (fromConnectorItem) {
1242 				foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
1243 					extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem,
1244 												  ViewLayer::specFromID(fromConnectorItem->attachedToViewLayerID()),
1245 												  false, parentCommand);
1246 					fromConnectorItem->tempRemove(toConnectorItem, false);
1247 					toConnectorItem->tempRemove(fromConnectorItem, false);
1248 				}
1249 			}
1250 		}
1251 	}
1252 }
1253 
deleteTracesSlot(QSet<ItemBase * > & deletedItems,QHash<ItemBase *,SketchWidget * > & otherDeletedItems,QList<long> & deletedIDs,bool isForeign,QUndoCommand * parentCommand)1254 void SketchWidget::deleteTracesSlot(QSet<ItemBase *> & deletedItems, QHash<ItemBase *, SketchWidget *> & otherDeletedItems, QList<long> & deletedIDs, bool isForeign, QUndoCommand * parentCommand) {
1255 	Q_UNUSED(parentCommand);
1256 	foreach (ItemBase * itemBase, deletedItems) {
1257 		if (itemBase->itemType() == ModelPart::Wire) continue;
1258 
1259 		if (isForeign) {
1260 			itemBase = findItem(itemBase->id());
1261 			if (itemBase == NULL) continue;
1262 
1263 			// only foreign items need move/transform; the current view carries its own viewgeometry
1264 			itemBase->saveGeometry();
1265 		}
1266 
1267 		bool isJumper = (itemBase->itemType() == ModelPart::Jumper);
1268 
1269 		foreach (ConnectorItem * fromConnectorItem, itemBase->cachedConnectorItems()) {
1270 			QList<ConnectorItem *> connectorItems;
1271 			foreach (ConnectorItem * ci, fromConnectorItem->connectedToItems()) connectorItems << ci;
1272 			ConnectorItem * crossConnectorItem = fromConnectorItem->getCrossLayerConnectorItem();
1273 			if (crossConnectorItem) {
1274 				foreach (ConnectorItem * ci, crossConnectorItem->connectedToItems()) connectorItems << ci;
1275 			}
1276 
1277 			foreach (ConnectorItem * toConnectorItem, connectorItems) {
1278 				Wire * wire = qobject_cast<Wire *>(toConnectorItem->attachedTo());
1279 				if (wire == NULL) continue;
1280 
1281 				if (isJumper || (wire->isTraceType(getTraceFlag()))) {
1282 					QList<Wire *> wires;
1283 					QList<ConnectorItem *> ends;
1284 					wire->collectChained(wires, ends);
1285 					foreach (Wire * w, wires) {
1286 						if (!deletedIDs.contains(w->id())) {
1287 							otherDeletedItems.insert(w, this);
1288 							deletedIDs.append(w->id());
1289 						}
1290 					}
1291 				}
1292 			}
1293 		}
1294 	}
1295 }
1296 
extendChangeConnectionCommand(BaseCommand::CrossViewType crossView,long fromID,const QString & fromConnectorID,long toID,const QString & toConnectorID,ViewLayer::ViewLayerPlacement viewLayerPlacement,bool connect,QUndoCommand * parentCommand)1297 ChangeConnectionCommand * SketchWidget::extendChangeConnectionCommand(BaseCommand::CrossViewType crossView,
1298 												 long fromID, const QString & fromConnectorID,
1299 												 long toID, const QString & toConnectorID,
1300 												 ViewLayer::ViewLayerPlacement viewLayerPlacement,
1301 												 bool connect, QUndoCommand * parentCommand)
1302 {
1303 	ItemBase * fromItem = findItem(fromID);
1304 	if (fromItem == NULL) {
1305 		return NULL;  // for now
1306 	}
1307 
1308 	ItemBase * toItem = findItem(toID);
1309 	if (toItem == NULL) {
1310 		return NULL;		// for now
1311 	}
1312 
1313 	ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, viewLayerPlacement);
1314 	if (fromConnectorItem == NULL) return NULL; // for now
1315 
1316 	ConnectorItem * toConnectorItem = findConnectorItem(toItem, toConnectorID, viewLayerPlacement);
1317 	if (toConnectorItem == NULL) return NULL; // for now
1318 
1319 	return extendChangeConnectionCommand(crossView, fromConnectorItem, toConnectorItem, viewLayerPlacement, connect, parentCommand);
1320 }
1321 
extendChangeConnectionCommand(BaseCommand::CrossViewType crossView,ConnectorItem * fromConnectorItem,ConnectorItem * toConnectorItem,ViewLayer::ViewLayerPlacement viewLayerPlacement,bool connect,QUndoCommand * parentCommand)1322 ChangeConnectionCommand * SketchWidget::extendChangeConnectionCommand(BaseCommand::CrossViewType crossView,
1323 												 ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem,
1324 												 ViewLayer::ViewLayerPlacement viewLayerPlacement,
1325 												 bool connect, QUndoCommand * parentCommand)
1326 {
1327 	// cases:
1328 	//		delete
1329 	//		paste
1330 	//		drop (wire)
1331 	//		drop (part)
1332 	//		move (part)
1333 	//		move (wire)
1334 	//		drag wire end
1335 	//		drag out new wire
1336 
1337 	ItemBase * fromItem = fromConnectorItem->attachedTo();
1338 	if (fromItem == NULL) {
1339 		return  NULL;  // for now
1340 	}
1341 
1342 	ItemBase * toItem = toConnectorItem->attachedTo();
1343 	if (toItem == NULL) {
1344 		return NULL;		// for now
1345 	}
1346 
1347 	return new ChangeConnectionCommand(this, crossView,
1348 								fromItem->id(), fromConnectorItem->connectorSharedID(),
1349 								toItem->id(), toConnectorItem->connectorSharedID(),
1350 								viewLayerPlacement, connect, parentCommand);
1351 }
1352 
1353 
createWire(ConnectorItem * from,ConnectorItem * to,ViewGeometry::WireFlags wireFlags,bool dontUpdate,BaseCommand::CrossViewType crossViewType,QUndoCommand * parentCommand)1354 long SketchWidget::createWire(ConnectorItem * from, ConnectorItem * to,
1355 							  ViewGeometry::WireFlags wireFlags, bool dontUpdate,
1356 							  BaseCommand::CrossViewType crossViewType, QUndoCommand * parentCommand)
1357 {
1358 	if (from == NULL || to == NULL) {
1359 		return -1;
1360 	}
1361 
1362 	long newID = ItemBase::getNextID();
1363 	ViewGeometry viewGeometry;
1364 	QPointF fromPos = from->sceneAdjustedTerminalPoint(NULL);
1365 	viewGeometry.setLoc(fromPos);
1366 	QPointF toPos = to->sceneAdjustedTerminalPoint(NULL);
1367 	QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y());
1368 	viewGeometry.setLine(line);
1369 	viewGeometry.setWireFlags(wireFlags);
1370 
1371 	DebugDialog::debug(QString("creating wire %11: %1, flags: %6, from %7 %8, to %9 %10, frompos: %2 %3, topos: %4 %5")
1372 		.arg(newID)
1373 		.arg(fromPos.x()).arg(fromPos.y())
1374 		.arg(toPos.x()).arg(toPos.y())
1375 		.arg(wireFlags)
1376 		.arg(from->attachedToTitle()).arg(from->connectorSharedID())
1377 		.arg(to->attachedToTitle()).arg(to->connectorSharedID())
1378 		.arg(m_viewID)
1379 		);
1380 
1381 	ViewLayer::ViewLayerPlacement viewLayerPlacement = createWireViewLayerPlacement(from, to);
1382 
1383 	new AddItemCommand(this, crossViewType, ModuleIDNames::WireModuleIDName, viewLayerPlacement, viewGeometry, newID, false, -1, parentCommand);
1384 	new CheckStickyCommand(this, crossViewType, newID, false, CheckStickyCommand::RemoveOnly, parentCommand);
1385 	ChangeConnectionCommand * ccc = new ChangeConnectionCommand(this, crossViewType, from->attachedToID(), from->connectorSharedID(),
1386 						newID, "connector0",
1387 						viewLayerPlacement,							// ViewLayer::specFromID(from->attachedToViewLayerID())
1388 						true, parentCommand);
1389 	ccc->setUpdateConnections(!dontUpdate);
1390 	ccc = new ChangeConnectionCommand(this, crossViewType, to->attachedToID(), to->connectorSharedID(),
1391 						newID, "connector1",
1392 						viewLayerPlacement,							// ViewLayer::specFromID(to->attachedToViewLayerID())
1393 						true, parentCommand);
1394 	ccc->setUpdateConnections(!dontUpdate);
1395 
1396 	return newID;
1397 }
1398 
createWireViewLayerPlacement(ConnectorItem * from,ConnectorItem * to)1399 ViewLayer::ViewLayerPlacement SketchWidget::createWireViewLayerPlacement(ConnectorItem * from, ConnectorItem * to) {
1400 	Q_UNUSED(to);
1401 	return from->attachedToViewLayerPlacement();
1402 }
1403 
moveItem(long id,ViewGeometry & viewGeometry,bool updateRatsnest)1404 void SketchWidget::moveItem(long id, ViewGeometry & viewGeometry, bool updateRatsnest) {
1405 	ItemBase * itemBase = findItem(id);
1406 	if (itemBase != NULL) {
1407 		if (updateRatsnest) {
1408 			ratsnestConnect(itemBase, true);
1409 		}
1410 		itemBase->moveItem(viewGeometry);
1411         if (m_infoView) m_infoView->updateLocation(itemBase);
1412 	}
1413 }
1414 
simpleMoveItem(long id,QPointF p)1415 void SketchWidget::simpleMoveItem(long id, QPointF p) {
1416 	ItemBase * itemBase = findItem(id);
1417 	if (itemBase != NULL) {
1418 		itemBase->setItemPos(p);
1419         if (m_infoView) m_infoView->updateLocation(itemBase);
1420 	}
1421 }
1422 
moveItem(long id,const QPointF & p,bool updateRatsnest)1423 void SketchWidget::moveItem(long id, const QPointF & p, bool updateRatsnest) {
1424 	ItemBase * itemBase = findItem(id);
1425 	if (itemBase != NULL) {
1426 		itemBase->setPos(p);
1427 		if (updateRatsnest) {
1428 			ratsnestConnect(itemBase, true);
1429 		}
1430         emit itemMovedSignal(itemBase);
1431         if (m_infoView) m_infoView->updateLocation(itemBase);
1432 	}
1433 }
1434 
updateWire(long id,const QString & connectorID,bool updateRatsnest)1435 void SketchWidget::updateWire(long id, const QString & connectorID, bool updateRatsnest) {
1436 	ItemBase * itemBase = findItem(id);
1437 	if (itemBase == NULL) return;
1438 
1439 	Wire * wire = qobject_cast<Wire *>(itemBase);
1440 	if (wire == NULL) return;
1441 
1442 	ConnectorItem * connectorItem = findConnectorItem(wire, connectorID, ViewLayer::specFromID(wire->viewLayerID()));
1443 	if (connectorItem == NULL) return;
1444 
1445 	if (updateRatsnest) {
1446 		ratsnestConnect(connectorItem, true);
1447 	}
1448 
1449 	wire->simpleConnectedMoved(connectorItem);
1450 }
1451 
rotateItem(long id,double degrees)1452 void SketchWidget::rotateItem(long id, double degrees) {
1453 	//DebugDialog::debug(QString("rotating %1 %2").arg(id).arg(degrees) );
1454 
1455 	if (!isVisible()) return;
1456 
1457 	ItemBase * itemBase = findItem(id);
1458 	if (itemBase != NULL) {
1459 		itemBase->rotateItem(degrees, false);
1460         if (m_infoView) m_infoView->updateRotation(itemBase);
1461 	}
1462 
1463 }
transformItem(long id,const QMatrix & matrix)1464 void SketchWidget::transformItem(long id, const QMatrix & matrix) {
1465 	ItemBase * itemBase = findItem(id);
1466 	if (itemBase != NULL) {
1467 		itemBase->transformItem2(matrix);
1468         if (m_infoView) m_infoView->updateRotation(itemBase);
1469 	}
1470 }
1471 
flipItem(long id,Qt::Orientations orientation)1472 void SketchWidget::flipItem(long id, Qt::Orientations orientation) {
1473 	//DebugDialog::debug(QString("flipping %1 %2").arg(id).arg(orientation) );
1474 
1475 	if (!isVisible()) return;
1476 
1477 	ItemBase * itemBase = findItem(id);
1478 	if (itemBase != NULL) {
1479 		itemBase->flipItem(orientation);
1480         if (m_infoView) m_infoView->updateRotation(itemBase);
1481 		ratsnestConnect(itemBase, true);
1482 	}
1483 }
1484 
changeWire(long fromID,QLineF line,QPointF pos,bool updateConnections,bool updateRatsnest)1485 void SketchWidget::changeWire(long fromID, QLineF line, QPointF pos, bool updateConnections, bool updateRatsnest)
1486 {
1487 	/*
1488 	DebugDialog::debug(QString("change wire %1; %2,%3,%4,%5; %6,%7; %8")
1489 			.arg(fromID)
1490 			.arg(line.x1())
1491 			.arg(line.y1())
1492 			.arg(line.x2())
1493 			.arg(line.y2())
1494 			.arg(pos.x())
1495 			.arg(pos.y())
1496 			.arg(updateConnections) );
1497 	*/
1498 
1499 	ItemBase * fromItem = findItem(fromID);
1500 	if (fromItem == NULL) return;
1501 
1502 	Wire* wire = dynamic_cast<Wire *>(fromItem);
1503 	if (wire == NULL) return;
1504 
1505 	wire->setLineAnd(line, pos, true);
1506 	if (updateConnections) {
1507         QList<ConnectorItem *> already;
1508 		wire->updateConnections(wire->connector0(), false, already);
1509 		wire->updateConnections(wire->connector1(), false, already);
1510 	}
1511 
1512 	if (updateRatsnest) {
1513 		ratsnestConnect(wire->connector0(), true);
1514 		ratsnestConnect(wire->connector1(), true);
1515 	}
1516 }
1517 
rotateLeg(long fromID,const QString & fromConnectorID,const QPolygonF & leg,bool active)1518 void SketchWidget::rotateLeg(long fromID, const QString & fromConnectorID, const QPolygonF & leg, bool active)
1519 {
1520 	ItemBase * fromItem = findItem(fromID);
1521 	if (fromItem == NULL) {
1522 		DebugDialog::debug("rotate leg exit 1");
1523 		return;
1524 	}
1525 
1526 	ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, ViewLayer::specFromID(fromItem->viewLayerID()));
1527 	if (fromConnectorItem == NULL) {
1528 		DebugDialog::debug("rotate leg exit 2");
1529 		return;
1530 	}
1531 
1532 	fromConnectorItem->rotateLeg(leg, active);
1533 }
1534 
1535 
changeLeg(long fromID,const QString & fromConnectorID,const QPolygonF & leg,bool relative,const QString & why)1536 void SketchWidget::changeLeg(long fromID, const QString & fromConnectorID, const QPolygonF & leg, bool relative, const QString & why)
1537 {
1538 	changeLegAux(fromID, fromConnectorID, leg, false, relative, true, why);
1539 }
1540 
recalcLeg(long fromID,const QString & fromConnectorID,const QPolygonF & leg,bool relative,bool active,const QString & why)1541 void SketchWidget::recalcLeg(long fromID, const QString & fromConnectorID, const QPolygonF & leg, bool relative, bool active, const QString & why)
1542 {
1543 	changeLegAux(fromID, fromConnectorID, leg, true, relative, active, why);
1544 }
1545 
changeLegAux(long fromID,const QString & fromConnectorID,const QPolygonF & leg,bool reset,bool relative,bool active,const QString & why)1546 void SketchWidget::changeLegAux(long fromID, const QString & fromConnectorID, const QPolygonF & leg, bool reset, bool relative, bool active, const QString & why)
1547 {
1548 	ItemBase * fromItem = findItem(fromID);
1549 	if (fromItem == NULL) {
1550 		DebugDialog::debug("change leg exit 1");
1551 		return;
1552 	}
1553 
1554 	ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, ViewLayer::specFromID(fromItem->viewLayerID()));
1555 	if (fromConnectorItem == NULL) {
1556 		DebugDialog::debug("change leg exit 2");
1557 		return;
1558 	}
1559 
1560 	if (reset) {
1561 		fromConnectorItem->resetLeg(leg, relative, active, why);
1562 	}
1563 	else {
1564 		fromConnectorItem->setLeg(leg, relative, why);
1565 	}
1566 
1567     QList<ConnectorItem *> already;
1568 	fromItem->updateConnections(fromConnectorItem, false, already);
1569 }
1570 
selectItem(long id,bool state,bool updateInfoView,bool doEmit)1571 void SketchWidget::selectItem(long id, bool state, bool updateInfoView, bool doEmit) {
1572 	this->clearHoldingSelectItem();
1573 	ItemBase * item = findItem(id);
1574 	if (item != NULL) {
1575 		item->setSelected(state);
1576 		if(updateInfoView) {
1577 			// show something in the info view, even if it's not selected
1578 			viewItemInfo(item);
1579 		}
1580 		if (doEmit) {
1581 			emit itemSelectedSignal(id, state);
1582 		}
1583 	}
1584 
1585 	PaletteItem *pitem = dynamic_cast<PaletteItem*>(item);
1586 	if(pitem) {
1587 		setLastPaletteItemSelected(pitem);
1588 	}
1589 }
1590 
selectDeselectAllCommand(bool state)1591 void SketchWidget::selectDeselectAllCommand(bool state) {
1592 	this->clearHoldingSelectItem();
1593 
1594 	SelectItemCommand * cmd = stackSelectionState(false, NULL);
1595 	cmd->setText(state ? tr("Select All") : tr("Deselect"));
1596 	cmd->setSelectItemType( state ? SelectItemCommand::SelectAll : SelectItemCommand::DeselectAll );
1597 
1598 	m_undoStack->push(cmd);
1599 
1600 }
1601 
selectAllItems(bool state,bool doEmit)1602 void SketchWidget::selectAllItems(bool state, bool doEmit) {
1603 	foreach (QGraphicsItem * item, this->scene()->items()) {
1604 		item->setSelected(state);
1605 	}
1606 
1607 	if (doEmit) {
1608 		emit selectAllItemsSignal(state, false);
1609 	}
1610 }
1611 
cut()1612 void SketchWidget::cut() {
1613 	copy();
1614 	cutDeleteAux("Cut", false, NULL);
1615 }
1616 
copy()1617 void SketchWidget::copy() {
1618 	QList<QGraphicsItem *> tlBases;
1619     foreach (QGraphicsItem * item, scene()->selectedItems()) {
1620 		ItemBase * itemBase =  ItemBase::extractTopLevelItemBase(item);
1621 		if (itemBase == NULL) continue;
1622 		if (itemBase->getRatsnest()) continue;
1623 		if (tlBases.contains(itemBase)) continue;
1624 
1625         QList<ItemBase *> superSubs = collectSuperSubs(itemBase);
1626         if (superSubs.count() > 0) {
1627             foreach (ItemBase * supersub, superSubs) {
1628                 if (!tlBases.contains(supersub)) tlBases.append(supersub);
1629             }
1630             continue;
1631         }
1632 
1633 		tlBases.append(itemBase);
1634 	}
1635 
1636 	QList<ItemBase *> bases;
1637 	sortAnyByZ(tlBases, bases);
1638 
1639 	// sort them in z-order so the copies also appear in the same order
1640 
1641 	copyAux(bases, true);
1642 }
1643 
copyAux(QList<ItemBase * > & bases,bool saveBoundingRects)1644 void SketchWidget::copyAux(QList<ItemBase *> & bases, bool saveBoundingRects)
1645 {
1646 	for (int i = bases.count() - 1; i >= 0; i--) {
1647 		Wire * wire = qobject_cast<Wire *>(bases.at(i));
1648 		if (wire == NULL) continue;
1649 		if (!wire->getTrace()) continue;
1650 
1651 		QList<ConnectorItem *> ends;
1652 		QList<Wire *> wires;
1653 		wire->collectChained(wires, ends);
1654 		if (ends.count() < 2) {
1655 			// trace is dangling
1656 			bases.removeAt(i);
1657 			continue;
1658 		}
1659 
1660 		foreach (ConnectorItem * end, ends) {
1661 			if (bases.contains(end->attachedTo())) continue;
1662 			if (bases.contains(end->attachedTo()->layerKinChief())) continue;
1663 
1664 			// trace is dangling
1665 			bases.removeAt(i);
1666 			break;
1667 		}
1668 	}
1669 
1670 	if (bases.count() == 0) {
1671 		return;
1672 	}
1673 
1674     QByteArray itemData;
1675 	QList<long> modelIndexes;
1676 	copyHeart(bases, saveBoundingRects, itemData, modelIndexes);
1677 
1678 	// only preserve connections for copied items that connect to each other
1679 	QByteArray newItemData = removeOutsideConnections(itemData, modelIndexes);
1680 
1681     QMimeData *mimeData = new QMimeData;
1682     mimeData->setData("application/x-dnditemsdata", newItemData);
1683 	mimeData->setData("text/plain", newItemData);
1684 
1685 	QClipboard *clipboard = QApplication::clipboard();
1686 	if (clipboard == NULL) {
1687 		// shouldn't happen
1688 		delete mimeData;
1689 		return;
1690 	}
1691 
1692 	clipboard->setMimeData(mimeData, QClipboard::Clipboard);
1693 }
1694 
pasteHeart(QByteArray & itemData,bool seekOutsideConnections)1695 void SketchWidget::pasteHeart(QByteArray & itemData, bool seekOutsideConnections) {
1696 	QList<ModelPart *> modelParts;
1697 	QHash<QString, QRectF> boundingRects;
1698 	if (m_sketchModel->paste(m_referenceModel, itemData, modelParts, boundingRects, true)) {
1699 		QRectF r;
1700 		QRectF boundingRect = boundingRects.value(this->viewName(), r);
1701 		QList<long> newIDs;
1702 		this->loadFromModelParts(modelParts, BaseCommand::SingleView, NULL, true, &r, seekOutsideConnections, newIDs);
1703 	}
1704 }
1705 
copyHeart(QList<ItemBase * > & bases,bool saveBoundingRects,QByteArray & itemData,QList<long> & modelIndexes)1706 void SketchWidget::copyHeart(QList<ItemBase *> & bases, bool saveBoundingRects, QByteArray & itemData, QList<long> & modelIndexes) {
1707 	QXmlStreamWriter streamWriter(&itemData);
1708 
1709 	streamWriter.writeStartElement("module");
1710 	streamWriter.writeAttribute("fritzingVersion", Version::versionString());
1711 
1712 	if (saveBoundingRects) {
1713 		QRectF itemsBoundingRect;
1714 		foreach (ItemBase * itemBase, bases) {
1715 			if (itemBase->getRatsnest()) continue;
1716 
1717 			itemsBoundingRect |= itemBase->sceneBoundingRect();
1718 		}
1719 
1720 		QHash<QString, QRectF> boundingRects;
1721 		boundingRects.insert(m_viewName, itemsBoundingRect);
1722 		emit copyBoundingRectsSignal(boundingRects);
1723 
1724 		streamWriter.writeStartElement("boundingRects");
1725 		foreach (QString key, boundingRects.keys()) {
1726 			streamWriter.writeStartElement("boundingRect");
1727 			streamWriter.writeAttribute("name", key);
1728 			QRectF r = boundingRects.value(key);
1729 			streamWriter.writeAttribute("rect", QString("%1 %2 %3 %4")
1730 						.arg(r.left())
1731 						.arg(r.top())
1732 						.arg(r.width())
1733 						.arg(r.height()));
1734 			streamWriter.writeEndElement();
1735 		}
1736 		streamWriter.writeEndElement();
1737 	}
1738 
1739 	streamWriter.writeStartElement("instances");
1740 	foreach (ItemBase * base, bases) {
1741 		if (base->getRatsnest()) continue;
1742 
1743 		base->modelPart()->saveInstances("", streamWriter, false);
1744 		modelIndexes.append(base->modelPart()->modelIndex());
1745 	}
1746 	streamWriter.writeEndElement();
1747 	streamWriter.writeEndElement();
1748 }
1749 
removeOutsideConnections(const QByteArray & itemData,QList<long> & modelIndexes)1750 QByteArray SketchWidget::removeOutsideConnections(const QByteArray & itemData, QList<long> & modelIndexes) {
1751 	// now have to remove each connection that points to a part outside of the set of parts being copied
1752 
1753 	QDomDocument domDocument;
1754 	QString errorStr;
1755 	int errorLine;
1756 	int errorColumn;
1757 	bool result = domDocument.setContent(itemData, &errorStr, &errorLine, &errorColumn);
1758 	if (!result) return ___emptyByteArray___;
1759 
1760 	QDomElement root = domDocument.documentElement();
1761    	if (root.isNull()) {
1762    		return ___emptyByteArray___;
1763 	}
1764 
1765 	QDomElement instances = root.firstChildElement("instances");
1766 	if (instances.isNull()) return ___emptyByteArray___;
1767 
1768 	QDomElement instance = instances.firstChildElement("instance");
1769 	while (!instance.isNull()) {
1770 		QDomElement views = instance.firstChildElement("views");
1771 		if (!views.isNull()) {
1772 			QDomElement view = views.firstChildElement();
1773 			while (!view.isNull()) {
1774 				QDomElement connectors = view.firstChildElement("connectors");
1775 				if (!connectors.isNull()) {
1776 					QDomElement connector = connectors.firstChildElement("connector");
1777 					while (!connector.isNull()) {
1778 						QDomElement connects = connector.firstChildElement("connects");
1779 						if (!connects.isNull()) {
1780 							QDomElement connect = connects.firstChildElement("connect");
1781 							QList<QDomElement> toDelete;
1782 							while (!connect.isNull()) {
1783 								long modelIndex = connect.attribute("modelIndex").toLong();
1784 								if (!modelIndexes.contains(modelIndex)) {
1785 									toDelete.append(connect);
1786 								}
1787 
1788 								connect = connect.nextSiblingElement("connect");
1789 							}
1790 
1791 							foreach (QDomElement connect, toDelete) {
1792 								QDomNode removed = connects.removeChild(connect);
1793 								if (removed.isNull()) {
1794 									DebugDialog::debug("removed is null");
1795 								}
1796 							}
1797 						}
1798 						connector = connector.nextSiblingElement("connector");
1799 					}
1800 				}
1801 
1802 				view = view.nextSiblingElement();
1803 			}
1804 		}
1805 
1806 		instance = instance.nextSiblingElement("instance");
1807 	}
1808 
1809 	return domDocument.toByteArray();
1810 }
1811 
1812 
dragEnterEvent(QDragEnterEvent * event)1813 void SketchWidget::dragEnterEvent(QDragEnterEvent *event)
1814 {
1815 	if (dragEnterEventAux(event)) {
1816 		setupAutoscroll(false);
1817 		event->acceptProposedAction();
1818 	}
1819 	else if (event->mimeData()->hasFormat("application/x-dndsketchdata")) {
1820 		if (event->source() != this) {
1821 			m_movingItem = NULL;
1822 			SketchWidget * other = dynamic_cast<SketchWidget *>(event->source());
1823 			if (other == NULL) {
1824 				throw "drag enter event from unknown source";
1825 			}
1826 
1827 			// TODO: this checkunder will probably never work
1828 			m_checkUnder = other->m_checkUnder;
1829 
1830 			m_movingItem = new QGraphicsSvgItem();
1831 			m_movingItem->setSharedRenderer(other->m_movingSVGRenderer);
1832 			this->scene()->addItem(m_movingItem);
1833 			m_movingItem->setPos(mapToScene(event->pos()) - other->m_movingSVGOffset);
1834 		}
1835 		event->acceptProposedAction();
1836 	}
1837 	else {
1838 		// subclass seems to call acceptProposedAction so don't invoke it
1839 		// QGraphicsView::dragEnterEvent(event);
1840 		event->ignore();
1841 	}
1842 }
1843 
dragEnterEventAux(QDragEnterEvent * event)1844 bool SketchWidget::dragEnterEventAux(QDragEnterEvent *event) {
1845 	if (!event->mimeData()->hasFormat("application/x-dnditemdata")) return false;
1846 
1847 	scene()->setSceneRect(scene()->sceneRect());	// prevents inadvertent scrolling when dragging in items from the parts bin
1848 	m_clearSceneRect = true;
1849 
1850 	m_droppingWire = false;
1851     QByteArray itemData = event->mimeData()->data("application/x-dnditemdata");
1852     QDataStream dataStream(&itemData, QIODevice::ReadOnly);
1853 
1854     QString moduleID;
1855     QPointF offset;
1856     dataStream >> moduleID >> offset;
1857 
1858     moduleID = checkDroppedModuleID(moduleID);
1859 	ModelPart * modelPart = m_referenceModel->retrieveModelPart(moduleID);
1860 	if (modelPart ==  NULL) return false;
1861 
1862 	if (!canDropModelPart(modelPart)) return false;
1863 
1864 	m_droppingWire = (modelPart->itemType() == ModelPart::Wire);
1865 	m_droppingOffset = offset;
1866 
1867 	if (ItemDrag::cache().contains(this)) {
1868 		m_droppingItem->setVisible(true);
1869 	}
1870 	else {
1871 		ViewGeometry viewGeometry;
1872 		QPointF p = QPointF(this->mapToScene(event->pos())) - offset;
1873 		viewGeometry.setLoc(p);
1874 
1875 		long fromID = ItemBase::getNextID();
1876 
1877 		bool doConnectors = true;
1878 
1879 		// create temporary item for dragging
1880 		m_droppingItem = addItemAuxTemp(modelPart, defaultViewLayerPlacement(modelPart), viewGeometry, fromID, doConnectors, m_viewID, true);
1881         QSizeF size = m_droppingItem->sceneBoundingRect().size();
1882         if (size.width() < offset.x() || size.height() < offset.y()) {
1883             offset = m_droppingOffset = QPointF(size.width() / 2, size.height() / 2);
1884         }
1885 
1886 		QHash<long, ItemBase *> savedItems;
1887 		QHash<Wire *, ConnectorItem *> savedWires;
1888 		findAlignmentAnchor(m_droppingItem, savedItems, savedWires);
1889 
1890 		ItemDrag::cache().insert(this, m_droppingItem);
1891 		//m_droppingItem->setCacheMode(QGraphicsItem::ItemCoordinateCache);
1892 		connect(ItemDrag::singleton(), SIGNAL(dragIsDoneSignal(ItemDrag *)), this, SLOT(dragIsDoneSlot(ItemDrag *)));
1893 	}
1894 	//ItemDrag::_setPixmapVisible(false);
1895 
1896 	m_checkUnder.clear();
1897 	if (checkUnder()) {
1898 		m_checkUnder.append(m_droppingItem);
1899 	}
1900 
1901 
1902 // make sure relevant layer is visible
1903 	ViewLayer::ViewLayerID viewLayerID;
1904 	if (m_droppingWire) {
1905 		viewLayerID = getWireViewLayerID(m_droppingItem->getViewGeometry(), m_droppingItem->viewLayerPlacement());
1906 	}
1907 	else if(modelPart->moduleID().compare(ModuleIDNames::RulerModuleIDName) == 0) {
1908 		viewLayerID = getRulerViewLayerID();
1909 	}
1910 	else if(modelPart->moduleID().compare(ModuleIDNames::NoteModuleIDName) == 0) {
1911 		viewLayerID = getNoteViewLayerID();
1912 	}
1913 	else {
1914 		viewLayerID = getPartViewLayerID();
1915 	}
1916 
1917 	ensureLayerVisible(viewLayerID);  // TODO: if any layer in the dragged part is visible, then don't bother calling ensureLayerVisible
1918 	return true;
1919 }
1920 
canDropModelPart(ModelPart * modelPart)1921 bool SketchWidget::canDropModelPart(ModelPart * modelPart) {
1922 	LayerList layerList = modelPart->viewLayers(viewID());
1923 	foreach (ViewLayer::ViewLayerID viewLayerID, m_viewLayers.keys()) {
1924 		if (layerList.contains(viewLayerID)) return true;
1925 	}
1926 
1927 	return false;
1928 }
1929 
dragLeaveEvent(QDragLeaveEvent * event)1930 void SketchWidget::dragLeaveEvent(QDragLeaveEvent * event) {
1931 	Q_UNUSED(event);
1932 	turnOffAutoscroll();
1933 
1934 	if (m_droppingItem != NULL) {
1935 		if (m_clearSceneRect) {
1936 			m_clearSceneRect = false;
1937 			scene()->setSceneRect(QRectF());
1938 		}
1939 		m_droppingItem->setVisible(false);
1940 		//ItemDrag::_setPixmapVisible(true);
1941 	}
1942 	else {
1943 		// dragging sketch items
1944 		if (m_movingItem) {
1945 			delete m_movingItem;
1946 			m_movingItem = NULL;
1947 		}
1948 	}
1949 
1950 	//QGraphicsView::dragLeaveEvent(event);		// we override QGraphicsView::dragEnterEvent so don't call the subclass dragLeaveEvent here
1951 }
1952 
dragMoveEvent(QDragMoveEvent * event)1953 void SketchWidget::dragMoveEvent(QDragMoveEvent *event)
1954 {
1955     if (event->mimeData()->hasFormat("application/x-dnditemdata")) {
1956     	dragMoveHighlightConnector(event->pos());
1957         event->acceptProposedAction();
1958         return;
1959     }
1960 
1961 	if (event->mimeData()->hasFormat("application/x-dndsketchdata")) {
1962 		if (event->source() == this) {
1963 			m_globalPos = this->mapToGlobal(event->pos());
1964 			if ((QApplication::keyboardModifiers() & Qt::ShiftModifier) != 0) {
1965 				QPointF p = GraphicsUtils::calcConstraint(m_mousePressGlobalPos, m_globalPos);
1966 				m_globalPos.setX(p.x());
1967 				m_globalPos.setY(p.y());
1968 			}
1969 
1970 			moveItems(m_globalPos, true, m_rubberBandLegWasEnabled);
1971 			m_moveEventCount++;
1972 		}
1973 		else {
1974 			SketchWidget * other = dynamic_cast<SketchWidget *>(event->source());
1975 			if (other == NULL) {
1976 				throw "drag move event from unknown source";
1977 			}
1978 			m_movingItem->setPos(mapToScene(event->pos()) - other->m_movingSVGOffset);
1979 		}
1980 		event->acceptProposedAction();
1981 		return;
1982 	}
1983 
1984 	//QGraphicsView::dragMoveEvent(event);   // we override QGraphicsView::dragEnterEvent so don't call the subclass dragMoveEvent here
1985 }
1986 
dragMoveHighlightConnector(QPoint eventPos)1987 void SketchWidget::dragMoveHighlightConnector(QPoint eventPos) {
1988 	if (m_droppingItem == NULL) return;
1989 
1990 	m_globalPos = this->mapToGlobal(eventPos);
1991 	checkAutoscroll(m_globalPos);
1992 
1993 	QPointF loc = this->mapToScene(eventPos) - m_droppingOffset;
1994 	if (m_alignToGrid && (m_alignmentItem != NULL)) {
1995 		QPointF l =  m_alignmentItem->getViewGeometry().loc();
1996 		alignLoc(loc, m_alignmentStartPoint, loc, l);
1997 
1998 		//QPointF q = m_alignmentItem->pos();
1999 		//if (q != l) {
2000 		//	DebugDialog::debug(QString("m alignment %1 %2, %3 %4").arg(q.x()).arg(q.y()).arg(l.x()).arg(l.y()));
2001 		//}
2002 	}
2003 
2004 	m_droppingItem->setItemPos(loc);
2005 	if (m_checkUnder.contains(m_droppingItem)) {
2006 		m_droppingItem->findConnectorsUnder();
2007 	}
2008 
2009 }
2010 
dropEvent(QDropEvent * event)2011 void SketchWidget::dropEvent(QDropEvent *event)
2012 {
2013 	m_alignmentItem = NULL;
2014 
2015 	turnOffAutoscroll();
2016 	clearHoldingSelectItem();
2017 
2018     if (event->mimeData()->hasFormat("application/x-dnditemdata")) {
2019 		dropItemEvent(event);
2020     }
2021 	else if (event->mimeData()->hasFormat("application/x-dndsketchdata")) {
2022 		if (m_movingItem) {
2023 			delete m_movingItem;
2024 			m_movingItem = NULL;
2025 		}
2026 
2027 		ConnectorItem::clearEqualPotentialDisplay();
2028 		if (event->source() == this) {
2029 			checkMoved(false);
2030 			m_savedItems.clear();
2031 			m_savedWires.clear();
2032 		}
2033 		else {
2034 			SketchWidget * other = dynamic_cast<SketchWidget *>(event->source());
2035 			if (other == NULL) {
2036 				throw "drag and drop from unknown source";
2037 			}
2038 
2039 			ItemBase * ref = other->m_moveReferenceItem;
2040 			QPointF originalPos = ref->getViewGeometry().loc();
2041 			other->copyDrop();
2042 			QPointF startLocal = other->mapFromGlobal(QPoint(other->m_mousePressGlobalPos.x(), other->m_mousePressGlobalPos.y()));
2043 			QPointF sceneLocal = other->mapToScene(startLocal.x(), startLocal.y());
2044 			QPointF offset = sceneLocal - originalPos;
2045 			m_pasteOffset = this->mapToScene(event->pos()) - offset - originalPos;
2046 
2047 			DebugDialog::debug(QString("other %1 %2, event %3 %4")
2048 				.arg(startLocal.x()).arg(startLocal.y())
2049 				.arg(event->pos().x()).arg(event->pos().y())
2050 			);
2051 			m_pasteCount = 0;
2052 			emit dropPasteSignal(this);
2053 		}
2054         event->acceptProposedAction();
2055 	}
2056 	else {
2057 		QGraphicsView::dropEvent(event);
2058 	}
2059 
2060 	DebugDialog::debug("after drop event");
2061 
2062 }
2063 
dropItemEvent(QDropEvent * event)2064 void SketchWidget::dropItemEvent(QDropEvent *event) {
2065 	if (m_droppingItem == NULL) return;
2066 
2067 	if (m_clearSceneRect) {
2068 		m_clearSceneRect = false;
2069 		scene()->setSceneRect(QRectF());
2070 	}
2071 
2072 	ModelPart * modelPart = m_droppingItem->modelPart();
2073 	if (modelPart == NULL) return;
2074 	if (modelPart->modelPartShared() == NULL) return;
2075 
2076     ModelPart::ItemType itemType = modelPart->itemType();
2077 
2078 	QUndoCommand* parentCommand = new TemporaryCommand(tr("Add %1").arg(m_droppingItem->title()));
2079 
2080 	stackSelectionState(false, parentCommand);
2081 	CleanUpWiresCommand * cuw = new CleanUpWiresCommand(this, CleanUpWiresCommand::Noop, parentCommand);
2082 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
2083 
2084 	m_droppingItem->saveGeometry();
2085 	ViewGeometry viewGeometry = m_droppingItem->getViewGeometry();
2086 
2087 	long fromID = m_droppingItem->id();
2088 
2089 	BaseCommand::CrossViewType crossViewType = BaseCommand::CrossView;
2090 	switch (modelPart->itemType()) {
2091 		case ModelPart::Ruler:
2092 		case ModelPart::Note:
2093 			// rulers and notes are local to a particular view
2094 			crossViewType = BaseCommand::SingleView;
2095 			break;
2096 		default:
2097 			break;
2098 	}
2099 
2100     ViewLayer::ViewLayerPlacement viewLayerPlacement;
2101     getDroppedItemViewLayerPlacement(modelPart, viewLayerPlacement);
2102 	AddItemCommand * addItemCommand = newAddItemCommand(crossViewType, modelPart, modelPart->moduleID(), viewLayerPlacement, viewGeometry, fromID, true, -1, true, parentCommand);
2103 	addItemCommand->setDropOrigin(this);
2104 
2105 	new SetDropOffsetCommand(this, fromID, m_droppingOffset, parentCommand);
2106 
2107 	new CheckStickyCommand(this, crossViewType, fromID, false, CheckStickyCommand::RemoveOnly, parentCommand);
2108 
2109 	SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
2110 	selectItemCommand->addRedo(fromID);
2111 
2112 	new ShowLabelFirstTimeCommand(this, crossViewType, fromID, true, true, parentCommand);
2113 
2114 	if (modelPart->itemType() == ModelPart::Wire && !m_lastColorSelected.isEmpty()) {
2115 		new WireColorChangeCommand(this, fromID, m_lastColorSelected, m_lastColorSelected, 1.0, 1.0, parentCommand);
2116 	}
2117 
2118 	bool gotConnector = false;
2119 
2120 	// jrc: 24 aug 2010: don't see why restoring color on dropped item is necessary
2121 	//QList<ConnectorItem *> connectorItems;
2122 	foreach (ConnectorItem * connectorItem, m_droppingItem->cachedConnectorItems()) {
2123 		//connectorItem->setMarked(false);
2124 		//connectorItems.append(connectorItem);
2125 		ConnectorItem * to = connectorItem->overConnectorItem();
2126 		if (to != NULL) {
2127 			to->connectorHover(to->attachedTo(), false);
2128 			connectorItem->setOverConnectorItem(NULL);   // clean up
2129 			extendChangeConnectionCommand(BaseCommand::CrossView, connectorItem, to, ViewLayer::specFromID(connectorItem->attachedToViewLayerID()), true, parentCommand);
2130 			gotConnector = true;
2131 		}
2132 		//connectorItem->clearConnectorHover();
2133 	}
2134 	//foreach (ConnectorItem * connectorItem, connectorItems) {
2135 		//if (!connectorItem->marked()) {
2136 			//connectorItem->restoreColor(false, 0, true);
2137 		//}
2138 	//}
2139 	//m_droppingItem->clearConnectorHover();
2140 
2141 	clearTemporaries();
2142 
2143 	killDroppingItem();
2144 
2145 	if (gotConnector) {
2146 	    new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
2147 		new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
2148 		cuw->setDirection(CleanUpWiresCommand::UndoOnly);
2149 	}
2150 
2151     if (itemType == ModelPart::CopperFill) {
2152         m_undoStack->waitPushTemporary(parentCommand, 10);
2153     }
2154     else {
2155         m_undoStack->waitPush(parentCommand, 10);
2156     }
2157 
2158 
2159     event->acceptProposedAction();
2160 
2161 	emit dropSignal(event->pos());
2162 }
2163 
stackSelectionState(bool pushIt,QUndoCommand * parentCommand)2164 SelectItemCommand* SketchWidget::stackSelectionState(bool pushIt, QUndoCommand * parentCommand) {
2165 
2166 	// if pushIt assumes m_undoStack->beginMacro has previously been called
2167 
2168 	//DebugDialog::debug(QString("stacking"));
2169 
2170 	// DebugDialog::debug(QString("stack selection state %1 %2").arg(pushIt).arg((long) parentCommand));
2171 	SelectItemCommand* selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
2172 	const QList<QGraphicsItem *> sitems = scene()->selectedItems();
2173  	for (int i = 0; i < sitems.size(); ++i) {
2174  		ItemBase * base = ItemBase::extractTopLevelItemBase(sitems.at(i));
2175  		if (base == NULL) continue;
2176 
2177  		 selectItemCommand->addUndo(base->id());
2178 		 //DebugDialog::debug(QString("\tstacking %1").arg(base->id()));
2179     }
2180 
2181 	selectItemCommand->setText(tr("Selection"));
2182 
2183     if (pushIt) {
2184      	m_undoStack->push(selectItemCommand);
2185     }
2186 
2187     return selectItemCommand;
2188 }
2189 
moveByArrow(double dx,double dy,QKeyEvent * event)2190 bool SketchWidget::moveByArrow(double dx, double dy, QKeyEvent * event) {
2191 	bool rubberBandLegEnabled = false;
2192 	DebugDialog::debug(QString("move by arrow %1").arg(event == NULL ? false : event->isAutoRepeat()));
2193 	if (event == NULL || !event->isAutoRepeat()) {
2194 		m_dragBendpointWire = NULL;
2195 		clearHoldingSelectItem();
2196 		m_savedItems.clear();
2197 		m_savedWires.clear();
2198 		m_moveEventCount = 0;
2199 		m_arrowTotalX = m_arrowTotalY = 0;
2200 
2201 		QPoint cp = QCursor::pos();
2202 		QPoint wp = this->mapFromGlobal(cp);
2203 		QPointF sp = this->mapToScene(wp);
2204 		Wire * wire = dynamic_cast<Wire *>(scene()->itemAt(sp, QTransform()));
2205 		bool draggingWire = false;
2206 		if (wire != NULL) {
2207 			if (canChainWire(wire) && wire->hasConnections()) {
2208 				if (canDragWire(wire) && ((event->modifiers() & altOrMetaModifier()) != 0)) {
2209 					prepDragWire(wire);
2210 					draggingWire = true;
2211 				}
2212 			}
2213 		}
2214 
2215 		if (!draggingWire) {
2216 			rubberBandLegEnabled = (event != NULL) && ((event->modifiers() & altOrMetaModifier()) != 0);
2217 			prepMove(NULL, rubberBandLegEnabled, true);
2218 		}
2219 		if (m_savedItems.count() == 0) return false;
2220 
2221 		m_mousePressScenePos = this->mapToScene(this->rect().center());
2222 		m_movingByArrow = true;
2223 	}
2224 	else {
2225 		//DebugDialog::debug("autorepeat");
2226 	}
2227 
2228 	if (event != NULL && (event->modifiers() & Qt::ShiftModifier)) {
2229 		dx *= 10;
2230 		dy *= 10;
2231 	}
2232 
2233 	if (m_alignToGrid) {
2234 		dx *= gridSizeInches() * GraphicsUtils::SVGDPI;
2235 		dy *= gridSizeInches() * GraphicsUtils::SVGDPI;
2236 	}
2237 
2238 	m_arrowTotalX += dx;
2239 	m_arrowTotalY += dy;
2240 
2241 	QPoint globalPos = mapFromScene(m_mousePressScenePos + QPointF(m_arrowTotalX, m_arrowTotalY));
2242 	globalPos = mapToGlobal(globalPos);
2243 	moveItems(globalPos, false, rubberBandLegEnabled);
2244 	m_moveEventCount++;
2245 	return true;
2246 }
2247 
2248 
mousePressEvent(QMouseEvent * event)2249 void SketchWidget::mousePressEvent(QMouseEvent *event)
2250 {
2251     m_originatingItem = NULL;
2252 	m_draggingBendpoint = false;
2253 	if (m_movingByArrow) return;
2254 
2255 	m_movingByMouse = true;
2256 
2257 	QMouseEvent * hackEvent = NULL;
2258 	if (event->button() == Qt::MidButton && !spaceBarIsPressed()) {
2259 		m_middleMouseIsPressed = true;
2260 		setDragMode(QGraphicsView::ScrollHandDrag);
2261 		setCursor(Qt::OpenHandCursor);
2262 		// make the event look like a left button press to fool the underlying drag mode implementation
2263 		event = hackEvent = new QMouseEvent(event->type(), event->pos(), event->globalPos(), Qt::LeftButton, event->buttons() | Qt::LeftButton, event->modifiers());
2264 	}
2265 
2266 	m_dragBendpointWire = NULL;
2267 	m_spaceBarWasPressed = spaceBarIsPressed();
2268 	if (m_spaceBarWasPressed) {
2269 		InfoGraphicsView::mousePressEvent(event);
2270 		if (hackEvent) delete hackEvent;
2271 		return;
2272 	}
2273 
2274 	//setRenderHint(QPainter::Antialiasing, false);
2275 
2276 	clearHoldingSelectItem();
2277 	m_savedItems.clear();
2278 	m_savedWires.clear();
2279 	m_moveEventCount = 0;
2280 	m_holdingSelectItemCommand = stackSelectionState(false, NULL);
2281 	m_mousePressScenePos = mapToScene(event->pos());
2282 	m_mousePressGlobalPos = event->globalPos();
2283 
2284     squashShapes(m_mousePressScenePos);
2285 	QList<QGraphicsItem *> items = this->items(event->pos());
2286 	QGraphicsItem* wasItem = getClickedItem(items);
2287 
2288 	m_anyInRotation = false;
2289 	// mouse event gets passed through to individual QGraphicsItems
2290 	QGraphicsView::mousePressEvent(event);
2291 	if (m_anyInRotation) {
2292 		m_anyInRotation = false;
2293         unsquashShapes();
2294 		return;
2295 	}
2296 
2297 	items = this->items(event->pos());
2298 	QGraphicsItem* item = getClickedItem(items);
2299 	if (item != wasItem) {
2300 		// if the item was deleted during mousePressEvent
2301 		// for example, by shift-clicking a connectorItem
2302 		return;
2303 	}
2304 
2305     unsquashShapes();
2306 
2307 	if (item == NULL) {
2308 		if (items.length() == 1) {
2309 			// if we unambiguously click on a partlabel whose owner is unselected, go ahead and activate it
2310 			PartLabel * partLabel =  dynamic_cast<PartLabel *>(items[0]);
2311 			if (partLabel != NULL) {
2312 				partLabel->owner()->setSelected(true);
2313 				return;
2314 			}
2315 		}
2316 
2317 		clickBackground(event);
2318 		if (m_infoView != NULL) {
2319 			m_infoView->viewItemInfo(this, NULL, true);
2320 		}
2321 		return;
2322 	}
2323 
2324 	PartLabel * partLabel =  dynamic_cast<PartLabel *>(item);
2325 	if (partLabel != NULL) {
2326 		viewItemInfo(partLabel->owner());
2327 		setLastPaletteItemSelectedIf(partLabel->owner());
2328 		return;
2329 	}
2330 
2331 	// Note's child items (at the moment) are the resize grip and the text editor
2332 	Note * note = dynamic_cast<Note *>(item->parentItem());
2333 	if (note != NULL)  {
2334 		return;
2335 	}
2336 
2337 	Stripbit * stripbit = dynamic_cast<Stripbit *>(item);
2338 	if (stripbit) return;
2339 
2340 	ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
2341 	if (itemBase) {
2342 		viewItemInfo(itemBase);
2343 		setLastPaletteItemSelectedIf(itemBase);
2344 	}
2345 
2346 	if (resizingBoardPress(itemBase)) {
2347 		return;
2348 	}
2349 
2350 	ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(wasItem);
2351 	if (connectorItem != NULL && connectorItem->isDraggingLeg()) {
2352 		return;
2353 	}
2354 
2355 	Wire * wire = dynamic_cast<Wire *>(item);
2356 	if ((event->button() == Qt::LeftButton) && (wire != NULL)) {
2357 		if (canChainWire(wire) && wire->hasConnections()) {
2358 			if (canDragWire(wire) && ((event->modifiers() & altOrMetaModifier()) != 0)) {
2359 				prepDragWire(wire);
2360 				return;
2361 			}
2362 			else {
2363 				m_dragCurve = curvyWiresIndicated(event->modifiers()) && wire->canHaveCurve();
2364 				m_dragBendpointWire = wire;
2365 				m_dragBendpointPos = event->pos();
2366                 if (m_connectorDragWire) {
2367                     // if you happen to drag on a wire which is on top of a connector
2368                     // drag the bendpoint on the wire rather than the new wire from the connector
2369                     // maybe a better approach to block mousePressConnectorEvent?
2370                     delete m_connectorDragWire;
2371                     m_connectorDragWire = NULL;
2372                 }
2373 				return;
2374 			}
2375 		}
2376 	}
2377 
2378 	if (resizingJumperItemPress(itemBase)) {
2379 		return;
2380 	}
2381 
2382     if (event->button() == Qt::LeftButton) {
2383 	    prepMove(itemBase ? itemBase : dynamic_cast<ItemBase *>(item->parentItem()), (event->modifiers() & altOrMetaModifier()) != 0, true);
2384 	    if (m_alignToGrid && (itemBase == NULL) && (event->modifiers() == Qt::NoModifier)) {
2385 		    Wire * wire = dynamic_cast<Wire *>(item->parentItem());
2386 		    if (wire != NULL && wire->draggingEnd()) {
2387 			    ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(item);
2388 			    if (connectorItem != NULL) {
2389 				    m_draggingBendpoint = (connectorItem->connectionsCount() > 0);
2390 				    this->m_alignmentStartPoint = mapToScene(event->pos()) - connectorItem->sceneAdjustedTerminalPoint(NULL);
2391 			    }
2392 		    }
2393 	    }
2394 
2395 	    m_moveReferenceItem = m_savedItems.count() > 0 ? m_savedItems.values().at(0) : NULL;
2396 
2397 	    setupAutoscroll(true);
2398     }
2399 }
2400 
prepMove(ItemBase * originatingItem,bool rubberBandLegEnabled,bool includeRatsnest)2401 void SketchWidget::prepMove(ItemBase * originatingItem, bool rubberBandLegEnabled, bool includeRatsnest) {
2402     m_originatingItem = originatingItem;
2403 	m_rubberBandLegWasEnabled = rubberBandLegEnabled;
2404 	m_checkUnder.clear();
2405 	//DebugDialog::debug("prep move check under = false");
2406 	QSet<Wire *> wires;
2407 	QList<ItemBase *> items;
2408 	foreach (QGraphicsItem * gitem,  this->scene()->selectedItems()) {
2409 		ItemBase *itemBase = dynamic_cast<ItemBase *>(gitem);
2410 		if (itemBase == NULL) continue;
2411 		if (itemBase->moveLock()) continue;
2412 
2413 		items.append(itemBase);
2414 	}
2415 
2416 
2417 	//DebugDialog::debug(QString("prep move items %1").arg(items.count()));
2418 
2419 	int originalItemsCount = items.count();
2420 
2421 	for (int i = 0; i < items.count(); i++) {
2422 		ItemBase * itemBase = items[i];
2423 		if (itemBase->itemType() == ModelPart::Wire) {
2424             Wire * wire = qobject_cast<Wire *>(itemBase);
2425 			if (wire->isTraceType(getTraceFlag())) {
2426 				wires.insert(wire);
2427                // wire->debugInfo("adding wire");
2428 			}
2429             else if (includeRatsnest && wire->getRatsnest()) {
2430 				wires.insert(wire);
2431             }
2432 			continue;
2433 		}
2434 
2435 		ItemBase * chief = itemBase->layerKinChief();
2436 		if (chief->moveLock()) continue;
2437 
2438 		m_savedItems.insert(chief->id(), chief);
2439         //chief->debugInfo("adding saved");
2440 		if (chief->isSticky()) {
2441 			foreach(ItemBase * sitemBase, chief->stickyList()) {
2442 				if (sitemBase->isVisible()) {
2443 					if (sitemBase->itemType() == ModelPart::Wire) {
2444                         Wire * wire = qobject_cast<Wire *>(sitemBase);
2445                         if (wire->isTraceType(getTraceFlag())) {
2446 						    wires.insert(wire);
2447                             //wire->debugInfo("adding wire");
2448                         }
2449                         else if (includeRatsnest && wire->getRatsnest()) {
2450 				            wires.insert(wire);
2451                         }
2452 					}
2453 					else {
2454 						m_savedItems.insert(sitemBase->layerKinChief()->id(), sitemBase);
2455                         //sitemBase->debugInfo("adding sitem saved");
2456 						if (!items.contains(sitemBase)) {
2457 							items.append(sitemBase);
2458 						}
2459 					}
2460 				}
2461 			}
2462 		}
2463 
2464 		QSet<ItemBase *> set;
2465 		if (collectFemaleConnectees(chief, set)) {  // for historical reasons returns true if chief has male connectors
2466 			if (i < originalItemsCount) {
2467 
2468 				m_checkUnder.append(chief);
2469 			}
2470 		}
2471 		foreach (ItemBase * sitemBase, set) {
2472 			if (!items.contains(sitemBase)) {
2473 				items.append(sitemBase);
2474 			}
2475 		}
2476         QSet<Wire *> tempWires;
2477 		chief->collectWireConnectees(tempWires);
2478         foreach (Wire * wire, tempWires) {
2479             if (wire->isTraceType(getTraceFlag())) {
2480                 wires.insert(wire);
2481             }
2482             else if (includeRatsnest && wire->getRatsnest()) {
2483 				wires.insert(wire);
2484             }
2485         }
2486 	}
2487 
2488 	for (int i = m_checkUnder.count() - 1; i >= 0; i--) {
2489 		ItemBase * itemBase = m_checkUnder.at(i);
2490 		foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
2491 			bool gotOne = false;
2492 			foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) {
2493 				if (m_savedItems.value(toConnectorItem->attachedToID(), NULL) != NULL) {
2494 					m_checkUnder.removeAt(i);
2495 					gotOne = true;
2496 					break;
2497 				}
2498 			}
2499 			if (gotOne) break;
2500 		}
2501 	}
2502 
2503 	if (wires.count() > 0) {
2504 		QList<ItemBase *> freeWires;
2505 		categorizeDragWires(wires, freeWires);
2506 		m_checkUnder.append(freeWires);
2507 	}
2508 
2509 	categorizeDragLegs(rubberBandLegEnabled);
2510 
2511 	foreach (ItemBase * itemBase, m_savedItems.values()) {
2512 		itemBase->saveGeometry();
2513 	}
2514 
2515 	foreach (Wire * w, m_savedWires.keys()) {
2516 		w->saveGeometry();
2517 	}
2518 
2519 	findAlignmentAnchor(originatingItem, m_savedItems, m_savedWires);
2520 }
2521 
alignLoc(QPointF & loc,const QPointF startPoint,const QPointF newLoc,const QPointF originalLoc)2522 void SketchWidget::alignLoc(QPointF & loc, const QPointF startPoint, const QPointF newLoc, const QPointF originalLoc)
2523 {
2524 	// in the standard case, startpoint is the center of the connectorItem, newLoc is the current mouse position,
2525 	// originalLoc is the original mouse position.  newpos is therefore the new position of the center of the connectorItem
2526 	// and ny and ny make up the nearest grid point.  nx, ny - newloc give just the offset from the grid, which is then
2527 	// applied to loc, which is the location of the item being dragged
2528 
2529 	QPointF newPos = startPoint + newLoc - originalLoc;
2530 	double ny = GraphicsUtils::getNearestOrdinate(newPos.y(), gridSizeInches() * GraphicsUtils::SVGDPI);
2531 	double nx = GraphicsUtils::getNearestOrdinate(newPos.x(), gridSizeInches() * GraphicsUtils::SVGDPI);
2532 	loc.setX(loc.x() + nx - newPos.x());
2533 	loc.setY(loc.y() + ny - newPos.y());
2534 }
2535 
2536 
findAlignmentAnchor(ItemBase * originatingItem,QHash<long,ItemBase * > & savedItems,QHash<Wire *,ConnectorItem * > & savedWires)2537 void SketchWidget::findAlignmentAnchor(ItemBase * originatingItem, 	QHash<long, ItemBase *> & savedItems, QHash<Wire *, ConnectorItem *> & savedWires)
2538 {
2539 	m_alignmentItem = NULL;
2540 	if (!m_alignToGrid) return;
2541 
2542 	if (originatingItem) {
2543 		foreach (ConnectorItem * connectorItem, originatingItem->cachedConnectorItems()) {
2544 			m_alignmentStartPoint = connectorItem->sceneAdjustedTerminalPoint(NULL);
2545 			m_alignmentItem = originatingItem;
2546 			return;
2547 		}
2548 		if (canAlignToTopLeft(originatingItem)) {
2549 			m_alignmentStartPoint = originatingItem->pos();
2550 			m_alignmentItem = originatingItem;
2551 			return;
2552 		}
2553 		if (canAlignToCenter(originatingItem)) {
2554 			m_alignmentStartPoint = originatingItem->sceneBoundingRect().center();
2555 			m_alignmentItem = originatingItem;
2556 			return;
2557 		}
2558 	}
2559 
2560 	foreach (ItemBase * itemBase, savedItems) {
2561 		foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
2562 				m_alignmentStartPoint = connectorItem->sceneAdjustedTerminalPoint(NULL);
2563 				m_alignmentItem = itemBase;
2564 				return;
2565 		}
2566 	}
2567 
2568 	foreach (Wire * w, savedWires.keys()) {
2569 		m_alignmentItem = w;
2570 		m_alignmentStartPoint = w->connector0()->sceneAdjustedTerminalPoint(NULL);
2571 		return;
2572 	}
2573 
2574 	foreach (ItemBase * itemBase, savedItems) {
2575 		if (canAlignToTopLeft(itemBase)) {
2576 			m_alignmentStartPoint = itemBase->pos();
2577 			m_alignmentItem = itemBase;
2578 			return;
2579 		}
2580 		if (canAlignToCenter(itemBase)) {
2581 			m_alignmentStartPoint = itemBase->sceneBoundingRect().center();
2582 			m_alignmentItem = itemBase;
2583 			return;
2584 		}
2585 	}
2586 }
2587 
2588 struct ConnectionThing {
2589 	Wire * wire;
2590 	ConnectionStatus status[2];
2591 };
2592 
2593 
categorizeDragLegs(bool rubberBandLegEnabled)2594 void SketchWidget::categorizeDragLegs(bool rubberBandLegEnabled)
2595 {
2596 	m_stretchingLegs.clear();
2597 	if (!rubberBandLegEnabled) return;
2598 
2599 	QSet<ItemBase *> passives;
2600 	foreach (ItemBase * itemBase, m_savedItems.values()) {
2601 		if (itemBase->itemType() == ModelPart::Wire) continue;
2602 		if (!itemBase->hasRubberBandLeg()) continue;
2603 
2604 		// 1. we are dragging a part with rubberBand legs which are attached to some part not being dragged along (i.e. a breadboard)
2605 		//		so we stretch those attached legs
2606 		// 2. a part has rubberBand legs attached to multiple parts, and we are only dragging some of the parts
2607 
2608 		foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
2609 			if (!connectorItem->hasRubberBandLeg()) continue;
2610 			if (connectorItem->connectionsCount() == 0) continue;
2611 
2612 			bool treatAsNormal = true;
2613 			foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) {
2614 				if (toConnectorItem->attachedToItemType() == ModelPart::Wire) continue;
2615 				if (m_savedItems.value(toConnectorItem->attachedTo()->layerKinChief()->id(), NULL)) continue;
2616 
2617 				treatAsNormal = false;
2618 				break;
2619 			}
2620 			if (treatAsNormal) continue;
2621 
2622 			if (itemBase->isSelected()) {
2623 				// itemBase is being dragged, but the connector doesn't come along
2624 				connectorItem->prepareToStretch(true);
2625 				m_stretchingLegs.insert(itemBase, connectorItem);
2626 				continue;
2627 			}
2628 
2629 			// itemBase has connectors stuck into multiple parts, not all of which are being dragged
2630 			// but we're in a loop of savedItems, so we have to treat it later
2631 			passives.insert(itemBase);
2632 		}
2633 	}
2634 
2635 	foreach (ItemBase * itemBase, passives) {
2636 		// we're not actually dragging the itemBase
2637 		// one of its connectors is coming along for the ride
2638 		m_savedItems.remove(itemBase->id());
2639 		foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
2640 			if (!connectorItem->hasRubberBandLeg()) continue;
2641 			if (connectorItem->connectionsCount() == 0) continue;
2642 
2643 			foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) {
2644 				if (toConnectorItem->attachedToItemType() == ModelPart::Wire) continue;
2645 				ItemBase * chief = toConnectorItem->attachedTo()->layerKinChief();
2646 				if (m_savedItems.value(chief->id(), NULL) == NULL) {
2647 					// connected to another part
2648 					// treat it as passive as well
2649 				}
2650 
2651 				// the connector is passively dragged along with the part it is connected to
2652 				// but the part it is attached to stays put
2653 				connectorItem->prepareToStretch(false);
2654 				m_stretchingLegs.insert(chief, connectorItem);
2655 				break;
2656 			}
2657 		}
2658 	}
2659 }
2660 
categorizeDragWires(QSet<Wire * > & wires,QList<ItemBase * > & freeWires)2661 void SketchWidget::categorizeDragWires(QSet<Wire *> & wires, QList<ItemBase *> & freeWires)
2662 {
2663 	foreach (Wire * w, wires) {
2664 		QList<Wire *> chainedWires;
2665 		QList<ConnectorItem *> ends;
2666 		w->collectChained(chainedWires, ends);
2667 		foreach (Wire * ww, chainedWires) {
2668 			wires.insert(ww);
2669 		}
2670 	}
2671 
2672 	QList<ConnectionThing *> connectionThings;
2673 	QHash<ItemBase *, ConnectionThing *> ctHash;
2674 	foreach (Wire * w, wires) {
2675 		ConnectionThing * ct = new ConnectionThing;
2676 		ct->wire = w;
2677 		ct->status[0] = ct->status[1] = UNDETERMINED_;
2678 		connectionThings.append(ct);
2679 		ctHash.insert(w, ct);
2680 	}
2681 
2682 	// handle free first
2683 	foreach (Wire * w, wires) {
2684 		if (w->getTrace()) continue;
2685 
2686 		QList<ConnectorItem *> pairs;
2687 		pairs << w->connector0() << w->connector1();
2688 		for (int i = 0; i < pairs.count(); i++) {
2689 			ConnectorItem * ci = pairs.at(i);
2690 			if (ci->connectionsCount() == 0) {
2691 				ConnectionThing * ct = ctHash.value(ci->attachedTo());
2692 				if (ct->status[i] != UNDETERMINED_) continue;
2693 
2694 				// if one end is FREE, treat all connected wires as free (except possibly if the other end connector is attached to something)
2695 				QList<Wire *> chainedWires;
2696 				QList<ConnectorItem *> ends;
2697 				ct->wire->collectChained(chainedWires, ends);
2698 				ends.clear();
2699 				foreach (Wire * ww, chainedWires) {
2700 					ends.append(ww->connector0());
2701 					ends.append(ww->connector1());
2702 				}
2703 				foreach (ConnectorItem * end, ends) {
2704 					if (end->connectionsCount() == 0) {
2705 						ctHash.value(end->attachedTo())->status[i] = FREE_;
2706 					}
2707 					else {
2708 						bool onlyWires = true;
2709 						foreach (ConnectorItem * to, end->connectedToItems()) {
2710 							if (to->attachedToItemType() != ModelPart::Wire) {
2711 								onlyWires = false;
2712 								break;
2713 							}
2714 						}
2715 						if (onlyWires) {
2716 							ctHash.value(end->attachedTo())->status[i] = FREE_;
2717 						}
2718 					}
2719 				}
2720 			}
2721 		}
2722 	}
2723 
2724 	int noChangeCount = 0;
2725 	QList<ItemBase *> outWires;
2726 	while (connectionThings.count() > 0) {
2727 		ConnectionThing * ct = connectionThings.takeFirst();
2728 		bool changed = false;
2729 
2730 		QList<ConnectorItem *> from;
2731 		from.append(ct->wire->connector0());
2732 		from.append(ct->wire->connector1());
2733 		for (int i = 0; i < 2; i++) {
2734 			if (ct->status[i] != UNDETERMINED_) continue;
2735 
2736 			foreach (ConnectorItem * toConnectorItem, from.at(i)->connectedToItems()) {
2737 				if (m_savedItems.keys().contains(toConnectorItem->attachedTo()->layerKinChief()->id())) {
2738 					changed = true;
2739 					ct->status[i] = IN_;
2740 					break;
2741 				}
2742 
2743 				bool notWire = toConnectorItem->attachedToItemType() != ModelPart::Wire;
2744 
2745 				if (notWire || outWires.contains(toConnectorItem->attachedTo())) {
2746 					changed = true;
2747 					ct->status[i] = OUT_;
2748 					break;
2749 				}
2750 			}
2751 			if (ct->status[i] != UNDETERMINED_) continue;
2752 
2753 			ItemBase * stickingTo = ct->wire->stickingTo();
2754 			if (stickingTo != NULL) {
2755 				QPointF p = from.at(i)->sceneAdjustedTerminalPoint(NULL);
2756 				if (stickingTo->contains(stickingTo->mapFromScene(p))) {
2757 					if (m_savedItems.keys().contains(stickingTo->layerKinChief()->id())) {
2758 						ct->status[i] = IN_;
2759 						changed = true;
2760 					}
2761 				}
2762 			}
2763 			if (ct->status[i] != UNDETERMINED_) continue;
2764 
2765 			// it's not connected and not stuck
2766 
2767 			if (ct->wire->getTrace() && from.at(i)->connectionsCount() == 0) {
2768 				QPointF p = from.at(i)->sceneAdjustedTerminalPoint(NULL);
2769 				foreach (QGraphicsItem * item,  scene()->items(p)) {
2770 					ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
2771 					if (itemBase == NULL) continue;
2772 
2773 					ct->status[i] = m_savedItems.keys().contains(itemBase->layerKinChief()->id()) ? IN_ : OUT_;
2774 					changed = true;
2775 					break;
2776 				}
2777 			}
2778 			if (ct->status[i] != UNDETERMINED_) continue;
2779 
2780 			if (from.at(i)->connectionsCount() == 0) {
2781 				DebugDialog::debug("FREE_ wire should not be found at this point");
2782 			}
2783 		}
2784 
2785 		if (ct->status[0] != UNDETERMINED_ && ct->status[1] != UNDETERMINED_) {
2786 			if (ct->status[0] == IN_) {
2787 				if (ct->status[1] == IN_ || ct->status[1] == FREE_) {
2788 					m_savedItems.insert(ct->wire->id(), ct->wire);
2789                     //ct->wire->debugInfo("adding saved free");
2790 					if (ct->status[1] == FREE_) freeWires.append(ct->wire);
2791 				}
2792 				else {
2793 					// attach the connector that stays IN
2794 					m_savedWires.insert(ct->wire, ct->wire->connector0());
2795                     //ct->wire->debugInfo("adding saved in");
2796 				}
2797 			}
2798 			else if (ct->status[0] == OUT_) {
2799 				if (ct->status[1] == IN_) {
2800 					// attach the connector that stays in
2801 					m_savedWires.insert(ct->wire, ct->wire->connector1());
2802 				}
2803 				else {
2804 					// don't drag this; both ends are connected OUT
2805 					outWires.append(ct->wire);
2806 				}
2807 			}
2808 			else /* ct->status[0] == FREE_ */ {
2809 				if (ct->status[1] == IN_) {
2810 					// attach wire that stays IN
2811 					m_savedItems.insert(ct->wire->id(), ct->wire);
2812 					freeWires.append(ct->wire);
2813 				}
2814 				else if (ct->status[1] == FREE_) {
2815 					// both sides are free, so if the wire is selected, drag it
2816 					if (ct->wire->isSelected()) {
2817 						m_savedItems.insert(ct->wire->id(), ct->wire);
2818 						freeWires.append(ct->wire);
2819                         //ct->wire->debugInfo("adding saved free 2");
2820 					}
2821 					else {
2822 						outWires.append(ct->wire);
2823 					}
2824 				}
2825 				else {
2826 					// don't drag this; both ends are connected OUT
2827 					outWires.append(ct->wire);
2828 				}
2829 			}
2830 			delete ct;
2831 			noChangeCount = 0;
2832 		}
2833 		else {
2834 			connectionThings.append(ct);
2835 			if (changed) {
2836 				noChangeCount = 0;
2837 			}
2838 			else {
2839 				if (++noChangeCount > connectionThings.count()) {
2840 					QList<ConnectionThing *> cts;
2841 					foreach (ConnectionThing * ct, connectionThings) {
2842 						// if one end is OUT and the other end is unaccounted for at this pass, then both ends are OUT
2843 						if ((ct->status[0] == FREE_ || ct->status[0] == OUT_) ||
2844 							(ct->status[1] == FREE_ || ct->status[1] == OUT_))
2845 						{
2846 							noChangeCount = 0;
2847 							outWires.append(ct->wire);
2848 							delete ct;
2849 						}
2850 						else {
2851 							cts.append(ct);
2852 						}
2853 					}
2854 					if (noChangeCount == 0) {
2855 						// get ready for another pass, we got rid of some
2856 						connectionThings.clear();
2857 						foreach (ConnectionThing * ct, cts) {
2858 							connectionThings.append(ct);
2859 						}
2860 					}
2861 					else {
2862 						// we've elimated all OUT items so mark everybody IN
2863 						foreach (ConnectionThing * ct, connectionThings) {
2864 							m_savedItems.insert(ct->wire->id(), ct->wire);
2865                             //ct->wire->debugInfo("adding saved in 2");
2866 							delete ct;
2867 						}
2868 						connectionThings.clear();
2869 					}
2870 				}
2871 			}
2872 		}
2873 	}
2874 }
2875 
clickBackground(QMouseEvent *)2876 void SketchWidget::clickBackground(QMouseEvent *)
2877 {
2878 	// in here if you clicked on the sketch itself,
2879 
2880 }
2881 
prepDragWire(Wire * wire)2882 void SketchWidget::prepDragWire(Wire * wire)
2883 {
2884 	bool drag = true;
2885 	foreach (ConnectorItem * toConnectorItem, wire->connector0()->connectedToItems()) {
2886 		if (toConnectorItem->attachedToItemType() == ModelPart::Wire) {
2887 			m_savedWires.insert(qobject_cast<Wire *>(toConnectorItem->attachedTo()), toConnectorItem);
2888 		}
2889 		else {
2890 			drag = false;
2891 			break;
2892 		}
2893 	}
2894 	if (drag) {
2895 		foreach (ConnectorItem * toConnectorItem, wire->connector1()->connectedToItems()) {
2896 			if (toConnectorItem->attachedToItemType() == ModelPart::Wire) {
2897 				m_savedWires.insert(qobject_cast<Wire *>(toConnectorItem->attachedTo()), toConnectorItem);
2898 			}
2899 			else {
2900 				drag = false;
2901 				break;
2902 			}
2903 		}
2904 	}
2905 	if (!drag) {
2906 		m_savedWires.clear();
2907 		return;
2908 	}
2909 
2910 	m_savedItems.clear();
2911 	m_savedItems.insert(wire->id(), wire);
2912     //wire->debugInfo("adding saved wire");
2913 	wire->saveGeometry();
2914 	foreach (Wire * w, m_savedWires.keys()) {
2915 		w->saveGeometry();
2916 	}
2917 	setupAutoscroll(true);
2918 }
2919 
prepDragBendpoint(Wire * wire,QPoint eventPos,bool dragCurve)2920 void SketchWidget::prepDragBendpoint(Wire * wire, QPoint eventPos, bool dragCurve)
2921 {
2922 	m_bendpointWire = wire;
2923 	wire->saveGeometry();
2924 	ViewGeometry vg = m_bendpointVG = wire->getViewGeometry();
2925 	QPointF newPos = mapToScene(eventPos);
2926 
2927 	if (dragCurve) {
2928 		setupAutoscroll(true);
2929 		wire->initDragCurve(newPos);
2930 		wire->grabMouse();
2931         unsquashShapes();
2932 		return;
2933 	}
2934 
2935 	QPointF oldPos = wire->pos();
2936 	QLineF oldLine = wire->line();
2937 	Bezier left, right;
2938 	bool curved = wire->initNewBendpoint(newPos, left, right);
2939 	//DebugDialog::debug(QString("oldpos"), oldPos);
2940 	//DebugDialog::debug(QString("oldline p1"), oldLine.p1());
2941 	//DebugDialog::debug(QString("oldline p2"), oldLine.p2());
2942 	QLineF newLine(oldLine.p1(), newPos - oldPos);
2943 	wire->setLine(newLine);
2944 	if (curved) wire->changeCurve(&left);
2945 	vg.setLoc(newPos);
2946 	QLineF newLine2(QPointF(0,0), oldLine.p2() + oldPos - newPos);
2947 	vg.setLine(newLine2);
2948 	ConnectorItem * oldConnector1 = wire->connector1();
2949 	m_connectorDragWire = this->createTempWireForDragging(wire, wire->modelPart(), oldConnector1, vg, wire->viewLayerPlacement());
2950 	if (curved) {
2951 		right.translateToZero();
2952 		m_connectorDragWire->changeCurve(&right);
2953 	}
2954 	ConnectorItem * newConnector1 = m_connectorDragWire->connector1();
2955 	foreach (ConnectorItem * toConnectorItem, oldConnector1->connectedToItems()) {
2956 		oldConnector1->tempRemove(toConnectorItem, false);
2957 		toConnectorItem->tempRemove(oldConnector1, false);
2958 		newConnector1->tempConnectTo(toConnectorItem, false);
2959 		toConnectorItem->tempConnectTo(newConnector1, false);
2960 	}
2961 	oldConnector1->tempConnectTo(m_connectorDragWire->connector0(), false);
2962 	m_connectorDragWire->connector0()->tempConnectTo(oldConnector1, false);
2963 	m_connectorDragConnector = oldConnector1;
2964 
2965 	setupAutoscroll(true);
2966 
2967 	m_connectorDragWire->initDragEnd(m_connectorDragWire->connector0(), newPos);
2968 	m_connectorDragWire->grabMouse();
2969     unsquashShapes();
2970     //m_connectorDragWire->debugInfo("grabbing mouse");
2971 }
2972 
collectFemaleConnectees(ItemBase * itemBase,QSet<ItemBase * > & items)2973 bool SketchWidget::collectFemaleConnectees(ItemBase * itemBase, QSet<ItemBase *> & items) {
2974 	Q_UNUSED(itemBase);
2975 	Q_UNUSED(items);
2976 	return false;
2977 }
2978 
checkUnder()2979 bool SketchWidget::checkUnder() {
2980 	return false;
2981 };
2982 
draggingWireEnd()2983 bool SketchWidget::draggingWireEnd() {
2984 	if (m_connectorDragWire != NULL) return true;
2985 
2986 	QGraphicsItem * mouseGrabberItem = scene()->mouseGrabberItem();
2987 	Wire * wire = dynamic_cast<Wire *>(mouseGrabberItem);
2988 	if (wire == NULL) {
2989 		ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(mouseGrabberItem);
2990 		if (connectorItem == NULL) return false;
2991 		if (connectorItem->attachedToItemType() != ModelPart::Wire) return false;
2992 
2993 		wire = qobject_cast<Wire *>(connectorItem->attachedTo());
2994 	}
2995 
2996     // wire->debugInfo("mouse grabber");
2997 	return wire->draggingEnd();
2998 }
2999 
mouseMoveEvent(QMouseEvent * event)3000 void SketchWidget::mouseMoveEvent(QMouseEvent *event) {
3001 	// if its just dragging a wire end do default
3002 	// otherwise handle all move action here
3003 
3004 	if (m_movingByArrow) return;
3005 
3006 	QPointF scenePos = mapToScene(event->pos());
3007 
3008 	double posx = scenePos.x() / GraphicsUtils::SVGDPI;
3009 	double posy = scenePos.y() / GraphicsUtils::SVGDPI;
3010 
3011 	if ( event->buttons() & (Qt::LeftButton | Qt::RightButton ) ) {
3012 		QRectF selectionRect = this->scene()->selectionArea().boundingRect();
3013 		double width = selectionRect.width() / GraphicsUtils::SVGDPI;
3014 		double height = selectionRect.height() / GraphicsUtils::SVGDPI;
3015 		emit cursorLocationSignal(posx, posy, width, height);
3016 		//DebugDialog::debug(QString("pos= %1,%2  size= %3 %4").arg(posx).arg(posy).arg(width).arg(height));
3017 	} else {
3018 		emit cursorLocationSignal(posx, posy);
3019 		//DebugDialog::debug(QString("pos= %1,%2").arg(posx).arg(posy));
3020 	}
3021 
3022 	if (m_dragBendpointWire != NULL) {
3023 		Wire * tempWire = m_dragBendpointWire;
3024 		m_dragBendpointWire = NULL;
3025 		prepDragBendpoint(tempWire, m_dragBendpointPos, m_dragCurve);
3026 		m_draggingBendpoint = true;
3027         DebugDialog::debug("dragging bendpoint");
3028 		this->m_alignmentStartPoint = mapToScene(m_dragBendpointPos);		// not sure this will be correct...
3029 		return;
3030 	}
3031 
3032 	if (m_spaceBarWasPressed) {
3033 		InfoGraphicsView::mouseMoveEvent(event);
3034 		return;
3035 	}
3036 
3037 	if (m_savedItems.count() > 0) {
3038 		if ((event->buttons() & Qt::LeftButton) && !draggingWireEnd()) {
3039 			m_globalPos = event->globalPos();
3040 			if ((m_globalPos - m_mousePressGlobalPos).manhattanLength() >= QApplication::startDragDistance()) {
3041 				QMimeData *mimeData = new QMimeData;
3042 				mimeData->setData("application/x-dndsketchdata", NULL);
3043 
3044 				QDrag * drag = new QDrag(this);
3045 				drag->setMimeData(mimeData);
3046 				//QBitmap bitmap = *CursorMaster::MoveCursor->bitmap();
3047 				//drag->setDragCursor(bitmap, Qt::MoveAction);
3048 
3049 				QPointF offset;
3050 				QString svg = makeMoveSVG(GraphicsUtils::SVGDPI,  GraphicsUtils::StandardFritzingDPI, offset);
3051 				m_movingSVGRenderer = new QSvgRenderer(new QXmlStreamReader(svg));
3052 				m_movingSVGOffset = m_mousePressScenePos - offset;
3053 
3054 				m_moveEventCount = 0;					// reset m_moveEventCount to make sure that equal potential highlights are cleared
3055 				m_movingByMouse = false;
3056 
3057 				drag->exec();
3058 
3059                 delete m_movingSVGRenderer;
3060 				m_movingSVGRenderer = NULL;
3061 				return;
3062 			}
3063 		}
3064 	}
3065 
3066 	m_moveEventCount++;
3067 	if (m_alignToGrid && m_draggingBendpoint) {
3068 		QPointF sp(scenePos);
3069 		alignLoc(sp, sp, QPointF(0,0), QPointF(0,0));
3070 		QPointF p = mapFromScene(sp);
3071 		QPoint pp(qRound(p.x()), qRound(p.y()));
3072 		QPointF q = mapToGlobal(pp);
3073 		QMouseEvent alignedEvent(event->type(), pp, QPoint(q.x(), q.y()), event->button(), event->buttons(), event->modifiers());
3074 
3075 		//DebugDialog::debug(QString("sketch move event %1,%2").arg(sp.x()).arg(sp.y()));
3076 		QGraphicsView::mouseMoveEvent(&alignedEvent);
3077 		return;
3078 	}
3079 
3080 	if (draggingWireEnd()) {
3081        // DebugDialog::debug("dragging wire end");
3082 		checkAutoscroll(event->globalPos());
3083 	}
3084 
3085     QList<ItemBase *> squashed;
3086 	if (event->buttons() == Qt::NoButton) {
3087         squashShapes(scenePos);
3088     }
3089 
3090 	QGraphicsView::mouseMoveEvent(event);
3091     unsquashShapes();
3092 }
3093 
makeMoveSVG(double printerScale,double dpi,QPointF & offset)3094 QString SketchWidget::makeMoveSVG(double printerScale, double dpi, QPointF & offset)
3095 {
3096 
3097 	QRectF itemsBoundingRect;
3098 	foreach (ItemBase * itemBase, m_savedItems.values()) {
3099 		itemsBoundingRect |= itemBase->sceneBoundingRect();
3100 	}
3101 
3102 	double width = itemsBoundingRect.width();
3103 	double height = itemsBoundingRect.height();
3104 	offset = itemsBoundingRect.topLeft();
3105 
3106 	QString outputSVG = TextUtils::makeSVGHeader(printerScale, dpi, width, height);
3107 
3108 	foreach (ItemBase * itemBase, m_savedItems.values()) {
3109 		Wire * wire = qobject_cast<Wire *>(itemBase);
3110 		if (wire != NULL) {
3111 			outputSVG.append(makeWireSVG(wire, offset, dpi, printerScale, true));
3112 		}
3113 		else {
3114 			outputSVG.append(TextUtils::makeRectSVG(itemBase->sceneBoundingRect(), offset, dpi, printerScale));
3115 		}
3116 	}
3117 	//outputSVG.append(makeRectSVG(itemsBoundingRect, offset, dpi, printerScale));
3118 
3119 	outputSVG += "</svg>";
3120 
3121 	return outputSVG;
3122 }
3123 
3124 
3125 
moveItems(QPoint globalPos,bool checkAutoScrollFlag,bool rubberBandLegEnabled)3126 void SketchWidget::moveItems(QPoint globalPos, bool checkAutoScrollFlag, bool rubberBandLegEnabled)
3127 {
3128 	if (checkAutoScrollFlag) {
3129 		bool result = checkAutoscroll(globalPos);
3130 		if (!result) return;
3131 	}
3132 
3133 	QPoint q = mapFromGlobal(globalPos);
3134 	QPointF scenePos = mapToScene(q);
3135 
3136 	if (m_alignToGrid && (m_alignmentItem != NULL)) {
3137 		QPointF currentParentPos = m_alignmentItem->mapToParent(m_alignmentItem->mapFromScene(scenePos));
3138 		QPointF buttonDownParentPos = m_alignmentItem->mapToParent(m_alignmentItem->mapFromScene(m_mousePressScenePos));
3139 		alignLoc(scenePos, m_alignmentStartPoint, currentParentPos, buttonDownParentPos);
3140 	}
3141 
3142 /*
3143 	DebugDialog::debug(QString("scroll 1 sx:%1 sy:%2 sbx:%3 sby:%4 qx:%5 qy:%6")
3144 		.arg(scenePos.x()).arg(scenePos.y())
3145 		.arg(m_mousePressScenePos.x()).arg(m_mousePressScenePos.y())
3146 		.arg(q.x()).arg(q.y())
3147 		);
3148 */
3149 
3150 	if (m_moveEventCount == 0) {
3151 		// first time
3152 		m_moveDisconnectedFromFemale.clear();
3153 		foreach (ItemBase * item, m_savedItems) {
3154 			if (item->itemType() == ModelPart::Wire) continue;
3155 
3156 			//DebugDialog::debug(QString("disconnecting from female %1").arg(item->instanceTitle()));
3157 			disconnectFromFemale(item, m_savedItems, m_moveDisconnectedFromFemale, false, rubberBandLegEnabled, NULL);
3158 		}
3159 	}
3160 
3161 	foreach (ItemBase * itemBase, m_savedItems) {
3162 		QPointF currentParentPos = itemBase->mapToParent(itemBase->mapFromScene(scenePos));
3163 		QPointF buttonDownParentPos = itemBase->mapToParent(itemBase->mapFromScene(m_mousePressScenePos));
3164 		itemBase->setPos(itemBase->getViewGeometry().loc() + currentParentPos - buttonDownParentPos);
3165 		foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) {
3166 			connectorItem->stretchBy(currentParentPos - buttonDownParentPos);
3167 		}
3168 
3169 		if (m_checkUnder.contains(itemBase)) {
3170 			findConnectorsUnder(itemBase);
3171 		}
3172 
3173 /*
3174 		DebugDialog::debug(QString("scroll 2 lx:%1 ly:%2 cpx:%3 cpy:%4 qx:%5 qy:%6 px:%7 py:%8")
3175 		.arg(item->getViewGeometry().loc().x()).arg(item->getViewGeometry().loc().y())
3176 		.arg(currentParentPos.x()).arg(currentParentPos.y())
3177 		.arg(buttonDownParentPos.x()).arg(buttonDownParentPos.y())
3178 		.arg(item->pos().x()).arg(item->pos().y())
3179 		);
3180 */
3181 
3182 	}
3183 
3184 	foreach (Wire * wire, m_savedWires.keys()) {
3185 		wire->simpleConnectedMoved(m_savedWires.value(wire));
3186 	}
3187 
3188 	//DebugDialog::debug(QString("done move items %1").arg(QTime::currentTime().msec()) );
3189 
3190     if (m_infoView) {
3191         if (m_originatingItem) {
3192             m_infoView->updateLocation(m_originatingItem->layerKinChief());
3193         }
3194         else {
3195             foreach (ItemBase * itemBase, m_savedItems) {
3196                 m_infoView->updateLocation(itemBase->layerKinChief());
3197             }
3198         }
3199     }
3200 }
3201 
3202 
findConnectorsUnder(ItemBase * item)3203 void SketchWidget::findConnectorsUnder(ItemBase * item) {
3204 	Q_UNUSED(item);
3205 }
3206 
mouseReleaseEvent(QMouseEvent * event)3207 void SketchWidget::mouseReleaseEvent(QMouseEvent *event) {
3208 	//setRenderHint(QPainter::Antialiasing, true);
3209 
3210     //DebugDialog::debug("sketch mouse release event");
3211 
3212 	m_draggingBendpoint = false;
3213 	if (m_movingByArrow) return;
3214 
3215 	m_alignmentItem = NULL;
3216 	m_movingByMouse = false;
3217 
3218 	m_dragBendpointWire = NULL;
3219 
3220 	ConnectorItem::clearEqualPotentialDisplay();
3221 
3222 	if (m_spaceBarWasPressed) {
3223 
3224 		QMouseEvent * hackEvent = NULL;
3225 		if (m_middleMouseIsPressed) {
3226 		// make the event look like a left button press to fool the underlying drag mode implementation
3227 			event = hackEvent = new QMouseEvent(event->type(), event->pos(), event->globalPos(), Qt::LeftButton, event->buttons() | Qt::LeftButton, event->modifiers());
3228 		}
3229 
3230 		InfoGraphicsView::mouseReleaseEvent(event);
3231 		m_spaceBarWasPressed = false;
3232 		if (m_middleMouseIsPressed) {
3233 			m_middleMouseIsPressed = false;
3234 			setDragMode(QGraphicsView::RubberBandDrag);
3235 			setCursor(Qt::ArrowCursor);
3236 		}
3237 
3238 		//DebugDialog::debug("turning off spacebar was");
3239 		if (hackEvent) delete hackEvent;
3240 		return;
3241 	}
3242 
3243 	if (resizingBoardRelease()) {
3244 		InfoGraphicsView::mouseReleaseEvent(event);
3245 		return;
3246 	}
3247 
3248 	if (resizingJumperItemRelease()) {
3249 		InfoGraphicsView::mouseReleaseEvent(event);
3250 		return;
3251 	}
3252 
3253 	turnOffAutoscroll();
3254 
3255 	QGraphicsView::mouseReleaseEvent(event);
3256 
3257 	if (m_connectorDragWire != NULL) {
3258 		// remove again (may not have been removed earlier)
3259 		if (m_connectorDragWire->scene() != NULL) {
3260             removeDragWire();
3261 			//m_infoView->unregisterCurrentItem();
3262 			updateInfoView();
3263 
3264 		}
3265 
3266 		if (m_bendpointWire) {
3267 			// click on wire but no drag:  restore original state of wire
3268 			foreach (ConnectorItem * toConnectorItem, m_connectorDragWire->connector1()->connectedToItems()) {
3269 				m_connectorDragWire->connector1()->tempRemove(toConnectorItem, false);
3270 				toConnectorItem->tempRemove(m_connectorDragWire->connector1(), false);
3271 				m_bendpointWire->connector1()->tempConnectTo(toConnectorItem, false);
3272 				toConnectorItem->tempConnectTo(m_bendpointWire->connector1(), false);
3273 			}
3274 			m_bendpointWire->connector1()->tempRemove(m_connectorDragWire->connector0(), false);
3275 			m_connectorDragWire->connector0()->tempRemove(m_bendpointWire->connector1(), false);
3276 			m_bendpointWire->setLine(m_bendpointVG.line());
3277 		}
3278 
3279 		DebugDialog::debug("deleting connector drag wire");
3280 		delete m_connectorDragWire;
3281 
3282 		m_bendpointWire = m_connectorDragWire = NULL;
3283 		m_savedItems.clear();
3284 		m_savedWires.clear();
3285 		m_connectorDragConnector = NULL;
3286 		return;
3287 	}
3288 
3289     // make sure this is cleared
3290     m_bendpointWire = NULL;
3291 
3292 	if (m_moveEventCount == 0) {
3293 		if (this->m_holdingSelectItemCommand != NULL) {
3294 			if (m_holdingSelectItemCommand->updated()) {
3295 				SelectItemCommand* tempCommand = m_holdingSelectItemCommand;
3296 				m_holdingSelectItemCommand = NULL;
3297 				//DebugDialog::debug(QString("scene changed push select %1").arg(scene()->selectedItems().count()));
3298 				m_undoStack->push(tempCommand);
3299 			}
3300 			else {
3301 				clearHoldingSelectItem();
3302 			}
3303 		}
3304 	}
3305 	m_savedItems.clear();
3306 	m_savedWires.clear();
3307 }
3308 
checkMoved(bool wait)3309 bool SketchWidget::checkMoved(bool wait)
3310 {
3311 	if (m_moveEventCount == 0) {
3312 		return false;
3313 	}
3314 
3315 	int moveCount = m_savedItems.count();
3316 	if (moveCount <= 0) {
3317 		return false;
3318 	}
3319 
3320 	ItemBase * saveBase = NULL;
3321 	foreach (ItemBase * item, m_savedItems) {
3322 		saveBase = item;
3323 		break;
3324 	}
3325 
3326 	clearHoldingSelectItem();
3327 
3328 	QString moveString;
3329 	QString viewName = ViewLayer::viewIDName(m_viewID);
3330 
3331 	if (moveCount == 1) {
3332 		moveString = tr("Move %2 (%1)").arg(viewName).arg(saveBase->title());
3333 	}
3334 	else {
3335 		moveString = tr("Move %2 items (%1)").arg(viewName).arg(QString::number(moveCount));
3336 	}
3337 
3338 	QUndoCommand * parentCommand = new QUndoCommand(moveString);
3339 
3340 	bool hasBoard = false;
3341 
3342 	foreach (ItemBase * item, m_savedItems) {
3343 		rememberSticky(item, parentCommand);
3344 	}
3345 
3346 	CleanUpWiresCommand * cuw = new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3347 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3348 
3349 	moveLegBendpoints(true, parentCommand);
3350 
3351 	bool gotConnection = true;
3352 
3353 	MoveItemsCommand * moveItemsCommand = new MoveItemsCommand(this, true, parentCommand);
3354 
3355 	foreach (ItemBase * item, m_savedItems) {
3356 		if (item == NULL) continue;
3357 
3358 		ViewGeometry viewGeometry(item->getViewGeometry());
3359 		item->saveGeometry();
3360 
3361 		moveItemsCommand->addItem(item->id(), viewGeometry.loc(), item->getViewGeometry().loc());
3362 
3363 		if (item->itemType() == ModelPart::Breadboard) {
3364 			hasBoard = true;
3365 			continue;
3366 		}
3367 
3368 		// TODO: boardtypes and breadboard types are always sticky
3369         if (Board::isBoard(item)) {
3370 			hasBoard = true;
3371 		}
3372 	}
3373 
3374 	foreach (ItemBase * item, m_savedItems) {
3375 		new CheckStickyCommand(this, BaseCommand::SingleView, item->id(), false, CheckStickyCommand::RedoOnly, parentCommand);
3376 	}
3377 
3378 	foreach (ItemBase * item, m_savedWires.keys()) {
3379 		rememberSticky(item, parentCommand);
3380 	}
3381 
3382 	foreach (Wire * wire, m_savedWires.keys()) {
3383 		if (wire == NULL) continue;
3384 
3385 		moveItemsCommand->addWire(wire->id(), m_savedWires.value(wire)->connectorSharedID());
3386 	}
3387 
3388 	foreach (ItemBase * item, m_savedWires.keys()) {
3389 		new CheckStickyCommand(this, BaseCommand::SingleView, item->id(), false, CheckStickyCommand::RedoOnly, parentCommand);
3390 	}
3391 
3392 	foreach (ConnectorItem * fromConnectorItem, m_moveDisconnectedFromFemale.uniqueKeys()) {
3393 		foreach (ConnectorItem * toConnectorItem, m_moveDisconnectedFromFemale.values(fromConnectorItem)) {
3394 			extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem, ViewLayer::specFromID(fromConnectorItem->attachedToViewLayerID()), false, parentCommand);
3395 			gotConnection = true;
3396 		}
3397 	}
3398 
3399 	QList<ConnectorItem *> restoreConnectorItems;
3400 	foreach (ItemBase * item, m_savedItems) {
3401 		foreach (ConnectorItem * fromConnectorItem, item->cachedConnectorItems()) {
3402 			if (item->itemType() == ModelPart::Wire) {
3403 				if (fromConnectorItem->connectionsCount() > 0) {
3404 					continue;
3405 				}
3406 			}
3407 
3408 			ConnectorItem * toConnectorItem = fromConnectorItem->overConnectorItem();
3409 			if (toConnectorItem != NULL) {
3410 				toConnectorItem->connectorHover(item, false);
3411 				fromConnectorItem->setOverConnectorItem(NULL);   // clean up
3412 				gotConnection = true;
3413 				extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem,
3414 					ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()),
3415 					true, parentCommand);
3416 			}
3417 			restoreConnectorItems.append(fromConnectorItem);
3418 			fromConnectorItem->clearConnectorHover();
3419 		}
3420 
3421 		item->clearConnectorHover();
3422 	}
3423 
3424     QList<ConnectorItem *> visited;
3425 	foreach (ConnectorItem * connectorItem, restoreConnectorItems) {
3426 		connectorItem->restoreColor(visited);
3427 	}
3428 
3429 	// must restore legs after connections are restored (redo direction)
3430 	moveLegBendpoints(false, parentCommand);
3431 
3432 	clearTemporaries();
3433 
3434 	if (gotConnection) {
3435 	    new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
3436 		new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
3437 		cuw->setDirection(CleanUpWiresCommand::UndoOnly);
3438 	}
3439     if (wait) {
3440         m_undoStack->waitPush(parentCommand, PropChangeDelay);
3441     }
3442     else {
3443 	    m_undoStack->push(parentCommand);
3444     }
3445 
3446 	return true;
3447 }
3448 
setReferenceModel(ReferenceModel * referenceModel)3449 void SketchWidget::setReferenceModel(ReferenceModel *referenceModel) {
3450 	m_referenceModel = referenceModel;
3451 }
3452 
referenceModel()3453 ReferenceModel * SketchWidget::referenceModel() {
3454 	return m_referenceModel;
3455 }
3456 
setSketchModel(SketchModel * sketchModel)3457 void SketchWidget::setSketchModel(SketchModel * sketchModel) {
3458 	m_sketchModel = sketchModel;
3459 }
3460 
itemAddedSlot(ModelPart * modelPart,ItemBase *,ViewLayer::ViewLayerPlacement viewLayerPlacement,const ViewGeometry & viewGeometry,long id,SketchWidget * dropOrigin)3461 void SketchWidget::itemAddedSlot(ModelPart * modelPart, ItemBase *, ViewLayer::ViewLayerPlacement viewLayerPlacement, const ViewGeometry & viewGeometry, long id, SketchWidget * dropOrigin) {
3462 	if (dropOrigin != NULL && dropOrigin != this) {
3463 		placePartDroppedInOtherView(modelPart, viewLayerPlacement, viewGeometry, id, dropOrigin);
3464 	}
3465 	else {
3466 		addItemAux(modelPart, viewLayerPlacement, viewGeometry, id, true, m_viewID, false);
3467 	}
3468 }
3469 
placePartDroppedInOtherView(ModelPart * modelPart,ViewLayer::ViewLayerPlacement viewLayerPlacement,const ViewGeometry & viewGeometry,long id,SketchWidget * dropOrigin)3470 ItemBase * SketchWidget::placePartDroppedInOtherView(ModelPart * modelPart, ViewLayer::ViewLayerPlacement viewLayerPlacement, const ViewGeometry & viewGeometry, long id, SketchWidget * dropOrigin)
3471 {
3472 	// offset the part
3473 	QPointF from = dropOrigin->mapToScene(QPoint(0, 0));
3474 	QPointF to = this->mapToScene(QPoint(0, 0));
3475 	QPointF dp = viewGeometry.loc() - from;
3476 	ViewGeometry vg(viewGeometry);
3477 	vg.setLoc(to + dp);
3478 	ItemBase * itemBase = addItemAux(modelPart, viewLayerPlacement, vg, id, true, m_viewID, false);
3479 	if (m_alignToGrid && (itemBase != NULL)) {
3480 		alignOneToGrid(itemBase);
3481 	}
3482 
3483 	return itemBase;
3484 }
3485 
itemDeletedSlot(long id)3486 void SketchWidget::itemDeletedSlot(long id) {
3487 	ItemBase * pitem = findItem(id);
3488 	if (pitem != NULL) {
3489 		deleteItem(pitem, false, false, false);
3490 	}
3491 }
3492 
selectionChangedSlot()3493 void SketchWidget::selectionChangedSlot() {
3494 	if (m_ignoreSelectionChangeEvents > 0) {
3495 		return;
3496 	}
3497 
3498 	emit selectionChangedSignal();
3499 
3500 	if (m_holdingSelectItemCommand != NULL) {
3501 		//DebugDialog::debug("update holding command");
3502 
3503 		int selCount = 0;
3504 		ItemBase* saveBase = NULL;
3505 		QString selString;
3506 		m_holdingSelectItemCommand->clearRedo();
3507 		const QList<QGraphicsItem *> sitems = scene()->selectedItems();
3508 		foreach (QGraphicsItem * item, scene()->selectedItems()) {
3509 	 		ItemBase * base = dynamic_cast<ItemBase *>(item);
3510 	 		if (base == NULL) continue;
3511 
3512 			saveBase = base;
3513 	 		m_holdingSelectItemCommand->addRedo(base->layerKinChief()->id());
3514 	 		selCount++;
3515 	    }
3516 		if (selCount == 1) {
3517 			selString = tr("Select %1").arg(saveBase->title());
3518 		}
3519 		else {
3520 			selString = tr("Select %1 items").arg(QString::number(selCount));
3521 		}
3522 		m_holdingSelectItemCommand->setText(selString);
3523 		m_holdingSelectItemCommand->setUpdated(true);
3524 	}
3525 }
3526 
clearHoldingSelectItem()3527 void SketchWidget::clearHoldingSelectItem() {
3528 	// DebugDialog::debug("clear holding");
3529 	if (m_holdingSelectItemCommand != NULL) {
3530 		delete m_holdingSelectItemCommand;
3531 		m_holdingSelectItemCommand = NULL;
3532 	}
3533 }
3534 
clearSelection()3535 void SketchWidget::clearSelection() {
3536 	this->scene()->clearSelection();
3537 	emit clearSelectionSignal();
3538 }
3539 
clearSelectionSlot()3540 void SketchWidget::clearSelectionSlot() {
3541 	this->scene()->clearSelection();
3542 }
3543 
itemSelectedSlot(long id,bool state)3544 void SketchWidget::itemSelectedSlot(long id, bool state) {
3545 	ItemBase * item = findItem(id);
3546 	//DebugDialog::debug(QString("got item selected signal %1 %2 %3 %4").arg(id).arg(state).arg(item != NULL).arg(m_viewID));
3547 	if (item != NULL) {
3548 		item->setSelected(state);
3549 	}
3550 
3551 	PaletteItem *pitem = dynamic_cast<PaletteItem*>(item);
3552 	if(pitem) {
3553 		setLastPaletteItemSelected(pitem);
3554 	}
3555 }
3556 
3557 
prepLegSelection(ItemBase * itemBase)3558 void SketchWidget::prepLegSelection(ItemBase * itemBase)
3559 {
3560 	this->clearHoldingSelectItem();
3561 	this->m_moveEventCount = 0;  // clear this so an extra MoveItemCommand isn't posted
3562 
3563 	if (itemBase->isSelected()) return;
3564 
3565 	m_holdingSelectItemCommand = stackSelectionState(false, NULL);
3566 	itemBase->setSelected(true);
3567 }
3568 
prepLegBendpointMove(ConnectorItem * from,int index,QPointF oldPos,QPointF newPos,ConnectorItem * to,bool changeConnections)3569 void SketchWidget::prepLegBendpointMove(ConnectorItem * from, int index, QPointF oldPos, QPointF newPos, ConnectorItem * to, bool changeConnections)
3570 {
3571 	this->m_moveEventCount = 0;  // clear this so an extra MoveItemCommand isn't posted
3572 
3573 	QUndoCommand * parentCommand = new QUndoCommand();
3574 
3575 	if (m_holdingSelectItemCommand) {
3576 		SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
3577 		selectItemCommand->copyUndo(m_holdingSelectItemCommand);
3578 		selectItemCommand->copyRedo(m_holdingSelectItemCommand);
3579 		clearHoldingSelectItem();
3580 	}
3581 
3582 	long fromID = from->attachedToID();
3583 
3584 	QString fromConnectorID = from->connectorSharedID();
3585 
3586 	long toID = -1;
3587 	QString toConnectorID;
3588 	if (changeConnections && (to != NULL)) {
3589 		toID = to->attachedToID();
3590 		toConnectorID = to->connectorSharedID();
3591 	}
3592 
3593 	if (changeConnections) {
3594 		new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3595 	    new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3596 	}
3597 
3598 	MoveLegBendpointCommand * mlbc = new MoveLegBendpointCommand(this, fromID, fromConnectorID, index, oldPos, newPos, parentCommand);
3599 	mlbc->setUndoOnly();
3600 
3601 	if (changeConnections) {
3602 		QList< QPointer<ConnectorItem> > former = from->connectedToItems();
3603 
3604 		QString prefix;
3605 		QString suffix;
3606 		if (to == NULL) {
3607 			if (former.count() > 0) {
3608 				prefix = tr("Disconnect");
3609 				suffix = tr("from %1").arg(former.at(0)->attachedToInstanceTitle());
3610 			}
3611 			else {
3612 				prefix = tr("Move leg of");
3613 			}
3614 		}
3615 		else {
3616 			prefix = tr("Connect");
3617 			suffix = tr("to %1").arg(to->attachedToInstanceTitle());
3618 		}
3619 
3620 		parentCommand->setText(QObject::tr("%1 %2,%3 %4")
3621 				.arg(prefix)
3622 				.arg(from->attachedTo()->instanceTitle())
3623 				.arg(from->connectorSharedName())
3624 				.arg(suffix)
3625 				);
3626 
3627 
3628 		if (former.count() > 0) {
3629 			QList<ConnectorItem *> connectorItems;
3630 			connectorItems.append(from);
3631 			ConnectorItem::collectEqualPotential(connectorItems, true, ViewGeometry::RatsnestFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag);
3632 
3633 			foreach (ConnectorItem * formerConnectorItem, former) {
3634 				ChangeConnectionCommand * ccc = extendChangeConnectionCommand(BaseCommand::CrossView, from, formerConnectorItem,
3635 												ViewLayer::specFromID(from->attachedToViewLayerID()),
3636 												false, parentCommand);
3637 				ccc->setUpdateConnections(false);
3638 				from->tempRemove(formerConnectorItem, false);
3639 				formerConnectorItem->tempRemove(from, false);
3640 			}
3641 
3642 		}
3643 		if (to != NULL) {
3644 			ChangeConnectionCommand * ccc = extendChangeConnectionCommand(BaseCommand::CrossView, from, to, ViewLayer::specFromID(from->attachedToViewLayerID()), true, parentCommand);
3645 			ccc->setUpdateConnections(false);
3646 		}
3647 	}
3648 	else {
3649 		parentCommand->setText(QObject::tr("Change leg of %1,%2")
3650 				.arg(from->attachedTo()->instanceTitle())
3651 				.arg(from->connectorSharedName())
3652 			);
3653 	}
3654 
3655 	// change leg after connections have been restored
3656 	mlbc = new MoveLegBendpointCommand(this, fromID, fromConnectorID, index, oldPos, newPos, parentCommand);
3657 	mlbc->setRedoOnly();
3658 
3659 
3660 	if (changeConnections) {
3661 	    new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
3662 		new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
3663 	}
3664 
3665 	m_undoStack->push(parentCommand);
3666 }
3667 
prepLegCurveChange(ConnectorItem * from,int index,const class Bezier * oldB,const class Bezier * newB,bool triggerFirstTime)3668 void SketchWidget::prepLegCurveChange(ConnectorItem * from, int index, const class Bezier * oldB, const class Bezier * newB, bool triggerFirstTime)
3669 {
3670 	this->m_moveEventCount = 0;  // clear this so an extra MoveItemCommand isn't posted
3671 
3672 	QUndoCommand * parentCommand = new QUndoCommand(tr("Change leg curvature for %1.").arg(from->attachedToInstanceTitle()));
3673 
3674 	if (m_holdingSelectItemCommand) {
3675 		SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
3676 		selectItemCommand->copyUndo(m_holdingSelectItemCommand);
3677 		selectItemCommand->copyRedo(m_holdingSelectItemCommand);
3678 		clearHoldingSelectItem();
3679 	}
3680 
3681 	long fromID = from->attachedToID();
3682 
3683 	QString fromConnectorID = from->connectorSharedID();
3684 
3685 	ChangeLegCurveCommand * clcc = new ChangeLegCurveCommand(this, fromID, fromConnectorID, index, oldB, newB, parentCommand);
3686 	if (!triggerFirstTime) {
3687 		clcc->setSkipFirstRedo();
3688 	}
3689 
3690 	m_undoStack->push(parentCommand);
3691 }
3692 
prepLegBendpointChange(ConnectorItem * from,int oldCount,int newCount,int index,QPointF p,const class Bezier * bezier0,const class Bezier * bezier1,const class Bezier * bezier2,bool triggerFirstTime)3693 void SketchWidget::prepLegBendpointChange(ConnectorItem * from, int oldCount, int newCount, int index, QPointF p,
3694 					const class Bezier * bezier0, const class Bezier * bezier1, const class Bezier * bezier2, bool triggerFirstTime)
3695 {
3696 	this->m_moveEventCount = 0;  // clear this so an extra MoveItemCommand isn't posted
3697 
3698 	QUndoCommand * parentCommand = new QUndoCommand(tr("Change leg bendpoint for %1.").arg(from->attachedToInstanceTitle()));
3699 
3700 	if (m_holdingSelectItemCommand) {
3701 		SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
3702 		selectItemCommand->copyUndo(m_holdingSelectItemCommand);
3703 		selectItemCommand->copyRedo(m_holdingSelectItemCommand);
3704 		clearHoldingSelectItem();
3705 	}
3706 
3707 	long fromID = from->attachedToID();
3708 
3709 	QString fromConnectorID = from->connectorSharedID();
3710 
3711 	ChangeLegBendpointCommand * clbc = new ChangeLegBendpointCommand(this, fromID, fromConnectorID, oldCount, newCount, index, p, bezier0, bezier1, bezier2, parentCommand);
3712 	if (!triggerFirstTime) {
3713 		clbc->setSkipFirstRedo();
3714 	}
3715 
3716 	m_undoStack->push(parentCommand);
3717 }
3718 
wireChangedSlot(Wire * wire,const QLineF & oldLine,const QLineF & newLine,QPointF oldPos,QPointF newPos,ConnectorItem * from,ConnectorItem * to)3719 void SketchWidget::wireChangedSlot(Wire* wire, const QLineF & oldLine, const QLineF & newLine, QPointF oldPos, QPointF newPos, ConnectorItem * from, ConnectorItem * to) {
3720 	this->clearHoldingSelectItem();
3721 	this->m_moveEventCount = 0;  // clear this so an extra MoveItemCommand isn't posted
3722 
3723 	// TODO: make sure all these pointers to pointers to pointers aren't null...
3724 
3725 	if (wire == this->m_connectorDragWire) {
3726 		dragWireChanged(wire, from, to);
3727 		return;
3728 	}
3729 
3730 	clearDragWireTempCommand();
3731 	if ((to != NULL) && from->connectedToItems().contains(to)) {
3732 		// there's no change: the wire was dragged back to its original connection
3733         QList<ConnectorItem *> already;
3734 		from->attachedTo()->updateConnections(to, false, already);
3735 		return;
3736 	}
3737 
3738 	QUndoCommand * parentCommand = new QUndoCommand();
3739 
3740 	long fromID = wire->id();
3741 
3742 	QString fromConnectorID;
3743 	if (from != NULL) {
3744 		fromConnectorID = from->connectorSharedID();
3745 	}
3746 
3747 	long toID = -1;
3748 	QString toConnectorID;
3749 	if (to != NULL) {
3750 		toID = to->attachedToID();
3751 		toConnectorID = to->connectorSharedID();
3752 	}
3753 
3754 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3755 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3756 
3757 	rememberSticky(wire, parentCommand);
3758 
3759 
3760 	bool chained = false;
3761 	foreach (ConnectorItem * toConnectorItem, from->connectedToItems()) {
3762 		Wire * toWire = qobject_cast<Wire *>(toConnectorItem->attachedTo());
3763 		if (toWire) {
3764 			chained = true;
3765 			break;
3766 		}
3767 	}
3768 
3769 	new ChangeWireCommand(this, fromID, oldLine, newLine, oldPos, newPos, true, true, parentCommand);
3770 	new CheckStickyCommand(this, BaseCommand::SingleView, fromID, false, CheckStickyCommand::RedoOnly, parentCommand);
3771 
3772 	foreach (ConnectorItem * toConnectorItem, from->connectedToItems()) {
3773 		Wire * toWire = qobject_cast<Wire *>(toConnectorItem->attachedTo());
3774 		if (toWire == NULL) continue;
3775 
3776 		rememberSticky(toWire, parentCommand);
3777 
3778 		ViewGeometry vg = toWire->getViewGeometry();
3779 		QLineF nl = toWire->line();
3780 		QPointF np = toWire->pos();
3781 		new ChangeWireCommand(this, toWire->id(), vg.line(), nl, vg.loc(), np, true, true, parentCommand);
3782 		new CheckStickyCommand(this, BaseCommand::SingleView, toWire->id(), false, CheckStickyCommand::RedoOnly, parentCommand);
3783 	}
3784 
3785 	QList< QPointer<ConnectorItem> > former = from->connectedToItems();
3786 
3787 	QString prefix;
3788 	QString suffix;
3789 	if (to == NULL) {
3790 		if (former.count() > 0  && !chained) {
3791 			prefix = tr("Disconnect");
3792 			// the suffix is a little tricky to determine
3793 			// it might be multiple disconnects, or might be disconnecting a virtual wire, in which case, the
3794 			// title needs to come from the virtual wire's other connection's attachedTo()
3795 
3796 			// suffix = tr("from %1").arg(former->attachedToTitle());
3797 		}
3798 		else {
3799 			prefix = tr("Change");
3800 		}
3801 	}
3802 	else {
3803 		prefix = tr("Connect");
3804 		suffix = tr("to %1").arg(to->attachedToInstanceTitle());
3805 	}
3806 
3807 	parentCommand->setText(QObject::tr("%1 %2 %3").arg(prefix).arg(wire->title()).arg(suffix) );
3808 
3809 	if (!chained) {
3810 		if (former.count() > 0) {
3811 			foreach (ConnectorItem * formerConnectorItem, former) {
3812 				extendChangeConnectionCommand(BaseCommand::CrossView, from, formerConnectorItem,
3813 					ViewLayer::specFromID(wire->viewLayerID()),
3814 					false, parentCommand);
3815 				from->tempRemove(formerConnectorItem, false);
3816 				formerConnectorItem->tempRemove(from, false);
3817 			}
3818 
3819 		}
3820 		if (to != NULL) {
3821 			extendChangeConnectionCommand(BaseCommand::CrossView, from, to, ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand);
3822 		}
3823 	}
3824 
3825 	clearTemporaries();
3826 
3827     new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
3828 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
3829 	m_undoStack->waitPush(parentCommand, PropChangeDelay);
3830 }
3831 
dragWireChanged(Wire * wire,ConnectorItem * fromOnWire,ConnectorItem * to)3832 void SketchWidget::dragWireChanged(Wire* wire, ConnectorItem * fromOnWire, ConnectorItem * to)
3833 {
3834 	if (m_bendpointWire != NULL && wire->getRatsnest()) {
3835 		dragRatsnestChanged();
3836 		return;
3837 	}
3838 
3839 	prereleaseTempWireForDragging(m_connectorDragWire);
3840 	BaseCommand::CrossViewType crossViewType = BaseCommand::CrossView;
3841 	if (m_bendpointWire) {
3842 	}
3843 	else {
3844 		m_connectorDragConnector->tempRemove(m_connectorDragWire->connector1(), false);
3845 		m_connectorDragWire->connector1()->tempRemove(m_connectorDragConnector, false);
3846 
3847 		// if to and from are the same connector, you can't draw a wire to yourself
3848 		// or to == NULL and it's pcb or schematic view, bail out
3849 		if ((m_connectorDragConnector == to) || !canCreateWire(wire, fromOnWire, to)) {
3850 			clearDragWireTempCommand();
3851             removeDragWire();
3852 			return;
3853 		}
3854 	}
3855 
3856 	QUndoCommand * parentCommand = new QUndoCommand();
3857 	parentCommand->setText(tr("Create and connect wire"));
3858 
3859 	SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
3860 	if (m_tempDragWireCommand != NULL) {
3861 		selectItemCommand->copyUndo(m_tempDragWireCommand);
3862 		clearDragWireTempCommand();
3863 	}
3864 
3865 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3866 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3867 
3868 	m_connectorDragWire->saveGeometry();
3869 	bool doEmit = false;
3870 	long fromID = wire->id();
3871 
3872 	DebugDialog::debug(QString("m_connectorDragConnector:%1 %4 from:%2 to:%3")
3873 						.arg(m_connectorDragConnector->connectorSharedID())
3874 						.arg(fromOnWire->connectorSharedID())
3875 						.arg((to == NULL) ? "null" : to->connectorSharedID())
3876 						.arg(m_connectorDragConnector->attachedTo()->title()) );
3877 
3878 
3879 	// create a new wire with the same id as the temporary wire
3880 	ViewGeometry vg = m_connectorDragWire->getViewGeometry();
3881 	vg.setWireFlags(getTraceFlag());
3882 	new AddItemCommand(this, crossViewType, m_connectorDragWire->moduleID(), m_connectorDragWire->viewLayerPlacement(), vg, fromID, true, -1, parentCommand);
3883 	new CheckStickyCommand(this, crossViewType, fromID, false, CheckStickyCommand::RemoveOnly, parentCommand);
3884 	selectItemCommand->addRedo(fromID);
3885 
3886 	if (m_bendpointWire == NULL) {
3887 		ConnectorItem * anchor = wire->otherConnector(fromOnWire);
3888 		if (anchor != NULL) {
3889 			extendChangeConnectionCommand(BaseCommand::CrossView, anchor, m_connectorDragConnector, ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand);
3890 			doEmit = true;
3891 		}
3892 		if (to != NULL) {
3893 			extendChangeConnectionCommand(BaseCommand::CrossView, fromOnWire, to, ViewLayer::specFromID(wire->viewLayerID()), true, parentCommand);
3894 			doEmit = true;
3895 		}
3896 
3897 		setUpColor(m_connectorDragConnector, to, wire, parentCommand);
3898 	}
3899 	else {
3900 		new WireColorChangeCommand(this, wire->id(), m_bendpointWire->colorString(), m_bendpointWire->colorString(), m_bendpointWire->opacity(), m_bendpointWire->opacity(), parentCommand);
3901 		new WireWidthChangeCommand(this, wire->id(), m_bendpointWire->width(), m_bendpointWire->width(), parentCommand);
3902         if (m_bendpointWire->banded()) {
3903             new SetPropCommand(this, wire->id(), "banded", "Yes", "Yes", true, parentCommand);
3904         }
3905     }
3906 
3907 	if (m_bendpointWire) {
3908 		ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, m_bendpointWire->id(), m_bendpointWire->undoCurve(), m_bendpointWire->curve(), m_bendpointWire->getAutoroutable(), parentCommand);
3909 		cwcc->setUndoOnly();
3910 
3911 		// puts the wire in position at redo time
3912 		ChangeWireCommand * cwc = new ChangeWireCommand(this, m_bendpointWire->id(), m_bendpointVG.line(), m_bendpointWire->line(), m_bendpointVG.loc(), m_bendpointWire->pos(), true, false, parentCommand);
3913 		cwc->setRedoOnly();
3914 		foreach (ConnectorItem * toConnectorItem, wire->connector1()->connectedToItems()) {
3915 			toConnectorItem->tempRemove(wire->connector1(), false);
3916 			wire->connector1()->tempRemove(toConnectorItem, false);
3917 			new ChangeConnectionCommand(this, BaseCommand::CrossView,
3918 										m_bendpointWire->id(), m_connectorDragConnector->connectorSharedID(),
3919 										toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
3920 										ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()),
3921 										false, parentCommand);
3922 			new ChangeConnectionCommand(this, BaseCommand::CrossView,
3923 										wire->id(), wire->connector1()->connectorSharedID(),
3924 										toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
3925 										ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()),
3926 										true, parentCommand);
3927 		}
3928 
3929 
3930 		m_connectorDragConnector->tempRemove(wire->connector0(), false);
3931 		wire->connector0()->tempRemove(m_connectorDragConnector, false);
3932 		new ChangeConnectionCommand(this, BaseCommand::CrossView,
3933 										m_connectorDragConnector->attachedToID(), m_connectorDragConnector->connectorSharedID(),
3934 										wire->id(), wire->connector0()->connectorSharedID(),
3935 										ViewLayer::specFromID(wire->viewLayerID()),
3936 										true, parentCommand);
3937 
3938 		new ChangeWireCurveCommand(this, wire->id(), NULL, wire->curve(), wire->getAutoroutable(), parentCommand);
3939 		cwcc = new ChangeWireCurveCommand(this, m_bendpointWire->id(), m_bendpointWire->undoCurve(), m_bendpointWire->curve(), m_bendpointWire->getAutoroutable(), parentCommand);
3940 		cwcc->setRedoOnly();
3941 
3942 		// puts the wire in position at undo time
3943 		cwc = new ChangeWireCommand(this, m_bendpointWire->id(), m_bendpointVG.line(), m_bendpointWire->line(), m_bendpointVG.loc(), m_bendpointWire->pos(), true, false, parentCommand);
3944 		cwc->setUndoOnly();
3945 
3946 		SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
3947 		selectItemCommand->addRedo(m_bendpointWire->id());
3948 
3949 		m_bendpointWire = NULL;			// signal that we're done
3950 
3951 	}
3952 
3953 	clearTemporaries();
3954 
3955 	// remove the temporary wire
3956     removeDragWire();
3957 
3958 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
3959 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
3960 	m_undoStack->push(parentCommand);
3961 
3962 }
3963 
dragRatsnestChanged()3964 void SketchWidget::dragRatsnestChanged()
3965 {
3966 	// m_bendpointWire is the original wire
3967 	// m_connectorDragWire is temporary
3968 	// wire == m_connectorDragWire
3969 	// m_connectorDragConnector is from original wire
3970 
3971 	QList<Wire *> wires;
3972 	QList<ConnectorItem *> ends;
3973 	m_bendpointWire->collectChained(wires, ends);
3974 	if (ends.count() != 2) {
3975 		// ratsnest wires should always and only have two ends: we're screwed
3976 		return;
3977 	}
3978 
3979 	ViewLayer::ViewLayerPlacement viewLayerPlacement = createWireViewLayerPlacement(ends[0], ends[1]);
3980 	if (viewLayerPlacement == ViewLayer::UnknownPlacement) {
3981 		// for now this should not be possible
3982 		QMessageBox::critical(this, tr("Fritzing"), tr("This seems like an attempt to create a trace across layers. This circumstance should not arise: please contact the developers."));
3983 		return;
3984 	}
3985 
3986 	BaseCommand::CrossViewType crossViewType = BaseCommand::CrossView;
3987 
3988 	QUndoCommand * parentCommand = new QUndoCommand();
3989 	parentCommand->setText(tr("Create and connect %1").arg(m_viewID == ViewLayer::BreadboardView ? tr("wire") : tr("trace")));
3990 
3991 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3992 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
3993 
3994 	//SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
3995 
3996 	m_connectorDragWire->saveGeometry();
3997 	m_bendpointWire->saveGeometry();
3998 
3999 	double traceWidth = getTraceWidth();
4000 	QString tColor = traceColor(viewLayerPlacement);
4001     if (!this->m_lastColorSelected.isEmpty()) {
4002         tColor = this->m_lastColorSelected;
4003     }
4004 
4005 	long newID1 = ItemBase::getNextID();
4006 	ViewGeometry vg1 = m_connectorDragWire->getViewGeometry();
4007 	vg1.setWireFlags(getTraceFlag());
4008 	new AddItemCommand(this, crossViewType, m_connectorDragWire->moduleID(), viewLayerPlacement, vg1, newID1, true, -1, parentCommand);
4009 	new CheckStickyCommand(this, crossViewType, newID1, false, CheckStickyCommand::RemoveOnly, parentCommand);
4010 	new WireColorChangeCommand(this, newID1, tColor, tColor, 1.0, 1.0, parentCommand);
4011 	new WireWidthChangeCommand(this, newID1, traceWidth, traceWidth, parentCommand);
4012 
4013 	long newID2 = ItemBase::getNextID();
4014 	ViewGeometry vg2 = m_bendpointWire->getViewGeometry();
4015 	vg2.setWireFlags(getTraceFlag());
4016 	new AddItemCommand(this, crossViewType, m_bendpointWire->moduleID(), viewLayerPlacement, vg2, newID2, true, -1, parentCommand);
4017 	new CheckStickyCommand(this, crossViewType, newID2, false, CheckStickyCommand::RemoveOnly, parentCommand);
4018 	new WireColorChangeCommand(this, newID2, tColor, tColor, 1.0, 1.0, parentCommand);
4019 	new WireWidthChangeCommand(this, newID2, traceWidth, traceWidth, parentCommand);
4020 
4021 	new ChangeConnectionCommand(this, BaseCommand::CrossView,
4022 									newID2, m_connectorDragConnector->connectorSharedID(),
4023 									newID1, m_connectorDragWire->connector0()->connectorSharedID(),
4024 									viewLayerPlacement,					// ViewLayer::specFromID(wire->viewLayerID())
4025 									true, parentCommand);
4026 
4027 	foreach (ConnectorItem * toConnectorItem, m_bendpointWire->connector0()->connectedToItems()) {
4028 		new ChangeConnectionCommand(this, BaseCommand::CrossView,
4029 									newID2, m_bendpointWire->connector0()->connectorSharedID(),
4030 									toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
4031 									viewLayerPlacement,					// ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID())
4032 									true, parentCommand);
4033 	}
4034 	foreach (ConnectorItem * toConnectorItem, m_connectorDragWire->connector1()->connectedToItems()) {
4035 		new ChangeConnectionCommand(this, BaseCommand::CrossView,
4036 									newID1, m_connectorDragWire->connector1()->connectorSharedID(),
4037 									toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
4038 									viewLayerPlacement,					// ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID())
4039 									true, parentCommand);
4040 		m_connectorDragWire->connector1()->tempRemove(toConnectorItem, false);
4041 		toConnectorItem->tempRemove(m_connectorDragWire->connector1(), false);
4042 		m_bendpointWire->connector1()->tempConnectTo(toConnectorItem, false);
4043 		toConnectorItem->tempConnectTo(m_bendpointWire->connector1(), false);
4044 	}
4045 
4046 	m_bendpointWire->setPos(m_bendpointVG.loc());
4047 	m_bendpointWire->setLine(m_bendpointVG.line());
4048 	m_connectorDragConnector->tempRemove(m_connectorDragWire->connector0(), false);
4049 	m_connectorDragWire->connector0()->tempRemove(m_connectorDragConnector, false);
4050 	m_bendpointWire = NULL;			// signal that we're done
4051 
4052 	// remove the temporary wire
4053     removeDragWire();
4054 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
4055 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
4056 	m_undoStack->push(parentCommand);
4057 }
4058 
setUpColor(ConnectorItem * fromConnectorItem,ConnectorItem * toConnectorItem,Wire * wire,QUndoCommand * parentCommand)4059 void SketchWidget::setUpColor(ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, Wire * wire, QUndoCommand * parentCommand) {
4060 	Q_UNUSED(fromConnectorItem);
4061 	Q_UNUSED(toConnectorItem);
4062 
4063 	if (!this->m_lastColorSelected.isEmpty()) {
4064 		new WireColorChangeCommand(this, wire->id(), m_lastColorSelected, m_lastColorSelected, wire->opacity(), wire->opacity(), parentCommand);
4065 	}
4066 }
4067 
addViewLayer(ViewLayer * viewLayer)4068 void SketchWidget::addViewLayer(ViewLayer * viewLayer) {
4069 	ViewLayer * oldViewLayer = m_viewLayers.value(viewLayer->viewLayerID(), NULL);
4070 	if (oldViewLayer) {
4071 		delete oldViewLayer;
4072 	}
4073 
4074 	m_viewLayers.insert(viewLayer->viewLayerID(), viewLayer);
4075 	QAction* action = new QAction(QObject::tr("%1 Layer").arg(viewLayer->displayName()), this);
4076 	action->setData(QVariant::fromValue<ViewLayer *>(viewLayer));
4077 	action->setCheckable(true);
4078 	action->setChecked(viewLayer->visible());
4079 	action->setEnabled(true);
4080     connect(action, SIGNAL(triggered()), this, SLOT(toggleLayerVisibility()));
4081     viewLayer->setAction(action);
4082 }
4083 
setAllLayersVisible(bool visible)4084 void SketchWidget::setAllLayersVisible(bool visible) {
4085 	LayerList keys = m_viewLayers.keys();
4086 
4087 	for (int i = 0; i < keys.count(); i++) {
4088 		ViewLayer * viewLayer = m_viewLayers.value(keys[i]);
4089 		if (viewLayer != NULL && viewLayer->action()->isEnabled()) {
4090 			setLayerVisible(viewLayer, visible, true);
4091 		}
4092 	}
4093 }
4094 
calcItemCount()4095 ItemCount SketchWidget::calcItemCount() {
4096 	ItemCount itemCount;
4097 
4098 	// TODO: replace scene()->items()
4099 	QList<QGraphicsItem *> items = scene()->items();
4100 	QList<QGraphicsItem *> selItems = scene()->selectedItems();
4101 
4102 	itemCount.visLabelCount = itemCount.hasLabelCount = 0;
4103 	itemCount.selCount = 0;
4104 	itemCount.selHFlipable = itemCount.selVFlipable = itemCount.selRotatable  = itemCount.sel45Rotatable = 0;
4105 	itemCount.itemsCount = 0;
4106 	itemCount.obsoleteCount = 0;
4107 	itemCount.moveLockCount = 0;
4108 	itemCount.wireCount = 0;
4109 
4110 	for (int i = 0; i < selItems.count(); i++) {
4111 		ItemBase * itemBase = ItemBase::extractTopLevelItemBase(selItems[i]);
4112 		if (itemBase != NULL) {
4113 			itemCount.selCount++;
4114 
4115 			if (itemBase->moveLock()) {
4116 				itemCount.moveLockCount++;
4117 			}
4118 
4119 			if (itemBase->hasPartLabel()) {
4120 				itemCount.hasLabelCount++;
4121 				if (itemBase->isPartLabelVisible()) {
4122 					itemCount.visLabelCount++;
4123 				}
4124 			}
4125 
4126 			if (itemBase->isObsolete()) {
4127 				itemCount.obsoleteCount++;
4128 			}
4129 
4130 			if (itemBase->itemType() == ModelPart::Wire) {
4131 				itemCount.wireCount++;
4132 			}
4133             else {
4134 			    bool rotatable = itemBase->rotationAllowed();
4135 			    if (rotatable) {
4136 				    itemCount.selRotatable++;
4137 			    }
4138 
4139 			    rotatable = itemBase->rotation45Allowed();
4140 			    if (rotatable) {
4141 				    itemCount.sel45Rotatable++;
4142 			    }
4143 
4144 			    if (itemBase->canFlipHorizontal()) {
4145 				    itemCount.selHFlipable++;
4146 			    }
4147 
4148 			    if (itemBase->canFlipVertical()) {
4149 				    itemCount.selVFlipable++;
4150 			    }
4151             }
4152 		}
4153 	}
4154 
4155     /*
4156     DebugDialog::debug(QString("sc:%1 wc:%2 sr:%3 s45r:%4 sv:%5 sh:%6")
4157         .arg(itemCount.selCount)
4158         .arg(itemCount.wireCount)
4159         .arg(itemCount.selRotatable)
4160         .arg(itemCount.sel45Rotatable)
4161         .arg(itemCount.selVFlipable)
4162         .arg(itemCount.selHFlipable)
4163         );
4164     */
4165 
4166 	if (itemCount.selCount - itemCount.wireCount != itemCount.selRotatable) {
4167 		// if you can't rotate them all, then you can't rotate any
4168 		itemCount.selRotatable = 0;
4169 	}
4170 	if (itemCount.selCount - itemCount.wireCount != itemCount.sel45Rotatable) {
4171 		// if you can't rotate them all, then you can't rotate any
4172 		itemCount.sel45Rotatable = 0;
4173 	}
4174 	if (itemCount.selCount - itemCount.wireCount != itemCount.selVFlipable) {
4175 		itemCount.selVFlipable = 0;
4176 	}
4177 	if (itemCount.selCount - itemCount.wireCount != itemCount.selHFlipable) {
4178 		itemCount.selHFlipable = 0;
4179 	}
4180 	if (itemCount.selCount > 0) {
4181 		for (int i = 0; i < items.count(); i++) {
4182 			if (ItemBase::extractTopLevelItemBase(items[i]) != NULL) {
4183 				itemCount.itemsCount++;
4184 			}
4185 		}
4186 	}
4187 
4188 	return itemCount;
4189 }
4190 
layerIsVisible(ViewLayer::ViewLayerID viewLayerID)4191 bool SketchWidget::layerIsVisible(ViewLayer::ViewLayerID viewLayerID) {
4192 	ViewLayer * viewLayer = m_viewLayers.value(viewLayerID);
4193 	if (viewLayer == NULL) return false;
4194 
4195 	return viewLayer->visible();
4196 }
4197 
layerIsActive(ViewLayer::ViewLayerID viewLayerID)4198 bool SketchWidget::layerIsActive(ViewLayer::ViewLayerID viewLayerID) {
4199 	ViewLayer * viewLayer = m_viewLayers.value(viewLayerID);
4200 	if (viewLayer == NULL) return false;
4201 
4202 	return viewLayer->isActive();
4203 }
4204 
setLayerVisible(ViewLayer::ViewLayerID viewLayerID,bool vis,bool doChildLayers)4205 void SketchWidget::setLayerVisible(ViewLayer::ViewLayerID viewLayerID, bool vis, bool doChildLayers) {
4206 	ViewLayer * viewLayer = m_viewLayers.value(viewLayerID);
4207 	if (viewLayer) {
4208 		setLayerVisible(viewLayer, vis, doChildLayers);
4209 	}
4210 }
4211 
toggleLayerVisibility()4212 void SketchWidget::toggleLayerVisibility() {
4213 	QAction * action = qobject_cast<QAction *>(sender());
4214 	if (action == NULL) return;
4215 
4216 	ViewLayer * viewLayer = action->data().value<ViewLayer *>();
4217 	if (viewLayer == NULL) return;
4218 
4219 	setLayerVisible(viewLayer, !viewLayer->visible(), viewLayer->includeChildLayers());
4220 }
4221 
setLayerVisible(ViewLayer * viewLayer,bool visible,bool doChildLayers)4222 void SketchWidget::setLayerVisible(ViewLayer * viewLayer, bool visible, bool doChildLayers) {
4223 
4224 	LayerList viewLayerIDs;
4225 	viewLayerIDs.append(viewLayer->viewLayerID());
4226 
4227 	viewLayer->setVisible(visible);
4228 	if (doChildLayers) {
4229 		foreach (ViewLayer * childLayer, viewLayer->childLayers()) {
4230 			childLayer->setVisible(visible);
4231 			viewLayerIDs.append(childLayer->viewLayerID());
4232 		}
4233 	}
4234 
4235 	// TODO: replace scene()->items()
4236 	foreach (QGraphicsItem * item, scene()->items()) {
4237 		// want all items, not just topLevel
4238 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
4239 		if (itemBase) {
4240 			if (viewLayerIDs.contains(itemBase->viewLayerID())) {
4241 				itemBase->setHidden(!visible);
4242 				//DebugDialog::debug(QString("setting visible %1").arg(viewLayer->visible()));
4243 			}
4244 			continue;
4245 		}
4246 
4247 		PartLabel * partLabel = dynamic_cast<PartLabel *>(item);
4248 		if (partLabel && (viewLayerIDs.contains(partLabel->viewLayerID()))) {
4249 			partLabel->setHidden(!visible);
4250 		}
4251 	}
4252 }
4253 
setLayerActive(ViewLayer::ViewLayerID viewLayerID,bool active)4254 void SketchWidget::setLayerActive(ViewLayer::ViewLayerID viewLayerID, bool active) {
4255 	ViewLayer * viewLayer = m_viewLayers.value(viewLayerID);
4256 	if (viewLayer) {
4257 		setLayerActive(viewLayer, active);
4258 	}
4259 }
4260 
setLayerActive(ViewLayer * viewLayer,bool active)4261 void SketchWidget::setLayerActive(ViewLayer * viewLayer, bool active) {
4262 
4263 	LayerList viewLayerIDs;
4264 	viewLayerIDs.append(viewLayer->viewLayerID());
4265 
4266 	viewLayer->setActive(active);
4267 	foreach (ViewLayer * childLayer, viewLayer->childLayers()) {
4268 		childLayer->setActive(active);
4269 		viewLayerIDs.append(childLayer->viewLayerID());
4270 	}
4271 
4272 	// TODO: replace scene()->items()
4273 	foreach (QGraphicsItem * item, scene()->items()) {
4274 		// want all items, not just topLevel
4275 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
4276 		if (itemBase != NULL) {
4277             //itemBase->debugInfo("setActive");
4278             if (viewLayerIDs.contains(itemBase->viewLayerID())) {
4279 			    itemBase->setInactive(!active);
4280 			    //DebugDialog::debug(QString("setting visible %1").arg(viewLayer->visible()));
4281             }
4282             continue;
4283 		}
4284 
4285 		PartLabel * partLabel = dynamic_cast<PartLabel *>(item);
4286 		if (partLabel != NULL) {
4287             if (viewLayerIDs.contains(partLabel->viewLayerID())) {
4288 			    partLabel->setInactive(!active);
4289             }
4290             continue;
4291 		}
4292 	}
4293 }
4294 
sendToBack()4295 void SketchWidget::sendToBack() {
4296 	QList<ItemBase *> bases;
4297 	if (!startZChange(bases)) return;
4298 
4299 	QString text = QObject::tr("Bring forward");
4300 	continueZChangeMax(bases, bases.size() - 1, -1, greaterThan, -1, text);
4301 }
4302 
sendBackward()4303 void SketchWidget::sendBackward() {
4304 
4305 	QList<ItemBase *> bases;
4306 	if (!startZChange(bases)) return;
4307 
4308 	QString text = QObject::tr("Send backward");
4309 	continueZChange(bases, 0, bases.size(), lessThan, 1, text);
4310 }
4311 
bringForward()4312 void SketchWidget::bringForward() {
4313 	QList<ItemBase *> bases;
4314 	if (!startZChange(bases)) return;
4315 
4316 	QString text = QObject::tr("Bring forward");
4317 	continueZChange(bases, bases.size() - 1, -1, greaterThan, -1, text);
4318 
4319 }
4320 
bringToFront()4321 void SketchWidget::bringToFront() {
4322 	QList<ItemBase *> bases;
4323 	if (!startZChange(bases)) return;
4324 
4325 	QString text = QObject::tr("Bring to front");
4326 	continueZChangeMax(bases, 0, bases.size(), lessThan, 1, text);
4327 }
4328 
fitInWindow()4329 double SketchWidget::fitInWindow() {
4330 
4331 	QRectF itemsRect;
4332 	foreach(QGraphicsItem * item, scene()->items()) {
4333 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
4334 		if (itemBase == NULL) continue;
4335 		if (!itemBase->isEverVisible()) continue;
4336 
4337 		itemsRect |= itemBase->sceneBoundingRect();
4338 	}
4339 
4340     static const double borderFactor = 0.03;
4341     itemsRect.adjust(-itemsRect.width() * borderFactor, -itemsRect.height() * borderFactor, itemsRect.width() * borderFactor, itemsRect.height() * borderFactor);
4342 
4343 	QRectF viewRect = rect();
4344 
4345 	//fitInView(itemsRect.x(), itemsRect.y(), itemsRect.width(), itemsRect.height(), Qt::KeepAspectRatio);
4346 
4347 	double wRelation = (viewRect.width() - this->verticalScrollBar()->width() - 5)  / itemsRect.width();
4348 	double hRelation = (viewRect.height() - this->horizontalScrollBar()->height() - 5) / itemsRect.height();
4349 
4350 	//DebugDialog::debug(QString("scen rect: w%1 h%2").arg(itemsRect.width()).arg(itemsRect.height()));
4351 	//DebugDialog::debug(QString("view rect: w%1 h%2").arg(viewRect.width()).arg(viewRect.height()));
4352 	//DebugDialog::debug(QString("relations (v/s): w%1 h%2").arg(wRelation).arg(hRelation));
4353 
4354 	if(wRelation < hRelation) {
4355 		m_scaleValue = (wRelation * 100);
4356 	} else {
4357 		m_scaleValue = (hRelation * 100);
4358 	}
4359 
4360 	this->centerOn(itemsRect.center());
4361 	this->absoluteZoom(m_scaleValue);
4362 
4363 	return m_scaleValue;
4364 }
4365 
startZChange(QList<ItemBase * > & bases)4366 bool SketchWidget::startZChange(QList<ItemBase *> & bases) {
4367 	int selCount = bases.count();
4368 	if (selCount == 0) {
4369 		selCount = scene()->selectedItems().count();
4370 		if (selCount <= 0) return false;
4371 	}
4372 
4373 	const QList<QGraphicsItem *> items = scene()->items();
4374 	if (items.count() <= selCount) return false;
4375 
4376 	sortAnyByZ(items, bases);
4377 
4378 	return true;
4379 }
4380 
continueZChange(QList<ItemBase * > & bases,int start,int end,bool (* test)(int current,int start),int inc,const QString & text)4381 void SketchWidget::continueZChange(QList<ItemBase *> & bases, int start, int end, bool (*test)(int current, int start), int inc, const QString & text) {
4382 
4383 	bool moved = false;
4384 	int last = bases.size();
4385 	for (int i = start; test(i, end); i += inc) {
4386 		ItemBase * base = bases[i];
4387 
4388 		if (!base->getViewGeometry().selected()) continue;
4389 
4390 		int j = i - inc;
4391 		if (j >= 0 && j < last && bases[j]->viewLayerID() == base->viewLayerID()) {
4392 			bases.move(i, j);
4393 			moved = true;
4394 		}
4395 	}
4396 
4397 	if (!moved) {
4398 		return;
4399 	}
4400 
4401 	continueZChangeAux(bases, text);
4402 }
4403 
continueZChangeMax(QList<ItemBase * > & bases,int start,int end,bool (* test)(int current,int start),int inc,const QString & text)4404 void SketchWidget::continueZChangeMax(QList<ItemBase *> & bases, int start, int end, bool (*test)(int current, int start), int inc, const QString & text) {
4405 
4406 	QHash<ItemBase *, ItemBase *> marked;
4407 	bool moved = false;
4408 	int last = bases.size();
4409 	for (int i = start; test(i, end); i += inc) {
4410 		ItemBase * base = bases[i];
4411 		if (!base->getViewGeometry().selected()) continue;
4412 		if (marked[base] != NULL) continue;
4413 
4414 		marked.insert(base, base);
4415 
4416 		int dest = -1;
4417 		for (int j = i + inc; j >= 0 && j < last && bases[j]->viewLayerID() == base->viewLayerID(); j += inc) {
4418 			dest = j;
4419 		}
4420 
4421 		if (dest >= 0) {
4422 			moved = true;
4423 			bases.move(i, dest);
4424 			DebugDialog::debug(QString("moving %1 to %2").arg(i).arg(dest));
4425 			i -= inc;	// because we just modified the list and would miss the next item
4426 		}
4427 	}
4428 
4429 	if (!moved) {
4430 		return;
4431 	}
4432 
4433 	continueZChangeAux(bases, text);
4434 }
4435 
4436 
continueZChangeAux(QList<ItemBase * > & bases,const QString & text)4437 void SketchWidget::continueZChangeAux(QList<ItemBase *> & bases, const QString & text) {
4438 
4439 	ChangeZCommand * changeZCommand = new ChangeZCommand(this, NULL);
4440 
4441 	ViewLayer::ViewLayerID lastViewLayerID = ViewLayer::UnknownLayer;
4442 	double z = 0;
4443 	for (int i = 0; i < bases.size(); i++) {
4444 		double oldZ = bases[i]->getViewGeometry().z();
4445 		if (bases[i]->viewLayerID() != lastViewLayerID) {
4446 			lastViewLayerID = bases[i]->viewLayerID();
4447 			z = qFloor(oldZ);
4448 		}
4449 		else {
4450 			z += ViewLayer::getZIncrement();
4451 		}
4452 
4453 
4454 		if (oldZ == z) continue;
4455 
4456 		// optimize this by only adding z's that must change
4457 		// rather than changing all of them
4458 		changeZCommand->addTriplet(bases[i]->id(), oldZ, z);
4459 	}
4460 
4461 	changeZCommand->setText(text);
4462 	m_undoStack->push(changeZCommand);
4463 }
4464 
sortAnyByZ(const QList<QGraphicsItem * > & items,QList<ItemBase * > & bases)4465 void SketchWidget::sortAnyByZ(const QList<QGraphicsItem *> & items, QList<ItemBase *> & bases) {
4466 	for (int i = 0; i < items.size(); i++) {
4467 		ItemBase * base = dynamic_cast<ItemBase *>(items[i]);
4468 		if (base != NULL) {
4469 			bases.append(base);
4470 			base->saveGeometry();
4471 		}
4472 	}
4473 
4474     // order by z
4475     qSort(bases.begin(), bases.end(), ItemBase::zLessThan);
4476 }
4477 
lessThan(int a,int b)4478 bool SketchWidget::lessThan(int a, int b) {
4479 	return a < b;
4480 }
4481 
greaterThan(int a,int b)4482 bool SketchWidget::greaterThan(int a, int b) {
4483 	return a > b;
4484 }
4485 
changeZ(QHash<long,RealPair * > triplets,double (* pairAccessor)(RealPair *))4486 void SketchWidget::changeZ(QHash<long, RealPair * > triplets, double (*pairAccessor)(RealPair *) ) {
4487 
4488 	// TODO: replace scene->items
4489 	const QList<QGraphicsItem *> items = scene()->items();
4490 	for (int i = 0; i < items.size(); i++) {
4491 		// want all items, not just topLevel
4492 		ItemBase * itemBase = dynamic_cast<ItemBase *>(items[i]);
4493 		if (itemBase == NULL) continue;
4494 
4495 		RealPair * pair = triplets[itemBase->id()];
4496 		if (pair == NULL) continue;
4497 
4498 		double newZ = pairAccessor(pair);
4499         ViewLayer * viewLayer = m_viewLayers.value(itemBase->viewLayerID());
4500         if (viewLayer) {
4501             newZ = viewLayer->getZFromBelow(newZ, this->viewFromBelow());
4502         }
4503 		//DebugDialog::debug(QString("change z %1 %2").arg(itemBase->id()).arg(newZ));
4504 		items[i]->setZValue(newZ);
4505 
4506 	}
4507 }
4508 
getDragWireViewLayerID(ConnectorItem *)4509 ViewLayer::ViewLayerID SketchWidget::getDragWireViewLayerID(ConnectorItem *) {
4510 	return m_wireViewLayerID;
4511 }
4512 
getWireViewLayerID(const ViewGeometry & viewGeometry,ViewLayer::ViewLayerPlacement)4513 ViewLayer::ViewLayerID SketchWidget::getWireViewLayerID(const ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement) {
4514 	if (viewGeometry.getRatsnest()) {
4515 		return ViewLayer::BreadboardRatsnest;
4516 	}
4517 
4518 	return m_wireViewLayerID;
4519 }
4520 
getRulerViewLayerID()4521 ViewLayer::ViewLayerID SketchWidget::getRulerViewLayerID() {
4522 	return m_rulerViewLayerID;
4523 }
4524 
getPartViewLayerID()4525 ViewLayer::ViewLayerID SketchWidget::getPartViewLayerID() {
4526 	return m_partViewLayerID;
4527 }
4528 
getConnectorViewLayerID()4529 ViewLayer::ViewLayerID SketchWidget::getConnectorViewLayerID() {
4530 	return m_connectorViewLayerID;
4531 }
4532 
getLabelViewLayerID(ItemBase *)4533 ViewLayer::ViewLayerID SketchWidget::getLabelViewLayerID(ItemBase *) {
4534 	return ViewLayer::UnknownLayer;
4535 }
4536 
getNoteViewLayerID()4537 ViewLayer::ViewLayerID SketchWidget::getNoteViewLayerID() {
4538 	return m_noteViewLayerID;
4539 }
4540 
4541 
mousePressConnectorEvent(ConnectorItem * connectorItem,QGraphicsSceneMouseEvent * event)4542 void SketchWidget::mousePressConnectorEvent(ConnectorItem * connectorItem, QGraphicsSceneMouseEvent * event) {
4543 
4544 	ModelPart * wireModel = m_referenceModel->retrieveModelPart(ModuleIDNames::WireModuleIDName);
4545 	if (wireModel == NULL) return;
4546 
4547 	m_tempDragWireCommand = m_holdingSelectItemCommand;
4548 	m_holdingSelectItemCommand = NULL;
4549 	clearHoldingSelectItem();
4550 
4551 
4552 	// make sure wire layer is visible
4553 	ViewLayer::ViewLayerID viewLayerID = getDragWireViewLayerID(connectorItem);
4554 	ViewLayer * viewLayer = m_viewLayers.value(viewLayerID);
4555 	if (viewLayer != NULL && !viewLayer->visible()) {
4556 		setLayerVisible(viewLayer, true, true);
4557         emit updateLayerMenuSignal();
4558 	}
4559 
4560 
4561 	ViewGeometry viewGeometry;
4562    	QPointF p = QPointF(connectorItem->mapToScene(event->pos()));
4563    	viewGeometry.setLoc(p);
4564 	viewGeometry.setLine(QLineF(0,0,0,0));
4565 
4566 	m_connectorDragConnector = connectorItem;
4567 	m_connectorDragWire = createTempWireForDragging(NULL, wireModel, connectorItem, viewGeometry, ViewLayer::UnknownPlacement);
4568 	if (m_connectorDragWire == NULL) {
4569 		clearDragWireTempCommand();
4570 		return;
4571 	}
4572 
4573 	m_connectorDragWire->debugInfo("creating connector drag wire");
4574 
4575 	setupAutoscroll(true);
4576 
4577 	// give connector item the mouse, so wire doesn't get mouse moved events
4578 	m_connectorDragWire->setVisible(true);
4579 	m_connectorDragWire->grabMouse();
4580     unsquashShapes();
4581 	//m_connectorDragWire->debugInfo("grabbing mouse 2");
4582 	m_connectorDragWire->initDragEnd(m_connectorDragWire->connector0(), event->scenePos());
4583 	m_connectorDragConnector->tempConnectTo(m_connectorDragWire->connector1(), false);
4584 	m_connectorDragWire->connector1()->tempConnectTo(m_connectorDragConnector, false);
4585 	if (!m_lastColorSelected.isEmpty()) {
4586 		m_connectorDragWire->setColorString(m_lastColorSelected, m_connectorDragWire->opacity(), false);
4587 	}
4588 }
4589 
rotateX(double degrees,bool rubberBandLegEnabled,ItemBase * originatingItem)4590 void SketchWidget::rotateX(double degrees, bool rubberBandLegEnabled, ItemBase * originatingItem)
4591 {
4592     if (qAbs(degrees) < 0.01) return;
4593 
4594 	clearHoldingSelectItem();
4595 	m_savedItems.clear();
4596 	m_savedWires.clear();
4597 	prepMove(originatingItem, rubberBandLegEnabled, false);
4598 
4599 	QRectF itemsBoundingRect;
4600 	// want the bounding rect of the original selected items, not all the items that are secondarily being rotated
4601 	foreach (QGraphicsItem * item, scene()->selectedItems()) {
4602 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
4603 		if (itemBase == NULL) continue;
4604 
4605 		itemsBoundingRect |= (item->transform() * QTransform().translate(item->x(), item->y()))
4606                             .mapRect(itemBase->boundingRectWithoutLegs() /* | item->childrenBoundingRect() */);
4607 	}
4608 
4609 	QPointF center = itemsBoundingRect.center();
4610 
4611 	QTransform rotation;
4612 	rotation.rotate(degrees);
4613 
4614 	QString string = tr("Rotate %2 (%1)")
4615 			.arg(ViewLayer::viewIDName(m_viewID))
4616 			.arg((m_savedItems.count() == 1) ? m_savedItems.values().at(0)->title() : QString::number(m_savedItems.count() + m_savedWires.count()) + " items" );
4617 	QUndoCommand * parentCommand = new QUndoCommand(string);
4618 
4619 	//foreach (long id, m_savedItems.keys()) {
4620 		//m_savedItems.value(id)->debugInfo(QString("save item %1").arg(id));
4621 	//}
4622 
4623 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
4624     new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
4625 
4626 	// change legs after connections have been updated (undo direction)
4627 	moveLegBendpoints(true, parentCommand);
4628 
4629 	rotatePartLabels(degrees, rotation, center, parentCommand);
4630 
4631     QList<Wire *> wires;
4632 	foreach (ItemBase * itemBase, m_savedItems.values()) {
4633 		if (itemBase->itemType() == ModelPart::Wire) {
4634             wires << qobject_cast<Wire *>(itemBase);
4635         }
4636     }
4637 
4638     foreach (Wire * wire, wires) {
4639         rotateWire(wire, rotation, center, true, parentCommand);
4640     }
4641 
4642 	foreach (ItemBase * itemBase, m_savedItems.values()) {
4643 		switch (itemBase->itemType()) {
4644 			case ModelPart::Via:
4645 			case ModelPart::Hole:
4646 				{
4647 					QPointF p = itemBase->sceneBoundingRect().center();
4648 					QPointF d = p - center;
4649 					QPointF dt = rotation.map(d) + center;
4650 					ViewGeometry vg1 = itemBase->getViewGeometry();
4651 					ViewGeometry vg2(vg1);
4652 					vg2.setLoc(vg1.loc() + dt - p);
4653 					new MoveItemCommand(this, itemBase->id(), vg1, vg2, true, parentCommand);
4654 				}
4655 				break;
4656 
4657 			case ModelPart::Wire:
4658 				break;
4659 
4660 			default:
4661 				{
4662 					ViewGeometry vg1 = itemBase->getViewGeometry();
4663 					ViewGeometry vg2(vg1);
4664 					itemBase->calcRotation(rotation, center, vg2);
4665 					ConnectorPairHash connectorHash;
4666 					disconnectFromFemale(itemBase, m_savedItems, connectorHash, true, rubberBandLegEnabled, parentCommand);
4667 					new MoveItemCommand(this, itemBase->id(), vg1, vg1, true, parentCommand);
4668 					new RotateItemCommand(this, itemBase->id(), degrees, parentCommand);
4669 					new MoveItemCommand(this, itemBase->id(), vg2, vg2, true, parentCommand);
4670 				}
4671 				break;
4672 		}
4673 	}
4674 
4675     foreach (Wire * wire, wires) {
4676         rotateWire(wire, rotation, center, false, parentCommand);
4677     }
4678 
4679 	// change legs after connections have been updated (redo direction)
4680 	QList<ConnectorItem *> connectorItems;
4681 	foreach (ItemBase * itemBase, m_stretchingLegs.uniqueKeys()) {
4682 		foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) {
4683 			connectorItems.append(connectorItem);
4684 			QPolygonF oldLeg, newLeg;
4685 			bool active;
4686 			connectorItem->stretchDone(oldLeg, newLeg, active);
4687 			new RotateLegCommand(this, connectorItem->attachedToID(), connectorItem->connectorSharedID(), oldLeg, active, parentCommand);
4688 		}
4689 	}
4690 
4691 	foreach (Wire * wire, m_savedWires.keys()) {
4692 		ViewGeometry vg1 = wire->getViewGeometry();
4693 		ViewGeometry vg2(vg1);
4694 
4695 		ConnectorItem * rotater = m_savedWires.value(wire);
4696 		QPointF p0 = rotater->sceneAdjustedTerminalPoint(NULL);
4697 		QPointF d0 = p0 - center;
4698 		QPointF d0t = rotation.map(d0);
4699 
4700 		QPointF p1 = wire->otherConnector(rotater)->sceneAdjustedTerminalPoint(NULL);
4701 		if (rotater == wire->connector0()) {
4702 			new ChangeWireCommand(this, wire->id(), vg1.line(), QLineF(QPointF(0,0), p1 - (d0t + center)), vg1.loc(), d0t + center, true, true, parentCommand);
4703 		}
4704 		else {
4705 			new ChangeWireCommand(this, wire->id(), vg1.line(), QLineF(QPointF(0,0), d0t + center - p1), vg1.loc(), vg1.loc(), true, true, parentCommand);
4706 		}
4707 	}
4708     new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
4709 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
4710 
4711 	m_undoStack->push(parentCommand);
4712 }
4713 
rotateWire(Wire * wire,QTransform & rotation,QPointF center,bool undoOnly,QUndoCommand * parentCommand)4714 void SketchWidget::rotateWire(Wire * wire, QTransform & rotation, QPointF center, bool undoOnly, QUndoCommand * parentCommand) {
4715     //wire->debugInfo("rotating wire");
4716 	QPointF p0 = wire->connector0()->sceneAdjustedTerminalPoint(NULL);
4717 	QPointF d0 = p0 - center;
4718 	QPointF d0t = rotation.map(d0);
4719 
4720 	QPointF p1 = wire->connector1()->sceneAdjustedTerminalPoint(NULL);
4721 	QPointF d1 = p1 - center;
4722 	QPointF d1t = rotation.map(d1);
4723 
4724 	ViewGeometry vg1 = wire->getViewGeometry();
4725 	ChangeWireCommand * cwc = new ChangeWireCommand(this, wire->id(), vg1.line(), QLineF(QPointF(0,0), d1t - d0t), vg1.loc(), d0t + center, true, true, parentCommand);
4726     if (undoOnly) cwc->setUndoOnly();
4727     else cwc->setRedoOnly();
4728 
4729     const Bezier * bezier = wire->curve();
4730     if (bezier) {
4731         Bezier * newBezier = new Bezier();
4732         newBezier->set_endpoints(QPointF(0,0), d1t - d0t);
4733         QPointF c0 = p0 + bezier->cp0();
4734         QPointF dc0 = c0 - center;
4735         QPointF dc0t = rotation.map(dc0);
4736         newBezier->set_cp0(dc0t - d0t);
4737 
4738         QPointF c1 = p0 + bezier->cp1();
4739         QPointF dc1 = c1 - center;
4740         QPointF dc1t = rotation.map(dc1);
4741         newBezier->set_cp1(dc1t - d0t);
4742 
4743         ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, wire->id(), bezier, newBezier, wire->getAutoroutable(), parentCommand);
4744         if (undoOnly) cwcc->setUndoOnly();
4745         else cwcc->setRedoOnly();
4746     }
4747 
4748 }
4749 
rotatePartLabels(double degrees,QTransform &,QPointF center,QUndoCommand * parentCommand)4750 void SketchWidget::rotatePartLabels(double degrees, QTransform &, QPointF center, QUndoCommand * parentCommand)
4751 {
4752 	Q_UNUSED(center);
4753 	Q_UNUSED(degrees);
4754 	Q_UNUSED(parentCommand);
4755 }
4756 
flipX(Qt::Orientations orientation,bool rubberBandLegEnabled)4757 void SketchWidget::flipX(Qt::Orientations orientation, bool rubberBandLegEnabled)
4758 {
4759 	if (!this->isVisible()) return;
4760 
4761 	clearHoldingSelectItem();
4762 	m_savedItems.clear();
4763 	m_savedWires.clear();
4764 	prepMove(NULL, rubberBandLegEnabled, false);
4765 
4766 	QList <QGraphicsItem *> items = scene()->selectedItems();
4767 	QList <ItemBase *> targets;
4768 
4769 	for (int i = 0; i < items.size(); i++) {
4770 		// can't flip layerkin (layerkin flipped indirectly)
4771 		ItemBase *itemBase = ItemBase::extractTopLevelItemBase(items[i]);
4772 		if (itemBase == NULL) continue;
4773 
4774         if (Board::isBoard(itemBase)) continue;
4775 
4776 		switch (itemBase->itemType()) {
4777 			case ModelPart::Wire:
4778 			case ModelPart::Note:
4779 			case ModelPart::CopperFill:
4780 			case ModelPart::Unknown:
4781 			case ModelPart::Via:
4782 			case ModelPart::Hole:
4783 			case ModelPart::Breadboard:
4784 				continue;
4785 
4786 			default:
4787 				if (!itemBase->canFlip(orientation)) {
4788 					continue;
4789 				}
4790 				break;
4791 		}
4792 
4793 		targets.append(itemBase);
4794 	}
4795 
4796 	if (targets.count() <= 0) {
4797 		return;
4798 	}
4799 
4800 	QString string = tr("Flip %2 (%1)")
4801 			.arg(ViewLayer::viewIDName(m_viewID))
4802 			.arg((targets.count() == 1) ? targets[0]->title() : QString::number(targets.count()) + " items" );
4803 
4804 	QUndoCommand * parentCommand = new QUndoCommand(string);
4805 
4806 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
4807     new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
4808 
4809 	// change legs after connections have been updated (undo direction)
4810 	moveLegBendpoints(true, parentCommand);
4811 
4812 	QHash<long, ItemBase *> emptyList;			// emptylist is only used for a move command
4813 	ConnectorPairHash connectorHash;
4814 	foreach (ItemBase * item, targets) {
4815 		disconnectFromFemale(item, emptyList, connectorHash, true, rubberBandLegEnabled, parentCommand);
4816 
4817 		if (item->isSticky()) {
4818 			//TODO: apply transformation to stuck items
4819 		}
4820 		// TODO: if item has female connectors, then apply transform to connected items
4821 
4822 		new FlipItemCommand(this, item->id(), orientation, parentCommand);
4823 	}
4824 
4825 	// change legs after connections have been updated (redo direction)
4826 	foreach (ItemBase * itemBase, m_stretchingLegs.uniqueKeys()) {
4827 		foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) {
4828 			QPolygonF oldLeg, newLeg;
4829 			bool active;
4830 			connectorItem->stretchDone(oldLeg, newLeg, active);
4831 			new RotateLegCommand(this, connectorItem->attachedToID(), connectorItem->connectorSharedID(), oldLeg, active, parentCommand);
4832 		}
4833 	}
4834 
4835 	clearTemporaries();
4836 
4837     new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
4838 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
4839 	m_undoStack->push(parentCommand);
4840 
4841 }
4842 
findConnectorItem(ConnectorItem * foreignConnectorItem)4843 ConnectorItem * SketchWidget::findConnectorItem(ConnectorItem * foreignConnectorItem) {
4844 	ItemBase * itemBase = findItem(foreignConnectorItem->attachedTo()->layerKinChief()->id());
4845 
4846 	if (itemBase == NULL) {
4847 		return NULL;
4848 	}
4849 
4850 	ConnectorItem * result = findConnectorItem(itemBase, foreignConnectorItem->connectorSharedID(), ViewLayer::NewBottom);
4851 	if (result) return result;
4852 
4853 	return findConnectorItem(itemBase, foreignConnectorItem->connectorSharedID(), ViewLayer::NewTop);
4854 }
4855 
findConnectorItem(ItemBase * itemBase,const QString & connectorID,ViewLayer::ViewLayerPlacement viewLayerPlacement)4856 ConnectorItem * SketchWidget::findConnectorItem(ItemBase * itemBase, const QString & connectorID, ViewLayer::ViewLayerPlacement viewLayerPlacement) {
4857 
4858 	ConnectorItem * connectorItem = itemBase->findConnectorItemWithSharedID(connectorID, viewLayerPlacement);
4859 	if (connectorItem != NULL) return connectorItem;
4860 
4861 	DebugDialog::debug("used to seek layer kin");
4862 	/*
4863 	if (seekLayerKin) {
4864 		PaletteItem * pitem = qobject_cast<PaletteItem *>(itemBase);
4865 		if (pitem == NULL) return NULL;
4866 
4867 		foreach (ItemBase * lkpi, pitem->layerKin()) {
4868 			connectorItem = lkpi->findConnectorItemWithSharedID(connectorID);
4869 			if (connectorItem != NULL) return connectorItem;
4870 		}
4871 
4872 	}
4873 	*/
4874 
4875 	return NULL;
4876 }
4877 
getSelectedPart()4878 PaletteItem * SketchWidget::getSelectedPart(){
4879 	QList <QGraphicsItem *> items= scene()->selectedItems();
4880 	PaletteItem *item = NULL;
4881 
4882 	// dynamic cast returns null in cases where non-PaletteItems (i.e. wires and layerKin palette items) are selected
4883 	for(int i=0; i < items.count(); i++){
4884 		PaletteItem *temp = dynamic_cast<PaletteItem *>(items[i]);
4885 		if (temp == NULL) continue;
4886 
4887 		if (item != NULL) return NULL;  // there are multiple items selected
4888 		item = temp;
4889 	}
4890 
4891 	return item;
4892 }
4893 
setBackground(QColor color)4894 void SketchWidget::setBackground(QColor color) {
4895 	/*QBrush brush(color);
4896 	brush.setTexture(QPixmap("/home/merun/workspace/fritzing_trunk/phoenix/resources/images/schematic_grid_tile.png"));
4897 	scene()->setBackgroundBrush(brush);*/
4898 	scene()->setBackgroundBrush(QBrush(color));
4899 }
4900 
4901 
setBackgroundColor(QColor color,bool setPrefs)4902 void SketchWidget::setBackgroundColor(QColor color, bool setPrefs) {
4903     if (setPrefs) {
4904 		QSettings settings;
4905 		settings.setValue(QString("%1BackgroundColor").arg(getShortName()), color.name());
4906 	}
4907     setBackground(color);
4908 }
4909 
4910 
background()4911 const QColor& SketchWidget::background() {
4912 	return scene()->backgroundBrush().color();
4913 }
4914 
setItemMenu(QMenu * itemMenu)4915 void SketchWidget::setItemMenu(QMenu* itemMenu){
4916 	m_itemMenu = itemMenu;
4917 }
4918 
setWireMenu(QMenu * wireMenu)4919 void SketchWidget::setWireMenu(QMenu* wireMenu){
4920 	m_wireMenu = wireMenu;
4921 }
4922 
wireConnectedSlot(long fromID,QString fromConnectorID,long toID,QString toConnectorID)4923 void SketchWidget::wireConnectedSlot(long fromID, QString fromConnectorID, long toID, QString toConnectorID) {
4924 	ItemBase * fromItem = findItem(fromID);
4925 	if (fromItem == NULL) return;
4926 
4927 	Wire* wire = qobject_cast<Wire *>(fromItem);
4928 	if (wire == NULL) return;
4929 
4930 	ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, ViewLayer::specFromID(wire->viewLayerID()));
4931 	if (fromConnectorItem == NULL) {
4932 		// shouldn't be here
4933 		return;
4934 	}
4935 
4936 	ItemBase * toItem = findItem(toID);
4937 	if (toItem == NULL) {
4938 		// this was a disconnect
4939 		return;
4940 	}
4941 
4942 	ConnectorItem * toConnectorItem = findConnectorItem(toItem, toConnectorID, ViewLayer::specFromID(wire->viewLayerID()));
4943 	if (toConnectorItem == NULL) {
4944 		// shouldn't really be here
4945 		return;
4946 	}
4947 
4948 	QPointF p1(0,0), p2, pos;
4949 
4950 	ConnectorItem * other = wire->otherConnector(fromConnectorItem);
4951 	if (fromConnectorItem == wire->connector0()) {
4952 		pos = toConnectorItem->sceneAdjustedTerminalPoint(fromConnectorItem);
4953 		ConnectorItem * toConnector1 = other->firstConnectedToIsh();
4954 		if (toConnector1 == NULL) {
4955 			p2 = other->mapToScene(other->pos()) - pos;
4956 		}
4957 		else {
4958 			p2 = toConnector1->sceneAdjustedTerminalPoint(other);
4959 		}
4960 	}
4961 	else {
4962 		pos = wire->pos();
4963 		ConnectorItem * toConnector0 = other->firstConnectedToIsh();
4964 		if (toConnector0 == NULL) {
4965 			pos = wire->pos();
4966 		}
4967 		else {
4968 			pos = toConnector0->sceneAdjustedTerminalPoint(other);
4969 		}
4970 		p2 = toConnectorItem->sceneAdjustedTerminalPoint(fromConnectorItem) - pos;
4971 	}
4972 	wire->setLineAnd(QLineF(p1, p2), pos, true);
4973 
4974 	// here's the connect (model has been previously updated)
4975 	fromConnectorItem->connectTo(toConnectorItem);
4976 	toConnectorItem->connectTo(fromConnectorItem);
4977 
4978 	this->update();
4979 
4980 }
4981 
wireDisconnectedSlot(long fromID,QString fromConnectorID)4982 void SketchWidget::wireDisconnectedSlot(long fromID, QString fromConnectorID) {
4983 	DebugDialog::debug(QString("got wire disconnected"));
4984 	ItemBase * fromItem = findItem(fromID);
4985 	if (fromItem == NULL) return;
4986 
4987 	Wire* wire = qobject_cast<Wire *>(fromItem);
4988 	if (wire == NULL) return;
4989 
4990 	ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, ViewLayer::specFromID(wire->viewLayerID()));
4991 	if (fromConnectorItem == NULL) {
4992 		// shouldn't be here
4993 		return;
4994 	}
4995 
4996 	ConnectorItem * toConnectorItem = fromConnectorItem->firstConnectedToIsh();
4997 	if (toConnectorItem != NULL) {
4998 		fromConnectorItem->removeConnection(toConnectorItem, true);
4999 		toConnectorItem->removeConnection(fromConnectorItem, true);
5000 	}
5001 }
5002 
changeConnection(long fromID,const QString & fromConnectorID,long toID,const QString & toConnectorID,ViewLayer::ViewLayerPlacement viewLayerPlacement,bool connect,bool doEmit,bool updateConnections)5003 void SketchWidget::changeConnection(long fromID, const QString & fromConnectorID,
5004 									long toID, const QString & toConnectorID,
5005 									ViewLayer::ViewLayerPlacement viewLayerPlacement,
5006 									bool connect, bool doEmit, bool updateConnections)
5007 {
5008 	changeConnectionAux(fromID, fromConnectorID, toID, toConnectorID, viewLayerPlacement, connect, updateConnections);
5009 
5010 	if (doEmit) {
5011 		//TODO:  findPartOrWire not necessary for harmonize?
5012 		//fromID = findPartOrWire(fromID);
5013 		//toID = findPartOrWire(toID);
5014 		emit changeConnectionSignal(fromID, fromConnectorID, toID, toConnectorID, viewLayerPlacement, connect, updateConnections);
5015 	}
5016 }
5017 
changeConnectionAux(long fromID,const QString & fromConnectorID,long toID,const QString & toConnectorID,ViewLayer::ViewLayerPlacement viewLayerPlacement,bool connect,bool updateConnections)5018 void SketchWidget::changeConnectionAux(long fromID, const QString & fromConnectorID,
5019 									long toID, const QString & toConnectorID,
5020 									ViewLayer::ViewLayerPlacement viewLayerPlacement,
5021 									bool connect, bool updateConnections)
5022 {
5023     // only called from the above changeConnection() which is invoked only from a command object
5024 	DebugDialog::debug(QString("changeConnection: from %1 %2; to %3 %4 con:%5 v:%6")
5025 				.arg(fromID).arg(fromConnectorID)
5026 				.arg(toID).arg(toConnectorID)
5027 				.arg(connect).arg(m_viewID) );
5028 
5029 	ItemBase * fromItem = findItem(fromID);
5030 	if (fromItem == NULL) {
5031 		DebugDialog::debug(QString("change connection exit 1 %1").arg(fromID));
5032 		return;
5033 	}
5034 
5035 	ItemBase * toItem = findItem(toID);
5036 	if (toItem == NULL) {
5037 		DebugDialog::debug(QString("change connection exit 2 %1").arg(toID));
5038 		return;
5039 	}
5040 
5041 	ConnectorItem * fromConnectorItem = findConnectorItem(fromItem, fromConnectorID, viewLayerPlacement);
5042 	if (fromConnectorItem == NULL) {
5043 		// shouldn't be here
5044 		DebugDialog::debug(QString("change connection exit 3 %1 %2").arg(fromItem->id()).arg(fromConnectorID));
5045 		return;
5046 	}
5047 
5048 	ConnectorItem * toConnectorItem = findConnectorItem(toItem, toConnectorID, viewLayerPlacement);
5049 	if (toConnectorItem == NULL) {
5050 		// shouldn't be here
5051 		DebugDialog::debug(QString("change connection exit 4 %1 %2").arg(toItem->id()).arg(toConnectorID));
5052 		return;
5053 	}
5054 
5055 	//fromConnectorItem->debugInfo("   from");
5056 	//toConnectorItem->debugInfo("   to");
5057 
5058 
5059 	ratsnestConnect(fromConnectorItem, toConnectorItem, connect, true);
5060 
5061 	if (connect) {
5062         // canConnect checks for when a THT part has been swapped for an SMD part, and the connections are now on different layers
5063         // it seems very difficult to test this condition before the part has actually been created
5064         if (canConnect(fromItem, toItem)) {
5065 		    fromConnectorItem->connector()->connectTo(toConnectorItem->connector());
5066 		    fromConnectorItem->connectTo(toConnectorItem);
5067 		    toConnectorItem->connectTo(fromConnectorItem);
5068         }
5069 	}
5070 	else {
5071 		fromConnectorItem->connector()->disconnectFrom(toConnectorItem->connector());
5072 		fromConnectorItem->removeConnection(toConnectorItem, true);
5073 		toConnectorItem->removeConnection(fromConnectorItem, true);
5074 	}
5075 
5076 	if (updateConnections) {
5077         if (updateOK(fromConnectorItem, toConnectorItem)) {
5078             QList<ConnectorItem *> already;
5079 		    fromConnectorItem->attachedTo()->updateConnections(fromConnectorItem, false, already);
5080 		    toConnectorItem->attachedTo()->updateConnections(toConnectorItem, false, already);
5081         }
5082 	}
5083 }
5084 
changeConnectionSlot(long fromID,QString fromConnectorID,long toID,QString toConnectorID,ViewLayer::ViewLayerPlacement viewLayerPlacement,bool connect,bool updateConnections)5085 void SketchWidget::changeConnectionSlot(long fromID, QString fromConnectorID,
5086 												 long toID, QString toConnectorID,
5087 												 ViewLayer::ViewLayerPlacement viewLayerPlacement,
5088 												 bool connect, bool updateConnections)
5089 {
5090 	changeConnection(fromID, fromConnectorID,
5091 					 toID, toConnectorID, viewLayerPlacement,
5092 					 connect, false, updateConnections);
5093 }
5094 
keyReleaseEvent(QKeyEvent * event)5095 void SketchWidget::keyReleaseEvent(QKeyEvent * event) {
5096 	//DebugDialog::debug(QString("key release event %1").arg(event->isAutoRepeat()));
5097 	if (m_movingByArrow) {
5098 		m_autoScrollTimer.stop();
5099 		m_arrowTimer.start();
5100 		//DebugDialog::debug("key release event");
5101 	}
5102 	else {
5103 		QGraphicsView::keyReleaseEvent(event);
5104 	}
5105 }
5106 
arrowTimerTimeout()5107 void SketchWidget::arrowTimerTimeout() {
5108 	m_movingByArrow = false;
5109 	if (checkMoved(false)) {
5110 		m_savedItems.clear();
5111 		m_savedWires.clear();
5112 	}
5113 }
5114 
keyPressEvent(QKeyEvent * event)5115 void SketchWidget::keyPressEvent ( QKeyEvent * event ) {
5116 	//DebugDialog::debug("key press event");
5117 	if ((m_inFocus.length() == 0) && !m_movingByMouse) {
5118 		int dx = 0, dy = 0;
5119 		switch (event->key()) {
5120 			case Qt::Key_Up:
5121 				dy = -1;
5122 				break;
5123 			case Qt::Key_Down:
5124 				dy = 1;
5125 				break;
5126 			case Qt::Key_Left:
5127 				dx = -1;
5128 				break;
5129 			case Qt::Key_Right:
5130 				dx = 1;
5131 				break;
5132 			default:
5133 				break;
5134 		}
5135 		if (dx != 0 || dy != 0) {
5136 			m_arrowTimer.stop();
5137 			DebugDialog::debug("arrow press event");
5138 			ConnectorItem::clearEqualPotentialDisplay();
5139 			moveByArrow(dx, dy, event);
5140 			m_arrowTimer.start();
5141 			return;
5142 		}
5143 	}
5144 
5145 	QGraphicsView::keyPressEvent(event);
5146 }
5147 
makeDeleteItemCommand(ItemBase * itemBase,BaseCommand::CrossViewType crossView,QUndoCommand * parentCommand)5148 void SketchWidget::makeDeleteItemCommand(ItemBase * itemBase, BaseCommand::CrossViewType crossView, QUndoCommand * parentCommand) {
5149 
5150 	if (crossView == BaseCommand::CrossView) {
5151 		emit makeDeleteItemCommandPrepSignal(itemBase, true, parentCommand);
5152 	}
5153 	makeDeleteItemCommandPrepSlot(itemBase, false, parentCommand);
5154 
5155 	if (crossView == BaseCommand::CrossView) {
5156 		emit makeDeleteItemCommandFinalSignal(itemBase, true, parentCommand);
5157 	}
5158 	makeDeleteItemCommandFinalSlot(itemBase, false, parentCommand);
5159 
5160 }
5161 
makeDeleteItemCommandPrepSlot(ItemBase * itemBase,bool foreign,QUndoCommand * parentCommand)5162 void SketchWidget::makeDeleteItemCommandPrepSlot(ItemBase * itemBase, bool foreign, QUndoCommand * parentCommand)
5163 {
5164 	if (foreign) {
5165 		itemBase = findItem(itemBase->id());
5166 		if (itemBase == NULL) return;
5167 	}
5168 
5169 	if (itemBase->isPartLabelVisible()) {
5170 		ShowLabelCommand * slc = new ShowLabelCommand(this, parentCommand);
5171 		slc->add(itemBase->id(), true, true);
5172 	}
5173 
5174 	Note * note = qobject_cast<Note *>(itemBase);
5175 	if (note != NULL) {
5176 		ChangeNoteTextCommand * cntc = new ChangeNoteTextCommand(this, note->id(), note->text(), note->text(), QSizeF(), QSizeF(), parentCommand);
5177         cntc->setSkipFirstRedo();
5178 	}
5179 	else {
5180 		new ChangeLabelTextCommand(this, itemBase->id(), itemBase->instanceTitle(), itemBase->instanceTitle(), parentCommand);
5181 	}
5182 
5183 	if (!foreign) {
5184         QMap<QString, QString> propsMap;
5185 		prepDeleteProps(itemBase, itemBase->id(), "", propsMap, parentCommand);
5186 	}
5187 
5188 	rememberSticky(itemBase, parentCommand);
5189 
5190 	Wire * wire = qobject_cast<Wire *>(itemBase);
5191 	if (wire) {
5192 		const Bezier * bezier = wire->curve();
5193 		if (bezier && !bezier->isEmpty()) {
5194 			ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, itemBase->id(), bezier, NULL, wire->getAutoroutable(), parentCommand);
5195 			cwcc->setUndoOnly();
5196 		}
5197 	}
5198 
5199 	if (itemBase->hasRubberBandLeg()) {
5200 		foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
5201 			if (!connectorItem->hasRubberBandLeg()) continue;
5202 
5203 			// backwards order: curves then polys, since these will be trigged by undo
5204 			QVector<Bezier *> beziers = connectorItem->beziers();
5205 			for (int i = 0; i < beziers.count() - 1; i++) {
5206 				Bezier * bezier = beziers.at(i);
5207 				if (bezier == NULL) continue;
5208 				if (bezier->isEmpty()) continue;
5209 
5210 				ChangeLegCurveCommand * clcc = new ChangeLegCurveCommand(this, itemBase->id(), connectorItem->connectorSharedID(), i, bezier, bezier, parentCommand);
5211 				clcc->setUndoOnly();
5212 			}
5213 
5214 			QPolygonF poly = connectorItem->leg();
5215 			ChangeLegCommand * clc = new ChangeLegCommand(this, itemBase->id(), connectorItem->connectorSharedID(), poly, poly, true, true, "delete", parentCommand);
5216 			clc->setUndoOnly();
5217 
5218 			// TODO: beziers here
5219 		}
5220 	}
5221 }
5222 
makeDeleteItemCommandFinalSlot(ItemBase * itemBase,bool foreign,QUndoCommand * parentCommand)5223 void SketchWidget::makeDeleteItemCommandFinalSlot(ItemBase * itemBase, bool foreign, QUndoCommand * parentCommand)
5224 {
5225 	if (foreign) {
5226 		itemBase = findItem(itemBase->id());
5227 		if (itemBase == NULL) return;
5228 	}
5229 
5230 	ModelPart * mp = itemBase->modelPart();
5231 	// single view because this is called for each view
5232 	new DeleteItemCommand(this, BaseCommand::SingleView, mp->moduleID(), itemBase->viewLayerPlacement(), itemBase->getViewGeometry(), itemBase->id(), mp->modelIndex(), parentCommand);
5233 }
5234 
prepDeleteProps(ItemBase * itemBase,long id,const QString & newModuleID,QMap<QString,QString> & propsMap,QUndoCommand * parentCommand)5235 void SketchWidget::prepDeleteProps(ItemBase * itemBase, long id, const QString & newModuleID, QMap<QString, QString> & propsMap, QUndoCommand * parentCommand)
5236 {
5237 	// TODO: does this need to be generalized to the whole set of modelpart props?
5238 	// TODO: Ruler?
5239 
5240 	// TODO: this all belongs in PartFactory or in a call to the part itself
5241 
5242 	// NOTE:  prepDeleteProps is called after a swap and assumes that the new part is closely related to the old part
5243 	// meaning that the properties of itemBase (which is the old part) apply to the new part (which has not yet been created)
5244 	// this works most of the time, but does not, for example, when a ResizableBoard is swapped for a custom board shape
5245 
5246     bool boardToCustomBoard = false;
5247 	ModelPart * mp = (newModuleID.isEmpty()) ? itemBase->modelPart() : referenceModel()->retrieveModelPart(newModuleID);
5248     if (mp->itemType() == ModelPart::Logo && qobject_cast<Board *>(itemBase) != NULL) {
5249         boardToCustomBoard = true;
5250         mp = itemBase->modelPart();
5251     }
5252 
5253 	switch (mp->itemType()) {
5254 		case ModelPart::Wire:
5255 			{
5256 				Wire * wire = qobject_cast<Wire *>(itemBase);
5257 				new WireWidthChangeCommand(this, id, wire->width(), wire->width(), parentCommand);
5258 				new WireColorChangeCommand(this, id, wire->colorString(), wire->colorString(), wire->opacity(), wire->opacity(), parentCommand);
5259 			}
5260 			return;
5261 
5262 		case ModelPart::Board:
5263 			new ChangeBoardLayersCommand(this, m_boardLayers, m_boardLayers, parentCommand);
5264 			prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5265 			return;
5266 
5267 		case ModelPart::ResizableBoard:
5268 			{
5269 				new ChangeBoardLayersCommand(this, m_boardLayers, m_boardLayers, parentCommand);
5270 				ResizableBoard * brd = qobject_cast<ResizableBoard *>(itemBase);
5271 				if (brd) {
5272 					brd->saveParams();
5273 					QPointF p;
5274 					QSizeF sz;
5275 					brd->getParams(p, sz);
5276 					ResizeBoardCommand * rbc = new ResizeBoardCommand(this, id, sz.width(), sz.height(), sz.width(), sz.height(), parentCommand);
5277                     if (boardToCustomBoard) {
5278                         rbc->setUndoOnly();
5279                     }
5280 				}
5281 				prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5282 			}
5283 			return;
5284 
5285 		case ModelPart::Logo:
5286 			{
5287 				LogoItem * logo = qobject_cast<LogoItem *>(itemBase);
5288 				logo->saveParams();
5289 				QPointF p;
5290 				QSizeF sz;
5291 				logo->getParams(p, sz);
5292 				new ResizeBoardCommand(this, id, sz.width(), sz.height(), sz.width(), sz.height(), parentCommand);
5293 				QString logoProp = logo->prop("logo");
5294 				QString shapeProp = logo->prop("shape");
5295 				if (!logoProp.isEmpty()) {
5296 					new SetPropCommand(this, id, "logo", logoProp, logoProp, true, parentCommand);
5297 				}
5298 				else if (!shapeProp.isEmpty()) {
5299                     QString newName = logo->getNewLayerFileName(propsMap.value("layer"));
5300 					new LoadLogoImageCommand(this, id, shapeProp, logo->modelPart()->localProp("aspectratio").toSizeF(), logo->prop("lastfilename"), newName, false, parentCommand);
5301 				}
5302 				prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5303 			}
5304 			return;
5305 
5306 		case ModelPart::Jumper:
5307 			{
5308 				JumperItem * jumper = qobject_cast<JumperItem *>(itemBase);
5309 				jumper->saveParams();
5310 				QPointF p;
5311 				QPointF c0, c1;
5312 				jumper->getParams(p, c0, c1);
5313 				new ResizeJumperItemCommand(this, id, p, c0, c1, p, c0, c1, parentCommand);
5314 				prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5315 			}
5316 			return;
5317 
5318 		case ModelPart::CopperFill:
5319 			{
5320 				GroundPlane * groundPlane = dynamic_cast<GroundPlane *>(itemBase);
5321 				new SetPropCommand(this, id, "svg", groundPlane->svg(), groundPlane->svg(), true, parentCommand);
5322 				prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5323 			}
5324 			return;
5325 
5326         case ModelPart::Symbol:
5327             {
5328 				SymbolPaletteItem * sitem = dynamic_cast<SymbolPaletteItem *>(itemBase);
5329                 QString label = sitem->getLabel();
5330                 if (!label.isEmpty()) {
5331 				    new SetPropCommand(this, id, "label", label, label, true, parentCommand);
5332                 }
5333 				prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5334             }
5335             return;
5336 
5337 		default:
5338 			break;
5339 	}
5340 
5341     Pad* pad = qobject_cast<Pad *>(itemBase);
5342     if (pad != NULL) {
5343 		pad->saveParams();
5344 		QPointF p;
5345 		QSizeF sz;
5346 		pad->getParams(p, sz);
5347 		new ResizeBoardCommand(this, id, sz.width(), sz.height(), sz.width(), sz.height(), parentCommand);
5348         prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5349         return;
5350     }
5351 
5352 	Resistor * resistor =  qobject_cast<Resistor *>(itemBase);
5353 	if (resistor != NULL) {
5354 		new SetResistanceCommand(this, id, resistor->resistance(), resistor->resistance(), resistor->pinSpacing(), resistor->pinSpacing(), parentCommand);
5355 		prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5356 		return;
5357 	}
5358 
5359 	MysteryPart * mysteryPart = qobject_cast<MysteryPart *>(itemBase);
5360 	if (mysteryPart != NULL) {
5361 		new SetPropCommand(this, id, "chip label", mysteryPart->chipLabel(), mysteryPart->chipLabel(), true, parentCommand);
5362 		prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5363 		return;
5364 	}
5365 
5366     /*
5367 	PinHeader * pinHeader = qobject_cast<PinHeader *>(itemBase);
5368 	if (pinHeader != NULL) {
5369 		// deal with old-style pin headers (pre 0.6.4)
5370 		new SetPropCommand(this, id, "form", pinHeader->form(), PinHeader::findForm(newModuleID), true, parentCommand);
5371 		prepDeleteOtherProps(itemBase, id, newModuleID, parentCommand);
5372 		return;
5373 	}
5374     */
5375 
5376 	Hole * hole = qobject_cast<Hole *>(itemBase);
5377 	if (hole != NULL) {
5378 		new SetPropCommand(this, id, "hole size", hole->holeSize(), hole->holeSize(), true, parentCommand);
5379 		prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5380 		return;
5381 	}
5382 
5383 	prepDeleteOtherProps(itemBase, id, newModuleID, propsMap, parentCommand);
5384 }
5385 
prepDeleteOtherProps(ItemBase * itemBase,long id,const QString & newModuleID,QMap<QString,QString> & propsMap,QUndoCommand * parentCommand)5386 void SketchWidget::prepDeleteOtherProps(ItemBase * itemBase, long id, const QString & newModuleID, QMap<QString, QString> & propsMap, QUndoCommand * parentCommand)
5387 {
5388 	Capacitor * capacitor = qobject_cast<Capacitor *>(itemBase);
5389 	if (capacitor) {
5390 		QHash<QString, QString> properties;
5391 		capacitor->getProperties(properties);
5392 		foreach(QString prop, properties.keys()) {
5393 			new SetPropCommand(this, id, prop, properties.value(prop), properties.value(prop), true, parentCommand);
5394 		}
5395 	}
5396 
5397 	if (itemBase->moduleID().endsWith(ModuleIDNames::StripboardModuleIDName) || itemBase->moduleID().endsWith(ModuleIDNames::Stripboard2ModuleIDName)) {
5398 		QString buses = itemBase->prop("buses");
5399         QString newBuses = propsMap.value("buses");
5400         if (newBuses.isEmpty()) newBuses = buses;
5401 		if (!buses.isEmpty()) {
5402 			new SetPropCommand(this, id, "buses", buses, newBuses, true, parentCommand);
5403 		}
5404 
5405         QString layout = itemBase->prop("layout");
5406         QString newLayout = propsMap.value("layout");
5407         if (newLayout.isEmpty()) newLayout = layout;
5408 		if (!layout.isEmpty()) {
5409 			new SetPropCommand(this, id, "layout", layout, newLayout, true, parentCommand);
5410 		}
5411 	}
5412 
5413 	QString value = itemBase->modelPart()->localProp(ModelPartShared::PartNumberPropertyName).toString();
5414 	if (!value.isEmpty()) {
5415 		QString newValue = value;
5416 		if (!newModuleID.isEmpty()) {
5417 			newValue = "";
5418 			ModelPart * newModelPart = m_referenceModel->retrieveModelPart(newModuleID);
5419 			if (newModelPart != NULL) {
5420 				newValue = newModelPart->properties().value(ModelPartShared::PartNumberPropertyName, "");
5421 			}
5422 		}
5423 		new SetPropCommand(this, id, ModelPartShared::PartNumberPropertyName, value, newValue, true, parentCommand);
5424 	}
5425 }
5426 
rememberSticky(long id,QUndoCommand * parentCommand)5427 void SketchWidget::rememberSticky(long id, QUndoCommand * parentCommand) {
5428 	ItemBase * itemBase = findItem(id);
5429 	if (itemBase == NULL) return;
5430 
5431 	rememberSticky(itemBase, parentCommand);
5432 }
5433 
rememberSticky(ItemBase * itemBase,QUndoCommand * parentCommand)5434 void SketchWidget::rememberSticky(ItemBase * itemBase, QUndoCommand * parentCommand) {
5435 
5436 	QList< QPointer<ItemBase> > stickyList = itemBase->stickyList();
5437 	if (stickyList.count() <= 0) return;
5438 
5439 	CheckStickyCommand * checkStickyCommand = new CheckStickyCommand(this, BaseCommand::SingleView, itemBase->id(), false, CheckStickyCommand::UndoOnly, parentCommand);
5440 	if (itemBase->isBaseSticky()) {
5441 		foreach (ItemBase * sticking, stickyList) {
5442 			checkStickyCommand->stick(this, itemBase->id(), sticking->id(), true);
5443 		}
5444 	}
5445 	else if (itemBase->stickingTo() != NULL) {
5446 		checkStickyCommand->stick(this, itemBase->stickingTo()->id(), itemBase->id(), true);
5447 	}
5448 }
5449 
viewID()5450 ViewLayer::ViewID SketchWidget::viewID() {
5451 	return m_viewID;
5452 }
5453 
setViewLayerIDs(ViewLayer::ViewLayerID part,ViewLayer::ViewLayerID wire,ViewLayer::ViewLayerID connector,ViewLayer::ViewLayerID ruler,ViewLayer::ViewLayerID note)5454 void SketchWidget::setViewLayerIDs(ViewLayer::ViewLayerID part, ViewLayer::ViewLayerID wire, ViewLayer::ViewLayerID connector, ViewLayer::ViewLayerID ruler, ViewLayer::ViewLayerID note) {
5455 	m_partViewLayerID = part;
5456 	m_wireViewLayerID = wire;
5457 	m_connectorViewLayerID = connector;
5458 	m_rulerViewLayerID = ruler;
5459 	m_noteViewLayerID = note;
5460 }
5461 
dragIsDoneSlot(ItemDrag * itemDrag)5462 void SketchWidget::dragIsDoneSlot(ItemDrag * itemDrag) {
5463 	disconnect(itemDrag, SIGNAL(dragIsDoneSignal(ItemDrag *)), this, SLOT(dragIsDoneSlot(ItemDrag *)));
5464 	killDroppingItem();					// drag is done, but nothing dropped here: remove the temporary item
5465 }
5466 
5467 
clearTemporaries()5468 void SketchWidget::clearTemporaries() {
5469 	for (int i = 0; i < m_temporaries.count(); i++) {
5470 		QGraphicsItem * item = m_temporaries[i];
5471 		scene()->removeItem(item);
5472 		delete item;
5473 	}
5474 
5475 	m_temporaries.clear();
5476 }
5477 
killDroppingItem()5478 void SketchWidget::killDroppingItem() {
5479 	m_alignmentItem = NULL;
5480 	if (m_droppingItem != NULL) {
5481 		m_droppingItem->removeLayerKin();
5482 		this->scene()->removeItem(m_droppingItem);
5483         if (m_droppingItem->modelPart()) {
5484             delete m_droppingItem->modelPart();
5485         }
5486 		delete m_droppingItem;
5487 		m_droppingItem = NULL;
5488 	}
5489 }
5490 
getViewLayerID(ModelPart * modelPart,ViewLayer::ViewID viewID,ViewLayer::ViewLayerPlacement viewLayerPlacement)5491 ViewLayer::ViewLayerID SketchWidget::getViewLayerID(ModelPart * modelPart, ViewLayer::ViewID viewID, ViewLayer::ViewLayerPlacement viewLayerPlacement) {
5492 
5493     LayerList viewLayers = modelPart->viewLayers(viewID);
5494 
5495 	if (viewLayers.count() == 1) {
5496 		return viewLayers.at(0);
5497 	}
5498 
5499 	return multiLayerGetViewLayerID(modelPart, viewID, viewLayerPlacement, viewLayers);
5500 }
5501 
multiLayerGetViewLayerID(ModelPart * modelPart,ViewLayer::ViewID viewID,ViewLayer::ViewLayerPlacement viewLayerPlacement,LayerList & layerList)5502 ViewLayer::ViewLayerID SketchWidget::multiLayerGetViewLayerID(ModelPart * modelPart, ViewLayer::ViewID viewID, ViewLayer::ViewLayerPlacement viewLayerPlacement, LayerList & layerList) {
5503 	Q_UNUSED(modelPart);
5504 	Q_UNUSED(viewID);
5505 	Q_UNUSED(viewLayerPlacement);
5506 
5507     if (layerList.count() == 0) return ViewLayer::UnknownLayer;
5508 
5509 	return layerList.at(0);
5510 }
5511 
overSticky(ItemBase * itemBase)5512 ItemBase * SketchWidget::overSticky(ItemBase * itemBase) {
5513 	if (!itemBase->stickyEnabled()) return NULL;
5514 
5515 	foreach (QGraphicsItem * item, scene()->collidingItems(itemBase)) {
5516 		ItemBase * s = dynamic_cast<ItemBase *>(item);
5517 		if (s == NULL) continue;
5518 		if (s == itemBase) continue;
5519 		if (!s->isBaseSticky()) continue;
5520 
5521 		return s->layerKinChief();
5522 	}
5523 
5524 	return NULL;
5525 }
5526 
5527 
stickem(long stickTargetID,long stickSourceID,bool stick)5528 void SketchWidget::stickem(long stickTargetID, long stickSourceID, bool stick) {
5529 	ItemBase * stickTarget = findItem(stickTargetID);
5530 	if (stickTarget == NULL) return;
5531 
5532 	ItemBase * stickSource = findItem(stickSourceID);
5533 	if (stickSource == NULL) return;
5534 
5535 	stickTarget->addSticky(stickSource, stick);
5536 	stickSource->addSticky(stickTarget, stick);
5537 }
5538 
setChainDrag(bool chainDrag)5539 void SketchWidget::setChainDrag(bool chainDrag) {
5540 	m_chainDrag = chainDrag;
5541 }
5542 
stickyScoop(ItemBase * stickyOne,bool checkCurrent,CheckStickyCommand * checkStickyCommand)5543 void SketchWidget::stickyScoop(ItemBase * stickyOne, bool checkCurrent, CheckStickyCommand * checkStickyCommand) {
5544 	// TODO: use the shape rather than the rect
5545 	// need to find the best layerkin to use in that case
5546 	//foreach (QGraphicsItem * item, scene()->collidingItems(stickyOne)) {
5547 
5548 	QList<ItemBase *> added;
5549 	QList<ItemBase *> already;
5550 	QPolygonF poly = stickyOne->mapToScene(stickyOne->boundingRect());
5551 	foreach (QGraphicsItem * item, scene()->items(poly)) {
5552 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
5553 		if (itemBase == NULL) continue;
5554 
5555 		itemBase = itemBase->layerKinChief();
5556 
5557 		if (!itemBase->stickyEnabled()) continue;
5558 		if (added.contains(itemBase)) continue;
5559 		if (itemBase->isBaseSticky()) continue;
5560 		if (stickyOne->alreadySticking(itemBase)) {
5561 			already.append(itemBase);
5562 			continue;
5563 		}
5564 
5565 		stickyOne->addSticky(itemBase, true);
5566 		itemBase->addSticky(stickyOne, true);
5567 		if (checkStickyCommand) {
5568 			checkStickyCommand->stick(this, stickyOne->id(), itemBase->id(), true);
5569 		}
5570 
5571 		added.append(itemBase);
5572 	}
5573 
5574 	if (checkCurrent) {
5575 		foreach (ItemBase * itemBase, stickyOne->stickyList()) {
5576 			if (added.contains(itemBase)) continue;
5577 			if (already.contains(itemBase)) continue;
5578 
5579 			stickyOne->addSticky(itemBase, false);
5580 			itemBase->addSticky(stickyOne, false);
5581 			if (checkStickyCommand) {
5582 				checkStickyCommand->stick(this, stickyOne->id(), itemBase->id(), false);
5583 			}
5584 		}
5585 	}
5586 }
5587 
wireSplitSlot(Wire * wire,QPointF newPos,QPointF oldPos,const QLineF & oldLine)5588 void SketchWidget::wireSplitSlot(Wire* wire, QPointF newPos, QPointF oldPos, const QLineF & oldLine) {
5589 	if (!canChainWire(wire)) return;
5590 
5591 	this->clearHoldingSelectItem();
5592 	this->m_moveEventCount = 0;  // clear this so an extra MoveItemCommand isn't posted
5593 
5594 	QUndoCommand * parentCommand = new QUndoCommand();
5595 	parentCommand->setText(QObject::tr("Split Wire") );
5596 
5597 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
5598 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
5599 
5600 	long fromID = wire->id();
5601 
5602 	QLineF newLine(oldLine.p1(), newPos - oldPos);
5603 
5604 	long newID = ItemBase::getNextID();
5605 	ViewGeometry vg(wire->getViewGeometry());
5606 	vg.setLoc(newPos);
5607 	QLineF newLine2(QPointF(0,0), oldLine.p2() + oldPos - newPos);
5608 	vg.setLine(newLine2);
5609 
5610 	BaseCommand::CrossViewType crossView = wireSplitCrossView();
5611 
5612 	new AddItemCommand(this, crossView, ModuleIDNames::WireModuleIDName, wire->viewLayerPlacement(), vg, newID, true, -1, parentCommand);
5613 	new CheckStickyCommand(this, crossView, newID, false, CheckStickyCommand::RemoveOnly, parentCommand);
5614 	new WireColorChangeCommand(this, newID, wire->colorString(), wire->colorString(), wire->opacity(), wire->opacity(), parentCommand);
5615 	new WireWidthChangeCommand(this, newID, wire->width(), wire->width(), parentCommand);
5616     if (wire->banded()) {
5617         new SetPropCommand(this, newID, "banded", "Yes", "Yes", true, parentCommand);
5618     }
5619 
5620 	// disconnect from original wire and reconnect to new wire
5621 	ConnectorItem * connector1 = wire->connector1();
5622 	foreach (ConnectorItem * toConnectorItem, connector1->connectedToItems()) {
5623 		new ChangeConnectionCommand(this, crossView, toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
5624 			wire->id(), connector1->connectorSharedID(),
5625 			ViewLayer::specFromID(wire->viewLayerID()),
5626 			false, parentCommand);
5627 		new ChangeConnectionCommand(this, crossView, toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
5628 			newID, connector1->connectorSharedID(),
5629 			ViewLayer::specFromID(wire->viewLayerID()),
5630 			true, parentCommand);
5631 	}
5632 
5633 	new ChangeWireCommand(this, fromID, oldLine, newLine, oldPos, oldPos, true, false, parentCommand);
5634 
5635 	// connect the two wires
5636 	new ChangeConnectionCommand(this, crossView, wire->id(), connector1->connectorSharedID(),
5637 								newID, "connector0",
5638 								ViewLayer::specFromID(wire->viewLayerID()),
5639 								true, parentCommand);
5640 
5641 	SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
5642 	selectItemCommand->addRedo(newID);
5643 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
5644 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
5645 
5646 	m_undoStack->push(parentCommand);
5647 }
5648 
wireJoinSlot(Wire * wire,ConnectorItem * clickedConnectorItem)5649 void SketchWidget::wireJoinSlot(Wire* wire, ConnectorItem * clickedConnectorItem) {
5650 	// if (!canChainWire(wire)) return;  // can't join a wire in some views (for now)
5651 
5652 	this->clearHoldingSelectItem();
5653 	this->m_moveEventCount = 0;  // clear this so an extra MoveItemCommand isn't posted
5654 
5655 	QUndoCommand * parentCommand = new QUndoCommand();
5656 	parentCommand->setText(QObject::tr("Join Wire") );
5657 
5658 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
5659 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
5660 
5661 	// assumes there is one and only one item connected
5662 	ConnectorItem * toConnectorItem = clickedConnectorItem->connectedToItems()[0];
5663 	if (toConnectorItem == NULL) return;
5664 
5665 	Wire * toWire = qobject_cast<Wire *>(toConnectorItem->attachedTo());
5666 	if (toWire == NULL) return;
5667 
5668 	if (wire->id() > toWire->id()) {
5669 		// delete the wire with the higher id
5670 		// so we can keep the three views in sync
5671 		// i.e. the original wire has the lowest id in the chain
5672 		Wire * wtemp = toWire;
5673 		toWire = wire;
5674 		wire = wtemp;
5675 		ConnectorItem * ctemp = toConnectorItem;
5676 		toConnectorItem = clickedConnectorItem;
5677 		clickedConnectorItem = ctemp;
5678 	}
5679 
5680 	ConnectorItem * otherConnector = toWire->otherConnector(toConnectorItem);
5681 
5682 	BaseCommand::CrossViewType crossView = BaseCommand::CrossView; // wireSplitCrossView();
5683 
5684 	ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, wire->id(), wire->curve(), NULL, wire->getAutoroutable(), parentCommand);
5685 	cwcc->setUndoOnly();
5686 	cwcc = new ChangeWireCurveCommand(this, toWire->id(), toWire->curve(), NULL, toWire->getAutoroutable(),parentCommand);
5687 	cwcc->setUndoOnly();
5688 
5689 
5690 	// disconnect the wires
5691 	new ChangeConnectionCommand(this, crossView, wire->id(), clickedConnectorItem->connectorSharedID(),
5692 								toWire->id(), toConnectorItem->connectorSharedID(),
5693 								ViewLayer::specFromID(wire->viewLayerID()),
5694 								false, parentCommand);
5695 
5696 	// disconnect everyone from the other end of the wire being deleted, and reconnect to the remaining wire
5697 	foreach (ConnectorItem * otherToConnectorItem, otherConnector->connectedToItems()) {
5698 		new ChangeConnectionCommand(this, crossView, otherToConnectorItem->attachedToID(), otherToConnectorItem->connectorSharedID(),
5699 			toWire->id(), otherConnector->connectorSharedID(),
5700 			ViewLayer::specFromID(toWire->viewLayerID()),
5701 			false, parentCommand);
5702 		new ChangeConnectionCommand(this, crossView, otherToConnectorItem->attachedToID(), otherToConnectorItem->connectorSharedID(),
5703 			wire->id(), clickedConnectorItem->connectorSharedID(),
5704 			ViewLayer::specFromID(wire->viewLayerID()),
5705 			true, parentCommand);
5706 	}
5707 
5708 	toWire->saveGeometry();
5709 	makeDeleteItemCommand(toWire, crossView, parentCommand);
5710 
5711 	Bezier b0, b1;
5712 	QLineF newLine;
5713 	QPointF newPos;
5714 	if (otherConnector == toWire->connector1()) {
5715 		newPos = wire->pos();
5716 		newLine = QLineF(QPointF(0,0), toWire->pos() - wire->pos() + toWire->line().p2());
5717 		b0.copy(wire->curve());
5718 		b1.copy(toWire->curve());
5719 		b0.set_endpoints(wire->line().p1(), wire->line().p2());
5720 		b1.set_endpoints(toWire->line().p1(), toWire->line().p2());
5721 		b1.translate(toWire->pos() - wire->pos());
5722 
5723 	}
5724 	else {
5725 		newPos = toWire->pos();
5726 		newLine = QLineF(QPointF(0,0), wire->pos() - toWire->pos() + wire->line().p2());
5727 		b0.copy(toWire->curve());
5728 		b1.copy(wire->curve());
5729 		b0.set_endpoints(toWire->line().p1(), toWire->line().p2());
5730 		b1.set_endpoints(wire->line().p1(), wire->line().p2());
5731 		b1.translate(wire->pos() - toWire->pos());
5732 	}
5733 	new ChangeWireCommand(this, wire->id(), wire->line(), newLine, wire->pos(), newPos, true, false, parentCommand);
5734 	Bezier joinBezier = b0.join(&b1);
5735 	if (!joinBezier.isEmpty()) {
5736 		ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, wire->id(), wire->curve(), &joinBezier, wire->getAutoroutable(), parentCommand);
5737 		cwcc->setRedoOnly();
5738 	}
5739     new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
5740 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
5741 
5742 	m_undoStack->push(parentCommand);
5743 }
5744 
hoverEnterItem(QGraphicsSceneHoverEvent * event,ItemBase * item)5745 void SketchWidget::hoverEnterItem(QGraphicsSceneHoverEvent * event, ItemBase * item) {
5746 	if(m_infoViewOnHover || currentlyInfoviewed(item)) {
5747 		InfoGraphicsView::hoverEnterItem(event, item);
5748 	}
5749 
5750 	Wire * wire = dynamic_cast<Wire *>(item);
5751 	if (wire != NULL) {
5752 		if (canChainWire(wire)) {
5753 			bool segment = wire->connector0()->chained() && wire->connector1()->chained();
5754 			bool disconnected = wire->connector0()->connectionsCount() == 0 &&  wire->connector1()->connectionsCount() == 0;
5755 			statusMessage(QString("%1 to add a bendpoint %2")
5756 				.arg(disconnected ? tr("Double-click") : tr("Drag or double-click"))
5757 				.arg(segment ? tr("or alt-drag to move the segment") : tr("")));
5758 			m_lastHoverEnterItem = item;
5759 		}
5760 	}
5761 }
5762 
statusMessage(QString message,int timeout)5763 void SketchWidget::statusMessage(QString message, int timeout)
5764 {
5765 	// TODO: eventually do the connecting from the window not from the sketch
5766 	QMainWindow * mainWindow = qobject_cast<QMainWindow *>(window());
5767 	if (mainWindow == NULL) return;
5768 
5769 	if (m_statusConnectState == StatusConnectNotTried) {
5770 		bool result = connect(this, SIGNAL(statusMessageSignal(QString, int)),
5771 							  mainWindow, SLOT(statusMessage(QString, int)));
5772 		m_statusConnectState = (result) ? StatusConnectSucceeded : StatusConnectFailed;
5773 	}
5774 
5775 	if (m_statusConnectState == StatusConnectFailed) {
5776 		QStatusBar * sb = mainWindow->statusBar();
5777 		if (sb != NULL) {
5778 			sb->showMessage(message, timeout);
5779 		}
5780 	}
5781 	else {
5782 		emit statusMessageSignal(message, timeout);
5783 	}
5784 }
5785 
hoverLeaveItem(QGraphicsSceneHoverEvent * event,ItemBase * item)5786 void SketchWidget::hoverLeaveItem(QGraphicsSceneHoverEvent * event, ItemBase * item){
5787 	m_lastHoverEnterItem = NULL;
5788 
5789 	if(m_infoViewOnHover) {
5790 		InfoGraphicsView::hoverLeaveItem(event, item);
5791 	}
5792 
5793 	if (canChainWire(qobject_cast<Wire *>(item))) {
5794 		statusMessage(QString());
5795 	}
5796 }
5797 
hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event,ConnectorItem * item)5798 void SketchWidget::hoverEnterConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item) {
5799 	if(m_infoViewOnHover || currentlyInfoviewed(item->attachedTo())) {
5800 		InfoGraphicsView::hoverEnterConnectorItem(event, item);
5801 	}
5802 
5803 	if (item->attachedToItemType() == ModelPart::Wire) {
5804 		if (!this->m_chainDrag) return;
5805 		if (!item->chained()) return;
5806 
5807 		m_lastHoverEnterConnectorItem = item;
5808 		QString msg = hoverEnterWireConnectorMessage(event, item);
5809 		statusMessage(msg);
5810 	}
5811 	else {
5812 		QString msg = hoverEnterPartConnectorMessage(event, item);
5813 		statusMessage(msg);
5814 	}
5815 
5816 }
hoverEnterWireConnectorMessage(QGraphicsSceneHoverEvent * event,ConnectorItem * item)5817 const QString & SketchWidget::hoverEnterWireConnectorMessage(QGraphicsSceneHoverEvent * event, ConnectorItem * item)
5818 {
5819 	Q_UNUSED(event);
5820 	Q_UNUSED(item);
5821 
5822 	static QString message = tr("Double-click to delete this bend point");
5823 	return message;
5824 }
5825 
5826 
hoverEnterPartConnectorMessage(QGraphicsSceneHoverEvent * event,ConnectorItem * item)5827 const QString & SketchWidget::hoverEnterPartConnectorMessage(QGraphicsSceneHoverEvent * event, ConnectorItem * item)
5828 {
5829 	Q_UNUSED(event);
5830 	Q_UNUSED(item);
5831 
5832 	return ___emptyString___;
5833 }
5834 
hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event,ConnectorItem * item)5835 void SketchWidget::hoverLeaveConnectorItem(QGraphicsSceneHoverEvent * event, ConnectorItem * item) {
5836 	m_lastHoverEnterConnectorItem = NULL;
5837 
5838 	ItemBase* attachedTo = item->attachedTo();
5839 	if(attachedTo) {
5840 		if(m_infoViewOnHover || currentlyInfoviewed(attachedTo)) {
5841 			InfoGraphicsView::hoverLeaveConnectorItem(event, item);
5842 			if(attachedTo->collidesWithItem(item)) {
5843 				hoverEnterItem(event,attachedTo);
5844 			}
5845 		}
5846 		attachedTo->hoverLeaveConnectorItem(event, item);
5847 	}
5848 
5849 	if (attachedTo->itemType() == ModelPart::Wire) {
5850 		if (!this->m_chainDrag) return;
5851 		if (!item->chained()) return;
5852 
5853 		statusMessage(QString());
5854 	}
5855 	else {
5856 		statusMessage(QString());
5857 	}
5858 }
5859 
currentlyInfoviewed(ItemBase * item)5860 bool SketchWidget::currentlyInfoviewed(ItemBase *item) {
5861 	if(m_infoView) {
5862 		ItemBase * currInfoView = m_infoView->currentItem();
5863 		return !currInfoView || item == currInfoView;//->cu selItems.size()==1 && selItems[0] == item;
5864 	}
5865 	return false;
5866 }
5867 
cleanUpWires(bool doEmit,CleanUpWiresCommand * command)5868 void SketchWidget::cleanUpWires(bool doEmit, CleanUpWiresCommand * command) {
5869 	RoutingStatus routingStatus;
5870 	updateRoutingStatus(command, routingStatus, false);
5871 
5872 	if (doEmit) {
5873 		emit cleanUpWiresSignal(command);
5874 	}
5875 }
5876 
cleanUpWiresSlot(CleanUpWiresCommand * command)5877 void SketchWidget::cleanUpWiresSlot(CleanUpWiresCommand * command) {
5878 	RoutingStatus routingStatus;
5879 	updateRoutingStatus(command, routingStatus, false);
5880 }
5881 
noteChanged(ItemBase * item,const QString & oldText,const QString & newText,QSizeF oldSize,QSizeF newSize)5882 void SketchWidget::noteChanged(ItemBase * item, const QString &oldText, const QString & newText, QSizeF oldSize, QSizeF newSize) {
5883 	ChangeNoteTextCommand * command = new ChangeNoteTextCommand(this, item->id(), oldText, newText, oldSize, newSize, NULL);
5884 	command->setText(tr("Note text change"));
5885     command->setSkipFirstRedo();
5886 	m_undoStack->waitPush(command, PropChangeDelay);
5887 }
5888 
partLabelChanged(ItemBase * pitem,const QString & oldText,const QString & newText)5889 void SketchWidget::partLabelChanged(ItemBase * pitem,const QString & oldText, const QString &newText) {
5890 	// partLabelChanged triggered from inline editing the label
5891 
5892 	if (!m_current) {
5893 		// all three views get the partLabelChanged call, but only need to act on this once
5894 		return;
5895 	}
5896 
5897 	//if (currentlyInfoviewed(pitem))  {
5898 		// TODO: just change the affected item in the info view
5899 		//viewItemInfo(pitem);
5900 	//}
5901 
5902 	partLabelChangedAux(pitem, oldText, newText);
5903 }
5904 
partLabelChangedAux(ItemBase * pitem,const QString & oldText,const QString & newText)5905 void SketchWidget::partLabelChangedAux(ItemBase * pitem,const QString & oldText, const QString &newText)
5906 {
5907 	if (pitem == NULL) return;
5908 
5909 	ChangeLabelTextCommand * command = new ChangeLabelTextCommand(this, pitem->id(), oldText, newText, NULL);
5910 	command->setText(tr("Change %1 label to '%2'").arg(pitem->title()).arg(newText));
5911 	m_undoStack->waitPush(command, PropChangeDelay);
5912 }
5913 
setInfoViewOnHover(bool infoViewOnHover)5914 void SketchWidget::setInfoViewOnHover(bool infoViewOnHover) {
5915 	m_infoViewOnHover = infoViewOnHover;
5916 }
5917 
updateInfoView()5918 void SketchWidget::updateInfoView() {
5919     if (m_blockUI) return;
5920 
5921 	QTimer::singleShot(50, this,  SLOT(updateInfoViewSlot()));
5922 }
5923 
updateInfoViewSlot()5924 void SketchWidget::updateInfoViewSlot() {
5925 	foreach (QGraphicsItem * item, scene()->selectedItems())
5926 	{
5927 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
5928 		if (itemBase == NULL) continue;
5929 
5930 		ItemBase * chief = itemBase->layerKinChief();
5931 		viewItemInfo(chief);
5932 		return;
5933 	}
5934 
5935 	viewItemInfo(m_lastPaletteItemSelected);
5936 }
5937 
setUpSwap(SwapThing & swapThing,bool master)5938 long SketchWidget::setUpSwap(SwapThing & swapThing, bool master)
5939 {
5940     if (swapThing.firstTime) {
5941         swapThing.firstTime = false;
5942         swapThing.newID = swapStart(swapThing, master);
5943 
5944         // need to ensure the parts are all created first thing
5945         emit swapStartSignal(swapThing, master);
5946     }
5947 
5948     ItemBase * itemBase = swapThing.itemBase;
5949 	if (itemBase->viewID() != m_viewID) {
5950 		itemBase = findItem(itemBase->id());
5951 		if (itemBase == NULL) return swapThing.newID;
5952 	}
5953 
5954 	setUpSwapReconnect(swapThing, itemBase, swapThing.newID, master);
5955 	new CheckStickyCommand(this, BaseCommand::SingleView, swapThing.newID, false, CheckStickyCommand::RemoveOnly, swapThing.parentCommand);
5956 	if (itemBase->isPartLabelVisible()) {
5957 		ShowLabelCommand * slc = new ShowLabelCommand(this, swapThing.parentCommand);
5958 		slc->add(swapThing.newID, true, true);
5959 	}
5960 
5961 	if (master) {
5962 		SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, swapThing.parentCommand);
5963 		selectItemCommand->addRedo(swapThing.newID);
5964 		selectItemCommand->addUndo(itemBase->id());
5965 
5966 		new ChangeLabelTextCommand(this, itemBase->id(), itemBase->instanceTitle(), itemBase->instanceTitle(), swapThing.parentCommand);
5967 		new ChangeLabelTextCommand(this, swapThing.newID, itemBase->instanceTitle(), itemBase->instanceTitle(), swapThing.parentCommand);
5968 
5969         /*
5970 		foreach (Wire * wire, swapThing.wiresToDelete) {
5971 			QList<Wire *> chained;
5972 			QList<ConnectorItem *> ends;
5973 			wire->collectChained(chained, ends);
5974 			foreach (Wire * w, chained) {
5975 				if (!swapThing.wiresToDelete.contains(w)) swapThing.wiresToDelete.append(w);
5976 			}
5977 		}
5978 
5979 		makeWiresChangeConnectionCommands(swapThing.wiresToDelete, swapThing.parentCommand);
5980 		foreach (Wire * wire, swapThing.wiresToDelete) {
5981 			makeDeleteItemCommand(wire, BaseCommand::CrossView, swapThing.parentCommand);
5982 		}
5983         */
5984 
5985 		makeDeleteItemCommand(itemBase, BaseCommand::CrossView, swapThing.parentCommand);
5986 		selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, swapThing.parentCommand);
5987 		selectItemCommand->addRedo(swapThing.newID);  // to make sure new item is selected so it appears in the info view
5988 
5989 		prepDeleteProps(itemBase, swapThing.newID, swapThing.newModuleID, swapThing.propsMap, swapThing.parentCommand);
5990 	    new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, swapThing.parentCommand);
5991 		new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, swapThing.parentCommand);
5992 	}
5993 
5994 	return swapThing.newID;
5995 }
5996 
setUpSwapReconnect(SwapThing & swapThing,ItemBase * itemBase,long newID,bool master)5997 void SketchWidget::setUpSwapReconnect(SwapThing & swapThing, ItemBase * itemBase, long newID, bool master)
5998 {
5999 	ModelPart * newModelPart = m_referenceModel->retrieveModelPart(swapThing.newModuleID);
6000 	if (newModelPart == NULL) return;
6001 
6002 	QList<ConnectorItem *> fromConnectorItems(itemBase->cachedConnectorItems());
6003 
6004 	newModelPart->initConnectors();			//  make sure the connectors are set up
6005 	QList< QPointer<Connector> > newConnectors = newModelPart->connectors().values();
6006 
6007     bool checkReplacedby = (!itemBase->modelPart()->replacedby().isEmpty() && itemBase->modelPart()->replacedby() == swapThing.newModuleID);
6008 
6009 	QList<ConnectorItem *> notFound;
6010 	QList<ConnectorItem *> other;
6011 	QHash<ConnectorItem *, Connector *> found;
6012 	QHash<ConnectorItem *, ConnectorItem *> m2f;
6013 
6014 	foreach (ConnectorItem * fromConnectorItem, fromConnectorItems) {
6015 		QList<Connector *> candidates;
6016 		Connector * newConnector = NULL;
6017 
6018 		if (fromConnectorItem->connectorType() == Connector::Male) {
6019 			foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
6020 				if (toConnectorItem->connectorType() == Connector::Female) {
6021 					m2f.insert(fromConnectorItem, toConnectorItem);
6022 					break;
6023 				}
6024 			}
6025 		}
6026 
6027 		// matching by connectorid can lead to weird results because these all just usually count up from zero
6028 		// so only match by name and description (the latter is a bit of a hail mary)
6029 
6030 		QString fromName = fromConnectorItem->connectorSharedName();
6031 		QString fromDescription = fromConnectorItem->connectorSharedDescription();
6032         QString fromReplacedby = fromConnectorItem->connectorSharedReplacedby();
6033         //itemBase->debugInfo(QString("%1 %2").arg(fromName).arg(fromReplacedby));
6034 		foreach (Connector * connector, newConnectors) {
6035  			QString toName = connector->connectorSharedName();
6036             QString toID = connector->connectorSharedID();
6037             if (checkReplacedby) {
6038                 if (fromReplacedby.compare(toName, Qt::CaseInsensitive) == 0 || fromReplacedby.compare(toID) == 0) {
6039 				    candidates.clear();
6040                     candidates.append(connector);
6041                     //fromConnectorItem->debugInfo(QString("matched %1 %2").arg(toName).arg(toID));
6042                     break;
6043 			    }
6044             }
6045 
6046             bool gotOne = false;
6047 
6048 			QString toDescription = connector->connectorSharedDescription();
6049 			if (fromName.compare(toName, Qt::CaseInsensitive) == 0) {
6050 				gotOne = true;
6051 			}
6052 			else if (fromDescription.compare(toDescription, Qt::CaseInsensitive) == 0) {
6053 				gotOne = true;
6054 			}
6055 			else if (fromDescription.compare(toName, Qt::CaseInsensitive) == 0) {
6056 				gotOne = true;
6057 			}
6058 			else if (fromName.compare(toDescription, Qt::CaseInsensitive) == 0) {
6059 				gotOne = true;
6060 			}
6061 
6062             if (gotOne) {
6063                 candidates.append(connector);
6064             }
6065         }
6066 
6067 		if (candidates.count() > 0) {
6068 			newConnector = candidates[0];
6069 			if (candidates.count() > 1) {
6070 				foreach (Connector * connector, candidates) {
6071 					// this gets an exact match, if there is one
6072 					if (fromConnectorItem->connectorSharedID().compare(connector->connectorSharedID(), Qt::CaseInsensitive) == 0) {
6073 						newConnector = connector;
6074 						break;
6075 					}
6076 				}
6077 			}
6078 
6079 			newConnectors.removeOne(newConnector);
6080 			found.insert(fromConnectorItem, newConnector);
6081 			fromConnectorItem = fromConnectorItem->getCrossLayerConnectorItem();
6082 			if (fromConnectorItem) {
6083 				other.append(fromConnectorItem);
6084 				found.insert(fromConnectorItem, newConnector);
6085 			}
6086 		}
6087 		else {
6088 			notFound.append(fromConnectorItem);
6089 		}
6090 	}
6091 
6092 	QHash<QString, QPolygonF> legs;
6093 	QHash<QString, ConnectorItem *> formerLegs;
6094 	if (m2f.count() > 0 && (m_viewID == ViewLayer::BreadboardView)) {
6095 		checkFit(newModelPart, itemBase, newID, found, notFound, m2f, swapThing.byWire, legs, formerLegs, swapThing.parentCommand);
6096 	}
6097 
6098 	fromConnectorItems.append(other);
6099 	foreach (ConnectorItem * fromConnectorItem, fromConnectorItems) {
6100         //fromConnectorItem->debugInfo("from");
6101 		Connector * newConnector = found.value(fromConnectorItem, NULL);
6102 		foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
6103 			// delete connection to part being swapped out
6104 
6105 
6106 			Wire * wire = qobject_cast<Wire *>(toConnectorItem->attachedTo());
6107 			if (wire != NULL) {
6108 				if (wire->getRatsnest()) continue;
6109 
6110 				//if (newConnector == NULL && wire->getTrace() && wire->isTraceType(getTraceFlag())) {
6111 				//	swapThing.wiresToDelete.append(wire);
6112 				//	continue;
6113 				//}
6114 			}
6115 
6116 			// disconnect command created for each view individually
6117 			extendChangeConnectionCommand(BaseCommand::SingleView,
6118 										fromConnectorItem, toConnectorItem,
6119 										ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()),
6120 										false, swapThing.parentCommand);
6121 
6122 			bool cleanup = false;
6123 			if (newConnector) {
6124                 bool byWireFlag = (swapThing.byWire.value(fromConnectorItem, NULL) == newConnector);
6125                 bool swapped = false;
6126                 if (byWireFlag) {
6127                     swapThing.toConnectorItems.insert(fromConnectorItem, toConnectorItem);
6128                 }
6129                 else {
6130                     swapped = swappedGender(fromConnectorItem, newConnector);
6131                     if (swapped && m_viewID == ViewLayer::BreadboardView) {
6132                         swapThing.swappedGender.insert(fromConnectorItem, newConnector);
6133                         swapThing.toConnectorItems.insert(fromConnectorItem, toConnectorItem);
6134                     }
6135                 }
6136 				cleanup = byWireFlag || swapped;
6137 			}
6138 			if ((newConnector == NULL) || cleanup) {
6139 					// clean up after disconnect
6140 			}
6141 			else {
6142 				// reconnect directly without cleaning up; command created for each view
6143                 // if it's an smd swap, deal with reconnecting the wire at reconnect time
6144 				ChangeConnectionCommand * ccc = new ChangeConnectionCommand(this, BaseCommand::SingleView,
6145 										newID, newConnector->connectorSharedID(),
6146 										toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
6147 										ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()),
6148 										true, swapThing.parentCommand);
6149                 swapThing.reconnections.insert(fromConnectorItem, ccc);
6150 			}
6151 		}
6152 	}
6153 
6154     foreach (ConnectorItem * fromConnectorItem, swapThing.swappedGender.keys()) {
6155         makeSwapWire(swapThing.bbView, itemBase, newID, fromConnectorItem, swapThing.toConnectorItems.value(fromConnectorItem),
6156                         swapThing.swappedGender.value(fromConnectorItem), swapThing.parentCommand);
6157     }
6158 
6159     if (master && swapThing.byWire.count() > 0) {
6160         foreach (ConnectorItem * fromConnectorItem, swapThing.byWire.keys()) {
6161             makeSwapWire(swapThing.bbView, itemBase, newID, fromConnectorItem, swapThing.toConnectorItems.value(fromConnectorItem),
6162                             swapThing.byWire.value(fromConnectorItem), swapThing.parentCommand);
6163 		}
6164 
6165         foreach (ConnectorItem * fromConnectorItem, swapThing.byWire.keys()) {
6166             // if a part, for example a generic DIP, is reconnected by wires after swapping,
6167             // then remove any pcb and schematic direct reconnections
6168             QList<ConnectorItem *> toRemove;
6169             foreach (ConnectorItem * foreign, swapThing.reconnections.keys()) {
6170                 ConnectorItem * candidate = swapThing.bbView->findConnectorItem(foreign);
6171                 if (candidate == fromConnectorItem) {
6172                     toRemove << foreign;
6173                     swapThing.reconnections.value(foreign)->disable();
6174                 }
6175             }
6176             foreach (ConnectorItem * foreign, toRemove) {
6177                 swapThing.reconnections.remove(foreign);
6178             }
6179         }
6180     }
6181 
6182 
6183 	// changeConnection calls PaletteItemBase::connectedMoved which repositions the new part
6184 	// so slam in the desired position
6185     QPointF p = itemBase->getViewGeometry().loc();
6186     new SimpleMoveItemCommand(this, newID, p, p, swapThing.parentCommand);
6187 
6188 	foreach (QString connectorID, legs.keys()) {
6189 		// must be invoked after all the connections have been dealt with
6190 		QPolygonF poly = legs.value(connectorID);
6191 
6192 		ConnectorItem * connectorItem = formerLegs.value(connectorID, NULL);
6193 		if (connectorItem && connectorItem->hasRubberBandLeg()) {
6194 			poly = connectorItem->leg();
6195 		}
6196 
6197 		ChangeLegCommand * clc = new ChangeLegCommand(this, newID, connectorID, poly, poly, true, true, "swap", swapThing.parentCommand);
6198 		clc->setRedoOnly();
6199 
6200 		if (connectorItem && connectorItem->hasRubberBandLeg()) {
6201 			QVector<Bezier *> beziers = connectorItem->beziers();
6202 			for (int i = 0; i < beziers.count() - 1; i++) {
6203 				Bezier * bezier = beziers.at(i);
6204 				if (bezier == NULL) continue;
6205 				if (bezier->isEmpty()) continue;
6206 
6207 				ChangeLegCurveCommand * clcc = new ChangeLegCurveCommand(this, newID, connectorID, i, bezier, bezier, swapThing.parentCommand);
6208 				clcc->setRedoOnly();
6209 			}
6210 		}
6211 	}
6212 }
6213 
makeSwapWire(SketchWidget * bbView,ItemBase * itemBase,long newID,ConnectorItem * fromConnectorItem,ConnectorItem * toConnectorItem,Connector * newConnector,QUndoCommand * parentCommand)6214 void SketchWidget::makeSwapWire(SketchWidget * bbView, ItemBase * itemBase, long newID, ConnectorItem * fromConnectorItem, ConnectorItem * toConnectorItem, Connector * newConnector, QUndoCommand * parentCommand) {
6215 	Q_UNUSED(fromConnectorItem);
6216     long wireID = ItemBase::getNextID();
6217 	ViewGeometry vg;
6218 	new AddItemCommand(bbView, BaseCommand::CrossView, ModuleIDNames::WireModuleIDName, itemBase->viewLayerPlacement(), vg, wireID, false, -1, parentCommand);
6219 	new CheckStickyCommand(bbView, BaseCommand::CrossView, wireID, false, CheckStickyCommand::RemoveOnly, parentCommand);
6220 	new ChangeConnectionCommand(bbView, BaseCommand::CrossView, newID, newConnector->connectorSharedID(),
6221 								wireID, "connector0",
6222 								ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()),
6223 								true, parentCommand);
6224 	new ChangeConnectionCommand(bbView, BaseCommand::CrossView, toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
6225 								wireID, "connector1",
6226 								ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()),
6227 								bbView, parentCommand);
6228 }
6229 
checkFit(ModelPart * newModelPart,ItemBase * itemBase,long newID,QHash<ConnectorItem *,Connector * > & found,QList<ConnectorItem * > & notFound,QHash<ConnectorItem *,ConnectorItem * > & m2f,QHash<ConnectorItem *,Connector * > & byWire,QHash<QString,QPolygonF> & legs,QHash<QString,ConnectorItem * > & formerLegs,QUndoCommand * parentCommand)6230 void SketchWidget::checkFit(ModelPart * newModelPart, ItemBase * itemBase, long newID,
6231 							QHash<ConnectorItem *, Connector *> & found, QList<ConnectorItem *> & notFound,
6232 							QHash<ConnectorItem *, ConnectorItem *> & m2f, QHash<ConnectorItem *, Connector *> & byWire,
6233 							QHash<QString, QPolygonF> & legs, QHash<QString, ConnectorItem *> & formerLegs, QUndoCommand * parentCommand)
6234 {
6235 	if (found.count() == 0) return;
6236 
6237 	ItemBase * tempItemBase = addItemAuxTemp(newModelPart, itemBase->viewLayerPlacement(), itemBase->getViewGeometry(), newID, true, m_viewID, true);
6238 	if (tempItemBase == NULL) return;			// we're really screwed
6239 
6240 	checkFitAux(tempItemBase, itemBase, newID, found, notFound, m2f, byWire, legs, formerLegs, parentCommand);
6241 	delete tempItemBase;
6242 }
6243 
checkFitAux(ItemBase * tempItemBase,ItemBase * itemBase,long newID,QHash<ConnectorItem *,Connector * > & found,QList<ConnectorItem * > & notFound,QHash<ConnectorItem *,ConnectorItem * > & m2f,QHash<ConnectorItem *,Connector * > & byWire,QHash<QString,QPolygonF> & legs,QHash<QString,ConnectorItem * > & formerLegs,QUndoCommand * parentCommand)6244 void SketchWidget::checkFitAux(ItemBase * tempItemBase, ItemBase * itemBase, long newID,
6245 							QHash<ConnectorItem *, Connector *> & found, QList<ConnectorItem *> & notFound,
6246 							QHash<ConnectorItem *, ConnectorItem *> & m2f, QHash<ConnectorItem *, Connector *> & byWire,
6247 							QHash<QString, QPolygonF> & legs, QHash<QString, ConnectorItem *> & formerLegs, QUndoCommand * parentCommand)
6248 {
6249 	QPointF foundAnchor(std::numeric_limits<int>::max(), std::numeric_limits<int>::max());
6250 	QPointF newAnchor(std::numeric_limits<int>::max(), std::numeric_limits<int>::max());
6251 	QHash<ConnectorItem *, QPointF> foundPoints;
6252 	QHash<ConnectorItem *, QPointF> newPoints;
6253 	QHash<ConnectorItem *, ConnectorItem *> foundNews;
6254 	QHash<ConnectorItem *, ConnectorItem *> newFounds;
6255 	QList<ConnectorItem *> removeFromFound;
6256 
6257 	foreach (ConnectorItem * foundConnectorItem, found.keys()) {
6258 		if (m2f.value(foundConnectorItem, NULL) == NULL) {
6259 			// we only care about replacing the female connectors here
6260 
6261 			continue;
6262 		}
6263 
6264 		Connector * connector = found.value(foundConnectorItem);
6265 		ConnectorItem * newConnectorItem = NULL;
6266 		foreach (ConnectorItem * nci, tempItemBase->cachedConnectorItems()) {
6267 			if (nci->connector()->connectorShared() == connector->connectorShared()) {
6268 				newConnectorItem = nci;
6269 				break;
6270 			}
6271 		}
6272 		if (newConnectorItem == NULL) {
6273 			removeFromFound.append(foundConnectorItem);
6274 		}
6275 		else {
6276 			foundNews.insert(foundConnectorItem, newConnectorItem);
6277 			newFounds.insert(newConnectorItem, foundConnectorItem);
6278 
6279 			QPointF lastNew = newConnectorItem->sceneAdjustedTerminalPoint(NULL);
6280 			if (lastNew.x() < newAnchor.x()) newAnchor.setX(lastNew.x());
6281 			if (lastNew.y() < newAnchor.y()) newAnchor.setY(lastNew.y());
6282 			newPoints.insert(newConnectorItem, lastNew);
6283 			QPointF lastFound = foundConnectorItem->sceneAdjustedTerminalPoint(NULL);
6284 			if (lastFound.x() < foundAnchor.x()) foundAnchor.setX(lastFound.x());
6285 			if (lastFound.y() < foundAnchor.y()) foundAnchor.setY(lastFound.y());
6286 			foundPoints.insert(foundConnectorItem, lastFound);
6287 		}
6288 	}
6289 
6290 	foreach (ConnectorItem * connectorItem, removeFromFound) {
6291 		found.remove(connectorItem);
6292 		notFound.append(connectorItem);
6293 	}
6294 
6295 	if (found.count() == 0) {
6296 		return;
6297 	}
6298 
6299 
6300 	bool allCorrespond = true;
6301 	foreach (ConnectorItem * foundConnectorItem, foundNews.keys()) {
6302 		QPointF fp = foundPoints.value(foundConnectorItem) - foundAnchor;
6303 		ConnectorItem * newConnectorItem = foundNews.value(foundConnectorItem);
6304 		QPointF np = newPoints.value(newConnectorItem) - newAnchor;
6305 		if (!newConnectorItem->hasRubberBandLeg() && (qAbs(fp.x() - np.x()) >= CloseEnough || qAbs(fp.y() - np.y()) >= CloseEnough)) {
6306 			// pins can be off by a little
6307 			// but if even one connector is out of place, hook everything up by wires
6308 			allCorrespond = false;
6309 			break;
6310 		}
6311 	}
6312 
6313 	if (allCorrespond) {
6314 		if (tempItemBase->cachedConnectorItems().count() == found.count()) {
6315 			if (tempItemBase->hasRubberBandLeg()) {
6316 				foreach (ConnectorItem * connectorItem, tempItemBase->cachedConnectorItems()) {
6317 					legs.insert(connectorItem->connectorSharedID(), connectorItem->leg());
6318 					formerLegs.insert(connectorItem->connectorSharedID(), newFounds.value(connectorItem, NULL));
6319 				}
6320 			}
6321 
6322 			// it's a clean swap: all connectors line up
6323 			return;
6324 		}
6325 	}
6326 
6327 	// there's a mismatch in terms of connector count between the swaps,
6328 	// so make sure all target connections are open or to be swapped out
6329 	// establish the location of the new item's connectors
6330 
6331 	QHash<ConnectorItem *, ConnectorItem *> newConnections;
6332 
6333 	if (allCorrespond) {
6334 		QList<ConnectorItem *> alreadyFits = foundNews.values();
6335 		foreach (ConnectorItem * nci, tempItemBase->cachedConnectorItems()) {
6336 			if (alreadyFits.contains(nci)) continue;
6337 			if (nci->connectorType() != Connector::Male) continue;
6338 
6339 			// TODO: this doesn't handle all scenarios.  For example, if another part with a female connector is
6340 			// on top of the breadboard, and would be in the way
6341 
6342 			QPointF p = nci->sceneAdjustedTerminalPoint(NULL) - newAnchor + foundAnchor;			// eventual position of this new connector
6343 			ConnectorItem * connectorUnder = NULL;
6344 			foreach (QGraphicsItem * item, scene()->items(p)) {
6345 				ConnectorItem * cu = dynamic_cast<ConnectorItem *>(item);
6346 				if (cu == NULL || cu == nci || cu->attachedTo() == itemBase || cu->connectorType() != Connector::Female) {
6347 					continue;
6348 				}
6349 
6350 				connectorUnder = cu;
6351 				break;
6352 			}
6353 
6354 			if (connectorUnder == NULL) {
6355 				// safe?
6356 				continue;
6357 			}
6358 
6359 			foreach (ConnectorItem * uct, connectorUnder->connectedToItems()) {
6360 				if (uct->attachedTo() == itemBase) {
6361 					// we're safe, itemBase is swapping out
6362 					continue;
6363 				}
6364 
6365 				// some other part is in the way
6366 				allCorrespond = false;
6367 				break;
6368 			}
6369 
6370 			if (!allCorrespond) break;
6371 
6372 			newConnections.insert(nci, connectorUnder);			// add a new direct connection
6373 
6374 		}
6375 	}
6376 
6377 	if (allCorrespond) {
6378 		// the extra new connectors will also fit
6379 		foreach (ConnectorItem * nci, newConnections.keys()) {
6380 			ConnectorItem * toConnectorItem = newConnections.value(nci);
6381 			new ChangeConnectionCommand(this, BaseCommand::CrossView,
6382 									newID, nci->connectorSharedID(),
6383 									toConnectorItem->attachedToID(), toConnectorItem->connectorSharedID(),
6384 									ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()),
6385 									true, parentCommand);
6386 
6387 		}
6388 		if (tempItemBase->hasRubberBandLeg()) {
6389 			foreach (ConnectorItem * connectorItem, newConnections) {
6390 				legs.insert(connectorItem->connectorSharedID(), connectorItem->leg());
6391 			}
6392 			foreach (ConnectorItem * newConnectorItem, newFounds.keys()) {
6393 				legs.insert(newConnectorItem->connectorSharedID(), newConnectorItem->leg());
6394 				formerLegs.insert(newConnectorItem->connectorSharedID(), newFounds.value(newConnectorItem, NULL));
6395 			}
6396 		}
6397 
6398 		return;
6399 	}
6400 
6401 	// have to replace each found value with a wire
6402 	foreach (ConnectorItem * fci, found.keys()) {
6403 		byWire.insert(fci, found.value(fci));
6404 	}
6405 }
6406 
changeWireColor(const QString newColor)6407 void SketchWidget::changeWireColor(const QString newColor)
6408 {
6409 	m_lastColorSelected = newColor;
6410 	QList <Wire *> wires;
6411 	foreach (QGraphicsItem * item, scene()->selectedItems()) {
6412 		Wire * wire = dynamic_cast<Wire *>(item);
6413 		if (wire == NULL) continue;
6414 
6415 		wires.append(wire);
6416 	}
6417 
6418 	if (wires.count() <= 0) return;
6419 
6420 	QString commandString;
6421 	if (wires.count() == 1) {
6422 		commandString = tr("Change %1 color from %2 to %3")
6423 			.arg(wires[0]->instanceTitle())
6424 			.arg(wires[0]->colorString())
6425 			.arg(newColor);
6426 	}
6427 	else {
6428 		commandString = tr("Change color of %1 wires to %2")
6429 			.arg(wires.count())
6430 			.arg(newColor);
6431 	}
6432 
6433 	QUndoCommand* parentCommand = new QUndoCommand(commandString);
6434 	foreach (Wire * wire, wires) {
6435 		QList<Wire *> subWires;
6436 		wire->collectWires(subWires);
6437 
6438 		foreach (Wire * subWire, subWires) {
6439 			new WireColorChangeCommand(
6440 					this,
6441 					subWire->id(),
6442 					subWire->colorString(),
6443 					newColor,
6444 					subWire->opacity(),
6445 					subWire->opacity(),
6446 					parentCommand);
6447 		}
6448 	}
6449 
6450 	m_undoStack->waitPush(parentCommand, PropChangeDelay);
6451 }
6452 
changeWireWidthMils(const QString newWidthStr)6453 void SketchWidget::changeWireWidthMils(const QString newWidthStr)
6454 {
6455 	bool ok = false;
6456 	double newWidth = newWidthStr.toDouble(&ok);
6457 	if (!ok) return;
6458 
6459 	double trueWidth = GraphicsUtils::SVGDPI * newWidth / 1000.0;
6460 
6461 	QList <Wire *> wires;
6462 	foreach (QGraphicsItem * item, scene()->selectedItems()) {
6463 		Wire * wire = dynamic_cast<Wire *>(item);
6464 		if (wire == NULL) continue;
6465 		if (!wire->getTrace()) continue;
6466 
6467 		wires.append(wire);
6468 	}
6469 
6470 	if (wires.count() <= 0) return;
6471 
6472 	QString commandString;
6473 	if (wires.count() == 1) {
6474 		commandString = tr("Change %1 width from %2 to %3")
6475 			.arg(wires[0]->instanceTitle())
6476 			.arg((int) wires[0]->mils())
6477 			.arg(newWidth);
6478 	}
6479 	else {
6480 		commandString = tr("Change width of %1 wires to %2")
6481 			.arg(wires.count())
6482 			.arg(newWidth);
6483 	}
6484 
6485 	QUndoCommand* parentCommand = new QUndoCommand(commandString);
6486 	foreach (Wire * wire, wires) {
6487 		QList<Wire *> subWires;
6488 		wire->collectWires(subWires);
6489 
6490 		foreach (Wire * subWire, subWires) {
6491 			new WireWidthChangeCommand(
6492 					this,
6493 					subWire->id(),
6494 					subWire->width(),
6495 					trueWidth,
6496 					parentCommand);
6497 		}
6498 	}
6499 
6500 	m_undoStack->waitPush(parentCommand, PropChangeDelay);
6501 }
6502 
changeWireColor(long wireId,const QString & color,double opacity)6503 void SketchWidget::changeWireColor(long wireId, const QString& color, double opacity) {
6504 	ItemBase *item = findItem(wireId);
6505 	Wire* wire = qobject_cast<Wire*>(item);
6506 	if (wire) {
6507 		wire->setColorString(color, opacity, true);
6508 		updateInfoView();
6509 	}
6510 }
6511 
changeWireWidth(long wireId,double width)6512 void SketchWidget::changeWireWidth(long wireId, double width) {
6513 	ItemBase *item = findItem(wireId);
6514 	Wire* wire = qobject_cast<Wire*>(item);
6515 	if (wire) {
6516 		wire->setWireWidth(width, this, getWireStrokeWidth(wire, width));
6517 		updateInfoView();
6518 	}
6519 }
6520 
swappingEnabled(ItemBase * itemBase)6521 bool SketchWidget::swappingEnabled(ItemBase * itemBase) {
6522 	if (itemBase == NULL) {
6523 		return m_referenceModel->swapEnabled();
6524 	}
6525 
6526 	return (m_referenceModel->swapEnabled() && itemBase->isSwappable());
6527 }
6528 
resizeEvent(QResizeEvent * event)6529 void SketchWidget::resizeEvent(QResizeEvent * event) {
6530 	InfoGraphicsView::resizeEvent(event);
6531 
6532 	QPoint s(event->size().width(), event->size().height());
6533 	QPointF p = this->mapToScene(s);
6534 
6535 	QPointF z = this->mapToScene(QPoint(0,0));
6536 
6537 //	DebugDialog::debug(QString("resize event %1 %2, %3 %4, %5 %6")		/* , %3 %4 %5 %6, %7 %8 %9 %10 */
6538 //		.arg(event->size().width()).arg(event->size().height())
6539 //		.arg(p.x()).arg(p.y())
6540 //		.arg(z.x()).arg(z.y())
6541 //		.arg(sr.left()).arg(sr.top()).arg(sr.width()).arg(sr.height())
6542 //		.arg(sr.left()).arg(sr.top()).arg(ir.width()).arg(ir.height())
6543 //
6544 //	);
6545 
6546 	if (m_sizeItem != NULL) {
6547 		m_sizeItem->setLine(z.x(), z.y(), p.x(), p.y());
6548 	}
6549 
6550 	emit resizeSignal();
6551 }
6552 
addViewLayers()6553 void SketchWidget::addViewLayers() {
6554 }
6555 
addViewLayersAux(const LayerList & layers,const LayerList & layersFromBelow,float startZ)6556 void SketchWidget::addViewLayersAux(const LayerList & layers, const LayerList & layersFromBelow, float startZ) {
6557 	m_z = startZ;
6558 	foreach(ViewLayer::ViewLayerID vlId, layers) {
6559 		addViewLayer(new ViewLayer(vlId, true, m_z));
6560 		m_z += 1;
6561 	}
6562 
6563     double z = startZ;
6564 	foreach(ViewLayer::ViewLayerID vlId, layersFromBelow) {
6565         ViewLayer * viewLayer = m_viewLayers.value(vlId, NULL);
6566         if (viewLayer) viewLayer->setInitialZFromBelow(z);
6567 		z += 1;
6568 	}
6569 }
6570 
setIgnoreSelectionChangeEvents(bool ignore)6571 void SketchWidget::setIgnoreSelectionChangeEvents(bool ignore) {
6572 	if (ignore) {
6573 		m_ignoreSelectionChangeEvents++;
6574 	}
6575 	else {
6576 		m_ignoreSelectionChangeEvents--;
6577 	}
6578 }
6579 
hideConnectors(bool hide)6580 void SketchWidget::hideConnectors(bool hide) {
6581 	foreach (QGraphicsItem * item, scene()->items()) {
6582 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
6583 		if (itemBase == NULL) continue;
6584 		if (!itemBase->isVisible()) continue;
6585 
6586 		foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
6587 			connectorItem->setVisible(!hide);
6588 		}
6589 	}
6590 }
6591 
saveLayerVisibility()6592 void SketchWidget::saveLayerVisibility()
6593 {
6594 	m_viewLayerVisibility.clear();
6595 	foreach (ViewLayer::ViewLayerID viewLayerID, m_viewLayers.keys()) {
6596 		ViewLayer * viewLayer = m_viewLayers.value(viewLayerID);
6597 		if (viewLayer == NULL) continue;
6598 
6599 		m_viewLayerVisibility.insert(viewLayerID, viewLayer->visible());
6600 	}
6601 }
6602 
restoreLayerVisibility()6603 void SketchWidget::restoreLayerVisibility()
6604 {
6605 	foreach (ViewLayer::ViewLayerID viewLayerID, m_viewLayerVisibility.keys()) {
6606 		setLayerVisible(m_viewLayers.value(viewLayerID),  m_viewLayerVisibility.value(viewLayerID), false);
6607 	}
6608 }
6609 
changeWireFlags(long wireId,ViewGeometry::WireFlags wireFlags)6610 void SketchWidget::changeWireFlags(long wireId, ViewGeometry::WireFlags wireFlags)
6611 {
6612 	ItemBase *item = findItem(wireId);
6613 	if(Wire* wire = qobject_cast<Wire*>(item)) {
6614 		wire->setWireFlags(wireFlags);
6615 	}
6616 }
6617 
disconnectFromFemale(ItemBase * item,QHash<long,ItemBase * > & savedItems,ConnectorPairHash & connectorHash,bool doCommand,bool rubberBandLegEnabled,QUndoCommand * parentCommand)6618 bool SketchWidget::disconnectFromFemale(ItemBase * item, QHash<long, ItemBase *> & savedItems, ConnectorPairHash & connectorHash, bool doCommand, bool rubberBandLegEnabled, QUndoCommand * parentCommand)
6619 {
6620 	// schematic and pcb view connections are always via wires so this is a no-op.  breadboard view has its own version.
6621 
6622 	Q_UNUSED(item);
6623 	Q_UNUSED(savedItems);
6624 	Q_UNUSED(parentCommand);
6625 	Q_UNUSED(connectorHash);
6626 	Q_UNUSED(doCommand);
6627 	Q_UNUSED(rubberBandLegEnabled);
6628 	return false;
6629 }
6630 
spaceBarIsPressedSlot(bool isPressed)6631 void SketchWidget::spaceBarIsPressedSlot(bool isPressed) {
6632 	if (m_middleMouseIsPressed) return;
6633 
6634 	m_spaceBarIsPressed = isPressed;
6635 	if (isPressed) {
6636 		setDragMode(QGraphicsView::ScrollHandDrag);
6637 		//setInteractive(false);
6638 		//CursorMaster::instance()->addCursor(this, Qt::OpenHandCursor);
6639         //setCursor(Qt::OpenHandCursor);
6640         //DebugDialog::debug("setting open hand cursor");
6641 	}
6642 	else {
6643         //CursorMaster::instance()->removeCursor(this);
6644 		setDragMode(QGraphicsView::RubberBandDrag);
6645 		//setInteractive(true);
6646 		//setCursor(Qt::ArrowCursor);
6647 	}
6648 }
6649 
updateRoutingStatus(CleanUpWiresCommand * command,RoutingStatus & routingStatus,bool manual)6650 void SketchWidget::updateRoutingStatus(CleanUpWiresCommand* command, RoutingStatus & routingStatus, bool manual)
6651 {
6652 	//DebugDialog::debug("update ratsnest status");
6653 
6654 
6655 	routingStatus.zero();
6656 	updateRoutingStatus(routingStatus, manual);
6657 
6658 	if (routingStatus != m_routingStatus) {
6659 		if (command) {
6660 			// changing state after the command has already been executed
6661 			command->addRoutingStatus(this, m_routingStatus, routingStatus);
6662 		}
6663 
6664 		emit routingStatusSignal(this, routingStatus);
6665 
6666 		m_routingStatus = routingStatus;
6667 	}
6668 }
6669 
updateRoutingStatus(RoutingStatus & routingStatus,bool manual)6670 void SketchWidget::updateRoutingStatus(RoutingStatus & routingStatus, bool manual)
6671 {
6672 	//DebugDialog::debug(QString("update routing status %1 %2 %3")
6673 	//	.arg(m_viewID)
6674 	//	.arg(m_ratsnestUpdateConnect.count())
6675 	//	.arg(m_ratsnestUpdateDisconnect.count())
6676 	//	);
6677 
6678 	// TODO: think about ways to optimize this...
6679 
6680 	QList< QPointer<VirtualWire> > ratsToDelete;
6681 
6682 	QList< QList<ConnectorItem *> > ratnestsToUpdate;
6683 	QList<ConnectorItem *> visited;
6684 	foreach (QGraphicsItem * item, scene()->items()) {
6685 		ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(item);
6686 		if (connectorItem == NULL) continue;
6687 		if (visited.contains(connectorItem)) continue;
6688 
6689 		//if (this->viewID() == ViewLayer::SchematicView) {
6690 		//    connectorItem->debugInfo("testing urs");
6691         //}
6692 
6693 		VirtualWire * vw = qobject_cast<VirtualWire *>(connectorItem->attachedTo());
6694 		if (vw != NULL) {
6695 			if (vw->connector0()->connectionsCount() == 0 || vw->connector1()->connectionsCount() == 0) {
6696 				ratsToDelete.append(vw);
6697 			}
6698 			visited << vw->connector0() << vw->connector1();
6699 			continue;
6700 		}
6701 
6702 
6703 		QList<ConnectorItem *> connectorItems;
6704 		connectorItems.append(connectorItem);
6705 		ConnectorItem::collectEqualPotential(connectorItems, true, ViewGeometry::RatsnestFlag);
6706 		visited.append(connectorItems);
6707 
6708 		//if (this->viewID() == ViewLayer::SchematicView) {
6709         //	DebugDialog::debug("________________________");
6710 		//	foreach (ConnectorItem * ci, connectorItems) ci->debugInfo("cep");
6711 		//}
6712 
6713 		bool doRatsnest = manual || checkUpdateRatsnest(connectorItems);
6714 		if (!doRatsnest && connectorItems.count() <= 1) continue;
6715 
6716 		QList<ConnectorItem *> partConnectorItems;
6717 		ConnectorItem::collectParts(connectorItems, partConnectorItems, includeSymbols(), ViewLayer::NewTopAndBottom);
6718 		if (partConnectorItems.count() < 1) continue;
6719 		if (!doRatsnest && partConnectorItems.count() <= 1) continue;
6720 
6721 	    //if (this->viewID() == ViewLayer::SchematicView) {
6722 		//    DebugDialog::debug("________________________");
6723         //    foreach (ConnectorItem * pci, partConnectorItems) {
6724 		//		pci->debugInfo("pc 1");
6725 		//	}
6726         //}
6727 
6728 		for (int i = partConnectorItems.count() - 1; i >= 0; i--) {
6729 			ConnectorItem * ci = partConnectorItems[i];
6730 
6731 			if (!ci->attachedTo()->isEverVisible()) {
6732 				partConnectorItems.removeAt(i);
6733 			}
6734 		}
6735 
6736 		if (partConnectorItems.count() < 1) continue;
6737 
6738 		if (doRatsnest) {
6739 			ratnestsToUpdate.append(partConnectorItems);
6740 		}
6741 
6742 		if (partConnectorItems.count() <= 1) continue;
6743 
6744 	    //if (this->viewID() == ViewLayer::SchematicView) {
6745 		//    DebugDialog::debug("________________________");
6746         //    foreach (ConnectorItem * pci, partConnectorItems) {
6747 		//		pci->debugInfo("pc 2");
6748 		//	}
6749         //}
6750 
6751 		GraphUtils::scoreOneNet(partConnectorItems, this->getTraceFlag(), routingStatus);
6752 	}
6753 
6754 	routingStatus.m_jumperItemCount /= 4;			// since we counted each connector twice on two layers (4 connectors per jumper item)
6755 
6756 	// can't do this in the above loop since VirtualWires and ConnectorItems are added and deleted
6757 	foreach (QList<ConnectorItem *> partConnectorItems, ratnestsToUpdate) {
6758 		//partConnectorItems.at(0)->debugInfo("display ratsnest");
6759 	    //if (this->viewID() == ViewLayer::SchematicView) {
6760 		//    DebugDialog::debug("________________________");
6761         //    foreach (ConnectorItem * pci, partConnectorItems) {
6762 		//		pci->debugInfo("pc 3");
6763 		//	}
6764         //}
6765 
6766 		partConnectorItems.at(0)->displayRatsnest(partConnectorItems, this->getTraceFlag());
6767 	}
6768 
6769 	foreach(QPointer<VirtualWire> vw, ratsToDelete) {
6770 		if (vw != NULL) {
6771 			//vw->debugInfo("removing rat 2");
6772 			vw->scene()->removeItem(vw);
6773 			delete vw;
6774 		}
6775 	}
6776 
6777 
6778 	m_ratsnestUpdateConnect.clear();
6779 	m_ratsnestUpdateDisconnect.clear();
6780 
6781         /*
6782         // uncomment for live drc
6783         CMRouter cmRouter(this);
6784         QString message;
6785         bool result = cmRouter.drc(message);
6786         */
6787 }
6788 
6789 
ensureLayerVisible(ViewLayer::ViewLayerID viewLayerID)6790 void SketchWidget::ensureLayerVisible(ViewLayer::ViewLayerID viewLayerID)
6791 {
6792 	ViewLayer * viewLayer = m_viewLayers.value(viewLayerID, NULL);
6793 	if (viewLayer == NULL) return;
6794 
6795 	if (!viewLayer->visible()) {
6796 		setLayerVisible(viewLayer, true, true);
6797 	}
6798 }
6799 
clearDragWireTempCommand()6800 void SketchWidget::clearDragWireTempCommand()
6801 {
6802 	if (m_tempDragWireCommand) {
6803 		delete m_tempDragWireCommand;
6804 		m_tempDragWireCommand = NULL;
6805 	}
6806 }
6807 
autoScrollTimeout()6808 void SketchWidget::autoScrollTimeout()
6809 {
6810 	//DebugDialog::debug(QString("scrolling dx:%1 dy:%2").arg(m_autoScrollX).arg(m_autoScrollY) );
6811 
6812 	if (m_autoScrollX == 0 && m_autoScrollY == 0 ) return;
6813 
6814 
6815 	if (m_autoScrollX != 0) {
6816 		QScrollBar * h = horizontalScrollBar();
6817 		h->setValue(m_autoScrollX + h->value());
6818 	}
6819 	if (m_autoScrollY != 0) {
6820 		QScrollBar * v = verticalScrollBar();
6821 		v->setValue(m_autoScrollY + v->value());
6822 	}
6823 
6824 	//DebugDialog::debug(QString("autoscrolling %1 %2").arg(m_autoScrollX).arg(m_autoScrollX));
6825 }
6826 
dragAutoScrollTimeout()6827 void SketchWidget::dragAutoScrollTimeout()
6828 {
6829 	autoScrollTimeout();
6830 	dragMoveHighlightConnector(mapFromGlobal(m_globalPos));
6831 }
6832 
6833 
moveAutoScrollTimeout()6834 void SketchWidget::moveAutoScrollTimeout()
6835 {
6836 	autoScrollTimeout();
6837 	moveItems(m_globalPos, true, m_rubberBandLegWasEnabled);
6838 }
6839 
selectedModuleID()6840 const QString &SketchWidget::selectedModuleID() {
6841 	if(m_lastPaletteItemSelected) {
6842 		return m_lastPaletteItemSelected->moduleID();
6843 	}
6844 
6845     foreach (QGraphicsItem * item, scene()->selectedItems()) {
6846         ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
6847         if (itemBase) return itemBase->moduleID();
6848     }
6849 
6850 	return ___emptyString___;
6851 }
6852 
wireSplitCrossView()6853 BaseCommand::CrossViewType SketchWidget::wireSplitCrossView()
6854 {
6855 	return BaseCommand::CrossView;
6856 }
6857 
canDeleteItem(QGraphicsItem * item,int count)6858 bool SketchWidget::canDeleteItem(QGraphicsItem * item, int count)
6859 {
6860 	Q_UNUSED(count);
6861 	ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
6862 	if (itemBase == NULL) return false;
6863 
6864 	if (itemBase->moveLock()) return false;
6865 
6866 	ItemBase * chief = itemBase->layerKinChief();
6867 	if (chief == NULL) return false;
6868 
6869 	if (chief->moveLock()) return false;
6870 
6871 	return true;
6872 }
6873 
canCopyItem(QGraphicsItem * item,int count)6874 bool SketchWidget::canCopyItem(QGraphicsItem * item, int count)
6875 {
6876 	Q_UNUSED(count);
6877 	ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
6878 	if (itemBase == NULL) return false;
6879 
6880 	ItemBase * chief = itemBase->layerKinChief();
6881 	if (chief == NULL) return false;
6882 
6883 	return true;
6884 }
6885 
canChainMultiple()6886 bool SketchWidget::canChainMultiple() {
6887 	return false;
6888 }
6889 
canChainWire(Wire * wire)6890 bool SketchWidget::canChainWire(Wire * wire) {
6891 	if (!this->m_chainDrag) return false;
6892 	if (wire == NULL) return false;
6893 
6894 	return true;
6895 }
6896 
canDragWire(Wire * wire)6897 bool SketchWidget::canDragWire(Wire * wire) {
6898 	if (wire == NULL) return false;
6899 
6900 	return true;
6901 }
6902 
canCreateWire(Wire * dragWire,ConnectorItem * from,ConnectorItem * to)6903 bool SketchWidget::canCreateWire(Wire * dragWire, ConnectorItem * from, ConnectorItem * to)
6904 {
6905 	Q_UNUSED(dragWire);
6906 	Q_UNUSED(from);
6907 	Q_UNUSED(to);
6908 	return true;
6909 }
6910 
6911 /*
6912 bool SketchWidget::modifyNewWireConnections(Wire * dragWire, ConnectorItem * fromOnWire, ConnectorItem * from, ConnectorItem * to, QUndoCommand * parentCommand)
6913 {
6914 	Q_UNUSED(dragWire);
6915 	Q_UNUSED(fromOnWire);
6916 	Q_UNUSED(from);
6917 	Q_UNUSED(to);
6918 	Q_UNUSED(parentCommand);
6919 	return false;
6920 }
6921 */
6922 
setupAutoscroll(bool moving)6923 void SketchWidget::setupAutoscroll(bool moving) {
6924     m_autoScrollX = m_autoScrollY = 0;
6925 	m_autoScrollThreshold = (moving) ? MoveAutoScrollThreshold : DragAutoScrollThreshold;
6926 	m_autoScrollCount = 0;
6927 	connect(&m_autoScrollTimer, SIGNAL(timeout()), this,
6928 		moving ? SLOT(moveAutoScrollTimeout()) : SLOT(dragAutoScrollTimeout()));
6929 	//DebugDialog::debug("set up autoscroll");
6930 }
6931 
turnOffAutoscroll()6932 void SketchWidget::turnOffAutoscroll() {
6933 	m_autoScrollTimer.stop();
6934 	disconnect(&m_autoScrollTimer, SIGNAL(timeout()), this, SLOT(moveAutoScrollTimeout()));
6935 	disconnect(&m_autoScrollTimer, SIGNAL(timeout()), this, SLOT(dragAutoScrollTimeout()));
6936 	//DebugDialog::debug("turn off autoscroll");
6937 
6938 }
6939 
checkAutoscroll(QPoint globalPos)6940 bool SketchWidget::checkAutoscroll(QPoint globalPos)
6941 {
6942 	QRect r = rect();
6943 	QPoint q = mapFromGlobal(globalPos);
6944 
6945 	if (verticalScrollBar()->isVisible()) {
6946 		r.setWidth(width() - verticalScrollBar()->width());
6947 	}
6948 
6949 	if (horizontalScrollBar()->isVisible()) {
6950 		r.setHeight(height() - horizontalScrollBar()->height());
6951 	}
6952 
6953 	if (!r.contains(q)) {
6954 		m_autoScrollX = m_autoScrollY = 0;
6955 		if (m_autoScrollCount < m_autoScrollThreshold) {
6956 			m_autoScrollCount = 0;
6957 		}
6958 		return false;
6959 	}
6960 
6961 	//DebugDialog::debug(QString("check autoscroll %1, %2 %3").arg(QTime::currentTime().msec()).arg(q.x()).arg(q.y()) );
6962 
6963 	r.adjust(16,16,-16,-16);						// these should be set someplace
6964 	bool autoScroll = !r.contains(q);
6965 	if (autoScroll) {
6966 		if (++m_autoScrollCount < m_autoScrollThreshold) {
6967 			m_autoScrollX = m_autoScrollY = 0;
6968 			//DebugDialog::debug("in autoscrollThreshold");
6969 			return true;
6970 		}
6971 
6972 		if (m_clearSceneRect) {
6973 			scene()->setSceneRect(QRectF());
6974 			m_clearSceneRect = true;
6975 		}
6976 
6977 		int dx = 0, dy = 0;
6978 		if (q.x() > r.right()) {
6979 			dx = q.x() - r.right();
6980 		}
6981 		else if (q.x() < r.left()) {
6982 			dx = q.x() - r.left();
6983 		}
6984 		if (q.y() > r.bottom()) {
6985 			dy = q.y() - r.bottom();
6986 		}
6987 		else if (q.y() < r.top()) {
6988 			dy = q.y() - r.top();
6989 		}
6990 
6991 		int div = 3;
6992 		if (dx != 0) {
6993 			m_autoScrollX = (dx + ((dx > 0) ? div : -div)) / (div + 1);		// ((m_autoScrollX > 0) ? 1 : -1)
6994 		}
6995 		if (dy != 0) {
6996 			m_autoScrollY = (dy + ((dy > 0) ? div : -div)) / (div + 1);		// ((m_autoScrollY > 0) ? 1 : -1)
6997 		}
6998 
6999 		if (!m_autoScrollTimer.isActive()) {
7000 			//DebugDialog::debug("starting autoscroll timer");
7001 			m_autoScrollTimer.start(10);
7002 		}
7003 
7004 	}
7005 	else {
7006 		m_autoScrollX = m_autoScrollY = 0;
7007 		if (m_autoScrollCount < m_autoScrollThreshold) {
7008 			m_autoScrollCount = 0;
7009 		}
7010 	}
7011 
7012 	//DebugDialog::debug(QString("autoscroll %1 %2").arg(m_autoScrollX).arg(m_autoScrollY) );
7013 	return true;
7014 }
7015 
setWireVisible(Wire * wire)7016 void SketchWidget::setWireVisible(Wire * wire) {
7017 	Q_UNUSED(wire);
7018 }
7019 
forwardRoutingStatus(const RoutingStatus & routingStatus)7020 void SketchWidget::forwardRoutingStatus(const RoutingStatus & routingStatus) {
7021 
7022 	emit routingStatusSignal(this, routingStatus);
7023 }
7024 
matchesLayer(ModelPart * modelPart)7025 bool SketchWidget::matchesLayer(ModelPart * modelPart) {
7026     LayerList viewLayers = modelPart->viewLayers(m_viewID);
7027 
7028 	foreach (ViewLayer* viewLayer, m_viewLayers) {
7029 		if (viewLayers.contains(viewLayer->viewLayerID())) return true;
7030 	}
7031 
7032 	return false;
7033 }
7034 
viewName()7035 const QString & SketchWidget::viewName() {
7036 	return m_viewName;
7037 }
7038 
setNoteText(long itemID,const QString & newText)7039 void SketchWidget::setNoteText(long itemID, const QString & newText) {
7040 	ItemBase * itemBase = findItem(itemID);
7041 	if (itemBase == NULL) return;
7042 
7043 	Note * note = qobject_cast<Note *>(itemBase);
7044 	if (note == NULL) return;
7045 
7046 	note->setText(newText, false);
7047 }
7048 
incInstanceTitle(long itemID)7049 void SketchWidget::incInstanceTitle(long itemID) {
7050 	ItemBase * itemBase = findItem(itemID);
7051 	if (itemBase) {
7052 		itemBase->ensureUniqueTitle(itemBase->instanceTitle(), true);
7053 	}
7054 
7055 	emit updatePartLabelInstanceTitleSignal(itemID);
7056 }
7057 
updatePartLabelInstanceTitleSlot(long itemID)7058 void SketchWidget::updatePartLabelInstanceTitleSlot(long itemID) {
7059 	ItemBase * itemBase = findItem(itemID);
7060 	if (itemBase) {
7061 		itemBase->updatePartLabelInstanceTitle();
7062 	}
7063 }
7064 
setInstanceTitle(long itemID,const QString & oldText,const QString & newText,bool isUndoable,bool doEmit)7065 void SketchWidget::setInstanceTitle(long itemID, const QString & oldText, const QString & newText, bool isUndoable, bool doEmit) {
7066 	// isUndoable is true when setInstanceTitle is called from the infoview
7067 
7068 	ItemBase * itemBase = findItem(itemID);
7069 	if (itemBase == NULL) return;
7070 
7071 	if (!isUndoable) {
7072 		itemBase->setInstanceTitle(newText, false);
7073 		if (doEmit && currentlyInfoviewed(itemBase))  {
7074 			// TODO: just change the affected item in the info view
7075 			viewItemInfo(itemBase);
7076 		}
7077 
7078 		if (doEmit) {
7079 			emit setInstanceTitleSignal(itemID, oldText, newText, isUndoable, false);
7080 		}
7081 	}
7082 	else {
7083 		if (oldText.compare(newText) == 0) return;
7084 
7085         /*
7086         // this doesn't work correctly--undo somehow gets messed up
7087         LogoItem * logoItem = qobject_cast<LogoItem *>(itemBase);
7088         if (logoItem && logoItem->hasLogo()) {
7089             // instead of changing part label, change logo
7090             if (logoItem->logo().compare(newText) == 0) return;
7091 
7092             setProp(logoItem, "logo", tr("logo"), logoItem->logo(), newText, true);
7093             return;
7094         }
7095         */
7096 
7097 		partLabelChangedAux(itemBase, oldText, newText);
7098 	}
7099 }
7100 
showPartLabel(long itemID,bool showIt)7101 void SketchWidget::showPartLabel(long itemID, bool showIt) {
7102 
7103 	ItemBase * itemBase = findItem(itemID);
7104 	if (itemBase != NULL) {
7105 		itemBase->showPartLabel(showIt, m_viewLayers.value(getLabelViewLayerID(itemBase)));
7106 	}
7107 }
7108 
hidePartLabel(ItemBase * item)7109 void SketchWidget::hidePartLabel(ItemBase * item) {
7110 	QList <ItemBase *> itemBases;
7111 	itemBases.append(item);
7112 	showPartLabelsAux(false, itemBases);
7113 }
7114 
7115 
collectParts(QList<ItemBase * > & partList)7116 void SketchWidget::collectParts(QList<ItemBase *> & partList) {
7117     // using PaletteItem instead of ItemBase ensures layerKinChiefs only
7118 	foreach (QGraphicsItem * item, scene()->items()) {
7119 		PaletteItem * pitem = dynamic_cast<PaletteItem *>(item);
7120 		if (pitem == NULL) continue;
7121 		if (pitem->itemType() == ModelPart::Symbol) continue;
7122 
7123 		partList.append(pitem);
7124 	}
7125 }
7126 
movePartLabel(long itemID,QPointF newPos,QPointF newOffset)7127 void SketchWidget::movePartLabel(long itemID, QPointF newPos, QPointF newOffset)
7128 {
7129 	ItemBase * item = findItem(itemID);
7130 	if (item == NULL) return;
7131 
7132 	item->movePartLabel(newPos, newOffset);
7133 }
7134 
setCurrent(bool current)7135 void SketchWidget::setCurrent(bool current) {
7136 	m_current = current;
7137 }
7138 
partLabelMoved(ItemBase * itemBase,QPointF oldPos,QPointF oldOffset,QPointF newPos,QPointF newOffset)7139 void SketchWidget::partLabelMoved(ItemBase * itemBase, QPointF oldPos, QPointF oldOffset, QPointF newPos, QPointF newOffset)
7140 {
7141 	MoveLabelCommand * command = new MoveLabelCommand(this, itemBase->id(), oldPos, oldOffset, newPos, newOffset, NULL);
7142 	command->setText(tr("Move label '%1'").arg(itemBase->title()));
7143 	m_undoStack->push(command);
7144 }
7145 
7146 
rotateFlipPartLabel(ItemBase * itemBase,double degrees,Qt::Orientations flipDirection)7147 void SketchWidget::rotateFlipPartLabel(ItemBase * itemBase, double degrees, Qt::Orientations flipDirection) {
7148 	RotateFlipLabelCommand * command = new RotateFlipLabelCommand(this, itemBase->id(), degrees, flipDirection, NULL);
7149 	command->setText(tr("%1 label '%2'").arg((degrees != 0) ? tr("Rotate") : tr("Flip")).arg(itemBase->title()));
7150 	m_undoStack->push(command);
7151 }
7152 
7153 
rotateFlipPartLabel(long itemID,double degrees,Qt::Orientations flipDirection)7154 void SketchWidget::rotateFlipPartLabel(long itemID, double degrees, Qt::Orientations flipDirection) {
7155 	ItemBase * itemBase = findItem(itemID);
7156 	if (itemBase == NULL) return;
7157 
7158 	itemBase->doRotateFlipPartLabel(degrees, flipDirection);
7159 }
7160 
7161 
showPartLabels(bool show)7162 void SketchWidget::showPartLabels(bool show)
7163 {
7164 	QList<ItemBase *> itemBases;
7165 	foreach (QGraphicsItem * item, scene()->selectedItems()) {
7166 		ItemBase * itemBase = ItemBase::extractTopLevelItemBase(item);
7167 		if (itemBase == NULL) continue;
7168 
7169 		if (itemBase->hasPartLabel()) {
7170 			itemBases.append(itemBase);
7171 		}
7172 	}
7173 
7174 	if (itemBases.count() <= 0) return;
7175 
7176 	showPartLabelsAux(show, itemBases);
7177 }
7178 
showPartLabelsAux(bool show,QList<ItemBase * > & itemBases)7179 void SketchWidget::showPartLabelsAux(bool show, QList<ItemBase *> & itemBases)
7180 {
7181 	ShowLabelCommand * showLabelCommand = new ShowLabelCommand(this, NULL);
7182 	QString text;
7183 	if (show) {
7184 		text = tr("show %n part label(s)", "", itemBases.count());
7185 	}
7186 	else {
7187 		text = tr("hide %n part label(s)", "", itemBases.count());
7188 	}
7189 	showLabelCommand->setText(text);
7190 
7191 	foreach (ItemBase * itemBase, itemBases) {
7192 		showLabelCommand->add(itemBase->id(), itemBase->isPartLabelVisible(), show);
7193 	}
7194 
7195 	m_undoStack->push(showLabelCommand);
7196 }
7197 
noteSizeChanged(ItemBase * itemBase,const QSizeF & oldSize,const QSizeF & newSize)7198 void SketchWidget::noteSizeChanged(ItemBase * itemBase, const QSizeF & oldSize, const QSizeF & newSize)
7199 {
7200 	ResizeNoteCommand * command = new ResizeNoteCommand(this, itemBase->id(), oldSize, newSize, NULL);
7201 	command->setText(tr("Resize Note"));
7202 	m_undoStack->push(command);
7203 	clearHoldingSelectItem();
7204 }
7205 
resizeNote(long itemID,const QSizeF & size)7206 void SketchWidget::resizeNote(long itemID, const QSizeF & size)
7207 {
7208 	Note * note = qobject_cast<Note *>(findItem(itemID));
7209 	if (note == NULL) return;
7210 
7211 	note->setSize(size);
7212 }
7213 
renderToSVG(RenderThing & renderThing,QGraphicsItem * board,const LayerList & layers)7214 QString SketchWidget::renderToSVG(RenderThing & renderThing, QGraphicsItem * board, const LayerList & layers)
7215 {
7216     renderThing.board = board;
7217 	if (board) {
7218 		renderThing.offsetRect = board->sceneBoundingRect();
7219 	}
7220 	return renderToSVG(renderThing, layers);
7221 }
7222 
7223 
renderToSVG(RenderThing & renderThing,const LayerList & layers)7224 QString SketchWidget::renderToSVG(RenderThing & renderThing, const LayerList & layers)
7225 {
7226 
7227 	QList<QGraphicsItem *> itemsAndLabels;
7228 	QRectF itemsBoundingRect;
7229     QList<QGraphicsItem *> items;
7230     if (renderThing.selectedItems) {
7231         items = scene()->selectedItems();
7232     }
7233     else if (renderThing.board == NULL) {
7234         items = scene()->items();
7235     }
7236     else {
7237         items = scene()->collidingItems(renderThing.board);
7238         items << renderThing.board;
7239     }
7240 	foreach (QGraphicsItem * item, items) {
7241 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
7242 		if (itemBase == NULL) continue;
7243 		if (itemBase->hidden() || itemBase->layerHidden()) continue;
7244         if (!renderThing.renderBlocker) {
7245             Pad * pad = qobject_cast<Pad *>(itemBase);
7246             if (pad != NULL && pad->copperBlocker()) {
7247                 continue;
7248             }
7249         }
7250 
7251 		if (itemBase == itemBase->layerKinChief() && itemBase->isPartLabelVisible()) {
7252 			if (layers.contains(itemBase->partLabelViewLayerID())) {
7253 				itemsAndLabels.append(itemBase->partLabel());
7254 				itemsBoundingRect |= itemBase->partLabelSceneBoundingRect();
7255 			}
7256 		}
7257 
7258 		if (!itemBase->isVisible()) continue;
7259 		if (!layers.contains(itemBase->viewLayerID())) continue;
7260 
7261 		itemsAndLabels.append(itemBase);
7262 		itemsBoundingRect |= item->sceneBoundingRect();
7263 	}
7264 
7265     renderThing.itemsBoundingRect = itemsBoundingRect;
7266 	return renderToSVG(renderThing, itemsAndLabels);
7267 }
7268 
translateSVG(QString & svg,QPointF loc,double dpi,double printerScale)7269 QString translateSVG(QString & svg, QPointF loc, double dpi, double printerScale) {
7270 	loc.setX(loc.x() * dpi / printerScale);
7271 	loc.setY(loc.y() * dpi / printerScale);
7272 	if (loc.x() != 0 || loc.y() != 0) {
7273 		svg = QString("<g transform='translate(%1,%2)' >%3</g>")
7274 			.arg(loc.x())
7275 			.arg(loc.y())
7276 			.arg(svg);
7277 	}
7278 	return svg;
7279 }
7280 
renderToSVG(RenderThing & renderThing,QList<QGraphicsItem * > & itemsAndLabels)7281 QString SketchWidget::renderToSVG(RenderThing & renderThing, QList<QGraphicsItem *> & itemsAndLabels)
7282 {
7283 	renderThing.empty = true;
7284 
7285 	double width = renderThing.itemsBoundingRect.width();
7286 	double height = renderThing.itemsBoundingRect.height();
7287 	QPointF offset = renderThing.itemsBoundingRect.topLeft();
7288 
7289 	if (!renderThing.offsetRect.isEmpty()) {
7290 		offset = renderThing.offsetRect.topLeft();
7291 		width = renderThing.offsetRect.width();
7292 		height = renderThing.offsetRect.height();
7293 	}
7294 
7295 	renderThing.imageRect.setRect(offset.x(), offset.y(), width, height);
7296 	QString outputSVG = TextUtils::makeSVGHeader(renderThing.printerScale, renderThing.dpi, width, height);
7297 
7298 	QHash<QString, QString> svgHash;
7299 
7300 	// put them in z order
7301 	qSort(itemsAndLabels.begin(), itemsAndLabels.end(), zLessThan);
7302 
7303 	QList<ItemBase *> gotLabel;
7304 	foreach (QGraphicsItem * item, itemsAndLabels) {
7305 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
7306 		if (itemBase == NULL) {
7307 			PartLabel * partLabel = dynamic_cast<PartLabel *>(item);
7308 			if (partLabel == NULL) continue;
7309 
7310 			QString labelSvg = partLabel->owner()->makePartLabelSvg(renderThing.blackOnly, renderThing.dpi, renderThing.printerScale);
7311 			if (labelSvg.isEmpty()) continue;
7312 
7313             labelSvg = translateSVG(labelSvg, partLabel->owner()->partLabelScenePos() - offset, renderThing.dpi, renderThing.printerScale);
7314             labelSvg = QString("<g partID='%1' id='partLabel'>%2</g>").arg(partLabel->owner()->id()).arg(labelSvg);
7315 
7316 			renderThing.empty = false;
7317 			outputSVG.append(labelSvg);
7318 			continue;
7319 		}
7320 
7321 		if (itemBase->itemType() != ModelPart::Wire) {
7322             double factor;
7323 			QString itemSvg = itemBase->retrieveSvg(itemBase->viewLayerID(), svgHash, renderThing.blackOnly, renderThing.dpi, factor);
7324 			if (itemSvg.isEmpty()) continue;
7325 
7326             TextUtils::fixMuch(itemSvg, false);
7327 
7328             QDomDocument doc;
7329             QString errorStr;
7330 	        int errorLine;
7331 	        int errorColumn;
7332             if (doc.setContent(itemSvg, &errorStr, &errorLine, &errorColumn)) {
7333                 bool changed = false;
7334                 if (renderThing.renderBlocker) {
7335                     Pad * pad = qobject_cast<Pad *>(itemBase);
7336                     if (pad && pad->copperBlocker()) {
7337                         QDomNodeList nodeList = doc.documentElement().elementsByTagName("rect");
7338                         for (int n = 0; n < nodeList.count(); n++) {
7339                             QDomElement element = nodeList.at(n).toElement();
7340                             element.setAttribute("fill-opacity", 1);
7341                             changed = true;
7342                         }
7343                     }
7344                 }
7345 
7346 			    foreach (ConnectorItem * ci, itemBase->cachedConnectorItems()) {
7347                     SvgIdLayer * svgIdLayer = ci->connector()->fullPinInfo(itemBase->viewID(), itemBase->viewLayerID());
7348                     if (renderThing.hideTerminalPoints && !svgIdLayer->m_terminalId.isEmpty()) {
7349                         // these tend to be degenerate shapes and can cause trouble at gerber export time
7350                         if (hideTerminalID(doc, svgIdLayer->m_terminalId)) changed = true;
7351                     }
7352 
7353                     if (ensureStrokeWidth(doc, svgIdLayer->m_svgId, factor)) changed = true;
7354 
7355 				    if (!ci->hasRubberBandLeg()) continue;
7356 
7357                     // at the moment, the legs don't get a partID, but since there are no legs in PCB view, we don't care
7358 				    outputSVG.append(ci->makeLegSvg(offset, renderThing.dpi, renderThing.printerScale, renderThing.blackOnly));
7359 			    }
7360 
7361                 if (changed) {
7362                     itemSvg = doc.toString(0);
7363                 }
7364             }
7365 
7366             QTransform t = itemBase->transform();
7367             itemSvg = TextUtils::svgTransform(itemSvg, t, false, QString());
7368             itemSvg = translateSVG(itemSvg, itemBase->scenePos() - offset, renderThing.dpi, renderThing.printerScale);
7369             itemSvg =  QString("<g partID='%1'>%2</g>").arg(itemBase->id()).arg(itemSvg);
7370 			outputSVG.append(itemSvg);
7371 			renderThing.empty = false;
7372 
7373 			/*
7374 			// TODO:  deal with rotations and flips
7375 			QString shifted = splitter->shift(loc.x(), loc.y(), xmlName);
7376 			outputSVG.append(shifted);
7377 			empty = false;
7378 			splitter->shift(-loc.x(), -loc.y(), xmlName);
7379 			*/
7380 		}
7381 		else {
7382 			Wire * wire = qobject_cast<Wire *>(itemBase);
7383 			if (wire == NULL) continue;
7384 
7385 			//if (wire->getTrace()) {
7386 			//	DebugDialog::debug(QString("trace %1 %2,%3 %4,%5")
7387 			//		.arg(wire->id())
7388 			//		.arg(wire->line().p1().x())
7389 			//		.arg(wire->line().p1().y())
7390 			//		.arg(wire->line().p2().x())
7391 			//		.arg(wire->line().p2().y())
7392 			//		);
7393 			//}
7394 
7395             QString wireSvg = makeWireSVG(wire, offset, renderThing.dpi, renderThing.printerScale, renderThing.blackOnly);
7396             wireSvg = QString("<g partID='%1'>%2</g>").arg(wire->id()).arg(wireSvg);
7397 			outputSVG.append(wireSvg);
7398 			renderThing.empty = false;
7399 		}
7400 		extraRenderSvgStep(itemBase, offset, renderThing.dpi, renderThing.printerScale, outputSVG);
7401 	}
7402 
7403 	outputSVG += "</svg>";
7404 
7405 	return outputSVG;
7406 }
7407 
extraRenderSvgStep(ItemBase * itemBase,QPointF offset,double dpi,double printerScale,QString & outputSvg)7408 void SketchWidget::extraRenderSvgStep(ItemBase * itemBase, QPointF offset, double dpi, double printerScale, QString & outputSvg)
7409 {
7410 	Q_UNUSED(itemBase);
7411 	Q_UNUSED(offset);
7412 	Q_UNUSED(dpi);
7413 	Q_UNUSED(printerScale);
7414 	Q_UNUSED(outputSvg);
7415 }
7416 
makeWireSVG(Wire * wire,QPointF offset,double dpi,double printerScale,bool blackOnly)7417 QString SketchWidget::makeWireSVG(Wire * wire, QPointF offset, double dpi, double printerScale, bool blackOnly)
7418 {
7419 	QString shadow;
7420     bool dashed = false;
7421 	if (wire->hasShadow()) {
7422 		shadow = makeWireSVGAux(wire, wire->shadowWidth(), wire->shadowHexString(), offset, dpi, printerScale, blackOnly, false);
7423 
7424         if (wire->banded()) {
7425             dashed = true;
7426             shadow += makeWireSVGAux(wire, wire->width(), "white", offset, dpi, printerScale, blackOnly, false);
7427         }
7428 	}
7429 
7430 
7431 
7432 	return shadow + makeWireSVGAux(wire, wire->width(), wire->hexString(), offset, dpi, printerScale, blackOnly, dashed);
7433 }
7434 
makeWireSVGAux(Wire * wire,double width,const QString & color,QPointF offset,double dpi,double printerScale,bool blackOnly,bool dashed)7435 QString SketchWidget::makeWireSVGAux(Wire * wire, double width, const QString & color, QPointF offset, double dpi, double printerScale, bool blackOnly, bool dashed)
7436 {
7437 	if (wire->isCurved()) {
7438 		QPolygonF poly = wire->sceneCurve(offset);
7439 		return TextUtils::makeCubicBezierSVG(poly, width, color, dpi, printerScale, blackOnly, dashed, Wire::TheDash);
7440 	}
7441 	else {
7442 		QLineF line = wire->getPaintLine();
7443 		QPointF p1 = wire->scenePos() + line.p1() - offset;
7444 		QPointF p2 = wire->scenePos() + line.p2() - offset;
7445 		return TextUtils::makeLineSVG(p1, p2, width, color, dpi, printerScale, blackOnly, dashed, Wire::TheDash);
7446 	}
7447 }
7448 
7449 
drawBackground(QPainter * painter,const QRectF & rect)7450 void SketchWidget::drawBackground( QPainter * painter, const QRectF & rect )
7451 {
7452 	if (!rect.isValid()) {
7453 		return;
7454 	}
7455 
7456 	//DebugDialog::debug(QString("draw background %1").arg(viewName()));
7457 
7458 	InfoGraphicsView::drawBackground(painter, rect);
7459 
7460 	// from:  http://www.qtcentre.org/threads/27364-Trying-to-draw-a-grid-on-a-QGraphicsScene-View
7461 
7462 	InfoGraphicsView::drawForeground(painter, rect);
7463 
7464 	// always draw the logo in the same place in the window
7465 	// no matter how the view is zoomed or scrolled
7466 
7467     static QPixmap * bgPixmap = NULL;
7468     if (bgPixmap == NULL) {
7469         bgPixmap = new QPixmap(":resources/images/fritzing_logo_background.png");
7470     }
7471     if (bgPixmap) {
7472 		QPointF p = painter->viewport().bottomLeft();
7473         int hOffset = 0;
7474         if (horizontalScrollBar()->isVisible()) {
7475             hOffset = horizontalScrollBar()->height();
7476         }
7477         p += QPointF(25, hOffset - 25 - bgPixmap->height());
7478 	    painter->save();
7479 	    painter->setWindow(painter->viewport());
7480 	    painter->setTransform(QTransform());
7481 	    painter->drawPixmap(p,*bgPixmap);
7482 	    painter->restore();
7483     }
7484 
7485 	if (m_showGrid) {
7486 		double gridSize = m_gridSizeInches * GraphicsUtils::SVGDPI;
7487         int intGridSize = int(gridSize * 10000);
7488         if (intGridSize > 0) {
7489 		    double left = int(rect.left() * 10000) - (int(rect.left() * 10000) % intGridSize);
7490 		    left /= 10000;
7491 		    double top = int(rect.top() * 10000) - (int(rect.top() * 10000) % intGridSize);
7492 		    top /= 10000;
7493 
7494 		    QVarLengthArray<QLineF, 100> linesX;
7495 		    for (double x = left; x < rect.right(); x += gridSize) {
7496 			    linesX.append(QLineF(x, rect.top(), x, rect.bottom()));
7497 		    }
7498 
7499 		    QVarLengthArray<QLineF, 100> linesY;
7500 		    for (double  y = top; y < rect.bottom(); y += gridSize) {
7501 			    linesY.append(QLineF(rect.left(), y, rect.right(), y));
7502 		    }
7503 
7504             //DebugDialog::debug(QString("lines %1 %2").arg(linesX.count()).arg(linesY.count()));
7505 
7506 		    QPen pen;
7507 		    pen.setColor(m_gridColor);
7508 		    pen.setWidth(0);
7509 		    pen.setCosmetic(true);
7510 		    //pen.setStyle(Qt::DotLine);
7511 		    //QVector<double> dashes;                   // removed dash pattern at forum suggestion: http://fritzing.org/forum/thread/855
7512 		    //dashes << 1 << 1;
7513 		    //pen.setDashPattern(dashes);
7514 		    painter->save();
7515 		    painter->setPen(pen);
7516 		    painter->drawLines(linesX.data(), linesX.size());
7517 		    painter->drawLines(linesY.data(), linesY.size());
7518 		    painter->restore();
7519         }
7520 	}
7521 
7522 
7523 
7524 }
7525 
7526 /*
7527 QPoint SketchWidget::calcFixedToCenterItemOffset(const QRect & viewPortRect, const QSizeF & helpSize) {
7528 	QPoint p((int) ((viewPortRect.width() - helpSize.width()) / 2.0),
7529 			 (int) ((viewPortRect.height() - helpSize.height()) / 2.0));
7530 	return p;
7531 }
7532 */
7533 
pushCommand(QUndoCommand * command,QObject * thing)7534 void SketchWidget::pushCommand(QUndoCommand * command, QObject * thing) {
7535 	if (m_undoStack) {
7536         CommandProgress * commandProgress = BaseCommand::initProgress();
7537         connect(commandProgress, SIGNAL(incRedo()), thing, SLOT(incCommandProgress()));
7538 		m_undoStack->push(command);
7539         BaseCommand::clearProgress();
7540 	}
7541 }
7542 
spaceBarIsPressed()7543 bool SketchWidget::spaceBarIsPressed() {
7544 	return m_spaceBarIsPressed || m_middleMouseIsPressed;
7545 }
7546 
defaultConnectorLayer(ViewLayer::ViewID viewId)7547 ViewLayer::ViewLayerID SketchWidget::defaultConnectorLayer(ViewLayer::ViewID viewId) {
7548 	switch(viewId) {
7549 		case ViewLayer::IconView: return ViewLayer::Icon;
7550 		case ViewLayer::BreadboardView: return ViewLayer::Breadboard;
7551 		case ViewLayer::SchematicView: return ViewLayer::Schematic;
7552 		case ViewLayer::PCBView: return ViewLayer::Copper0;
7553 		default: return ViewLayer::UnknownLayer;
7554 	}
7555 }
7556 
swappedGender(ConnectorItem * connectorItem,Connector * newConnector)7557 bool SketchWidget::swappedGender(ConnectorItem * connectorItem, Connector * newConnector)
7558 {
7559 	return (connectorItem->connectorType() != newConnector->connectorType());
7560 }
7561 
setLastPaletteItemSelected(PaletteItem * paletteItem)7562 void SketchWidget::setLastPaletteItemSelected(PaletteItem * paletteItem)
7563 {
7564 	m_lastPaletteItemSelected = paletteItem;
7565 	//DebugDialog::debug(QString("m_lastPaletteItemSelected:%1 %2").arg(paletteItem == NULL ? "NULL" : paletteItem->instanceTitle()).arg(m_viewID));
7566 }
7567 
setLastPaletteItemSelectedIf(ItemBase * itemBase)7568 void SketchWidget::setLastPaletteItemSelectedIf(ItemBase * itemBase)
7569 {
7570 	PaletteItem * paletteItem = qobject_cast<PaletteItem *>(itemBase);
7571 	if (paletteItem == NULL) return;
7572 
7573 	setLastPaletteItemSelected(paletteItem);
7574 }
7575 
setResistance(QString resistance,QString pinSpacing)7576 void SketchWidget::setResistance(QString resistance, QString pinSpacing)
7577 {
7578 	PaletteItem * item = getSelectedPart();
7579 	if (item == NULL) return;
7580 
7581 	ModelPart * modelPart = item->modelPart();
7582 
7583 	if (!modelPart->moduleID().endsWith(ModuleIDNames::ResistorModuleIDName)) return;
7584 
7585 	Resistor * resistor = qobject_cast<Resistor *>(item);
7586 	if (resistor == NULL) return;
7587 
7588 	if (resistance.isEmpty()) {
7589 		resistance = resistor->resistance();
7590 	}
7591 
7592 	if (pinSpacing.isEmpty()) {
7593 		pinSpacing = resistor->pinSpacing();
7594 	}
7595 
7596 	SetResistanceCommand * cmd = new SetResistanceCommand(this, item->id(), resistor->resistance(), resistance, resistor->pinSpacing(), pinSpacing, NULL);
7597 	cmd->setText(tr("Change Resistance from %1 to %2").arg(resistor->resistance()).arg(resistance));
7598 	m_undoStack->waitPush(cmd, PropChangeDelay);
7599 }
7600 
setResistance(long itemID,QString resistance,QString pinSpacing,bool doEmit)7601 void SketchWidget::setResistance(long itemID, QString resistance, QString pinSpacing, bool doEmit) {
7602 	ItemBase * item = findItem(itemID);
7603 	if (item == NULL) return;
7604 
7605 	Resistor * ritem = qobject_cast<Resistor *>(item);
7606 	if (ritem == NULL) return;
7607 
7608 	ritem->setResistance(resistance, pinSpacing, false);
7609 	viewItemInfo(item);
7610 
7611 	if (doEmit) {
7612 		emit setResistanceSignal(itemID, resistance, pinSpacing, false);
7613 	}
7614 }
7615 
setProp(ItemBase * item,const QString & prop,const QString & trProp,const QString & oldValue,const QString & newValue,bool redraw)7616 void SketchWidget::setProp(ItemBase * item, const QString & prop, const QString & trProp, const QString & oldValue, const QString & newValue, bool redraw)
7617 {
7618 	if (oldValue.isEmpty() && newValue.isEmpty()) return;
7619 
7620 	SetPropCommand * cmd = new SetPropCommand(this, item->id(), prop, oldValue, newValue, redraw, NULL);
7621 	cmd->setText(tr("Change %1 from %2 to %3").arg(trProp).arg(oldValue).arg(newValue));
7622 	// unhook triggered action from originating widget event
7623     m_undoStack->waitPush(cmd, PropChangeDelay);
7624 }
7625 
setHoleSize(ItemBase * item,const QString & prop,const QString & trProp,const QString & oldValue,const QString & newValue,QRectF & oldRect,QRectF & newRect,bool redraw)7626 void SketchWidget::setHoleSize(ItemBase * item, const QString & prop, const QString & trProp, const QString & oldValue, const QString & newValue, QRectF & oldRect, QRectF & newRect, bool redraw)
7627 {
7628 	if (oldValue.isEmpty() && newValue.isEmpty()) return;
7629 
7630 	QUndoCommand * parentCommand = new QUndoCommand(tr("Change %1 from %2 to %3").arg(trProp).arg(oldValue).arg(newValue));
7631 	new SetPropCommand(this, item->id(), prop, oldValue, newValue, redraw, parentCommand);
7632 	item->saveGeometry();
7633 	ViewGeometry vg(item->getViewGeometry());
7634 	QPointF p(vg.loc().x() + (oldRect.width() / 2) - (newRect.width() / 2),
7635 				vg.loc().y() + (oldRect.height() / 2) - (newRect.height() / 2));
7636 	vg.setLoc(p);
7637 	new MoveItemCommand(this, item->id(), item->getViewGeometry(), vg, false, parentCommand);
7638         //DebugDialog::debug("set hole", oldRect);
7639         //DebugDialog::debug("        ", newRect);
7640         //DebugDialog::debug("        ", item->getViewGeometry().loc());
7641         //DebugDialog::debug("        ", p);
7642     m_undoStack->waitPush(parentCommand, PropChangeDelay);
7643 }
7644 
setProp(long itemID,const QString & prop,const QString & value,bool redraw,bool doEmit)7645 void SketchWidget::setProp(long itemID, const QString & prop, const QString & value, bool redraw, bool doEmit) {
7646 	ItemBase * item = findItem(itemID);
7647 	if (item == NULL) return;
7648 
7649 	item->setProp(prop, value);
7650 	if (redraw) {
7651 		viewItemInfo(item);
7652 	}
7653 
7654 	if (doEmit) {
7655 		emit setPropSignal(itemID, prop, value, false, false);
7656 	}
7657 }
7658 
7659 // called from ResizeBoardCommand
resizeBoard(long itemID,double mmW,double mmH)7660 ItemBase * SketchWidget::resizeBoard(long itemID, double mmW, double mmH) {
7661 	ItemBase * itemBase = findItem(itemID);
7662 	if (itemBase == NULL) return NULL;
7663 
7664     bool resized = false;
7665 	switch (itemBase->itemType()) {
7666 		case ModelPart::ResizableBoard:
7667 			qobject_cast<ResizableBoard *>(itemBase)->resizeMM(mmW, mmH, m_viewLayers);
7668 			resized = true;
7669             break;
7670 
7671 		case ModelPart::Logo:
7672 			qobject_cast<LogoItem *>(itemBase)->resizeMM(mmW, mmH, m_viewLayers);
7673 			resized = true;
7674             break;
7675 
7676 		case ModelPart::Ruler:
7677 			qobject_cast<Ruler *>(itemBase)->resizeMM(mmW, mmH, m_viewLayers);
7678 			resized = true;
7679             break;
7680 	}
7681 
7682     if (!resized) {
7683 	    Pad * pad = qobject_cast<Pad *>(itemBase);
7684 	    if (pad) {
7685 		    pad->resizeMM(mmW, mmH, m_viewLayers);
7686 			resized = true;
7687 	    }
7688     }
7689 
7690     if (!resized) {
7691 	    SchematicFrame * schematicFrame = qobject_cast<SchematicFrame *>(itemBase);
7692 	    if (schematicFrame) {
7693 		    schematicFrame->resizeMM(mmW, mmH, m_viewLayers);
7694 		    resized = true;
7695 	    }
7696     }
7697 
7698     if (resized) {
7699         emit resizedSignal(itemBase);
7700     }
7701 
7702     return itemBase;
7703 }
7704 
resizeBoard(double mmW,double mmH,bool doEmit)7705 void SketchWidget::resizeBoard(double mmW, double mmH, bool doEmit)
7706 {
7707 	Q_UNUSED(doEmit);
7708 
7709 	PaletteItem * item = getSelectedPart();
7710 	if (item == NULL) {
7711 		return InfoGraphicsView::resizeBoard(mmW, mmH, doEmit);
7712 	}
7713 
7714 	switch (item->itemType()) {
7715 		case ModelPart::Ruler:
7716 			break;
7717 		case ModelPart::Logo:
7718             resizeWithHandle(item, mmW, mmH);
7719 		default:
7720 			return;
7721 	}
7722 
7723 	QString orig = item->prop("width");
7724 	QString temp = orig;
7725 	temp.chop(2);
7726 	double origw = temp.toDouble();
7727 	double origh = orig.endsWith("cm") ? 0 : 1;
7728 	QUndoCommand * parentCommand = new QUndoCommand(tr("Resize ruler to %1 %2").arg(mmW).arg((mmH == 0) ? "cm" : "in"));
7729 	new ResizeBoardCommand(this, item->id(), origw, origh, mmW, mmH, parentCommand);
7730 	m_undoStack->waitPush(parentCommand, PropChangeDelay);
7731 }
7732 
resizeWithHandle(ItemBase * itemBase,double mmW,double mmH)7733 void SketchWidget::resizeWithHandle(ItemBase * itemBase, double mmW, double mmH) {
7734 	double origw = itemBase->modelPart()->localProp("width").toDouble();
7735 	double origh = itemBase->modelPart()->localProp("height").toDouble();
7736 
7737 	if (mmH == 0 || mmW == 0) {
7738 		dynamic_cast<ResizableBoard *>(itemBase)->setInitialSize();
7739 		double w = itemBase->modelPart()->localProp("width").toDouble();
7740 		double h = itemBase->modelPart()->localProp("height").toDouble();
7741 		if (origw == w && origh == h) {
7742 			// no change
7743 			return;
7744 		}
7745 
7746 		viewItemInfo(itemBase);
7747 		mmW = w;
7748 		mmH = h;
7749 	}
7750 
7751 	QUndoCommand * parentCommand = new QUndoCommand(tr("Resize board to %1 %2").arg(mmW).arg(mmH));
7752 	rememberSticky(itemBase, parentCommand);
7753 	new ResizeBoardCommand(this, itemBase->id(), origw, origh, mmW, mmH, parentCommand);
7754 	new CheckStickyCommand(this, BaseCommand::SingleView, itemBase->id(), true, CheckStickyCommand::RedoOnly, parentCommand);
7755 	m_undoStack->waitPush(parentCommand, PropChangeDelay);
7756 
7757 }
7758 
addBendpoint(ItemBase * lastHoverEnterItem,ConnectorItem * lastHoverEnterConnectorItem,QPointF lastLocation)7759 void SketchWidget::addBendpoint(ItemBase * lastHoverEnterItem, ConnectorItem * lastHoverEnterConnectorItem, QPointF lastLocation) {
7760 	if (lastHoverEnterConnectorItem) {
7761 		Wire * wire = qobject_cast<Wire *>(lastHoverEnterConnectorItem->attachedTo());
7762 		if (wire != NULL) {
7763 			wireJoinSlot(wire, lastHoverEnterConnectorItem);
7764 		}
7765 	}
7766 	else if (lastHoverEnterItem) {
7767 		Wire * wire = qobject_cast<Wire *>(lastHoverEnterItem);
7768 		if (wire != NULL) {
7769 			wireSplitSlot(wire, lastLocation, wire->pos(), wire->line());
7770 		}
7771 	}
7772 }
7773 
flattenCurve(ItemBase * lastHoverEnterItem,ConnectorItem * lastHoverEnterConnectorItem,QPointF lastLocation)7774 void SketchWidget::flattenCurve(ItemBase * lastHoverEnterItem, ConnectorItem * lastHoverEnterConnectorItem, QPointF lastLocation) {
7775 	Q_UNUSED(lastLocation);
7776 	Wire * wire = NULL;
7777 	if (lastHoverEnterConnectorItem) {
7778 		wire = qobject_cast<Wire *>(lastHoverEnterConnectorItem->attachedTo());
7779 	}
7780 
7781 	if (wire == NULL && lastHoverEnterItem) {
7782 		wire = qobject_cast<Wire *>(lastHoverEnterItem);
7783 	}
7784 
7785 	if (wire != NULL) {
7786 		wireChangedCurveSlot(wire, wire->curve(), NULL, true);
7787 	}
7788 
7789 }
7790 
lastHoverEnterConnectorItem()7791 ConnectorItem * SketchWidget::lastHoverEnterConnectorItem() {
7792 	return m_lastHoverEnterConnectorItem;
7793 }
7794 
lastHoverEnterItem()7795 ItemBase * SketchWidget::lastHoverEnterItem() {
7796 	return m_lastHoverEnterItem;
7797 }
7798 
viewLayers()7799 LayerHash & SketchWidget::viewLayers() {
7800 	return m_viewLayers;
7801 }
7802 
setClipEnds(ClipableWire * vw,bool)7803 void SketchWidget::setClipEnds(ClipableWire * vw, bool) {
7804 	vw->setClipEnds(false);
7805 }
7806 
createTrace(Wire * wire,bool useLastWireColor)7807 void SketchWidget::createTrace(Wire * wire, bool useLastWireColor) {
7808 	QString commandString = tr("Create wire from Ratsnest");
7809 	createTrace(wire, commandString, getTraceFlag(), useLastWireColor);
7810 }
7811 
createTrace(Wire * fromWire,const QString & commandString,ViewGeometry::WireFlag flag,bool useLastWireColor)7812 void SketchWidget::createTrace(Wire * fromWire, const QString & commandString, ViewGeometry::WireFlag flag, bool useLastWireColor)
7813 {
7814 	QList<Wire *> done;
7815 	QUndoCommand * parentCommand = new QUndoCommand(commandString);
7816 
7817 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
7818 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
7819 
7820 	bool gotOne = false;
7821 	if (fromWire == NULL) {
7822 		foreach (QGraphicsItem * item, scene()->selectedItems()) {
7823 			Wire * wire = dynamic_cast<Wire *>(item);
7824 			if (wire == NULL) continue;
7825 			if (done.contains(wire)) continue;
7826 
7827 			gotOne = createOneTrace(wire, flag, false, done, useLastWireColor, parentCommand);
7828 		}
7829 	}
7830 	else {
7831 		gotOne = createOneTrace(fromWire, flag, false, done, useLastWireColor, parentCommand);
7832 	}
7833 
7834 	if (!gotOne) {
7835 		delete parentCommand;
7836 		return;
7837 	}
7838 
7839 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
7840 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
7841 	m_undoStack->push(parentCommand);
7842 }
7843 
7844 
createOneTrace(Wire * wire,ViewGeometry::WireFlag flag,bool allowAny,QList<Wire * > & done,bool useLastWireColor,QUndoCommand * parentCommand)7845 bool SketchWidget::createOneTrace(Wire * wire, ViewGeometry::WireFlag flag, bool allowAny, QList<Wire *> & done, bool useLastWireColor, QUndoCommand * parentCommand)
7846 {
7847 	QList<ConnectorItem *> ends;
7848 	Wire * trace = NULL;
7849 	if (wire->getRatsnest()) {
7850 		trace = wire->findTraced(getTraceFlag(), ends);
7851 	}
7852 	else if (wire->isTraceType(getTraceFlag())) {
7853 		trace = wire;
7854 	}
7855 	else if (!allowAny) {
7856 		// not eligible
7857 		return false;
7858 	}
7859 	else {
7860 		trace = wire->findTraced(getTraceFlag(), ends);
7861 	}
7862 
7863 	if (trace && trace->hasFlag(flag)) {
7864 		return false;
7865 	}
7866 
7867 	if (trace != NULL) {
7868 		removeWire(trace, ends, done, parentCommand);
7869 	}
7870 
7871 	QString colorString = traceColor(createWireViewLayerPlacement(ends[0], ends[1]));
7872     if (useLastWireColor && !this->m_lastColorSelected.isEmpty()) {
7873 		colorString = m_lastColorSelected;
7874 	}
7875 
7876 	long newID = createWire(ends[0], ends[1], flag, false, BaseCommand::CrossView, parentCommand);
7877 	// TODO: is this opacity constant stored someplace
7878 	new WireColorChangeCommand(this, newID, colorString, colorString, 1.0, 1.0, parentCommand);
7879 	new WireWidthChangeCommand(this, newID, getTraceWidth(), getTraceWidth(), parentCommand);
7880 	return true;
7881 }
7882 
selectAllWires(ViewGeometry::WireFlag flag)7883 void SketchWidget::selectAllWires(ViewGeometry::WireFlag flag)
7884 {
7885     QList<QGraphicsItem *> items = scene()->items();
7886     selectAllWiresFrom(flag, items);
7887 }
7888 
selectAllWiresFrom(ViewGeometry::WireFlag flag,QList<QGraphicsItem * > & items)7889 void SketchWidget::selectAllWiresFrom(ViewGeometry::WireFlag flag, QList<QGraphicsItem *> & items)
7890 {
7891 	QList<Wire *> wires;
7892 	foreach (QGraphicsItem * item, items) {
7893 		Wire * wire = dynamic_cast<Wire *>(item);
7894 		if (wire == NULL) continue;
7895 
7896 		if (wire->hasFlag(flag)) {
7897 			if (wire->parentItem() != NULL) {
7898 				// skip module wires
7899 				continue;
7900 			}
7901 
7902 			wires.append(wire);
7903 		}
7904 	}
7905 
7906 	if (wires.count() <= 0) {
7907 		// TODO: tell user?
7908 	}
7909 
7910 	QString wireName;
7911 	if (flag == getTraceFlag()) {
7912 		wireName = QObject::tr("Trace wires");
7913 	}
7914 	else if (flag == ViewGeometry::RatsnestFlag) {
7915 		wireName = QObject::tr("Ratsnest wires");
7916 	}
7917 	QUndoCommand * parentCommand = new QUndoCommand(QObject::tr("Select all %1").arg(wireName));
7918 
7919 	stackSelectionState(false, parentCommand);
7920 	SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
7921 	foreach (Wire * wire, wires) {
7922 		selectItemCommand->addRedo(wire->id());
7923 	}
7924 
7925 	scene()->clearSelection();
7926 	m_undoStack->push(parentCommand);
7927 }
7928 
tidyWires()7929 void SketchWidget::tidyWires() {
7930 }
7931 
updateConnectors()7932 void SketchWidget::updateConnectors() {
7933 	// update issue with 4.5.0?
7934 
7935     QList<ConnectorItem *> visited;
7936 	foreach (QGraphicsItem* item, scene()->items()) {
7937 		ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(item);
7938 		if (connectorItem == NULL) continue;
7939 
7940 		connectorItem->restoreColor(visited);
7941 	}
7942 }
7943 
getShortName()7944 const QString & SketchWidget::getShortName() {
7945 	return m_shortName;
7946 }
7947 
getBendpointWidths(Wire * wire,double width,double & bendpointWidth,double & bendpoint2Width,bool & negativeOffsetRect)7948 void SketchWidget::getBendpointWidths(Wire * wire, double width, double & bendpointWidth, double & bendpoint2Width, bool & negativeOffsetRect) {
7949 	Q_UNUSED(wire);
7950 	Q_UNUSED(width);
7951 	bendpoint2Width = bendpointWidth = -1;
7952 	negativeOffsetRect = true;
7953 }
7954 
standardBackground()7955 QColor SketchWidget::standardBackground() {
7956 	return RatsnestColors::backgroundColor(m_viewID);
7957 }
7958 
initBackgroundColor()7959 void SketchWidget::initBackgroundColor() {
7960 	setBackground(standardBackground());
7961 
7962 	QSettings settings;
7963 	QString colorName = settings.value(QString("%1BackgroundColor").arg(getShortName())).toString();
7964 	if (!colorName.isEmpty()) {
7965 		QColor color;
7966 		color.setNamedColor(colorName);
7967 		setBackground(color);
7968 	}
7969 
7970 	m_curvyWires = false;
7971 	QString curvy = settings.value(QString("%1CurvyWires").arg(getShortName())).toString();
7972 	if (!curvy.isEmpty()) {
7973 		m_curvyWires = (curvy.compare("1") == 0);
7974 	}
7975 }
7976 
includeSymbols()7977 bool SketchWidget::includeSymbols() {
7978 	return false;
7979 }
7980 
disconnectAll()7981 void SketchWidget::disconnectAll() {
7982 
7983 	QSet<ItemBase *> itemBases;
7984 	foreach (QGraphicsItem * item, scene()->selectedItems()) {
7985 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
7986 		if (itemBase == NULL) continue;
7987 
7988 		itemBases.insert(itemBase);
7989 	}
7990 
7991 	QList<ConnectorItem *> connectorItems;
7992 	foreach (ItemBase * itemBase, itemBases) {
7993 		ConnectorItem * fromConnectorItem = itemBase->rightClickedConnector();
7994 		if (fromConnectorItem == NULL) continue;
7995 
7996 		if (fromConnectorItem->connectedToWires()) {
7997 			connectorItems.append(fromConnectorItem);
7998 		}
7999 	}
8000 
8001 	if (connectorItems.count() <= 0) return;
8002 
8003 	QString string;
8004 	if (itemBases.count() == 1) {
8005 		ItemBase * firstItem = *(itemBases.begin());
8006 		string = tr("Disconnect all wires from %1").arg(firstItem->title());
8007 	}
8008 	else {
8009 		string = tr("Disconnect all wires from %1 items").arg(QString::number(itemBases.count()));
8010 	}
8011 
8012 	QUndoCommand * parentCommand = new QUndoCommand(string);
8013 
8014 	stackSelectionState(false, parentCommand);
8015 	new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
8016 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
8017 
8018 
8019 	QHash<ItemBase *, SketchWidget *> itemsToDelete;
8020 	disconnectAllSlot(connectorItems, itemsToDelete, parentCommand);
8021 	emit disconnectAllSignal(connectorItems, itemsToDelete, parentCommand);
8022 
8023 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
8024 	new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
8025 	foreach (ItemBase * item, itemsToDelete.keys()) {
8026 		itemsToDelete.value(item)->makeDeleteItemCommand(item, BaseCommand::CrossView, parentCommand);
8027 	}
8028 	m_undoStack->push(parentCommand);
8029 }
8030 
disconnectAllSlot(QList<ConnectorItem * > connectorItems,QHash<ItemBase *,SketchWidget * > & itemsToDelete,QUndoCommand * parentCommand)8031 void SketchWidget::disconnectAllSlot(QList<ConnectorItem *> connectorItems, QHash<ItemBase *, SketchWidget *> & itemsToDelete, QUndoCommand * parentCommand)
8032 {
8033 	// (jc 2011 Aug 16): this code is not hooked up and my last recollection is that it wasn't working
8034 
8035     return;
8036 
8037 	QList<ConnectorItem *> myConnectorItems;
8038 	foreach (ConnectorItem * ci, connectorItems) {
8039 		ItemBase * itemBase = findItem(ci->attachedToID());
8040 		if (itemBase == NULL) continue;
8041 
8042 		ConnectorItem * fromConnectorItem = findConnectorItem(itemBase, ci->connectorSharedID(), ViewLayer::NewTop);
8043 		if (fromConnectorItem != NULL) {
8044 			myConnectorItems.append(fromConnectorItem);
8045 		}
8046 		fromConnectorItem = findConnectorItem(itemBase, ci->connectorSharedID(), ViewLayer::NewBottom);
8047 		if (fromConnectorItem != NULL) {
8048 			myConnectorItems.append(fromConnectorItem);
8049 		}
8050 	}
8051 
8052 	QSet<ItemBase *> deletedItems;
8053 	foreach (ConnectorItem * fromConnectorItem, myConnectorItems) {
8054 		foreach (ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
8055 			if (toConnectorItem->attachedToItemType() == ModelPart::Wire) {
8056 				Wire * wire = qobject_cast<Wire *>(toConnectorItem->attachedTo());
8057 				if (!wire->getRatsnest()) {
8058 					QList<Wire *> chained;
8059 					QList<ConnectorItem *> ends;
8060 					wire->collectChained(chained, ends);
8061 					foreach (Wire * w, chained) {
8062 						itemsToDelete.insert(w, this);
8063 						deletedItems.insert(w);
8064 					}
8065 				}
8066 			}
8067 			else if (toConnectorItem->connectorType() == Connector::Female) {
8068 				if (ignoreFemale()) {
8069 					//fromConnectorItem->tempRemove(toConnectorItem, false);
8070 					//toConnectorItem->tempRemove(fromConnectorItem, false);
8071 					//extendChangeConnectionCommand(fromConnectorItem, toConnectorItem, false, true, parentCommand);
8072 				}
8073 				else {
8074 					ItemBase * detachee = fromConnectorItem->attachedTo();
8075 					QPointF newPos = calcNewLoc(detachee, toConnectorItem->attachedTo());
8076 					// delete connections
8077 					// add wires and connections for undisconnected connectors
8078 
8079 					detachee->saveGeometry();
8080 					ViewGeometry vg = detachee->getViewGeometry();
8081 					vg.setLoc(newPos);
8082 					new MoveItemCommand(this, detachee->id(), detachee->getViewGeometry(), vg, false, parentCommand);
8083 					QHash<long, ItemBase *> emptyList;
8084 					ConnectorPairHash connectorHash;
8085 					disconnectFromFemale(detachee, emptyList, connectorHash, true, false, parentCommand);
8086 					foreach (ConnectorItem * fConnectorItem, connectorHash.uniqueKeys()) {
8087 						if (myConnectorItems.contains(fConnectorItem)) {
8088 							// don't need to reconnect
8089 							continue;
8090 						}
8091 
8092 						foreach (ConnectorItem * tConnectorItem, connectorHash.values(fConnectorItem)) {
8093 							createWire(fConnectorItem, tConnectorItem, ViewGeometry::NoFlag, false, BaseCommand::CrossView, parentCommand);
8094 						}
8095 					}
8096 				}
8097 			}
8098 		}
8099 	}
8100 
8101 	deleteMiddle(deletedItems, parentCommand);
8102 }
8103 
8104 
canDisconnectAll()8105 bool SketchWidget::canDisconnectAll() {
8106 	return true;
8107 }
8108 
ignoreFemale()8109 bool SketchWidget::ignoreFemale() {
8110 	return true;
8111 }
8112 
calcNewLoc(ItemBase * moveBase,ItemBase * detachFrom)8113 QPointF SketchWidget::calcNewLoc(ItemBase * moveBase, ItemBase * detachFrom)
8114 {
8115 	QRectF dr = detachFrom->boundingRect();
8116 	dr.moveTopLeft(detachFrom->pos());
8117 
8118 	QPointF pos = moveBase->pos();
8119 	QRectF r = moveBase->boundingRect();
8120 	pos.setX(pos.x() + (r.width() / 2.0));
8121 	pos.setY(pos.y() + (r.height() / 2.0));
8122 	double d[4];
8123 	d[0] = qAbs(pos.y() - dr.top());
8124 	d[1] = qAbs(pos.y() - dr.bottom());
8125 	d[2] = qAbs(pos.x() - dr.left());
8126 	d[3] = qAbs(pos.x() - dr.right());
8127 	int ix = 0;
8128 	for (int i = 1; i < 4; i++) {
8129 		if (d[i] < d[ix]) {
8130 			ix = i;
8131 		}
8132 	}
8133 	QPointF newPos = moveBase->pos();
8134 	switch (ix) {
8135 		case 0:
8136 			newPos.setY(dr.top() - r.height());
8137 			break;
8138 		case 1:
8139 			newPos.setY(dr.bottom());
8140 			break;
8141 		case 2:
8142 			newPos.setX(dr.left() - r.width());
8143 			break;
8144 		case 3:
8145 			newPos.setX(dr.right());
8146 			break;
8147 	}
8148 	return newPos;
8149 }
8150 
findPartOrWire(long itemID)8151 long SketchWidget::findPartOrWire(long itemID)
8152 {
8153 	ItemBase * item = findItem(itemID);
8154 	if (item == NULL) return itemID;
8155 
8156 	if (item->itemType() != ModelPart::Wire) return itemID;
8157 
8158 	QList<Wire *> chained;
8159 	QList<ConnectorItem *> ends;
8160 	qobject_cast<Wire *>(item)->collectChained(chained, ends);
8161 	if (chained.length() <= 1) return itemID;
8162 
8163 	foreach (Wire * w, chained) {
8164 		if (w->id() < itemID) {
8165 			itemID = w->id();
8166 		}
8167 	}
8168 
8169 	return itemID;
8170 }
8171 
resizeJumperItem(long itemID,QPointF pos,QPointF c0,QPointF c1)8172 void SketchWidget::resizeJumperItem(long itemID, QPointF pos, QPointF c0, QPointF c1) {
8173 	ItemBase * item = findItem(itemID);
8174 	if (item == NULL) return;
8175 
8176 	if (item->itemType() != ModelPart::Jumper) return;
8177 
8178 	qobject_cast<JumperItem *>(item)->resize(pos, c0, c1);
8179 }
8180 
selectAllObsolete()8181 QList<ItemBase *> SketchWidget::selectAllObsolete()
8182 {
8183 	QSet<ItemBase *> itemBases;
8184 	foreach (QGraphicsItem * item, scene()->items()) {
8185 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
8186 		if (itemBase == NULL) continue;
8187 		if (!itemBase->isObsolete()) continue;
8188 
8189 		itemBases.insert(itemBase->layerKinChief());
8190 	}
8191 
8192 	selectAllItems(itemBases, QObject::tr("Select outdated parts"));
8193     return itemBases.toList();
8194 }
8195 
selectAllMoveLock()8196 int SketchWidget::selectAllMoveLock()
8197 {
8198 	QSet<ItemBase *> itemBases;
8199 	foreach (QGraphicsItem * item, scene()->items()) {
8200 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
8201 		if (itemBase == NULL) continue;
8202 		if (!itemBase->moveLock()) continue;
8203 
8204 		itemBases.insert(itemBase->layerKinChief());
8205 	}
8206 
8207 	return selectAllItems(itemBases, QObject::tr("Select locked parts"));
8208 }
8209 
selectAllItems(QSet<ItemBase * > & itemBases,const QString & msg)8210 int SketchWidget::selectAllItems(QSet<ItemBase *> & itemBases, const QString & msg)
8211 {
8212 	if (itemBases.count() <= 0) {
8213 		// TODO: tell user?
8214 		return 0;
8215 	}
8216 
8217 	QUndoCommand * parentCommand = new QUndoCommand(msg);
8218 
8219 	stackSelectionState(false, parentCommand);
8220 	SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
8221 	foreach (ItemBase * itemBase, itemBases) {
8222 		selectItemCommand->addRedo(itemBase->id());
8223 	}
8224 
8225 	scene()->clearSelection();
8226 	m_undoStack->push(parentCommand);
8227 
8228 	return itemBases.count();
8229 }
8230 
newAddItemCommand(BaseCommand::CrossViewType crossViewType,ModelPart * newModelPart,QString moduleID,ViewLayer::ViewLayerPlacement viewLayerPlacement,ViewGeometry & viewGeometry,qint64 id,bool updateInfoView,long modelIndex,bool addSubparts,QUndoCommand * parent)8231 AddItemCommand * SketchWidget::newAddItemCommand(BaseCommand::CrossViewType crossViewType, ModelPart * newModelPart, QString moduleID, ViewLayer::ViewLayerPlacement viewLayerPlacement, ViewGeometry & viewGeometry, qint64 id, bool updateInfoView, long modelIndex, bool addSubparts, QUndoCommand *parent)
8232 {
8233 	AddItemCommand * aic = new AddItemCommand(this, crossViewType, moduleID, viewLayerPlacement, viewGeometry, id, updateInfoView, modelIndex, parent);
8234     if (newModelPart == NULL) {
8235         newModelPart = m_referenceModel->retrieveModelPart(moduleID);
8236     }
8237     if (!newModelPart->hasSubparts() || !addSubparts) return aic;
8238 
8239     ModelPartShared * modelPartShared = newModelPart->modelPartShared();
8240     if (modelPartShared == NULL) return aic;
8241 
8242     foreach (ModelPartShared * mps, modelPartShared->subparts()) {
8243         long subID = ItemBase::getNextID();
8244         ViewGeometry vg = viewGeometry;
8245         vg.setLoc(vg.loc() + (mps->subpartOffset() * GraphicsUtils::SVGDPI));
8246         new AddItemCommand(this, crossViewType, mps->moduleID(), viewLayerPlacement, vg, subID, updateInfoView, -1, parent);
8247         AddSubpartCommand * asc = new AddSubpartCommand(this, crossViewType, id, subID, parent);
8248         asc->setRedoOnly();
8249     }
8250 
8251     return aic;
8252 }
8253 
partLabelsVisible()8254 bool SketchWidget::partLabelsVisible() {
8255 	foreach (QGraphicsItem * item, scene()->items()) {
8256 		ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
8257 		if (itemBase == NULL) continue;
8258 
8259 		if (itemBase->isPartLabelVisible()) return true;
8260 
8261 	}
8262 
8263 	return false;
8264 }
8265 
showLabelFirstTime(long itemID,bool show,bool doEmit)8266 void SketchWidget::showLabelFirstTime(long itemID, bool show, bool doEmit) {
8267 	if (doEmit) {
8268 		emit showLabelFirstTimeSignal(itemID, show, false);
8269 	}
8270 }
8271 
restorePartLabel(long itemID,QDomElement & element)8272 void SketchWidget::restorePartLabel(long itemID, QDomElement & element) {
8273 	ItemBase * itemBase = findItem(itemID);
8274 	if (itemBase == NULL) return;
8275 
8276 	itemBase->restorePartLabel(element, getLabelViewLayerID(itemBase));
8277 }
8278 
loadLogoImage(ItemBase * itemBase,const QString & oldSvg,const QSizeF oldAspectRatio,const QString & oldFilename,const QString & newFilename,bool addName)8279 void SketchWidget::loadLogoImage(ItemBase * itemBase, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename, const QString & newFilename, bool addName) {
8280 	if (oldSvg.isEmpty() && oldFilename.isEmpty()) {
8281         // have to swap in a custom shape then load the file
8282         emit swapBoardImageSignal(this, itemBase, newFilename, m_boardLayers == 1 ? ModuleIDNames::OneLayerBoardLogoImageModuleIDName : ModuleIDNames::BoardLogoImageModuleIDName, addName);
8283         return;
8284     }
8285 
8286     QUndoCommand * cmd = new LoadLogoImageCommand(this, itemBase->id(), oldSvg, oldAspectRatio, oldFilename, newFilename, addName, NULL);
8287 	cmd->setText(tr("Change image from %1 to %2").arg(oldFilename).arg(newFilename));
8288 	m_undoStack->waitPush(cmd, PropChangeDelay);
8289 }
8290 
loadLogoImage(long itemID,const QString & oldSvg,const QSizeF oldAspectRatio,const QString & oldFilename)8291 void SketchWidget::loadLogoImage(long itemID, const QString & oldSvg, const QSizeF oldAspectRatio, const QString & oldFilename) {
8292 	ItemBase * itemBase = findItem(itemID);
8293 	if (itemBase == NULL) return;
8294 
8295 	LogoItem * logoItem = qobject_cast<LogoItem *>(itemBase);
8296 	if (logoItem == NULL) return;
8297 
8298 	logoItem->reloadImage(oldSvg, oldAspectRatio, oldFilename, false);
8299 }
8300 
loadLogoImage(long itemID,const QString & newFilename,bool addName)8301 void SketchWidget::loadLogoImage(long itemID, const QString & newFilename, bool addName) {
8302 	ItemBase * itemBase = findItem(itemID);
8303 	if (itemBase == NULL) return;
8304 
8305 	LogoItem * logoItem = qobject_cast<LogoItem *>(itemBase);
8306 	if (logoItem == NULL) return;
8307 
8308 	logoItem->loadImage(newFilename, addName);
8309 }
8310 
paintEvent(QPaintEvent * event)8311 void SketchWidget::paintEvent ( QPaintEvent * event ) {
8312     //DebugDialog::debug("sketch widget paint event");
8313     if (scene()) {
8314         ((FGraphicsScene *) scene())->setDisplayHandles(true);
8315     }
8316     QGraphicsView::paintEvent(event);
8317 }
8318 
setNoteFocus(QGraphicsItem * item,bool inFocus)8319 void SketchWidget::setNoteFocus(QGraphicsItem * item, bool inFocus) {
8320 	if (inFocus) {
8321 		m_inFocus.append(item);
8322 	}
8323 	else {
8324 		m_inFocus.removeOne(item);
8325 	}
8326 }
8327 
defaultGridSizeInches()8328 double SketchWidget::defaultGridSizeInches() {
8329 	return 0.0;			// should never get here
8330 }
8331 
gridSizeInches()8332 double SketchWidget::gridSizeInches() {
8333 	return m_gridSizeInches;
8334 }
8335 
alignToGrid(bool align)8336 void SketchWidget::alignToGrid(bool align) {
8337 	m_alignToGrid = align;
8338 	QSettings settings;
8339 	settings.setValue(QString("%1AlignToGrid").arg(viewName()), align);
8340 }
8341 
showGrid(bool show)8342 void SketchWidget::showGrid(bool show) {
8343 	m_showGrid = show;
8344 	QSettings settings;
8345 	settings.setValue(QString("%1ShowGrid").arg(viewName()), show);
8346 	update();
8347 }
8348 
alignedToGrid()8349 bool SketchWidget::alignedToGrid() {
8350 	return m_alignToGrid;
8351 }
8352 
showingGrid()8353 bool SketchWidget::showingGrid() {
8354 	return m_showGrid;
8355 }
8356 
canAlignToTopLeft(ItemBase *)8357 bool SketchWidget::canAlignToTopLeft(ItemBase *)
8358 {
8359 	return false;
8360 }
8361 
canAlignToCenter(ItemBase *)8362 bool SketchWidget::canAlignToCenter(ItemBase *)
8363 {
8364 	return false;
8365 }
8366 
saveZoom(double zoom)8367 void SketchWidget::saveZoom(double zoom) {
8368 	m_zoom = zoom;
8369 }
8370 
retrieveZoom()8371 double SketchWidget::retrieveZoom() {
8372 	return m_zoom;
8373 }
8374 
setGridSize(const QString & newSize)8375 void SketchWidget::setGridSize(const QString & newSize)
8376 {
8377      QSettings settings;
8378      settings.setValue(QString("%1GridSize").arg(viewName()), newSize);
8379      m_gridSizeInches = TextUtils::convertToInches(newSize);
8380      m_gridSizeText = newSize;
8381      if (m_showGrid) {
8382          invalidateScene();
8383      }
8384 }
8385 
gridSizeText()8386 QString SketchWidget::gridSizeText() {
8387     if (!m_gridSizeText.isEmpty()) return m_gridSizeText;
8388 
8389     return QString("%1in").arg(m_gridSizeInches);
8390 }
8391 
initGrid()8392 void SketchWidget::initGrid() {
8393 	m_showGrid = m_alignToGrid = true;
8394 	m_gridSizeInches = defaultGridSizeInches();
8395 	QSettings settings;
8396 	QString szString = settings.value(QString("%1GridSize").arg(viewName()), "").toString();
8397 	if (!szString.isEmpty()) {
8398 		bool ok;
8399 		double temp = TextUtils::convertToInches(szString, &ok, false);
8400 		if (ok) {
8401 			m_gridSizeInches = temp;
8402             m_gridSizeText = szString;
8403 		}
8404 	}
8405 	m_alignToGrid = settings.value(QString("%1AlignToGrid").arg(viewName()), true).toBool();
8406 	m_showGrid = settings.value(QString("%1ShowGrid").arg(viewName()), true).toBool();
8407 }
8408 
8409 
copyDrop()8410 void SketchWidget::copyDrop() {
8411 
8412 	QList<ItemBase *> itemBases;
8413     foreach (ItemBase * itemBase, m_savedItems.values()) {
8414         QList<ItemBase *> superSubs = collectSuperSubs(itemBase);
8415         if (superSubs.count() > 0) {
8416             foreach (ItemBase * supersub, superSubs) {
8417                 if (!itemBases.contains(supersub)) itemBases.append(supersub);
8418             }
8419             continue;
8420         }
8421 
8422         itemBases.append(itemBase);
8423     }
8424 
8425     qSort(itemBases.begin(), itemBases.end(), ItemBase::zLessThan);
8426 	foreach (ItemBase * itemBase, itemBases) {
8427 		QPointF loc = itemBase->getViewGeometry().loc();
8428         itemBase->setItemPos(loc);
8429 	}
8430 	copyAux(itemBases, false);
8431 
8432 	m_savedItems.clear();
8433 	m_savedWires.clear();
8434 }
8435 
defaultViewLayerPlacement(ModelPart * modelPart)8436 ViewLayer::ViewLayerPlacement SketchWidget::defaultViewLayerPlacement(ModelPart * modelPart) {
8437     Q_UNUSED(modelPart);
8438 	//return (m_boardLayers == 1) ?  ViewLayer::NewBottom : ViewLayer::NewTop;
8439     return ViewLayer::NewTop;
8440 }
8441 
wireViewLayerPlacement(ConnectorItem *)8442 ViewLayer::ViewLayerPlacement SketchWidget::wireViewLayerPlacement(ConnectorItem *) {
8443 	return (m_boardLayers == 1) ? ViewLayer::NewBottom : ViewLayer::NewTop;
8444 }
8445 
changeBoardLayers(int layers,bool doEmit)8446 void SketchWidget::changeBoardLayers(int layers, bool doEmit) {
8447 	m_boardLayers = layers;
8448 
8449 	if (doEmit) {
8450 		emit changeBoardLayersSignal(layers, false);
8451 	}
8452 }
8453 
collectAllNets(QHash<ConnectorItem *,int> & indexer,QList<QList<class ConnectorItem * > * > & allPartConnectorItems,bool includeSingletons,bool bothSides)8454 void SketchWidget::collectAllNets(QHash<ConnectorItem *, int> & indexer, QList< QList<class ConnectorItem *>* > & allPartConnectorItems, bool includeSingletons, bool bothSides)
8455 {
8456 	// get the set of all connectors in the sketch
8457 	QList<ConnectorItem *> allConnectors;
8458 	foreach (QGraphicsItem * item, scene()->items()) {
8459 		ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(item);
8460 		if (connectorItem == NULL) continue;
8461 		if (!bothSides && connectorItem->attachedToViewLayerID() == ViewLayer::Copper1) continue;
8462 
8463 		allConnectors.append(connectorItem);
8464 	}
8465 
8466 	// find all the nets and make a list of nodes (i.e. part ConnectorItems) for each net
8467 	while (allConnectors.count() > 0) {
8468 
8469 		ConnectorItem * connectorItem = allConnectors.takeFirst();
8470 		QList<ConnectorItem *> connectorItems;
8471 		connectorItems.append(connectorItem);
8472 		ConnectorItem::collectEqualPotential(connectorItems, bothSides, ViewGeometry::NoFlag);
8473 		if (connectorItems.count() <= 0) {
8474 			continue;
8475 		}
8476 
8477 		foreach (ConnectorItem * ci, connectorItems) {
8478 			//if (connectorItems.count(ci) > 1) {
8479 				//DebugDialog::debug("collect equal potential bug");
8480 			//}
8481 			//DebugDialog::debug(QString("from in equal potential %1 %2").arg(ci->connectorSharedName()).arg(ci->attachedToInstanceTitle()));
8482 			allConnectors.removeOne(ci);
8483 		}
8484 
8485 		if (!includeSingletons && (connectorItems.count() <= 1)) {
8486 			continue;
8487 		}
8488 
8489 		QList<ConnectorItem *> * partConnectorItems = new QList<ConnectorItem *>;
8490 		ConnectorItem::collectParts(connectorItems, *partConnectorItems, includeSymbols(), ViewLayer::NewTopAndBottom);
8491 
8492 		for (int i = partConnectorItems->count() - 1; i >= 0; i--) {
8493 			if (!partConnectorItems->at(i)->attachedTo()->isEverVisible()) {
8494 				partConnectorItems->removeAt(i);
8495 			}
8496 		}
8497 
8498 		if ((partConnectorItems->count() <= 0) || (!includeSingletons && (partConnectorItems->count() <= 1))) {
8499 			delete partConnectorItems;
8500 			continue;
8501 		}
8502 
8503 		foreach (ConnectorItem * ci, *partConnectorItems) {
8504 			//if (partConnectorItems->count(ci) > 1) {
8505 				//DebugDialog::debug("collect Parts bug");
8506 			//}
8507 			if (!connectorItems.contains(ci)) {
8508 				// crossed layer: toss it
8509 				//DebugDialog::debug(QString("not in equal potential '%1' '%2' %3")
8510 				//	.arg(ci->connectorSharedName())
8511 				//	.arg(ci->attachedToInstanceTitle())
8512 				//	.arg(ci->attachedToViewLayerID()));
8513 				continue;
8514 			}
8515 			//if (indexer.keys().contains(ci)) {
8516 				//DebugDialog::debug(QString("connector item already indexed %1 %2").arg(ci->connectorSharedName()).arg(ci->attachedToInstanceTitle()));
8517 			//}
8518 			//int c = indexer.count();
8519 			//DebugDialog::debug(QString("insert indexer %1 '%2' '%3' %4")
8520 				//.arg(c)
8521 				//.arg(ci->connectorSharedName())
8522 				//.arg(ci->attachedToInstanceTitle())
8523 				//.arg(ci->attachedToViewLayerID()));
8524 			indexer.insert(ci, indexer.count());
8525 		}
8526 
8527 		//DebugDialog::debug("________________");
8528 		allPartConnectorItems.append(partConnectorItems);
8529 	}
8530 }
8531 
getViewLayerPlacement(ModelPart * modelPart,QDomElement & instance,QDomElement & view,ViewGeometry & viewGeometry)8532 ViewLayer::ViewLayerPlacement SketchWidget::getViewLayerPlacement(ModelPart * modelPart, QDomElement & instance, QDomElement & view, ViewGeometry & viewGeometry)
8533 {
8534 	Q_UNUSED(instance);
8535 
8536 	ViewLayer::ViewLayerPlacement viewLayerPlacement = defaultViewLayerPlacement(modelPart);
8537 
8538 	if (modelPart->moduleID().compare(ModuleIDNames::GroundPlaneModuleIDName) == 0) {
8539 		QString layer = view.attribute("layer");
8540 		if (layer.isEmpty()) return viewLayerPlacement;
8541 
8542 		ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString(layer);
8543 		if (viewLayerID == ViewLayer::GroundPlane1) {
8544 			return ViewLayer::NewTop;
8545 		}
8546 
8547 		return ViewLayer::NewBottom;
8548 	}
8549 
8550 	if (viewGeometry.getAnyTrace()) {
8551 		QString layer = view.attribute("layer");
8552 		if (layer.isEmpty()) return viewLayerPlacement;
8553 
8554 		ViewLayer::ViewLayerID viewLayerID = ViewLayer::viewLayerIDFromXmlString(layer);
8555         switch (viewLayerID) {
8556             case ViewLayer::Copper1Trace:
8557             case ViewLayer::GroundPlane1:
8558             case ViewLayer::Copper1:
8559 			    return ViewLayer::NewTop;
8560             case ViewLayer::Copper0Trace:
8561             case ViewLayer::GroundPlane0:
8562             case ViewLayer::Copper0:
8563 			    return ViewLayer::NewBottom;
8564             default:
8565                 break;
8566 		}
8567 	}
8568 
8569 	return viewLayerPlacement;
8570 
8571 }
8572 
routeBothSides()8573 bool SketchWidget::routeBothSides() {
8574 	return false;
8575 }
8576 
8577 
copyBoundingRectsSlot(QHash<QString,QRectF> & boundingRects)8578 void SketchWidget::copyBoundingRectsSlot(QHash<QString, QRectF> & boundingRects)
8579 {
8580 	QRectF itemsBoundingRect;
8581 	QList<QGraphicsItem *> tlBases;
8582 	foreach (QGraphicsItem * item, scene()->selectedItems()) {
8583 		ItemBase * itemBase =  ItemBase::extractTopLevelItemBase(item);
8584 		if (itemBase == NULL) continue;
8585 		if (itemBase->getRatsnest()) continue;
8586 
8587 		itemsBoundingRect |= itemBase->sceneBoundingRect();
8588 	}
8589 
8590 	boundingRects.insert(m_viewName, itemsBoundingRect);
8591 }
8592 
changeLayer(long id,double z,ViewLayer::ViewLayerID viewLayerID)8593 void SketchWidget::changeLayer(long id, double z, ViewLayer::ViewLayerID viewLayerID) {
8594 	Q_UNUSED(id);
8595 	Q_UNUSED(z);
8596 	Q_UNUSED(viewLayerID);
8597 }
8598 
resizingJumperItemRelease()8599 bool SketchWidget::resizingJumperItemRelease() {
8600 	return false;
8601 }
8602 
resizingJumperItemPress(ItemBase *)8603 bool SketchWidget::resizingJumperItemPress(ItemBase *) {
8604 	return false;
8605 }
8606 
resizingBoardPress(ItemBase * itemBase)8607 bool SketchWidget::resizingBoardPress(ItemBase * itemBase) {
8608     if (itemBase == NULL) return false;
8609 
8610 	// board's child items (at the moment) are the resize grips
8611 	ResizableBoard * rb = qobject_cast<ResizableBoard *>(itemBase->layerKinChief());
8612 	if (rb == NULL) return false;
8613 	if (!rb->inResize()) return false;
8614 
8615 	m_resizingBoard = rb;
8616 	m_resizingBoard->saveParams();
8617 	return true;
8618 }
8619 
resizingBoardRelease()8620 bool SketchWidget::resizingBoardRelease() {
8621 
8622 	if (m_resizingBoard == NULL) return false;
8623 
8624 	resizeBoard();
8625 	return true;
8626 }
8627 
resizeBoard()8628 void SketchWidget::resizeBoard() {
8629 	QSizeF oldSize;
8630 	QPointF oldPos;
8631 	m_resizingBoard->getParams(oldPos, oldSize);
8632 	QSizeF newSize;
8633 	QPointF newPos;
8634 	m_resizingBoard->saveParams();
8635 	m_resizingBoard->getParams(newPos, newSize);
8636 	QUndoCommand * parentCommand = new QUndoCommand(tr("Resize board to %1 %2").arg(newSize.width()).arg(newSize.height()));
8637 	rememberSticky(m_resizingBoard, parentCommand);
8638 	new ResizeBoardCommand(this, m_resizingBoard->id(), oldSize.width(), oldSize.height(), newSize.width(), newSize.height(), parentCommand);
8639 	if (oldPos != newPos) {
8640 		m_resizingBoard->saveGeometry();
8641 		ViewGeometry vg1 = m_resizingBoard->getViewGeometry();
8642 		ViewGeometry vg2 = vg1;
8643 		vg1.setLoc(oldPos);
8644 		vg2.setLoc(newPos);
8645 		new MoveItemCommand(this, m_resizingBoard->id(), vg1, vg2, false, parentCommand);
8646 	}
8647 	new CheckStickyCommand(this, BaseCommand::SingleView, m_resizingBoard->id(), true, CheckStickyCommand::RedoOnly, parentCommand);
8648 	m_undoStack->waitPush(parentCommand, 10);
8649 	m_resizingBoard = NULL;
8650 }
8651 
8652 
hasAnyNets()8653 bool SketchWidget::hasAnyNets() {
8654 	return false;
8655 }
8656 
ratsnestConnect(ItemBase * itemBase,bool connect)8657 void SketchWidget::ratsnestConnect(ItemBase * itemBase, bool connect) {
8658 	foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
8659 		ratsnestConnect(connectorItem, connect);
8660 	}
8661 }
8662 
ratsnestConnect(long id,const QString & connectorID,bool connect,bool doEmit)8663 void SketchWidget::ratsnestConnect(long id, const QString & connectorID, bool connect, bool doEmit) {
8664 	if (doEmit) {
8665 		emit ratsnestConnectSignal(id, connectorID, connect, false);
8666 	}
8667 
8668 	ItemBase * itemBase = findItem(id);
8669 	if (itemBase == NULL) return;
8670 
8671 	ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, itemBase->viewLayerPlacement());
8672 	if (connectorItem == NULL) return;
8673 
8674 	ratsnestConnect(connectorItem, connect);
8675 }
8676 
ratsnestConnect(ConnectorItem * c1,ConnectorItem * c2,bool connect,bool wait)8677 void SketchWidget::ratsnestConnect(ConnectorItem * c1, ConnectorItem * c2, bool connect, bool wait) {
8678     if (wait) {
8679         if (connect) {
8680             m_ratsnestCacheConnect << c1 << c2;
8681 	    }
8682 	    else {
8683             m_ratsnestCacheDisconnect << c1 << c2;
8684         }
8685         return;
8686     }
8687 
8688 	QList<ConnectorItem *> connectorItems;
8689 	connectorItems.append(c1);
8690 	connectorItems.append(c2);
8691 	ConnectorItem::collectEqualPotential(connectorItems, true, ViewGeometry::RatsnestFlag);
8692 	foreach (ConnectorItem * connectorItem, connectorItems) {
8693 		ratsnestConnect(connectorItem, connect);
8694 	}
8695 }
8696 
ratsnestConnect(ConnectorItem * connectorItem,bool connect)8697 void SketchWidget::ratsnestConnect(ConnectorItem * connectorItem, bool connect) {
8698 	if (connect) {
8699 		m_ratsnestUpdateConnect << connectorItem;
8700 	}
8701 	else {
8702 		m_ratsnestUpdateDisconnect << connectorItem;
8703 	}
8704 
8705 	//connectorItem->debugInfo(QString("rat connect %1").arg(connect));
8706 }
8707 
deleteRatsnest(Wire * ratsnest,QUndoCommand * parentCommand)8708 void SketchWidget::deleteRatsnest(Wire * ratsnest, QUndoCommand * parentCommand)
8709 {
8710 	// deleting a ratsnest really means deleting underlying connections
8711 
8712 	// assume ratsnest has only one connection at each end
8713 	ConnectorItem * source = ratsnest->connector0()->firstConnectedToIsh();
8714 	ConnectorItem * sink = ratsnest->connector1()->firstConnectedToIsh();
8715 
8716 	QList<ConnectorItem *> connectorItems;
8717 	connectorItems.append(source);
8718 	connectorItems.append(sink);
8719 	ConnectorItem::collectEqualPotential(connectorItems, true, ViewGeometry::RatsnestFlag);
8720 
8721 	QList<SketchWidget *> foreignSketchWidgets;
8722 	emit collectRatsnestSignal(foreignSketchWidgets);
8723 
8724 	// there are multiple possibilities for each pair of connectors:
8725 
8726 	// they are directly connected because they're each inserted into female connectors on the same bus
8727 	// they are directly connected with a wire
8728 	// they are "directly" connected through some combination of female connectors and wires (i.e. one part is connected to a wire which is inserted into a female connector)
8729 	// they are indirectly connected via other parts
8730 
8731 	// what if there are multiple direct connections--treat it as a single connection and delete them all
8732 
8733 	QList<ConnectorEdge *> cutSet;
8734 	GraphUtils::minCut(connectorItems, foreignSketchWidgets, source, sink, cutSet);
8735 	emit removeRatsnestSignal(cutSet, parentCommand);
8736 	foreach (ConnectorEdge * ce, cutSet) {
8737 		delete ce;
8738 	}
8739 }
8740 
removeRatsnestSlot(QList<ConnectorEdge * > & cutSet,QUndoCommand * parentCommand)8741 void SketchWidget::removeRatsnestSlot(QList<ConnectorEdge *> & cutSet, QUndoCommand * parentCommand)
8742 {
8743 	QHash<ConnectorItem *, ConnectorItem *> detachItems;			// key is part to be detached, value is part to detach from
8744 	QSet<ItemBase *> deletedItems;
8745 	QList<long> deletedIDs;
8746 
8747 	foreach (ConnectorEdge * ce, cutSet) {
8748 
8749 		if (ce->c0->attachedToViewID() != viewID()) continue;
8750 		if (ce->c1->attachedToViewID() != viewID()) continue;
8751 
8752 		if (ce->wire) {
8753 			QList<ConnectorItem *> ends;
8754 			QList<Wire *> wires;
8755 			ce->wire->collectChained(wires, ends);
8756 			foreach (Wire * w, wires) {
8757 				if (!deletedIDs.contains(w->id())) {
8758 					deletedItems.insert(w);
8759 					deletedIDs.append(w->id());
8760 				}
8761 			}
8762 		}
8763 		else {
8764 			// we have to detach the source or sink from a female connector
8765 			if (ce->c0->connectorType() == Connector::Female) {
8766 				detachItems.insert(ce->c1, ce->c0);
8767 			}
8768 			else {
8769 				detachItems.insert(ce->c0, ce->c1);
8770 			}
8771 		}
8772 	}
8773 
8774 	foreach (ConnectorItem * detacheeConnector, detachItems.keys()) {
8775 		ItemBase * detachee = detacheeConnector->attachedTo();
8776 		ConnectorItem * detachFromConnector = detachItems.value(detacheeConnector);
8777 		ItemBase * detachFrom = detachFromConnector->attachedTo();
8778 		QPointF newPos = calcNewLoc(detachee, detachFrom);
8779 
8780 		// delete connections
8781 		// add wires and connections for undisconnected connectors
8782 
8783 		detachee->saveGeometry();
8784 		ViewGeometry vg = detachee->getViewGeometry();
8785 		vg.setLoc(newPos);
8786 		new MoveItemCommand(this, detachee->id(), detachee->getViewGeometry(), vg, false, parentCommand);
8787 		QHash<long, ItemBase *> emptyList;
8788 		ConnectorPairHash connectorHash;
8789 		disconnectFromFemale(detachee, emptyList, connectorHash, true, false, parentCommand);
8790 		foreach (ConnectorItem * fromConnectorItem, connectorHash.uniqueKeys()) {
8791 			if (detachItems.keys().contains(fromConnectorItem)) {
8792 				// don't need to reconnect
8793 				continue;
8794 			}
8795 			if (detachItems.values().contains(fromConnectorItem)) {
8796 				// don't need to reconnect
8797 				continue;
8798 			}
8799 
8800 			foreach (ConnectorItem * toConnectorItem, connectorHash.values(fromConnectorItem)) {
8801 				createWire(fromConnectorItem, toConnectorItem, ViewGeometry::NoFlag, false, BaseCommand::CrossView, parentCommand);
8802 			}
8803 		}
8804 	}
8805 
8806 
8807 	deleteAux(deletedItems, parentCommand, false);
8808 }
8809 
addDefaultParts()8810 void SketchWidget::addDefaultParts() {
8811 }
8812 
vScrollToZero()8813 void SketchWidget::vScrollToZero() {
8814 	verticalScrollBar()->setValue(verticalScrollBar()->minimum());
8815 }
8816 
getTopZ()8817 float SketchWidget::getTopZ() {
8818 	return m_z;
8819 }
8820 
addWatermark(const QString & filename)8821 QGraphicsItem * SketchWidget::addWatermark(const QString &filename)
8822 {
8823 	QGraphicsSvgItem * item = new QGraphicsSvgItem(filename);
8824 	if (item == NULL) return NULL;
8825 
8826 	this->scene()->addItem(item);
8827 	return item;
8828 }
8829 
acceptsTrace(const ViewGeometry &)8830 bool SketchWidget::acceptsTrace(const ViewGeometry &) {
8831 	return false;
8832 }
8833 
alignOneToGrid(ItemBase * itemBase)8834 QPointF SketchWidget::alignOneToGrid(ItemBase * itemBase) {
8835 	if (m_alignToGrid) {
8836 		QHash<long, ItemBase *> savedItems;
8837 		QHash<Wire *, ConnectorItem *> savedWires;
8838 		findAlignmentAnchor(itemBase, savedItems, savedWires);
8839 		if (m_alignmentItem) {
8840 			m_alignmentItem = NULL;
8841 			QPointF loc = itemBase->pos();
8842 			alignLoc(loc, m_alignmentStartPoint, QPointF(0,0), QPointF(0, 0));
8843 			QPointF result = loc - itemBase->pos();
8844 			itemBase->setPos(loc);
8845 			return result;
8846 		}
8847 	}
8848 
8849 	return QPointF(0, 0);
8850 }
8851 
getTraceFlag()8852 ViewGeometry::WireFlag SketchWidget::getTraceFlag() {
8853 	return ViewGeometry::NormalFlag;
8854 }
8855 
changeBus(ItemBase * itemBase,bool connect,const QString & oldBus,const QString & newBus,QList<ConnectorItem * > & connectorItems,const QString & message,const QString & oldLayout,const QString & newLayout)8856 void SketchWidget::changeBus(ItemBase * itemBase, bool connect, const QString & oldBus, const QString & newBus, QList<ConnectorItem *> & connectorItems, const QString & message, const QString & oldLayout, const QString & newLayout)
8857 {
8858 	QUndoCommand * parentCommand = new QUndoCommand(message);
8859 	CleanUpWiresCommand * cuwc = new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
8860 	foreach(ConnectorItem * connectorItem, connectorItems) {
8861 		cuwc->addRatsnestConnect(connectorItem->attachedToID(), connectorItem->connectorSharedID(), connect);
8862 	}
8863 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
8864 
8865 	new SetPropCommand(this, itemBase->id(), "buses", oldBus, newBus, true, parentCommand);
8866 	new SetPropCommand(this, itemBase->id(), "layout", oldLayout, newLayout, true, parentCommand);
8867 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
8868 	cuwc = new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
8869 	foreach(ConnectorItem * connectorItem, connectorItems) {
8870 		cuwc->addRatsnestConnect(connectorItem->attachedToID(), connectorItem->connectorSharedID(), connect);
8871 	}
8872 
8873 	m_undoStack->waitPush(parentCommand, PropChangeDelay);
8874 
8875 }
8876 
filenameIf()8877 const QString & SketchWidget::filenameIf()
8878 {
8879 	static QString filename;
8880 	emit filenameIfSignal(filename);
8881 	return filename;
8882 }
8883 
setItemDropOffset(long id,QPointF offset)8884 void SketchWidget::setItemDropOffset(long id, QPointF offset)
8885 {
8886 	ItemBase * itemBase = findItem(id);
8887 	if (itemBase == NULL) return;
8888 
8889 	itemBase->setDropOffset(offset);
8890 }
8891 
createTempWireForDragging(Wire * fromWire,ModelPart * wireModel,ConnectorItem * connectorItem,ViewGeometry & viewGeometry,ViewLayer::ViewLayerPlacement spec)8892 Wire * SketchWidget::createTempWireForDragging(Wire * fromWire, ModelPart * wireModel, ConnectorItem * connectorItem, ViewGeometry & viewGeometry, ViewLayer::ViewLayerPlacement spec)
8893 {
8894 	Q_UNUSED(fromWire);
8895 	if (spec == ViewLayer::UnknownPlacement) {
8896 		spec = wireViewLayerPlacement(connectorItem);
8897 	}
8898 	return qobject_cast<Wire *>(addItemAuxTemp(wireModel, spec, viewGeometry, ItemBase::getNextID(), true, m_viewID, true));
8899 }
8900 
prereleaseTempWireForDragging(Wire *)8901 void SketchWidget::prereleaseTempWireForDragging(Wire*)
8902 {
8903 }
8904 
wireChangedCurveSlot(Wire * wire,const Bezier * oldB,const Bezier * newB,bool triggerFirstTime)8905 void SketchWidget::wireChangedCurveSlot(Wire* wire, const Bezier * oldB, const Bezier * newB, bool triggerFirstTime) {
8906 	this->clearHoldingSelectItem();
8907 	this->m_moveEventCount = 0;  // clear this so an extra MoveItemCommand isn't posted
8908 
8909 	ChangeWireCurveCommand * cwcc = new ChangeWireCurveCommand(this, wire->id(), oldB, newB, wire->getAutoroutable(), NULL);
8910 	cwcc->setText("Change wire curvature");
8911 	if (!triggerFirstTime) {
8912 		cwcc->setSkipFirstRedo();
8913 	}
8914 	m_undoStack->push(cwcc);
8915 }
8916 
changeWireCurve(long id,const Bezier * bezier,bool autoroutable)8917 void SketchWidget::changeWireCurve(long id, const Bezier * bezier, bool autoroutable) {
8918 	Wire * wire = qobject_cast<Wire *>(findItem(id));
8919 	if (wire == NULL) return;
8920 
8921 	wire->changeCurve(bezier);
8922     wire->setAutoroutable(autoroutable);
8923 }
8924 
8925 
changeLegCurve(long id,const QString & connectorID,int index,const Bezier * bezier)8926 void SketchWidget::changeLegCurve(long id, const QString & connectorID, int index, const Bezier * bezier) {
8927 	ItemBase * itemBase = findItem(id);
8928 	if (itemBase == NULL) return;
8929 
8930 	ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID()));
8931 	if (connectorItem == NULL) return;
8932 
8933 	connectorItem->changeLegCurve(index, bezier);
8934 }
8935 
addLegBendpoint(long id,const QString & connectorID,int index,QPointF p,const class Bezier * bezierLeft,const class Bezier * bezierRight)8936 void SketchWidget::addLegBendpoint(long id, const QString & connectorID, int index, QPointF p, const class Bezier * bezierLeft, const class Bezier * bezierRight)
8937 {
8938 	ItemBase * itemBase = findItem(id);
8939 	if (itemBase == NULL) return;
8940 
8941 	ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID()));
8942 	if (connectorItem == NULL) return;
8943 
8944 	connectorItem->addLegBendpoint(index, p, bezierLeft, bezierRight);
8945 }
8946 
removeLegBendpoint(long id,const QString & connectorID,int index,const class Bezier * bezierCombined)8947 void SketchWidget::removeLegBendpoint(long id, const QString & connectorID, int index, const class Bezier * bezierCombined)
8948 {
8949 	ItemBase * itemBase = findItem(id);
8950 	if (itemBase == NULL) return;
8951 
8952 	ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID()));
8953 	if (connectorItem == NULL) return;
8954 
8955 	connectorItem->removeLegBendpoint(index, bezierCombined);
8956 }
8957 
moveLegBendpoint(long id,const QString & connectorID,int index,QPointF p)8958 void SketchWidget::moveLegBendpoint(long id, const QString & connectorID, int index, QPointF p)
8959 {
8960 	ItemBase * itemBase = findItem(id);
8961 	if (itemBase == NULL) return;
8962 
8963 	ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID()));
8964 	if (connectorItem == NULL) return;
8965 
8966 	connectorItem->moveLegBendpoint(index, p);
8967 }
8968 
moveLegBendpoints(bool undoOnly,QUndoCommand * parentCommand)8969 void SketchWidget::moveLegBendpoints(bool undoOnly, QUndoCommand * parentCommand)
8970 {
8971 	foreach (ItemBase * itemBase, m_stretchingLegs.uniqueKeys()) {
8972 		foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) {
8973 			moveLegBendpointsAux(connectorItem, undoOnly, parentCommand);
8974 		}
8975 	}
8976 }
8977 
moveLegBendpointsAux(ConnectorItem * connectorItem,bool undoOnly,QUndoCommand * parentCommand)8978 void SketchWidget::moveLegBendpointsAux(ConnectorItem * connectorItem, bool undoOnly, QUndoCommand * parentCommand)
8979 {
8980 	int index0, index1;
8981 	QPointF oldPos0, newPos0, oldPos1, newPos1;
8982 	connectorItem->moveDone(index0, oldPos0, newPos0, index1, oldPos1, newPos1);
8983 	MoveLegBendpointCommand * mlbc = new MoveLegBendpointCommand(this, connectorItem->attachedToID(), connectorItem->connectorSharedID(), index0, oldPos0, newPos0, parentCommand);
8984 	if (undoOnly) mlbc->setUndoOnly();
8985 	else mlbc->setRedoOnly();
8986 	if (index0 != index1) {
8987 		mlbc = new MoveLegBendpointCommand(this, connectorItem->attachedToID(), connectorItem->connectorSharedID(), index1, oldPos1, newPos1, parentCommand);
8988 		if (undoOnly) mlbc->setUndoOnly();
8989 		else mlbc->setRedoOnly();
8990 	}
8991 }
8992 
8993 
curvyWires()8994 bool SketchWidget::curvyWires()
8995 {
8996 	return m_curvyWires;
8997 }
8998 
setCurvyWires(bool curvyWires)8999 void SketchWidget::setCurvyWires(bool curvyWires)
9000 {
9001 	m_curvyWires = curvyWires;
9002 }
9003 
curvyWiresIndicated(Qt::KeyboardModifiers modifiers)9004 bool SketchWidget::curvyWiresIndicated(Qt::KeyboardModifiers modifiers)
9005 {
9006 	if (m_curvyWires) {
9007 		return ((modifiers & Qt::ControlModifier) == 0);
9008 	}
9009 
9010 	return ((modifiers & Qt::ControlModifier) != 0);
9011 }
9012 
setMoveLock(long id,bool lock)9013 void SketchWidget::setMoveLock(long id, bool lock)
9014 {
9015 	ItemBase * itemBase = findItem(id);
9016 	if (itemBase) itemBase->setMoveLock(lock);
9017 }
9018 
triggerRotate(ItemBase * itemBase,double degrees)9019 void SketchWidget::triggerRotate(ItemBase * itemBase, double degrees)
9020 {
9021     QList<QGraphicsItem *> selectedItems = scene()->selectedItems();
9022     setIgnoreSelectionChangeEvents(true);
9023     this->clearSelection();
9024     itemBase->setSelected(true);
9025     rotateX(degrees, false, itemBase);
9026     foreach (QGraphicsItem * item, selectedItems) {
9027         item->setSelected(true);
9028     }
9029     setIgnoreSelectionChangeEvents(false);
9030 }
9031 
makeWiresChangeConnectionCommands(const QList<Wire * > & wires,QUndoCommand * parentCommand)9032 void SketchWidget::makeWiresChangeConnectionCommands(const QList<Wire *> & wires, QUndoCommand * parentCommand)
9033 {
9034 	QStringList alreadyList;
9035 	foreach (Wire * wire, wires) {
9036 		QList<ConnectorItem *> wireConnectorItems;
9037 		wireConnectorItems << wire->connector0() << wire->connector1();
9038 		foreach (ConnectorItem * fromConnectorItem, wireConnectorItems) {
9039 			foreach(ConnectorItem * toConnectorItem, fromConnectorItem->connectedToItems()) {
9040 				QString already = ((fromConnectorItem->attachedToID() <= toConnectorItem->attachedToID()) ? QString("%1.%2.%3.%4") : QString("%3.%4.%1.%2"))
9041 					.arg(fromConnectorItem->attachedToID()).arg(fromConnectorItem->connectorSharedID())
9042 					.arg(toConnectorItem->attachedToID()).arg(toConnectorItem->connectorSharedID());
9043 				if (alreadyList.contains(already)) continue;
9044 
9045 				alreadyList.append(already);
9046 
9047 				extendChangeConnectionCommand(BaseCommand::CrossView, fromConnectorItem, toConnectorItem,
9048 											ViewLayer::specFromID(toConnectorItem->attachedToViewLayerID()),
9049 											false, parentCommand);
9050 			}
9051 		}
9052 	}
9053 }
9054 
changePinLabelsSlot(ItemBase * itemBase,bool singleRow)9055 void SketchWidget::changePinLabelsSlot(ItemBase * itemBase, bool singleRow)
9056 {
9057 	itemBase = this->findItem(itemBase->id());
9058 	if (itemBase == NULL) return;
9059 	if (itemBase->viewID() != ViewLayer::SchematicView) return;
9060 
9061 	PaletteItem * paletteItem = qobject_cast<PaletteItem *>(itemBase->layerKinChief());
9062 	if (paletteItem == NULL) return;
9063 
9064 	if (qobject_cast<Dip *>(itemBase)) {
9065 		paletteItem->changePinLabels(singleRow, true);
9066 	}
9067 	else if (qobject_cast<MysteryPart *>(itemBase)) {
9068 		paletteItem->changePinLabels(singleRow, false);
9069 	}
9070 	else {
9071         bool hasLayout, sip;
9072         QStringList labels = paletteItem->sipOrDipOrLabels(hasLayout, sip);
9073         if (labels.count() == 0) return;
9074 
9075         QTransform transform = paletteItem->untransform();
9076 
9077         QString svg = PartFactory::makeSchematicSipOrDipOr(labels, hasLayout, sip);
9078 		paletteItem->resetLayerKin(svg);
9079 		if (!hasLayout && !sip) {
9080 			paletteItem->resetConnectors();
9081 		}
9082 
9083         paletteItem->retransform(transform);
9084 	}
9085 
9086 }
9087 
changePinLabels(ItemBase * itemBase,bool singleRow)9088 void SketchWidget::changePinLabels(ItemBase * itemBase, bool singleRow)
9089 {
9090 	emit changePinLabelsSignal(itemBase, singleRow);
9091 	changePinLabelsSlot(itemBase, singleRow);
9092 }
9093 
renamePins(ItemBase * itemBase,const QStringList & oldLabels,const QStringList & newLabels,bool singleRow)9094 void SketchWidget::renamePins(ItemBase * itemBase, const QStringList & oldLabels, const QStringList & newLabels, bool singleRow)
9095 {
9096 	QUndoCommand * command = new RenamePinsCommand(this, itemBase->id(), oldLabels, newLabels, singleRow, NULL);
9097 	command->setText(tr("change pin labels"));
9098 	m_undoStack->waitPush(command, 10);
9099 }
9100 
renamePins(long id,const QStringList & labels,bool singleRow)9101 void SketchWidget::renamePins(long id, const QStringList & labels, bool singleRow)
9102 {
9103 	ItemBase * itemBase = findItem(id);
9104 	if (itemBase == NULL) return;
9105 
9106 	PaletteItem * paletteItem = qobject_cast<PaletteItem *>(itemBase->layerKinChief());
9107 	if (paletteItem == NULL) return;
9108 
9109 	paletteItem->renamePins(labels, singleRow);
9110 }
9111 
checkUpdateRatsnest(QList<ConnectorItem * > & connectorItems)9112 bool SketchWidget::checkUpdateRatsnest(QList<ConnectorItem *> & connectorItems) {
9113 	foreach (ConnectorItem * ci, m_ratsnestUpdateConnect) {
9114 		if (ci == NULL) continue;
9115 		if (connectorItems.contains(ci)) return true;
9116 	}
9117 	foreach (ConnectorItem * ci, m_ratsnestUpdateDisconnect) {
9118 		if (ci == NULL) continue;
9119 		if (connectorItems.contains(ci)) return true;
9120 	}
9121 
9122 	return false;
9123 }
9124 
getRatsnestColor(QColor & color)9125 void SketchWidget::getRatsnestColor(QColor & color)
9126 {
9127 	//RatsnestColors::reset(m_viewID);
9128 	color = RatsnestColors::netColor(m_viewID);
9129 }
9130 
makeOneRatsnestWire(ConnectorItem * source,ConnectorItem * dest,bool routed,QColor color,bool force)9131 VirtualWire * SketchWidget::makeOneRatsnestWire(ConnectorItem * source, ConnectorItem * dest, bool routed, QColor color, bool force) {
9132 	if (source->attachedTo() == dest->attachedTo()) {
9133 		if (source == dest) return NULL;
9134 
9135 		if (source->bus() == dest->bus() && dest->bus() != NULL) {
9136 			if (!force) return NULL;				// don't draw a wire within the same part on the same bus
9137 		}
9138 	}
9139 
9140 	long newID = ItemBase::getNextID();
9141 
9142 	ViewGeometry viewGeometry;
9143 	makeRatsnestViewGeometry(viewGeometry, source, dest);
9144 	viewGeometry.setRouted(routed);
9145 
9146 	//if (viewID() == ViewLayer::PCBView) {
9147 	//	source->debugInfo("making rat src");
9148 	//	dest->debugInfo("making rat dst");
9149 	//}
9150 
9151 	// ratsnest only added to one view
9152 	ItemBase * newItemBase = addItem(m_referenceModel->retrieveModelPart(ModuleIDNames::WireModuleIDName), source->attachedTo()->viewLayerPlacement(), BaseCommand::SingleView, viewGeometry, newID, -1, NULL);
9153 	VirtualWire * wire = qobject_cast<VirtualWire *>(newItemBase);
9154 	ConnectorItem * connector0 = wire->connector0();
9155 	source->tempConnectTo(connector0, false);
9156 	connector0->tempConnectTo(source, false);
9157 
9158 	ConnectorItem * connector1 = wire->connector1();
9159 	dest->tempConnectTo(connector1, false);
9160 	connector1->tempConnectTo(dest, false);
9161 
9162 	if (!source->attachedTo()->isVisible() || !dest->attachedTo()->isVisible()) {
9163 		wire->setVisible(false);
9164 	}
9165 
9166 	wire->setColor(color, ratsnestOpacity());
9167 	wire->setWireWidth(ratsnestWidth(), this, VirtualWire::ShapeWidthExtra + ratsnestWidth());
9168 
9169 	return wire;
9170 }
9171 
ratsnestOpacity()9172 double SketchWidget::ratsnestOpacity() {
9173 	return m_ratsnestOpacity;
9174 }
9175 
setRatsnestOpacity(double opacity)9176 void SketchWidget::setRatsnestOpacity(double opacity) {
9177 	m_ratsnestOpacity = opacity;
9178 }
9179 
ratsnestWidth()9180 double SketchWidget::ratsnestWidth() {
9181 	return m_ratsnestWidth;
9182 }
9183 
setRatsnestWidth(double width)9184 void SketchWidget::setRatsnestWidth(double width) {
9185 	m_ratsnestWidth = width;
9186 }
makeRatsnestViewGeometry(ViewGeometry & viewGeometry,ConnectorItem * source,ConnectorItem * dest)9187 void SketchWidget::makeRatsnestViewGeometry(ViewGeometry & viewGeometry, ConnectorItem * source, ConnectorItem * dest)
9188 {
9189 	QPointF fromPos = source->sceneAdjustedTerminalPoint(NULL);
9190 	viewGeometry.setLoc(fromPos);
9191 	QPointF toPos = dest->sceneAdjustedTerminalPoint(NULL);
9192 	QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y());
9193 	viewGeometry.setLine(line);
9194 	viewGeometry.setWireFlags(ViewGeometry::RatsnestFlag);
9195 }
9196 
traceColor(ViewLayer::ViewLayerPlacement)9197 const QString & SketchWidget::traceColor(ViewLayer::ViewLayerPlacement) {
9198 	return ___emptyString___;
9199 }
9200 
getTraceWidth()9201 double SketchWidget::getTraceWidth() {
9202 	return 1;
9203 }
9204 
setAnyInRotation()9205 void SketchWidget::setAnyInRotation()
9206 {
9207 	m_anyInRotation = true;
9208 }
9209 
removeWire(Wire * w,QList<ConnectorItem * > & ends,QList<Wire * > & done,QUndoCommand * parentCommand)9210 void SketchWidget::removeWire(Wire * w, QList<ConnectorItem *> & ends, QList<Wire *> & done, QUndoCommand * parentCommand)
9211 {
9212 	QList<Wire *> chained;
9213 	w->collectChained(chained, ends);
9214 	makeWiresChangeConnectionCommands(chained, parentCommand);
9215 	foreach (Wire * c, chained) {
9216 		makeDeleteItemCommand(c, BaseCommand::CrossView, parentCommand);
9217 		done.append(c);
9218 	}
9219 }
9220 
collectRatsnestSlot(QList<SketchWidget * > & foreignSketchWidgets)9221 void SketchWidget::collectRatsnestSlot(QList<SketchWidget *> & foreignSketchWidgets)
9222 {
9223 	foreignSketchWidgets << this;
9224 }
9225 
setGroundFillSeed(long id,const QString & connectorID,bool seed)9226 void SketchWidget::setGroundFillSeed(long id, const QString & connectorID, bool seed)
9227 {
9228 	ItemBase * itemBase = findItem(id);
9229 	if (itemBase == NULL) return;
9230 
9231 	ConnectorItem * connectorItem = findConnectorItem(itemBase, connectorID, ViewLayer::specFromID(itemBase->viewLayerID()));
9232 	if (connectorItem == NULL) return;
9233 
9234 	connectorItem->setGroundFillSeed(seed);
9235 }
9236 
9237 
resolveTemporary(bool resolve,ItemBase * itemBase)9238 void SketchWidget::resolveTemporary(bool resolve, ItemBase * itemBase)
9239 {
9240     if (resolve) {
9241         m_undoStack->resolveTemporary();
9242         return;
9243     }
9244 
9245     QTimer * timer = new QTimer();
9246     timer->setProperty("temporary", QVariant::fromValue(itemBase));
9247     timer->setSingleShot(true);
9248     timer->setInterval(10);
9249     connect(timer, SIGNAL(timeout()), this, SLOT(deleteTemporary()));
9250 
9251     // resolveTemporary is invoked indirectly from the temporary stack item via the itemBase, so defer deletion
9252     timer->start();
9253 }
9254 
9255 
deleteTemporary()9256 void SketchWidget::deleteTemporary() {
9257 
9258     QObject * s = sender();
9259     if (s == NULL) return;
9260 
9261     ItemBase * itemBase = s->property("temporary").value<ItemBase *>();
9262     if (itemBase) {
9263         deleteItem(itemBase->id(), true, true, false);
9264     }
9265 
9266     m_undoStack->deleteTemporary();
9267 
9268     s->deleteLater();
9269 }
9270 
checkDroppedModuleID(const QString & moduleID)9271 QString SketchWidget::checkDroppedModuleID(const QString & moduleID) {
9272     return moduleID;
9273 }
9274 
sameElectricalLayer2(ViewLayer::ViewLayerID,ViewLayer::ViewLayerID)9275 bool SketchWidget::sameElectricalLayer2(ViewLayer::ViewLayerID, ViewLayer::ViewLayerID) {
9276 	return true;
9277 }
9278 
canConnect(ItemBase * from,ItemBase * to)9279 bool SketchWidget::canConnect(ItemBase * from, ItemBase * to) {
9280     if (m_pasting) return true;             // no need to check in this case
9281 
9282     Wire * fromWire = qobject_cast<Wire *>(from);
9283     Wire * toWire = qobject_cast<Wire *>(to);
9284 
9285     if (fromWire != NULL && fromWire->getTrace()) {
9286         if (fromWire->isTraceType(getTraceFlag())) {
9287            return canConnect(fromWire, to);
9288         }
9289         else {
9290             bool can;
9291             emit canConnectSignal(fromWire, to, can);
9292             return can;
9293         }
9294     }
9295 
9296     if (toWire != NULL && toWire->getTrace()) {
9297         if (toWire->isTraceType(getTraceFlag())) {
9298             return canConnect(toWire, from);
9299         }
9300         else {
9301             bool can;
9302             emit canConnectSignal(toWire, from, can);
9303             return can;
9304         }
9305     }
9306 
9307 
9308     return true;
9309 
9310 }
9311 
canConnect(Wire *,ItemBase *)9312 bool SketchWidget::canConnect(Wire *, ItemBase *) {
9313     return true;
9314 }
9315 
canConnect(Wire * from,ItemBase * to,bool & connect)9316 void SketchWidget::canConnect(Wire * from, ItemBase * to, bool & connect) {
9317     if (!from->isTraceType(getTraceFlag())) return;
9318 
9319     //from->debugInfo("can connect");
9320     //to->debugInfo("\t");
9321     from = qobject_cast<Wire *>(findItem(from->id()));
9322     to = findItem(to->id());
9323     connect = canConnect(from, to);
9324 }
9325 
swapStart(SwapThing & swapThing,bool master)9326 long SketchWidget::swapStart(SwapThing & swapThing, bool master) {
9327     Q_UNUSED(master);
9328 
9329 	long newID = ItemBase::getNextID(swapThing.newModelIndex);
9330 
9331     ItemBase * itemBase = swapThing.itemBase;
9332 	if (itemBase->viewID() != m_viewID) {
9333 		itemBase = findItem(itemBase->id());
9334 		if (itemBase == NULL) return newID;
9335 	}
9336 
9337 	ViewGeometry vg = itemBase->getViewGeometry();
9338 	QTransform oldTransform = vg.transform();
9339 	bool needsTransform = false;
9340 	if (!oldTransform.isIdentity()) {
9341 		// restore identity transform
9342 		vg.setTransform(QTransform());
9343 		needsTransform = true;
9344 	}
9345 
9346 	new MoveItemCommand(this, itemBase->id(), vg, vg, false, swapThing.parentCommand);
9347 
9348 	// command created for each view
9349 	newAddItemCommand(BaseCommand::SingleView, NULL, swapThing.newModuleID, swapThing.viewLayerPlacement, vg, newID, true, swapThing.newModelIndex, true, swapThing.parentCommand);
9350 
9351 	if (needsTransform) {
9352 		QMatrix m;
9353 		m.setMatrix(oldTransform.m11(), oldTransform.m12(), oldTransform.m21(), oldTransform.m22(), 0, 0);
9354 		new TransformItemCommand(this, newID, m, m, swapThing.parentCommand);
9355 	}
9356 
9357     return newID;
9358 }
9359 
setPasting(bool pasting)9360 void SketchWidget::setPasting(bool pasting) {
9361     m_pasting = pasting;
9362 }
9363 
showUnrouted()9364 void SketchWidget::showUnrouted() {
9365 
9366     // MainWindow::routingStatusLabelMouse uses a different technique for collecting unrouted...
9367 
9368     // what about multiple boards
9369 
9370     QList< QList< ConnectorItem *>* > allPartConnectorItems;
9371 	QHash<ConnectorItem *, int> indexer;
9372 	collectAllNets(indexer, allPartConnectorItems, false, routeBothSides());
9373     QSet<ConnectorItem *> toShow;
9374     foreach (QList<ConnectorItem *> * connectorItems, allPartConnectorItems) {
9375 		ConnectorPairHash result;
9376 		GraphUtils::chooseRatsnestGraph(connectorItems, (ViewGeometry::RatsnestFlag | ViewGeometry::NormalFlag | ViewGeometry::PCBTraceFlag | ViewGeometry::SchematicTraceFlag) ^ getTraceFlag(), result);
9377         foreach (ConnectorItem * ck, result.uniqueKeys()) {
9378             toShow.insert(ck);
9379             foreach (ConnectorItem * cv, result.values(ck)) {
9380                 toShow.insert(cv);
9381             }
9382         }
9383     }
9384 
9385 	QList<ConnectorItem *> visited;
9386 	foreach (ConnectorItem * connectorItem, toShow) {
9387 		if (connectorItem->isActive() && connectorItem->isVisible() && !connectorItem->hidden() && !connectorItem->layerHidden()) {
9388 			connectorItem->showEqualPotential(true, visited);
9389 		}
9390 		else {
9391 			connectorItem = connectorItem->getCrossLayerConnectorItem();
9392 			if (connectorItem) connectorItem->showEqualPotential(true, visited);
9393 		}
9394 	}
9395 
9396     QString message = tr("Unrouted connections are highlighted in yellow.");
9397     if (toShow.count() == 0) message = tr("There are no unrouted connections");
9398     QMessageBox::information(this, tr("Unrouted connections"),
9399         tr("%1\n\n"
9400             "Note: you can also trigger this display by mousing down on the routing status text in the status bar.").arg(message));
9401 
9402 
9403 	visited.clear();
9404     foreach (ConnectorItem * connectorItem, toShow) {
9405 		if (connectorItem->isActive() && connectorItem->isVisible() && !connectorItem->hidden() && !connectorItem->layerHidden()) {
9406 			connectorItem->showEqualPotential(false, visited);
9407 		}
9408 		else {
9409 			connectorItem = connectorItem->getCrossLayerConnectorItem();
9410 			if (connectorItem) connectorItem->showEqualPotential(false, visited);
9411 		}
9412 	}
9413 }
9414 
showEvent(QShowEvent * event)9415 void SketchWidget::showEvent(QShowEvent * event) {
9416 	InfoGraphicsView::showEvent(event);
9417 	emit showing(this);
9418 }
9419 
removeDragWire()9420 void SketchWidget::removeDragWire() {
9421 	if (scene()->mouseGrabberItem() == m_connectorDragWire) {
9422 		// probably already ungrabbed by the wire, but just in case
9423 		m_connectorDragWire->ungrabMouse();
9424 		//m_connectorDragWire->debugInfo("ungrabbing mouse 2");
9425 	}
9426 
9427 	this->scene()->removeItem(m_connectorDragWire);
9428 }
9429 
selectItem(ItemBase * itemBase)9430 void SketchWidget::selectItem(ItemBase * itemBase) {
9431     QList<ItemBase *> itemBases;
9432     itemBases << itemBase;
9433     selectItems(itemBases);
9434 }
9435 
selectItemsWithModuleID(ModelPart * modelPart)9436 void SketchWidget::selectItemsWithModuleID(ModelPart * modelPart) {
9437     QSet<ItemBase *> itemBases;
9438     foreach (QGraphicsItem * item, scene()->items()) {
9439         ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
9440         if (itemBase && itemBase->moduleID() == modelPart->moduleID()) {
9441             itemBases.insert(itemBase->layerKinChief());
9442         }
9443     }
9444 
9445     if (itemBases.count() == 0) {
9446         QMessageBox::information(NULL, "Not found", tr("Part '%1' not found in sketch").arg(modelPart->title()));
9447         return;
9448     }
9449 
9450     selectItems(itemBases.values());
9451 }
9452 
addToSketch(QList<ModelPart * > & modelParts)9453 void SketchWidget::addToSketch(QList<ModelPart *> & modelParts) {
9454     if (modelParts.count() == 0) {
9455         modelParts = this->m_referenceModel->allParts();
9456     }
9457 
9458     QUndoCommand* parentCommand = new QUndoCommand(tr("Add %1 parts").arg(modelParts.count()));
9459 	stackSelectionState(false, parentCommand);
9460 	new CleanUpWiresCommand(this, CleanUpWiresCommand::Noop, parentCommand);
9461 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
9462 
9463     int ix = 0;
9464     QList<long> ids;
9465     int columns = 50;
9466     foreach (ModelPart * modelPart, modelParts) {
9467         ViewGeometry viewGeometry;
9468         int x = (ix % columns) * 100;
9469         int y = (ix / columns) * 100;
9470         ix++;
9471         viewGeometry.setLoc(QPointF(x, y));
9472         ViewLayer::ViewLayerPlacement viewLayerPlacement;
9473         getDroppedItemViewLayerPlacement(modelPart, viewLayerPlacement);
9474         long id = ItemBase::getNextID();
9475 	    newAddItemCommand(BaseCommand::CrossView, modelPart, modelPart->moduleID(), viewLayerPlacement, viewGeometry, id, true, -1, true, parentCommand);
9476         ids.append(id);
9477     }
9478 
9479     new PackItemsCommand(this, columns, ids, parentCommand);
9480 
9481     m_undoStack->waitPush(parentCommand, 10);
9482 }
9483 
9484 
selectItems(QList<ItemBase * > startingItemBases)9485 void SketchWidget::selectItems(QList<ItemBase *> startingItemBases) {
9486     QSet<ItemBase *> itemBases;
9487     foreach (ItemBase * itemBase, startingItemBases) {
9488         if (itemBase) itemBases.insert(itemBase->layerKinChief());
9489     }
9490 
9491     QSet<ItemBase *> already;
9492     foreach (QGraphicsItem * item, scene()->selectedItems()) {
9493         ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
9494         if (itemBase) already.insert(itemBase->layerKinChief());
9495     }
9496 
9497     bool theSame = (itemBases.count() == already.count());
9498     if (theSame) {
9499         foreach(ItemBase * itemBase, itemBases) {
9500             if (!already.contains(itemBase)) {
9501                 theSame = false;
9502                 break;
9503             }
9504         }
9505     }
9506 
9507     if (theSame) return;
9508 
9509     int count = 0;
9510     ItemBase * theItemBase = NULL;
9511     foreach(ItemBase * itemBase, itemBases) {
9512         if (itemBase) {
9513             theItemBase = itemBase;
9514             count++;
9515         }
9516     }
9517 
9518     QString message;
9519     if (count == 0) {
9520         message = tr("Deselect all");
9521     }
9522     else if (count == 1) {
9523         message = tr("Select %1").arg(theItemBase->instanceTitle());
9524     }
9525     else {
9526         message = tr("Select %1 items").arg(count);
9527     }
9528 	QUndoCommand * parentCommand = new QUndoCommand(message);
9529 
9530 	stackSelectionState(false, parentCommand);
9531 	SelectItemCommand * selectItemCommand = new SelectItemCommand(this, SelectItemCommand::NormalSelect, parentCommand);
9532     foreach(ItemBase * itemBase, itemBases) {
9533         if (itemBase) {
9534             selectItemCommand->addRedo(itemBase->id());
9535         }
9536     }
9537 
9538 	scene()->clearSelection();
9539 	m_undoStack->push(parentCommand);
9540 }
9541 
getClickedItem(QList<QGraphicsItem * > & items)9542 QGraphicsItem * SketchWidget::getClickedItem(QList<QGraphicsItem *> & items) {
9543 	foreach (QGraphicsItem * gitem, items) {
9544 		if (gitem->acceptedMouseButtons() != Qt::NoButton) {
9545             bool ok = true;
9546             emit clickedItemCandidateSignal(gitem, ok);
9547             if (ok) {
9548 			    return gitem;
9549             }
9550 		}
9551 	}
9552 
9553     return NULL;
9554 }
9555 
blockUI(bool block)9556 void SketchWidget::blockUI(bool block) {
9557     m_blockUI = block;
9558 }
9559 
viewItemInfo(ItemBase * item)9560 void SketchWidget::viewItemInfo(ItemBase * item) {
9561     if (m_blockUI) return;
9562 
9563     InfoGraphicsView::viewItemInfo(item);
9564 }
9565 
getAutorouterSettings()9566 QHash<QString, QString> SketchWidget::getAutorouterSettings() {
9567     return QHash<QString, QString>();
9568 }
9569 
setAutorouterSettings(QHash<QString,QString> &)9570 void SketchWidget::setAutorouterSettings(QHash<QString, QString> &) {
9571 }
9572 
hidePartLayer(long id,ViewLayer::ViewLayerID viewLayerID,bool hide)9573 void SketchWidget::hidePartLayer(long id, ViewLayer::ViewLayerID viewLayerID, bool hide) {
9574     ItemBase * itemBase = findItem(id);
9575     if (itemBase == NULL) return;
9576 
9577     hidePartLayer(itemBase, viewLayerID, hide);
9578 }
9579 
9580 
hidePartLayer(ItemBase * itemBase,ViewLayer::ViewLayerID viewLayerID,bool hide)9581 void SketchWidget::hidePartLayer(ItemBase * itemBase, ViewLayer::ViewLayerID viewLayerID, bool hide)
9582 {
9583     if (itemBase->viewLayerID() == viewLayerID) {
9584         itemBase->setLayerHidden(hide);
9585     }
9586     else {
9587         foreach (ItemBase * lkpi, itemBase->layerKinChief()->layerKin()) {
9588             if (lkpi->viewLayerID() == viewLayerID) {
9589                 lkpi->setLayerHidden(hide);
9590                 break;
9591             }
9592         }
9593     }
9594 }
9595 
cleanupRatsnests(bool doEmit)9596 void SketchWidget::cleanupRatsnests(bool doEmit) {
9597     cleanupRatsnests(m_ratsnestCacheConnect, true);
9598     cleanupRatsnests(m_ratsnestCacheDisconnect, false);
9599 
9600     if (doEmit) emit cleanupRatsnestsSignal(false);
9601 }
9602 
cleanupRatsnests(QList<QPointer<ConnectorItem>> & connectorItems,bool connect)9603 void SketchWidget::cleanupRatsnests(QList< QPointer<ConnectorItem> > & connectorItems, bool connect) {
9604     QList<ConnectorItem *> cis;
9605     foreach (ConnectorItem * connectorItem, connectorItems) {
9606         if (connectorItem) cis << connectorItem;
9607     }
9608     connectorItems.clear();
9609 
9610     QSet<ConnectorItem *> set = cis.toSet();
9611     cis.clear();
9612     QList<ConnectorItem *> cis2 = set.toList();
9613 
9614 	ConnectorItem::collectEqualPotential(cis2, true, ViewGeometry::RatsnestFlag);
9615 	foreach (ConnectorItem * connectorItem, cis2) {
9616 		ratsnestConnect(connectorItem, connect);
9617 	}
9618 }
9619 
addSubpart(long id,long subpartID,bool doEmit)9620 void SketchWidget::addSubpart(long id, long subpartID, bool doEmit) {
9621     ItemBase * super = findItem(id);
9622     if (super == NULL) return;
9623 
9624     ItemBase * sub = findItem(subpartID);
9625     if (sub == NULL) return;
9626 
9627     super->addSubpart(sub);
9628 
9629     sub->setInstanceTitle("", true);
9630 
9631     if (doEmit) {
9632         emit addSubpartSignal(id, subpartID, false);
9633     }
9634 }
9635 
getDroppedItemViewLayerPlacement(ModelPart * modelPart,ViewLayer::ViewLayerPlacement & viewLayerPlacement)9636 void SketchWidget::getDroppedItemViewLayerPlacement(ModelPart * modelPart, ViewLayer::ViewLayerPlacement & viewLayerPlacement) {
9637     emit getDroppedItemViewLayerPlacementSignal(modelPart, viewLayerPlacement);
9638 }
9639 
collectSuperSubs(ItemBase * itemBase)9640 QList<ItemBase *> SketchWidget::collectSuperSubs(ItemBase * itemBase) {
9641     QList<ItemBase *> itemBases;
9642 
9643     if (itemBase->superpart()) {
9644         itemBases.append(itemBase->superpart()->layerKinChief());
9645         foreach (ItemBase * subpart, itemBase->superpart()->subparts()) {
9646             if (!itemBases.contains(subpart->layerKinChief())) {
9647                 itemBases.append(subpart->layerKinChief());
9648             }
9649         }
9650     }
9651     else if (itemBase->subparts().count() > 0) {
9652         itemBases.append(itemBase->layerKinChief());
9653         foreach (ItemBase * subpart, itemBase->subparts()) {
9654             if (!itemBases.contains(subpart->layerKinChief())) {
9655                 itemBases.append(subpart->layerKinChief());
9656             }
9657         }
9658     }
9659 
9660     return itemBases;
9661 }
9662 
moveItem(ItemBase * itemBase,double x,double y)9663 void SketchWidget::moveItem(ItemBase * itemBase, double x, double y)
9664 {
9665     if (itemBase == NULL) return;
9666 
9667     QPointF p = itemBase->pos();
9668     if (qAbs(x - p.x()) < 0.01 && qAbs(y - p.y()) < 0.01) return;
9669 
9670     bool alignToGrid = m_alignToGrid;
9671     m_alignToGrid = false;
9672     moveByArrow(x - p.x(), y - p.y(), NULL);
9673 
9674 	m_movingByArrow = false;
9675 	checkMoved(false);
9676 	m_savedItems.clear();
9677 	m_savedWires.clear();
9678     m_alignToGrid = alignToGrid;
9679 }
9680 
alignItems(Qt::Alignment alignment)9681 void SketchWidget::alignItems(Qt::Alignment alignment) {
9682 	bool rubberBandLegEnabled = false;
9683 	m_dragBendpointWire = NULL;
9684 	clearHoldingSelectItem();
9685 	m_savedItems.clear();
9686 	m_savedWires.clear();
9687 	m_moveEventCount = 0;
9688 	prepMove(NULL, rubberBandLegEnabled, true);
9689 
9690     QMultiHash<Wire *, ConnectorItem *> unsaved;
9691     QSet<Wire *> unsavedSet;
9692     foreach (ItemBase * itemBase, m_savedItems) {
9693         if (itemBase->itemType() == ModelPart::Wire) {
9694             Wire * wire  = qobject_cast<Wire *>(itemBase);
9695             QList<Wire *> wires;
9696             QList<ConnectorItem *> ends;
9697             wire->collectChained(wires, ends);
9698             foreach (Wire * w, wires) {
9699                 if (!unsavedSet.contains(w)) {
9700                     QList< QPointer<ConnectorItem> > toList = w->connector0()->connectedToItems();
9701                     foreach (ConnectorItem * end, ends) {
9702                         if (toList.contains(end)) {
9703                             unsaved.insert(w, end);
9704                             break;
9705                         }
9706                     }
9707                     unsavedSet.insert(w);
9708                 }
9709             }
9710         }
9711     }
9712 
9713     foreach (Wire * w, unsavedSet) m_savedItems.remove(w->id());
9714 
9715     int count = 0;
9716     double left = std::numeric_limits<int>::max();
9717     double top = std::numeric_limits<int>::max();
9718     double right = std::numeric_limits<int>::min();
9719     double bottom = std::numeric_limits<int>::min();
9720     double hcTotal = 0;
9721     double vcTotal = 0;
9722     foreach (ItemBase * itemBase, m_savedItems) {
9723         QRectF r = itemBase->sceneBoundingRect();
9724         if (r.left() < left) left = r.left();
9725         if (r.right() > right) right = r.right();
9726         if (r.top() < top) top = r.top();
9727         if (r.bottom() > bottom) bottom = r.bottom();
9728         count++;
9729         QPointF center = r.center();
9730         hcTotal += center.x();
9731         vcTotal += center.y();
9732     }
9733 
9734     if (count < 2) return;
9735 
9736     hcTotal /= count;
9737     vcTotal /= count;
9738 
9739 	if (m_moveEventCount == 0) {
9740 		// first time
9741 		m_moveDisconnectedFromFemale.clear();
9742 		foreach (ItemBase * item, m_savedItems) {
9743 			if (item->itemType() == ModelPart::Wire) continue;
9744 
9745 			//DebugDialog::debug(QString("disconnecting from female %1").arg(item->instanceTitle()));
9746 			disconnectFromFemale(item, m_savedItems, m_moveDisconnectedFromFemale, false, rubberBandLegEnabled, NULL);
9747 		}
9748 	}
9749 
9750 	foreach (ItemBase * itemBase, m_savedItems) {
9751         if (itemBase->itemType() == ModelPart::Wire) continue;
9752 
9753         QRectF r = itemBase->sceneBoundingRect();
9754         QPointF dp;
9755 
9756         switch (alignment) {
9757             case Qt::AlignLeft:
9758                 dp.setY(0);
9759                 dp.setX(left - r.left());
9760                 break;
9761             case Qt::AlignHCenter:
9762                 dp.setY(0);
9763                 dp.setX(hcTotal - r.center().x());
9764                 break;
9765             case Qt::AlignRight:
9766                 dp.setY(0);
9767                 dp.setX(right - r.right());
9768                 break;
9769             case Qt::AlignTop:
9770                 dp.setX(0);
9771                 dp.setY(top - r.top());
9772                 break;
9773             case Qt::AlignVCenter:
9774                 dp.setX(0);
9775                 dp.setY(vcTotal - r.center().y());
9776                 break;
9777             case Qt::AlignBottom:
9778                 dp.setX(0);
9779                 dp.setY(bottom - r.bottom());
9780                 break;
9781             default:
9782                 dp.setY(0);
9783                 dp.setX(left - r.left());
9784                 break;
9785         }
9786 
9787 		itemBase->setPos(itemBase->getViewGeometry().loc() + dp);
9788 		foreach (ConnectorItem * connectorItem, m_stretchingLegs.values(itemBase)) {
9789 			connectorItem->stretchBy(dp);
9790 		}
9791 
9792 		if (m_checkUnder.contains(itemBase)) {
9793 			findConnectorsUnder(itemBase);
9794 		}
9795 	}
9796 
9797 	foreach (Wire * wire, m_savedWires.keys()) {
9798         ConnectorItem * ci = m_savedWires.value(wire);
9799 		wire->simpleConnectedMoved(ci);
9800 	}
9801 
9802     foreach (Wire * w, unsaved.keys()) {
9803         foreach (ConnectorItem * end, unsaved.values(w)) {
9804             w->simpleConnectedMoved(end);
9805             m_savedWires.insert(w, end);
9806         }
9807     }
9808 	//DebugDialog::debug(QString("done move items %1").arg(QTime::currentTime().msec()) );
9809 
9810     if (m_infoView) {
9811         foreach (ItemBase * itemBase, m_savedItems) {
9812             m_infoView->updateLocation(itemBase->layerKinChief());
9813         }
9814     }
9815 
9816 	m_movingByArrow = false;
9817     m_moveEventCount++;
9818 	checkMoved(false);
9819 	m_savedItems.clear();
9820 	m_savedWires.clear();
9821 }
9822 
squashShapes(QPointF scenePos)9823 void SketchWidget::squashShapes(QPointF scenePos)
9824 {
9825     if (viewID() == ViewLayer::BreadboardView) return;
9826 
9827     unsquashShapes();
9828 
9829     // topmost connectoritem
9830     // topmost wire
9831     // topmost shape
9832     // topmost
9833 
9834     ConnectorItem * connectorItem = NULL;
9835     Wire * wire = NULL;
9836     QList<QGraphicsItem *> itms = scene()->items(scenePos);
9837     if (itms.count() <= 1) return;
9838 
9839     int ix = 0;
9840     for (; ix < itms.count(); ix++) {
9841         QGraphicsItem * item = itms.at(ix);
9842         connectorItem = dynamic_cast<ConnectorItem *>(item);
9843         if (connectorItem && connectorItem->acceptHoverEvents()) {
9844             ItemBase * itemBase = connectorItem->attachedTo();
9845             if (itemBase->inactive()) continue;
9846             if (itemBase->hidden()) continue;
9847             if (itemBase->layerHidden()) continue;
9848 
9849             break;
9850         }
9851 
9852         wire = dynamic_cast<Wire *>(item);
9853         if (wire != NULL && wire->acceptHoverEvents() && !wire->inactive() && !wire->hidden() && !wire->layerHidden()) break;
9854     }
9855 
9856     if (ix == 0) {
9857         return;
9858     }
9859 
9860     if (wire == NULL && connectorItem == NULL) {
9861         int smallest = 0;
9862         double smallestArea = std::numeric_limits<double>::max();
9863         for (ix = 0; ix < itms.count(); ix++) {
9864             QGraphicsItem * item = itms.at(ix);
9865             ItemBase * itemBase = dynamic_cast<ItemBase *>(item);
9866             if (itemBase != NULL) {
9867                 if (itemBase->hidden() || itemBase->layerHidden() || itemBase->inactive()) continue;
9868 
9869                 QPainterPath painterPath = itemBase->mapToScene(itemBase->selectionShape());
9870                 if (painterPath.contains(scenePos)) {
9871                     break;
9872                 }
9873 
9874                 double a = itemBase->boundingRect().width() * itemBase->boundingRect().height();
9875                 if (a < smallestArea) {
9876                     smallest = ix;
9877                     smallestArea = a;
9878                 }
9879             }
9880         }
9881 
9882         if (ix == 0) return;   // use the topmost
9883 
9884         if (ix >= itms.count()) {
9885             ix = smallest;
9886         }
9887     }
9888 
9889     if (ix == 0) {
9890         return;
9891     }
9892 
9893     bool firstTime = true;
9894     for (int i = 0; i < ix; i++) {
9895         ItemBase * itemBase = dynamic_cast<ItemBase *>(itms.at(i));
9896         if (itemBase == NULL) continue;
9897 
9898         if (connectorItem != NULL && connectorItem->parentItem() == itemBase) {
9899             if (firstTime) {
9900                 // topmost  contains connectorItem
9901                 return;
9902             }
9903 
9904             // itembase lower in the list owns the connectorItem
9905             break;
9906         }
9907 
9908         firstTime = false;
9909         itemBase->setSquashShape(true);
9910         m_squashShapes << itemBase;
9911     }
9912 
9913 }
9914 
unsquashShapes()9915 void SketchWidget::unsquashShapes() {
9916     foreach (ItemBase * itemBase, m_squashShapes) {
9917         if (itemBase) itemBase->setSquashShape(false);
9918     }
9919     m_squashShapes.clear();
9920 }
9921 
contextMenuEvent(QContextMenuEvent * event)9922 void SketchWidget::contextMenuEvent(QContextMenuEvent *event)
9923 {
9924     // this event does not occur within mousePressEvent() so squashshapes is not called twice
9925     squashShapes(mapToScene(event->pos()));
9926     InfoGraphicsView::contextMenuEvent(event);
9927     unsquashShapes();
9928 }
9929 
mouseDoubleClickEvent(QMouseEvent * event)9930 void SketchWidget::mouseDoubleClickEvent(QMouseEvent *event)
9931 {
9932     squashShapes(mapToScene(event->pos()));
9933     InfoGraphicsView::mouseDoubleClickEvent(event);
9934     unsquashShapes();
9935 }
9936 
viewportEvent(QEvent * event)9937 bool SketchWidget::viewportEvent(QEvent *event) {
9938     if (event->type() == QEvent::ToolTip) {
9939         QHelpEvent * helpEvent = static_cast<QHelpEvent *>(event);
9940         squashShapes(mapToScene(helpEvent->pos()));
9941     }
9942 
9943     bool result = InfoGraphicsView::viewportEvent(event);
9944 
9945     if (event->type() == QEvent::ToolTip) {
9946         unsquashShapes();
9947     }
9948 
9949     return result;
9950 }
9951 
packItems(int columns,const QList<long> & ids,QUndoCommand * parent,bool doEmit)9952 void SketchWidget::packItems(int columns, const QList<long> & ids, QUndoCommand *parent, bool doEmit)
9953 {
9954     if (ids.count() < 2) return;
9955 
9956     QList<ItemBase *> itemBases;
9957     foreach (long id, ids) {
9958         ItemBase * itemBase = findItem(id);
9959         if (itemBase == NULL) return;
9960 
9961         itemBases << itemBase;
9962         itemBase->saveGeometry();
9963     }
9964 
9965     QPointF initial = itemBases.at(0)->pos();
9966     double top = initial.y();
9967 
9968     QVector<double> heights((ids.count() + columns - 1) / columns, 0);
9969     for (int i = 0; i < itemBases.count(); i++) {
9970         ItemBase * itemBase = itemBases.at(i);
9971         QRectF r = itemBase->sceneBoundingRect();
9972         if (r.height() > heights.at(i / columns)) {
9973             heights[i / columns] = r.height();
9974         }
9975     }
9976 
9977     double offset = 10;
9978     double left = 0;
9979     for (int i = 0; i < itemBases.count(); i++) {
9980         if (i % columns == 0) {
9981             left = initial.x();
9982         }
9983 
9984         ItemBase * itemBase = itemBases.at(i);
9985         ViewGeometry vg1 = itemBase->getViewGeometry();
9986         ViewGeometry vg2 = vg1;
9987         vg2.setLoc(QPointF(left, top));
9988         new MoveItemCommand(this, itemBase->id(), vg1, vg2, true, parent);
9989         itemBase->setPos(left, top);
9990         left += itemBase->sceneBoundingRect().width() + offset;
9991         if (i % columns == columns - 1) {
9992             top += heights.at(i / columns) + offset;
9993         }
9994     }
9995 
9996     if (doEmit) {
9997         emit packItemsSignal(columns, ids, parent, false);
9998     }
9999 }
10000 
gridColor() const10001 QColor SketchWidget::gridColor() const {
10002     return m_gridColor;
10003 }
10004 
setGridColor(QColor color)10005 void SketchWidget::setGridColor(QColor color) {
10006     m_gridColor = color;
10007 }
10008 
everZoomed() const10009 bool SketchWidget::everZoomed() const {
10010     return m_everZoomed;
10011 }
10012 
setEverZoomed(bool everZoomed)10013 void SketchWidget::setEverZoomed(bool everZoomed) {
10014     m_everZoomed = everZoomed;
10015 }
10016 
updateOK(ConnectorItem *,ConnectorItem *)10017 bool SketchWidget::updateOK(ConnectorItem *, ConnectorItem *) {
10018     return true;
10019 }
10020 
testConnectors()10021 void SketchWidget::testConnectors()
10022 {
10023     static const QString templateModuleID("generic_female_pin_header_1_100mil");
10024 	static QRectF templateBoundingRect;
10025     static QPointF templateOffset;
10026 
10027 
10028     QSet<ItemBase *> already;
10029     foreach (QGraphicsItem * item, scene()->selectedItems()) {
10030         ItemBase * itemBase = dynamic_cast<PaletteItemBase *>(item);
10031         if (itemBase == NULL) continue;
10032 
10033         already.insert(itemBase->layerKinChief());
10034     }
10035 
10036     if (already.count() == 0) return;
10037 
10038     ModelPart * templateModelPart = referenceModel()->genFZP(templateModuleID, referenceModel());
10039     if (templateBoundingRect.isNull()) {
10040         long newID = ItemBase::getNextID();
10041 	    ViewGeometry viewGeometry;
10042         ItemBase * templateItem = addItemAuxTemp(templateModelPart, ViewLayer::NewTop, viewGeometry, newID, true, viewID(), true);
10043 	    templateBoundingRect = templateItem->sceneBoundingRect();
10044         QPointF templatePos = templateItem->cachedConnectorItems()[0]->sceneAdjustedTerminalPoint(NULL);
10045         templateOffset = templatePos - templateBoundingRect.topLeft();
10046         foreach (ItemBase * kin, templateItem->layerKin()) delete kin;
10047         delete templateItem;
10048     }
10049 
10050     QUndoCommand * parentCommand = new QUndoCommand(tr("test connectors"));
10051     new CleanUpWiresCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
10052 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::UndoOnly, parentCommand);
10053 
10054     foreach (ItemBase * itemBase, already) {
10055         QRectF sceneBoundingRect = itemBase->sceneBoundingRect();
10056         QPointF center = sceneBoundingRect.center();
10057 
10058         foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
10059             QPointF fromPos = connectorItem->sceneAdjustedTerminalPoint(NULL);
10060             QPointF toPos = fromPos;
10061             double d[4];
10062             d[0] = qAbs(fromPos.x() - sceneBoundingRect.left());
10063             d[1] = qAbs(fromPos.x() - sceneBoundingRect.right());
10064             d[2] = qAbs(fromPos.y() - sceneBoundingRect.top());
10065             d[3] = qAbs(fromPos.y() - sceneBoundingRect.bottom());
10066             int best = 0;
10067             for (int i = 1; i < 4; i++) {
10068                 if (d[i] < d[best]) best = i;
10069             }
10070             switch (best) {
10071                 case 0:
10072                     // left
10073                     toPos.setX(sceneBoundingRect.left() - (3 * templateBoundingRect.width()));
10074                     break;
10075                 case 1:
10076                     // right
10077                     toPos.setX(sceneBoundingRect.right() + (3 * templateBoundingRect.width()));
10078                     break;
10079                 case 2:
10080                     // top
10081                     toPos.setY(sceneBoundingRect.top() - (3 * templateBoundingRect.height()));
10082                     break;
10083                 case 3:
10084                     // bottom
10085                     toPos.setY(sceneBoundingRect.bottom() + (3 * templateBoundingRect.height()));
10086                     break;
10087             }
10088 
10089 
10090             long newID = ItemBase::getNextID();
10091             ViewGeometry vg;
10092             vg.setLoc(toPos - templateOffset);
10093             newAddItemCommand(BaseCommand::CrossView, templateModelPart, templateModelPart->moduleID(), itemBase->viewLayerPlacement(), vg, newID, true, -1, true, parentCommand);
10094 
10095             long wireID = ItemBase::getNextID();
10096             ViewGeometry vgw;
10097 	        vgw.setLoc(fromPos);
10098 	        QLineF line(0, 0, toPos.x() - fromPos.x(), toPos.y() - fromPos.y());
10099 	        vgw.setLine(line);
10100 	        vgw.setWireFlags(getTraceFlag());
10101             new AddItemCommand(this, BaseCommand::CrossView, ModuleIDNames::WireModuleIDName, itemBase->viewLayerPlacement(), vgw, wireID, false, -1, parentCommand);
10102             new ChangeConnectionCommand(this, BaseCommand::CrossView,
10103 								itemBase->id(), connectorItem->connectorSharedID(),
10104 								wireID, "connector0",
10105 								itemBase->viewLayerPlacement(), true, parentCommand);
10106             new ChangeConnectionCommand(this, BaseCommand::CrossView,
10107 								newID, "connector0",
10108 								wireID, "connector1",
10109 								itemBase->viewLayerPlacement(), true, parentCommand);
10110         }
10111     }
10112 
10113     new CleanUpWiresCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
10114 	new CleanUpRatsnestsCommand(this, CleanUpWiresCommand::RedoOnly, parentCommand);
10115 
10116     m_undoStack->waitPush(parentCommand, PropChangeDelay);
10117 }
10118 
updateWires()10119 void SketchWidget::updateWires() {
10120     QList<ConnectorItem *> already;
10121     ViewGeometry::WireFlag traceFlag = getTraceFlag();
10122     foreach (QGraphicsItem * item, scene()->items()) {
10123         Wire * wire = dynamic_cast<Wire *>(item);
10124         if (wire == NULL) continue;
10125         if (!wire->isTraceType(traceFlag)) continue;
10126 
10127         ConnectorItem * from = wire->connector0()->firstConnectedToIsh();
10128         if (from != NULL) wire->connectedMoved(from, wire->connector0(), already);
10129         ConnectorItem * to = wire->connector1()->firstConnectedToIsh();
10130         if (to != NULL) wire->connectedMoved(to, wire->connector1(), already);
10131     }
10132 }
10133 
viewGeometryConversionHack(ViewGeometry &,ModelPart *)10134 void SketchWidget::viewGeometryConversionHack(ViewGeometry &, ModelPart *)  {
10135 }
10136