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