1 /*******************************************************************
2
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
18
19 ********************************************************************
20
21 $Revision: 6984 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $
24
25 ********************************************************************/
26
27 #include "stripboard.h"
28 #include "../utils/graphicsutils.h"
29 #include "../utils/textutils.h"
30 #include "../utils/familypropertycombobox.h"
31 #include "../svg/gerbergenerator.h"
32 #include "../fsvgrenderer.h"
33 #include "../sketch/infographicsview.h"
34 #include "moduleidnames.h"
35 #include "../connectors/connectoritem.h"
36 #include "../connectors/busshared.h"
37 #include "../connectors/connectorshared.h"
38 #include "../debugdialog.h"
39
40 #include <QCursor>
41 #include <QBitmap>
42
43
44 //////////////////////////////////////////////////
45
46 // TODO:
47 //
48 // new cursors, new hover states?
49 // disconnect and reconnect affected parts
50 // swapping
51
52 static QCursor * SpotFaceCutterCursor = NULL;
53 static QCursor * MagicWandCursor = NULL;
54
55 static bool ShiftDown = false;
56 static QPointF OriginalShiftPos;
57 static bool ShiftX = false;
58 static bool ShiftY = false;
59 static bool SpaceBarWasPressed = false;
60 static const double MinMouseMove = 2;
61
62 static QPainterPath HPath;
63 static QPainterPath VPath;
64
65 static QString HorizontalString("horizontal strips");
66 static QString VerticalString("vertical strips");
67
68 /////////////////////////////////////////////////////////////////////
69
Stripbit(const QPainterPath & path,int x,int y,bool horizontal,QGraphicsItem * parent=0)70 Stripbit::Stripbit(const QPainterPath & path, int x, int y, bool horizontal, QGraphicsItem * parent = 0)
71 : QGraphicsPathItem(path, parent)
72 {
73 if (SpotFaceCutterCursor == NULL) {
74 QBitmap bitmap(":resources/images/cursor/spot_face_cutter.bmp");
75 QBitmap bitmapm(":resources/images/cursor/spot_face_cutter_mask.bmp");
76 SpotFaceCutterCursor = new QCursor(bitmap, bitmapm, 0, 0);
77 }
78
79 if (MagicWandCursor == NULL) {
80 QBitmap bitmap(":resources/images/cursor/magic_wand.bmp");
81 QBitmap bitmapm(":resources/images/cursor/magic_wand_mask.bmp");
82 MagicWandCursor = new QCursor(bitmap, bitmapm, 0, 0);
83 }
84
85 setZValue(-999); // beneath connectorItems
86
87 setPen(Qt::NoPen);
88 // TODO: don't hardcode this color
89 setBrush(QColor(0xbc, 0x94, 0x51)); // QColor(0xc4, 0x9c, 0x59)
90
91
92 m_horizontal = horizontal;
93 m_x = x;
94 m_y = y;
95 m_inHover = m_removed = false;
96
97 setAcceptHoverEvents(true);
98 setAcceptedMouseButtons(Qt::LeftButton);
99 setFlag(QGraphicsItem::ItemIsMovable, true);
100 setFlag(QGraphicsItem::ItemIsSelectable, false);
101
102 }
103
~Stripbit()104 Stripbit::~Stripbit() {
105 }
106
paint(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)107 void Stripbit::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) {
108 double newOpacity = 1;
109 if (m_removed) {
110 if (m_inHover) newOpacity = 0.50;
111 else newOpacity = 0.00;
112 }
113 else {
114 if (m_inHover) newOpacity = 0.40;
115 else newOpacity = 1.00;
116 }
117
118 double opacity = painter->opacity();
119 painter->setOpacity(newOpacity);
120 QGraphicsPathItem::paint(painter, option, widget);
121 painter->setOpacity(opacity);
122
123 }
124
mousePressEvent(QGraphicsSceneMouseEvent * event)125 void Stripbit::mousePressEvent(QGraphicsSceneMouseEvent *event)
126 {
127 InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
128 if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) {
129 event->ignore();
130 return;
131 }
132
133 if (!event->buttons() && Qt::LeftButton) {
134 event->ignore();
135 return;
136 }
137
138 if (dynamic_cast<ItemBase *>(this->parentItem())->moveLock()) {
139 event->ignore();
140 return;
141 }
142
143 if (event->modifiers() & Qt::ShiftModifier) {
144 ShiftDown = true;
145 ShiftX = ShiftY = false;
146 OriginalShiftPos = event->scenePos();
147 }
148
149 event->accept();
150 dynamic_cast<Stripboard *>(this->parentItem())->initCutting(this);
151 m_removed = !m_removed;
152 m_inHover = false;
153 m_changed = true;
154 update();
155
156
157 //DebugDialog::debug("got press");
158 }
159
mouseReleaseEvent(QGraphicsSceneMouseEvent * event)160 void Stripbit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
161 {
162 Q_UNUSED(event);
163 dynamic_cast<Stripboard *>(this->parentItem())->reinitBuses(true);
164 }
165
mouseMoveEvent(QGraphicsSceneMouseEvent * event)166 void Stripbit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
167 {
168 if (!event->buttons() && Qt::LeftButton) return;
169
170 if (ShiftDown && !(event->modifiers() & Qt::ShiftModifier)) {
171 ShiftDown = false;
172 }
173
174 //DebugDialog::debug("got move");
175
176 Stripbit * other = NULL;
177 QPointF p = event->scenePos();
178 if (ShiftDown) {
179 if (ShiftX) {
180 // moving along x, constrain y
181 p.setY(OriginalShiftPos.y());
182 }
183 else if (ShiftY) {
184 // moving along y, constrain x
185 p.setX(OriginalShiftPos.x());
186 }
187 else {
188 double dx = qAbs(p.x() - OriginalShiftPos.x());
189 double dy = qAbs(p.y() - OriginalShiftPos.y());
190 if (dx - dy > MinMouseMove) {
191 ShiftX = true;
192 }
193 else if (dy - dx > MinMouseMove) {
194 ShiftY = true;
195 }
196 }
197 }
198
199
200 if (!ShiftDown && (event->modifiers() & Qt::ShiftModifier)) {
201 ShiftDown = true;
202 ShiftX = ShiftY = false;
203 OriginalShiftPos = event->scenePos();
204 }
205
206 foreach (QGraphicsItem * item, scene()->items(p)) {
207 other = dynamic_cast<Stripbit *>(item);
208 if (other) break;
209 }
210
211 if (!other) return;
212
213 //DebugDialog::debug("got other");
214
215 if (other->removed() == m_removed) return;
216
217 //DebugDialog::debug("change other");
218
219 other->setRemoved(m_removed);
220 other->setChanged(true);
221 other->update();
222 dynamic_cast<Stripboard *>(this->parentItem())->restoreRowColors(other);
223
224 }
225
hoverEnterEvent(QGraphicsSceneHoverEvent * event)226 void Stripbit::hoverEnterEvent( QGraphicsSceneHoverEvent * event )
227 {
228 if (dynamic_cast<ItemBase *>(this->parentItem())->moveLock()) return;
229
230 InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
231 if (infoGraphicsView != NULL && infoGraphicsView->spaceBarIsPressed()) {
232 SpaceBarWasPressed = true;
233 return;
234 }
235
236 SpaceBarWasPressed = false;
237 setCursor(m_removed ? *MagicWandCursor : *SpotFaceCutterCursor);
238 Q_UNUSED(event);
239 m_inHover = true;
240 update();
241 }
242
hoverLeaveEvent(QGraphicsSceneHoverEvent * event)243 void Stripbit::hoverLeaveEvent ( QGraphicsSceneHoverEvent * event )
244 {
245 if (dynamic_cast<ItemBase *>(this->parentItem())->moveLock()) return;
246 if (SpaceBarWasPressed) return;
247
248 unsetCursor();
249 Q_UNUSED(event);
250 m_inHover = false;
251 update();
252 }
253
254
255
horizontal()256 bool Stripbit::horizontal() {
257 return m_horizontal;
258 }
259
setRemoved(bool removed)260 void Stripbit::setRemoved(bool removed) {
261 m_removed = removed;
262 }
263
removed()264 bool Stripbit::removed() {
265 return m_removed;
266 }
267
setChanged(bool changed)268 void Stripbit::setChanged(bool changed) {
269 m_changed = changed;
270 }
271
changed()272 bool Stripbit::changed() {
273 return m_changed;
274 }
275
y()276 int Stripbit::y() {
277 return m_y;
278 }
279
x()280 int Stripbit::x() {
281 return m_x;
282 }
283
makeRemovedString()284 QString Stripbit::makeRemovedString() {
285 return QString("%1.%2%3 ").arg(m_x).arg(m_y).arg(m_horizontal ? 'h' : 'v');
286 }
287
288 /////////////////////////////////////////////////////////////////////
289
StripConnector()290 StripConnector::StripConnector() {
291 down = right = NULL;
292 connectorItem = NULL;
293 }
294
295 struct StripLayout {
296 QString name;
297 int rows;
298 int columns;
299 QString buses;
300
301 StripLayout(QString name, int rows, int columns, QString buses);
302 };
303
StripLayout(QString name_,int rows_,int columns_,QString buses_)304 StripLayout::StripLayout(QString name_, int rows_, int columns_, QString buses_) {
305 name = name_;
306 rows = rows_;
307 columns = columns_;
308 buses = buses_;
309 }
310
311 static QList<StripLayout> StripLayouts;
312
313 /////////////////////////////////////////////////////////////////////
314
Stripboard(ModelPart * modelPart,ViewLayer::ViewID viewID,const ViewGeometry & viewGeometry,long id,QMenu * itemMenu,bool doLabel)315 Stripboard::Stripboard( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel)
316 : Perfboard(modelPart, viewID, viewGeometry, id, itemMenu, doLabel)
317 {
318 getXY(m_x, m_y, m_size);
319 if (StripLayouts.count() == 0) {
320 initStripLayouts();
321 }
322
323 m_layout = modelPart->localProp("layout").toString();
324 if (m_layout.isEmpty()) {
325 m_layout = modelPart->properties().value("m_layout");
326 if (!m_layout.isEmpty()) {
327 modelPart->setLocalProp("layout", m_layout);
328 }
329 }
330 }
331
~Stripboard()332 Stripboard::~Stripboard() {
333 foreach (StripConnector * sc, m_strips) {
334 delete sc;
335 }
336
337 m_strips.clear();
338 }
339
retrieveSvg(ViewLayer::ViewLayerID viewLayerID,QHash<QString,QString> & svgHash,bool blackOnly,double dpi,double & factor)340 QString Stripboard::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash<QString, QString> & svgHash, bool blackOnly, double dpi, double & factor)
341 {
342 QString svg = Perfboard::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor);
343 if (svg.isEmpty()) return svg;
344 if (!svg.contains(GerberGenerator::MagicBoardOutlineID)) return svg;
345
346 /*
347 QFile file(filename());
348 if (!file.open(QFile::ReadOnly | QFile::Text)) {
349 return svg;
350 }
351 QString originalSvg = file.readAll();
352 file.close();
353
354 QString stripSvg = originalSvg.left(svg.indexOf(">") + 1);
355 */
356
357 QString stripSvg;
358 foreach(QGraphicsItem * item, childItems()) {
359 Stripbit * stripbit = dynamic_cast<Stripbit *>(item);
360 if (stripbit == NULL) continue;
361 if (stripbit->removed()) continue;
362
363 QPointF p = stripbit->pos();
364 QRectF r = stripbit->boundingRect();
365
366 stripSvg += QString("<path stroke='none' stroke-width='0' fill='%6' "
367 "d='m%1,%2 %3,0 0,%4 -%3,0z m0,%4a%5,%5 0,1,0 0,-%4z m%3,-%4a%5,%5 0,1,0 0,%4z'/>\n")
368 .arg(p.x() * dpi / GraphicsUtils::SVGDPI)
369 .arg(p.y() * dpi / GraphicsUtils::SVGDPI)
370 .arg(r.width() * dpi / GraphicsUtils::SVGDPI )
371 .arg(r.height() * dpi / GraphicsUtils::SVGDPI)
372 .arg(r.height() * dpi * .5 / GraphicsUtils::SVGDPI)
373 .arg(blackOnly ? "black" : "#c49c59")
374 ;
375 }
376
377 svg.truncate(svg.lastIndexOf("</g>"));
378
379 return svg + stripSvg + "</g>\n";
380 }
381
genFZP(const QString & moduleid)382 QString Stripboard::genFZP(const QString & moduleid)
383 {
384 QString fzp = Perfboard::genFZP(moduleid);
385 fzp.replace(ModuleIDNames::PerfboardModuleIDName, ModuleIDNames::Stripboard2ModuleIDName);
386 fzp.replace("Perfboard", "Stripboard");
387 fzp.replace("perfboard", "stripboard");
388 fzp.replace("stripboard.svg", "perfboard.svg"); // replaced just above; restore it
389 QString findString("<properties>");
390 int ix = fzp.indexOf(findString);
391 if (ix > 0) {
392 fzp.insert(ix + findString.count(), "<property name='layout'></property>");
393 if (moduleid.endsWith(ModuleIDNames::StripboardModuleIDName)) {
394 fzp.insert(ix + findString.count(), "<property name='oldstyle'>yes</property>");
395 }
396 }
397 return fzp;
398 }
399
collectExtraInfo(QWidget * parent,const QString & family,const QString & prop,const QString & value,bool swappingEnabled,QString & returnProp,QString & returnValue,QWidget * & returnWidget,bool & hide)400 bool Stripboard::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide)
401 {
402 return Perfboard::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide);
403 }
404
addedToScene(bool temporary)405 void Stripboard::addedToScene(bool temporary)
406 {
407 Perfboard::addedToScene(temporary);
408 if (this->scene() == NULL) return;
409 if (temporary) return;
410 if (m_viewID != ViewLayer::BreadboardView) return;
411
412 QList<QGraphicsItem *> items = childItems();
413
414 if (HPath.isEmpty()) {
415 makeInitialPath();
416 }
417
418 int count = m_x * m_y;
419 for (int i = 0; i < count; i++) {
420 m_strips.append(new StripConnector);
421 }
422
423 bool oldStyle = false;
424 if (moduleID().endsWith(ModuleIDNames::StripboardModuleIDName) || !modelPart()->properties().value("oldstyle", "").isEmpty()) {
425 modelPart()->modelPartShared()->setModuleID(QString("%1.%2%3").arg(m_x).arg(m_y).arg(ModuleIDNames::Stripboard2ModuleIDName));
426 oldStyle = true;
427 m_layout = HorizontalString;
428 }
429
430 QString config = prop("buses");
431 QString additionalConfig;
432
433 foreach (ConnectorItem * ci, cachedConnectorItems()) {
434 int cx, cy;
435 getXY(cx, cy, ci->connectorSharedName());
436 StripConnector * sc = getStripConnector(cx, cy);
437 sc->connectorItem = ci;
438
439 if (cx < m_x - 1) {
440 Stripbit * stripbit = new Stripbit(HPath, cx, cy, true, this);
441 QRectF r = ci->rect();
442 stripbit->setPos(r.center().x(), r.top());
443 stripbit->setVisible(true);
444 sc->right = stripbit;
445 if (!oldStyle) {
446 additionalConfig += stripbit->makeRemovedString();
447 }
448 }
449
450 if (cy < m_y - 1) {
451 Stripbit * stripbit = new Stripbit(VPath, cx, cy, false, this);
452 QRectF r = ci->rect();
453 stripbit->setPos(r.left(), r.center().y());
454 stripbit->setVisible(true);
455 sc->down = stripbit;
456 if (oldStyle) {
457 additionalConfig += stripbit->makeRemovedString();
458 }
459 }
460 }
461
462 if (config.isEmpty() || oldStyle) {
463 config += additionalConfig;
464 setProp("buses", config);
465 }
466
467 if (m_layout.isEmpty()) m_layout = VerticalString;
468
469 QStringList removed = config.split(" ", QString::SkipEmptyParts);
470
471 foreach (QString name, removed) {
472 int cx, cy;
473 if (getXY(cx, cy, name)) {
474 StripConnector * sc = getStripConnector(cx, cy);
475 bool vertical = name.contains("v");
476 if (vertical) {
477 if (sc->down != NULL) sc->down->setRemoved(true);
478 }
479 else {
480 if (sc->right != NULL) sc->right->setRemoved(true);
481 }
482 }
483 }
484
485 reinitBuses(false);
486 }
487
genModuleID(QMap<QString,QString> & currPropsMap)488 QString Stripboard::genModuleID(QMap<QString, QString> & currPropsMap)
489 {
490 QString size = currPropsMap.value("size");
491 return size + ModuleIDNames::Stripboard2ModuleIDName;
492 }
493
initCutting(Stripbit *)494 void Stripboard::initCutting(Stripbit *)
495 {
496 m_beforeCut.clear();
497 foreach (QGraphicsItem * item, childItems()) {
498 Stripbit * stripbit = dynamic_cast<Stripbit *>(item);
499 if (stripbit == NULL) continue;
500
501 stripbit->setChanged(false);
502 if (stripbit->removed()) {
503 m_beforeCut += stripbit->makeRemovedString();
504 }
505 }
506 }
507
appendConnectors(QList<ConnectorItem * > & connectorItems,ConnectorItem * connectorItem)508 void appendConnectors(QList<ConnectorItem *> & connectorItems, ConnectorItem * connectorItem) {
509 if (connectorItem == NULL) return;
510
511 foreach (ConnectorItem * ci, connectorItem->connectedToItems()) {
512 connectorItems.append(ci);
513 }
514 }
515
reinitBuses(bool triggerUndo)516 void Stripboard::reinitBuses(bool triggerUndo)
517 {
518 if (triggerUndo) {
519 QString afterCut;
520 QSet<ConnectorItem *> affectedConnectors;
521 int changeCount = 0;
522 bool connect = true;
523 foreach (QGraphicsItem * item, childItems()) {
524 Stripbit * stripbit = dynamic_cast<Stripbit *>(item);
525 if (stripbit == NULL) continue;
526
527 if (stripbit->removed()) {
528 afterCut += stripbit->makeRemovedString();
529 }
530 }
531
532 collectTo(affectedConnectors);
533
534 InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
535 if (infoGraphicsView != NULL) {
536 QString changeType = (connect) ? tr("Restored") : tr("Cut") ;
537 QString changeText = tr("%1 %n strip(s)", "", changeCount).arg(changeType);
538 QList<ConnectorItem *> affected = affectedConnectors.toList();
539 infoGraphicsView->changeBus(this, connect, m_beforeCut, afterCut, affected, changeText, m_layout, m_layout); // affectedConnectors is used for updating ratsnests; it's just a wild guess
540 }
541
542 return;
543 }
544 if (viewID() != ViewLayer::BreadboardView) return;
545
546 foreach (BusShared * busShared, m_buses) delete busShared;
547 m_buses.clear();
548
549 foreach (QGraphicsItem * item, childItems()) {
550 Stripbit * stripbit = dynamic_cast<Stripbit *>(item);
551 if (stripbit == NULL) {
552 ConnectorItem * connectorItem = dynamic_cast<ConnectorItem *>(item);
553 if (connectorItem == NULL) continue;
554
555 connectorItem->connector()->connectorShared()->setBus(NULL);
556 connectorItem->connector()->setBus(NULL);
557 continue;
558 }
559
560 }
561
562 QString busPropertyString;
563 foreach (QGraphicsItem * item, childItems()) {
564 Stripbit * stripbit = dynamic_cast<Stripbit *>(item);
565 if (stripbit == NULL) continue;
566
567 if (stripbit->removed()) {
568 busPropertyString += stripbit->makeRemovedString();
569 }
570 }
571
572 QList<ConnectorItem *> visited;
573 for (int iy = 0; iy < m_y; iy++) {
574 for (int ix = 0; ix < m_x; ix++) {
575 StripConnector * sc = getStripConnector(ix, iy);
576 if (visited.contains(sc->connectorItem)) continue;
577
578 QList<ConnectorItem *> connected;
579 collectConnected(ix, iy, connected);
580 visited.append(connected);
581 nextBus(connected);
582 }
583 }
584
585 modelPart()->clearBuses();
586 modelPart()->initBuses();
587 modelPart()->setLocalProp("buses", busPropertyString);
588
589
590
591 QList<ConnectorItem *> visited2;
592 foreach (ConnectorItem * connectorItem, cachedConnectorItems()) {
593 connectorItem->restoreColor(visited2);
594 }
595
596 update();
597 }
598
collectConnected(int ix,int iy,QList<ConnectorItem * > & connected)599 void Stripboard::collectConnected(int ix, int iy, QList<ConnectorItem *> & connected) {
600 StripConnector * sc = getStripConnector(ix, iy);
601 if (connected.contains(sc->connectorItem)) return;
602
603 connected << sc->connectorItem;
604
605 if (sc->right != NULL && !sc->right->removed()) {
606 collectConnected(ix + 1, iy, connected);
607
608 }
609 if (sc->down != NULL && !sc->down->removed()) {
610 collectConnected(ix, iy + 1, connected);
611 }
612
613 StripConnector * left = ix > 0 ? getStripConnector(ix - 1, iy) : NULL;
614 if (left != NULL && left->right != NULL && !left->right->removed()) {
615 collectConnected(ix - 1, iy, connected);
616 }
617
618 StripConnector * up = iy > 0 ? getStripConnector(ix, iy - 1) : NULL;
619 if (up != NULL && up->down != NULL && !up->down->removed()) {
620 collectConnected(ix, iy - 1, connected);
621 }
622 }
623
nextBus(QList<ConnectorItem * > & soFar)624 void Stripboard::nextBus(QList<ConnectorItem *> & soFar)
625 {
626 if (soFar.count() > 1) {
627 BusShared * busShared = new BusShared(QString::number(m_buses.count()));
628 m_buses.append(busShared);
629 foreach (ConnectorItem * connectorItem, soFar) {
630 busShared->addConnectorShared(connectorItem->connector()->connectorShared());
631 }
632 }
633 soFar.clear();
634 }
635
setProp(const QString & prop,const QString & value)636 void Stripboard::setProp(const QString & prop, const QString & value)
637 {
638 if (prop.compare("buses") == 0) {
639 QStringList removed = value.split(" ", QString::SkipEmptyParts);
640 foreach (QGraphicsItem * item, childItems()) {
641 Stripbit * stripbit = dynamic_cast<Stripbit *>(item);
642 if (stripbit == NULL) continue;
643
644 QString removedString = stripbit->makeRemovedString();
645 removedString.chop(1); // remove trailing space
646 bool remove = removed.contains(removedString);
647 stripbit->setRemoved(remove);
648 if (remove) {
649 removed.removeOne(removedString);
650 }
651 }
652
653 reinitBuses(false);
654 return;
655 }
656
657 if (prop.compare("layout") == 0) {
658 m_layout = value;
659 }
660
661 Perfboard::setProp(prop, value);
662 }
663
getConnectedColor(ConnectorItem * ci,QBrush & brush,QPen & pen,double & opacity,double & negativePenWidth,bool & negativeOffsetRect)664 void Stripboard::getConnectedColor(ConnectorItem * ci, QBrush &brush, QPen &pen, double & opacity, double & negativePenWidth, bool & negativeOffsetRect) {
665 Perfboard::getConnectedColor(ci, brush, pen, opacity, negativePenWidth, negativeOffsetRect);
666 opacity *= .66667;
667 }
668
restoreRowColors(Stripbit * stripbit)669 void Stripboard::restoreRowColors(Stripbit * stripbit)
670 {
671 // TODO: find a quick way to update the buses (and connectorItem colors) in just the row...
672 Q_UNUSED(stripbit);
673 //int y = stripbit->y();
674 //stripbit = m_firstColumn.at(y);
675
676
677 //ci->restoreColor(false, 0, false);
678 }
679
getRowLabel()680 QString Stripboard::getRowLabel() {
681 return tr("rows");
682 }
683
getColumnLabel()684 QString Stripboard::getColumnLabel() {
685 return tr("columns");
686 }
687
makeInitialPath()688 void Stripboard::makeInitialPath() {
689 ConnectorItem * ciFirst = NULL;
690 ConnectorItem * ciNextH = NULL;
691 ConnectorItem * ciNextV = NULL;
692 foreach (ConnectorItem * ci, cachedConnectorItems()) {
693 int cx, cy;
694 getXY(cx, cy, ci->connectorSharedName());
695 if (cy == 0 && cx == 0) {
696 ciFirst = ci;
697 break;
698 }
699 }
700 foreach (ConnectorItem * ci, cachedConnectorItems()) {
701 int cx, cy;
702 getXY(cx, cy, ci->connectorSharedName());
703 if (cy == 0 && cx == 1) {
704 ciNextH = ci;
705 if (ciNextV) break;
706 }
707 else if (cy == 1 && cx == 0) {
708 ciNextV = ci;
709 if (ciNextH) break;
710 }
711 }
712
713 if (ciFirst == NULL) return;
714 if (ciNextH == NULL) return;
715 if (ciNextV == NULL) return;
716
717 QRectF r1 = ciFirst->rect();
718 QRectF rh = ciNextH->rect();
719
720 double h = r1.height();
721 double w = rh.center().x() - r1.center().x();
722
723 r1.moveTo(-(r1.width() / 2), 0);
724 rh.moveTo(w - (rh.width() / 2), 0);
725
726 HPath.addRect(0, 0, w / 2, h);
727 HPath.arcTo(r1, 90, -180);
728 HPath.addRect(w / 2, 0, w / 2, h);
729 HPath.moveTo(w, 0);
730 HPath.arcTo(rh, 90, 180);
731
732 r1 = ciFirst->rect();
733 QRectF rv = ciNextV->rect();
734
735 h = rv.center().y() - r1.center().y();
736 w = r1.width();
737
738 r1.moveTo(0, -(r1.height() / 2));
739 rv.moveTo(0, h - (rv.height() / 2));
740
741 VPath.addRect(0, 0, w, h / 2);
742 VPath.arcTo(r1, 0, -180);
743 VPath.addRect(0, h / 2, w, h / 2);
744 VPath.moveTo(0, h);
745 VPath.arcTo(rv, 0, 180);
746 }
747
getStripConnector(int ix,int iy)748 StripConnector * Stripboard::getStripConnector(int ix, int iy) {
749 return m_strips.at((iy * m_x) + ix);
750 }
751
swapEntry(const QString & text)752 void Stripboard::swapEntry(const QString & text) {
753
754 FamilyPropertyComboBox * comboBox = qobject_cast<FamilyPropertyComboBox *>(sender());
755 if (comboBox == NULL) return;
756
757 if (comboBox->prop().compare("layout", Qt::CaseInsensitive) == 0) {
758 InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
759 if (infoGraphicsView == NULL) return;
760
761 QString afterCut;
762 if (text.compare(HorizontalString, Qt::CaseInsensitive) == 0) {
763 foreach (QGraphicsItem * item, childItems()) {
764 Stripbit * stripbit = dynamic_cast<Stripbit *>(item);
765 if (stripbit == NULL) continue;
766
767 if (!stripbit->horizontal()) {
768 afterCut += stripbit->makeRemovedString();
769 }
770 }
771 }
772 else if (text.compare(VerticalString, Qt::CaseInsensitive) == 0) {
773 foreach (QGraphicsItem * item, childItems()) {
774 Stripbit * stripbit = dynamic_cast<Stripbit *>(item);
775 if (stripbit == NULL) continue;
776
777 if (stripbit->horizontal()) {
778 afterCut += stripbit->makeRemovedString();
779 }
780 }
781 }
782 else {
783 for (int i = 0; i < StripLayouts.count(); i++) {
784 if (StripLayouts.at(i).name.compare(text) == 0) {
785 StripLayout stripLayout = StripLayouts.at(i);
786 QMap<QString, QString> propsMap;
787 propsMap.insert("size", QString("%1.%2").arg(stripLayout.columns).arg(stripLayout.rows));
788 propsMap.insert("type", "Stripboard");
789 propsMap.insert("buses", stripLayout.buses);
790 propsMap.insert("layout", text);
791 InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
792 if (infoGraphicsView != NULL) {
793 infoGraphicsView->swap(family(), "size", propsMap, this);
794 }
795 return;
796 }
797 }
798 }
799
800 if (!afterCut.isEmpty()) {
801 initCutting(NULL);
802 QString changeText = tr("%1 layout").arg(text);
803 QSet<ConnectorItem *> affectedConnectors;
804 collectTo(affectedConnectors);
805 QList<ConnectorItem *> affected = affectedConnectors.toList();
806 infoGraphicsView->changeBus(this, true, m_beforeCut, afterCut, affected, changeText, m_layout, text); // affectedConnectors is used for updating ratsnests; it's just a wild guess
807 }
808
809 return;
810 }
811
812
813 Perfboard::swapEntry(text);
814 }
815
collectTo(QSet<ConnectorItem * > & affectedConnectors)816 void Stripboard::collectTo(QSet<ConnectorItem *> & affectedConnectors) {
817 foreach (ConnectorItem * connectorItem, cachedConnectorItems()) {
818 foreach (ConnectorItem * toConnectorItem, connectorItem->connectedToItems()) {
819 affectedConnectors.insert(toConnectorItem);
820 }
821 }
822 }
823
initStripLayouts()824 void Stripboard::initStripLayouts() {
825 QFile file(":/resources/templates/stripboards.xml");
826 QString errorStr;
827 int errorLine;
828 int errorColumn;
829 QDomDocument domDocument;
830
831 if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
832 DebugDialog::debug(QString("unable to parse stripboards.xml: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn));
833 return;
834 }
835
836 QDomElement root = domDocument.documentElement();
837 QDomElement stripboard = root.firstChildElement("stripboard");
838 while (!stripboard.isNull()) {
839 QString name = stripboard.attribute("name");
840 bool rok;
841 int rows = stripboard.attribute("rows").toInt(&rok);
842 bool cok;
843 int columns = stripboard.attribute("columns").toInt(&cok);
844 QString buses = stripboard.attribute("buses");
845 stripboard = stripboard.nextSiblingElement("stripboard");
846 if (!rok) continue;
847 if (!cok) continue;
848 if (name.isEmpty() || buses.isEmpty()) continue;
849
850 StripLayout stripLayout(name, rows, columns, buses);
851 StripLayouts.append(stripLayout);
852 }
853 }
854
collectValues(const QString & family,const QString & prop,QString & value)855 QStringList Stripboard::collectValues(const QString & family, const QString & prop, QString & value) {
856 QStringList values = Perfboard::collectValues(family, prop, value);
857
858 if (prop.compare("layout", Qt::CaseInsensitive) == 0) {
859 foreach (StripLayout stripLayout, StripLayouts) {
860 values.append(stripLayout.name);
861 value = m_layout;
862 }
863 }
864
865 return values;
866 }
867