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 of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
18
19 ********************************************************************
20
21 $Revision: 6998 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $
24
25 ********************************************************************/
26
27 #include "paletteitem.h"
28 #include "../debugdialog.h"
29 #include "../viewgeometry.h"
30 #include "../sketch/infographicsview.h"
31 #include "layerkinpaletteitem.h"
32 #include "../fsvgrenderer.h"
33 #include "partlabel.h"
34 #include "partfactory.h"
35 #include "../commands.h"
36 #include "../connectors/connectoritem.h"
37 #include "../connectors/connector.h"
38 #include "../connectors/svgidlayer.h"
39 #include "../layerattributes.h"
40 #include "../dialogs/pinlabeldialog.h"
41 #include "../utils/folderutils.h"
42 #include "../utils/textutils.h"
43 #include "../utils/graphicsutils.h"
44 #include "../utils/familypropertycombobox.h"
45 #include "../svg/svgfilesplitter.h"
46 #include "wire.h"
47
48 #include <QGraphicsSceneMouseEvent>
49 #include <QSvgRenderer>
50 #include <QtDebug>
51 #include <QPainter>
52 #include <QDomElement>
53 #include <QDir>
54 #include <QMessageBox>
55 #include <QPushButton>
56 #include <QVBoxLayout>
57 #include <QRegExp>
58 #include <QGroupBox>
59 #include <QLabel>
60 #include <limits>
61
62 /////////////////////////////////////////////////
63
currentUnits()64 QString HoleSettings::currentUnits() {
65 if (mmRadioButton->isChecked()) return QObject::tr("mm");
66 return QObject::tr("in");
67 }
68
holeSize()69 QString HoleSettings::holeSize() {
70 return QString("%1,%2").arg(holeDiameter).arg(ringThickness);
71 }
72
73 /////////////////////////////////////////////////
74
75 static QRegExp LabelFinder("id=['|\"]label['|\"]");
76
77 static bool ByIDParseSuccessful = true;
78
79 static QRegExp ConnectorFinder("connector\\d+pin");
80 const QString PaletteItem::HoleSizePrefix("_hs_");
81
findNumber(const QString & string)82 int findNumber(const QString & string) {
83 int ix = string.indexOf(IntegerFinder);
84 if (ix < 0) {
85 return -1;
86 }
87
88 int result = IntegerFinder.cap(0).toInt();
89 int length = IntegerFinder.cap(0).length();
90
91 int jx = string.lastIndexOf(IntegerFinder);
92 if (jx >= ix + length) {
93 return -1;
94 }
95
96 return result;
97 }
98
byID(Connector * c1,Connector * c2)99 bool byID(Connector * c1, Connector * c2)
100 {
101 int i1 = findNumber(c1->connectorSharedID());
102 if (i1 < 0) {
103 ByIDParseSuccessful = false;
104 return true;
105 }
106 int i2 = findNumber(c2->connectorSharedID());
107 if (i2 < 0) {
108 ByIDParseSuccessful = false;
109 return true;
110 }
111
112 if (i2 == i1 && c1 != c2) {
113 // should not be two connectors with the same number
114 ByIDParseSuccessful = false;
115 return true;
116 }
117
118 return i1 <= i2;
119 }
120
121 /////////////////////////////////////////////////
122
PaletteItem(ModelPart * modelPart,ViewLayer::ViewID viewID,const ViewGeometry & viewGeometry,long id,QMenu * itemMenu,bool doLabel)123 PaletteItem::PaletteItem( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel)
124 : PaletteItemBase(modelPart, viewID, viewGeometry, id, itemMenu)
125 {
126 m_flipCount = 0;
127 if(doLabel) {
128 m_partLabel = new PartLabel(this, NULL);
129 m_partLabel->setVisible(false);
130 } else {
131 m_partLabel = NULL;
132 }
133 }
134
~PaletteItem()135 PaletteItem::~PaletteItem() {
136 if (m_partLabel) {
137 delete m_partLabel;
138 }
139 }
140
renderImage(ModelPart * modelPart,ViewLayer::ViewID viewID,const LayerHash & viewLayers,ViewLayer::ViewLayerID viewLayerID,bool doConnectors,QString & error)141 bool PaletteItem::renderImage(ModelPart * modelPart, ViewLayer::ViewID viewID, const LayerHash & viewLayers, ViewLayer::ViewLayerID viewLayerID, bool doConnectors, QString & error) {
142 LayerAttributes layerAttributes;
143 initLayerAttributes(layerAttributes, viewID, viewLayerID, viewLayerPlacement(), doConnectors, true);
144 bool result = setUpImage(modelPart, viewLayers, layerAttributes);
145 error = layerAttributes.error;
146
147 m_syncMoved = this->pos();
148 return result;
149 }
150
loadLayerKin(const LayerHash & viewLayers,ViewLayer::ViewLayerPlacement viewLayerPlacement)151 void PaletteItem::loadLayerKin(const LayerHash & viewLayers, ViewLayer::ViewLayerPlacement viewLayerPlacement) {
152
153 if (m_modelPart == NULL) return;
154
155 ModelPartShared * modelPartShared = m_modelPart->modelPartShared();
156 if (modelPartShared == NULL) return;
157
158 qint64 id = m_id + 1;
159 ViewGeometry viewGeometry = m_viewGeometry;
160 viewGeometry.setZ(-1);
161
162
163 foreach (ViewLayer::ViewLayerID viewLayerID, viewLayers.keys()) {
164 if (viewLayerID == m_viewLayerID) continue;
165 if (!m_modelPart->hasViewFor(m_viewID, viewLayerID)) continue;
166
167 if (m_modelPart->itemType() == ModelPart::CopperFill) {
168 if (viewLayerPlacement == ViewLayer::NewTop) {
169 if (ViewLayer::bottomLayers().contains(viewLayerID)) continue;
170 }
171 else {
172 if (ViewLayer::topLayers().contains(viewLayerID)) continue;
173 }
174 }
175 else if (m_modelPart->flippedSMD()) {
176 if (viewLayerPlacement == ViewLayer::NewTop) {
177 if (ViewLayer::bottomLayers().contains(viewLayerID)) continue;
178 }
179 else {
180 if (ViewLayer::topLayers().contains(viewLayerID)) continue;
181 }
182 }
183 else if (m_modelPart->itemType() == ModelPart::Part) {
184 // through hole part
185 if (ViewLayer::silkLayers().contains(viewLayerID)) {
186 if (viewLayerPlacement == ViewLayer::NewTop) {
187 if (ViewLayer::bottomLayers().contains(viewLayerID)) continue;
188 }
189 else {
190 if (ViewLayer::topLayers().contains(viewLayerID)) continue;
191 }
192 }
193
194 }
195
196 makeOneKin(id, viewLayerID, viewLayerPlacement, viewGeometry, viewLayers);
197 }
198
199 }
200
makeOneKin(qint64 & id,ViewLayer::ViewLayerID viewLayerID,ViewLayer::ViewLayerPlacement viewLayerPlacement,ViewGeometry & viewGeometry,const LayerHash & viewLayers)201 void PaletteItem::makeOneKin(qint64 & id, ViewLayer::ViewLayerID viewLayerID, ViewLayer::ViewLayerPlacement viewLayerPlacement, ViewGeometry & viewGeometry, const LayerHash & viewLayers) {
202 LayerAttributes layerAttributes;
203 initLayerAttributes(layerAttributes, m_viewID, viewLayerID, viewLayerPlacement, true, true);
204
205 LayerKinPaletteItem * lkpi = newLayerKinPaletteItem(this, m_modelPart, viewGeometry, id, m_itemMenu, viewLayers, layerAttributes);
206 if (lkpi->ok()) {
207 DebugDialog::debug(QString("adding layer kin %1 %2 %3 %4")
208 .arg(id).arg(m_viewID).arg(viewLayerID)
209 .arg((long) lkpi, 0, 16)
210 );
211 addLayerKin(lkpi);
212 id++;
213 }
214 else {
215 delete lkpi;
216 }
217 }
218
219
addLayerKin(LayerKinPaletteItem * lkpi)220 void PaletteItem::addLayerKin(LayerKinPaletteItem * lkpi) {
221 m_layerKin.append(lkpi);
222 }
223
removeLayerKin()224 void PaletteItem::removeLayerKin() {
225 // assumes paletteitem is still in scene
226 for (int i = 0; i < m_layerKin.size(); i++) {
227 //DebugDialog::debug(QString("removing kin %1 %2").arg(m_layerKin[i]->id()).arg(m_layerKin[i]->z()));
228 this->scene()->removeItem(m_layerKin[i]);
229 delete m_layerKin[i];
230 }
231
232 m_layerKin.clear();
233 }
234
syncKinSelection(bool selected,PaletteItemBase * originator)235 void PaletteItem::syncKinSelection(bool selected, PaletteItemBase * originator) {
236 PaletteItemBase::syncKinSelection(selected, originator);
237
238 foreach (ItemBase * lkpi, m_layerKin) {
239 if (lkpi != originator && lkpi->isSelected() != selected) {
240 qobject_cast<LayerKinPaletteItem *>(lkpi)->blockItemSelectedChange(selected);
241 lkpi->setSelected(selected);
242 }
243 }
244
245 if (this != originator && this->isSelected() != selected) {
246 this->blockItemSelectedChange(selected);
247 this->setSelected(selected);
248 }
249 }
250
itemChange(GraphicsItemChange change,const QVariant & value)251 QVariant PaletteItem::itemChange(GraphicsItemChange change, const QVariant &value)
252 {
253 //DebugDialog::debug(QString("chief item change %1 %2").arg(this->id()).arg(change));
254 if (m_layerKin.count() > 0) {
255 if (change == ItemSelectedChange) {
256 bool selected = value.toBool();
257 if (m_blockItemSelectedChange && m_blockItemSelectedValue == selected) {
258 m_blockItemSelectedChange = false;
259 }
260 else {
261 syncKinSelection(selected, this);
262 }
263 }
264 //else if (change == ItemVisibleHasChanged && value.toBool()) {
265 //this->setSelected(syncSelected());
266 //this->setPos(m_offset + syncMoved());
267 //}
268 else if (change == ItemPositionHasChanged) {
269 this->syncKinMoved(this->m_offset, value.toPointF());
270 }
271 }
272
273 if (m_partLabel && m_partLabel->initialized()) {
274 if (change == ItemPositionHasChanged) {
275 m_partLabel->ownerMoved(value.toPointF());
276 }
277 else if (change == ItemSelectedChange) {
278 m_partLabel->update();
279 }
280 }
281
282 return PaletteItemBase::itemChange(change, value);
283 }
284
layerKin()285 const QList<class ItemBase *> & PaletteItem::layerKin()
286 {
287 return m_layerKin;
288 }
289
rotateItem(double degrees,bool includeRatsnest)290 void PaletteItem::rotateItem(double degrees, bool includeRatsnest) {
291 PaletteItemBase::rotateItem(degrees, includeRatsnest);
292 for (int i = 0; i < m_layerKin.count(); i++) {
293 m_layerKin[i]->rotateItem(degrees, includeRatsnest);
294 }
295 }
296
flipItem(Qt::Orientations orientation)297 void PaletteItem::flipItem(Qt::Orientations orientation) {
298 PaletteItemBase::flipItem(orientation);
299 foreach (ItemBase * lkpi, m_layerKin) {
300 lkpi->flipItem(orientation);
301 }
302 }
303
transformItem2(const QMatrix & matrix)304 void PaletteItem::transformItem2(const QMatrix & matrix) {
305 PaletteItemBase::transformItem2(matrix);
306 foreach (ItemBase * lkpi, m_layerKin) {
307 lkpi->transformItem2(matrix);
308 }
309 }
310
setTransforms()311 void PaletteItem::setTransforms() {
312 // only ever called when loading from file
313 // jrc 14 july 2013: this call seems redundant--transforms have already been set up by now
314
315 QTransform transform = getViewGeometry().transform();
316 if (transform.isIdentity()) return;
317
318 //debugInfo("set transforms " + TextUtils::svgMatrix(transform));
319 //debugInfo("\t " + TextUtils::svgMatrix(this->transform()));
320
321
322 setTransform(transform);
323 for (int i = 0; i < m_layerKin.count(); i++) {
324 //debugInfo("\t " + TextUtils::svgMatrix(m_layerKin[i]->getViewGeometry().transform()));
325 //debugInfo("\t " + TextUtils::svgMatrix(m_layerKin[i]->transform()));
326 m_layerKin[i]->setTransform2(m_layerKin[i]->getViewGeometry().transform());
327 }
328 }
329
moveItem(ViewGeometry & viewGeometry)330 void PaletteItem::moveItem(ViewGeometry & viewGeometry) {
331 PaletteItemBase::moveItem(viewGeometry);
332 for (int i = 0; i < m_layerKin.count(); i++) {
333 m_layerKin[i]->moveItem(viewGeometry);
334 }
335 }
336
setItemPos(QPointF & loc)337 void PaletteItem::setItemPos(QPointF & loc) {
338 PaletteItemBase::setItemPos(loc);
339 for (int i = 0; i < m_layerKin.count(); i++) {
340 m_layerKin[i]->setItemPos(loc);
341 }
342 }
343
updateConnections(bool includeRatsnest,QList<ConnectorItem * > & already)344 void PaletteItem::updateConnections(bool includeRatsnest, QList<ConnectorItem *> & already) {
345 updateConnectionsAux(includeRatsnest, already);
346 foreach (ItemBase * lkpi, m_layerKin) {
347 lkpi->updateConnectionsAux(includeRatsnest, already);
348 }
349 }
350
collectFemaleConnectees(QSet<ItemBase * > & items)351 bool PaletteItem::collectFemaleConnectees(QSet<ItemBase *> & items) {
352 bool hasMale = PaletteItemBase::collectFemaleConnectees(items);
353 foreach (ItemBase * lkpi, m_layerKin) {
354 if (lkpi->collectFemaleConnectees(items)) {
355 hasMale = true;
356 }
357 }
358 return hasMale;
359 }
360
collectWireConnectees(QSet<Wire * > & wires)361 void PaletteItem::collectWireConnectees(QSet<Wire *> & wires) {
362 PaletteItemBase::collectWireConnectees(wires);
363 foreach (ItemBase * lkpi, m_layerKin) {
364 qobject_cast<LayerKinPaletteItem *>(lkpi)->collectWireConnectees(wires);
365 }
366 }
367
mousePressEventK(PaletteItemBase * originalItem,QGraphicsSceneMouseEvent * event)368 bool PaletteItem::mousePressEventK(PaletteItemBase * originalItem, QGraphicsSceneMouseEvent *event) {
369 //DebugDialog::debug("layerkinchief got mouse press event");
370 /*
371
372 if (acceptsMousePressConnectorEvent(NULL, event) && isBuriedConnectorHit(event) ) return;
373 foreach(LayerKinPaletteItem * lkpi, m_layerKin) {
374 if (lkpi->isBuriedConnectorHit(event)) return;
375 }
376 */
377
378 return PaletteItemBase::mousePressEventK(originalItem, event);
379 }
380
mousePressEvent(QGraphicsSceneMouseEvent * event)381 void PaletteItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
382 {
383 InfoGraphicsView *infographics = InfoGraphicsView::getInfoGraphicsView(this);
384 if (infographics != NULL && infographics->spaceBarIsPressed()) {
385 event->ignore();
386 return;
387 }
388
389 mousePressEventK(this, event);
390 }
391
392
syncKinMoved(QPointF offset,QPointF newPos)393 void PaletteItem::syncKinMoved(QPointF offset, QPointF newPos) {
394 Q_UNUSED(offset); // ignore offset--should all be zeros now
395
396 //DebugDialog::debug(QString("sync kin moved %1 %2").arg(offset.x()).arg(offset.y()) );
397 //m_syncMoved = pos - offset;
398 //if (newPos != pos()) {
399 setPos(newPos);
400 foreach (ItemBase * lkpi, m_layerKin) {
401 lkpi->setPos(newPos);
402 }
403 //}
404 }
405
setInstanceTitle(const QString & title,bool initial)406 void PaletteItem::setInstanceTitle(const QString& title, bool initial) {
407 ItemBase::setInstanceTitle(title, initial);
408 foreach (ItemBase * lkpi, m_layerKin) {
409 lkpi->setInstanceTitle(title, initial);
410 }
411 }
412
setHidden(bool hide)413 void PaletteItem::setHidden(bool hide) {
414 ItemBase::setHidden(hide);
415 figureHover();
416 }
417
setInactive(bool inactivate)418 void PaletteItem::setInactive(bool inactivate) {
419 ItemBase::setInactive(inactivate);
420 figureHover();
421 }
422
figureHover()423 void PaletteItem::figureHover() {
424 setAcceptHoverEvents(true);
425 setAcceptedMouseButtons(ALLMOUSEBUTTONS);
426 foreach(ItemBase * lkpi, m_layerKin) {
427 lkpi->setAcceptHoverEvents(true);
428 lkpi->setAcceptedMouseButtons(ALLMOUSEBUTTONS);
429 }
430 }
431
clearModelPart()432 void PaletteItem::clearModelPart() {
433 foreach (ItemBase * lkpi, m_layerKin) {
434 lkpi->setModelPart(NULL);
435 }
436 ItemBase::clearModelPart();
437 }
438
resetID()439 void PaletteItem::resetID() {
440 ItemBase::resetID();
441 foreach (ItemBase * lkpi, m_layerKin) {
442 lkpi->resetID();
443 }
444 }
445
slamZ(double z)446 void PaletteItem::slamZ(double z) {
447 PaletteItemBase::slamZ(z);
448 foreach (ItemBase * lkpi, m_layerKin) {
449 lkpi->slamZ(z);
450 }
451 }
452
resetImage(InfoGraphicsView * infoGraphicsView)453 void PaletteItem::resetImage(InfoGraphicsView * infoGraphicsView) {
454 foreach (Connector * connector, modelPart()->connectors()) {
455 connector->unprocess(this->viewID(), this->viewLayerID());
456 }
457
458 LayerAttributes layerAttributes;
459 initLayerAttributes(layerAttributes, viewID(), viewLayerID(), viewLayerPlacement(), true, !m_selectionShape.isEmpty());
460 this->setUpImage(modelPart(), infoGraphicsView->viewLayers(), layerAttributes);
461
462 foreach (ItemBase * layerKin, m_layerKin) {
463 resetKinImage(layerKin, infoGraphicsView);
464 }
465 }
466
resetKinImage(ItemBase * layerKin,InfoGraphicsView * infoGraphicsView)467 void PaletteItem::resetKinImage(ItemBase * layerKin, InfoGraphicsView * infoGraphicsView)
468 {
469 foreach (Connector * connector, modelPart()->connectors()) {
470 connector->unprocess(layerKin->viewID(), layerKin->viewLayerID());
471 }
472 LayerAttributes layerAttributes;
473 initLayerAttributes(layerAttributes, layerKin->viewID(), layerKin->viewLayerID(), layerKin->viewLayerPlacement(), true, !layerKin->selectionShape().isEmpty());
474 qobject_cast<PaletteItemBase *>(layerKin)->setUpImage(modelPart(), infoGraphicsView->viewLayers(), layerAttributes);
475 }
476
genFZP(const QString & moduleid,const QString & templateName,int minPins,int maxPins,int steps,bool smd)477 QString PaletteItem::genFZP(const QString & moduleid, const QString & templateName, int minPins, int maxPins, int steps, bool smd)
478 {
479 QString FzpTemplate = "";
480 QString ConnectorFzpTemplate = "";
481
482
483 QFile file1(QString(":/resources/templates/%1.txt").arg(templateName));
484 file1.open(QFile::ReadOnly);
485 FzpTemplate = file1.readAll();
486 file1.close();
487 if (smd) {
488 FzpTemplate.replace("<layer layerId=\"copper0\"/>", "");
489 }
490
491 QFile file2(":/resources/templates/generic_sip_connectorFzpTemplate.txt");
492 file2.open(QFile::ReadOnly);
493 ConnectorFzpTemplate = file2.readAll();
494 file2.close();
495 if (smd) {
496 ConnectorFzpTemplate.replace("<p layer=\"copper0\" svgId=\"connector%1pin\"/>", "");
497 }
498
499 QStringList ss = moduleid.split("_");
500 int count = 0;
501 foreach (QString s, ss) {
502 bool ok;
503 int c = s.toInt(&ok);
504 if (ok) {
505 count = c;
506 break;
507 }
508 }
509
510 if (count > maxPins || count < minPins) return "";
511 if (count % steps != 0) return "";
512
513 QString middle;
514
515 for (int i = 0; i < count; i++) {
516 middle += ConnectorFzpTemplate.arg(i).arg(i + 1);
517 }
518
519 return FzpTemplate.arg(count).arg(middle);
520 }
521
collectExtraInfo(QWidget * parent,const QString & family,const QString & prop,const QString & value,bool swappingEnabled,QString & returnProp,QString & returnValue,QWidget * & returnWidget,bool & hide)522 bool PaletteItem::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide)
523 {
524 if (prop.compare("editable pin labels", Qt::CaseInsensitive) == 0 && value.compare("true") == 0) {
525 returnProp = "";
526 returnValue = value;
527
528 QPushButton * button = new QPushButton(tr("Edit Pin Labels"));
529 button->setObjectName("infoViewButton");
530 connect(button, SIGNAL(pressed()), this, SLOT(openPinLabelDialog()));
531 button->setEnabled(swappingEnabled);
532
533 returnWidget = button;
534
535 return true;
536 }
537
538 bool result = PaletteItemBase::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide);
539
540 if (prop.compare("layer") == 0 && modelPart()->flippedSMD() && returnWidget != NULL) {
541 bool disabled = true;
542 InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
543 if (infoGraphicsView && infoGraphicsView->boardLayers() == 2) disabled = false;
544 returnWidget->setDisabled(disabled);
545 }
546
547 return result;
548 }
549
collectValues(const QString & family,const QString & prop,QString & value)550 QStringList PaletteItem::collectValues(const QString & family, const QString & prop, QString & value) {
551 if (prop.compare("layer") == 0) {
552 if (modelPart()->flippedSMD()) {
553 QStringList values = PaletteItemBase::collectValues(family, prop, value);
554 for (int ix = values.count() - 1; ix >= 0; ix--) {
555 if (values.at(ix).isEmpty()) {
556 values.removeAt(ix);
557 }
558 }
559 if (values.count() < 2) {
560 ItemBase * itemBase = modelPart()->viewItem(ViewLayer::PCBView);
561 if (itemBase) {
562 values.clear();
563 values << TranslatedPropertyNames.value("bottom") << TranslatedPropertyNames.value("top");
564 if (itemBase->viewLayerID() == ViewLayer::Copper0) {
565 value = values.at(0);
566 }
567 else {
568 value = values.at(1);
569 }
570 }
571 }
572 return values;
573 }
574
575 if (modelPart()->itemType() == ModelPart::Part) {
576 QStringList values = PaletteItemBase::collectValues(family, prop, value);
577 if (values.count() == 0) {
578 ItemBase * itemBase = modelPart()->viewItem(ViewLayer::PCBView);
579 if (itemBase) {
580 values << TranslatedPropertyNames.value("bottom") << TranslatedPropertyNames.value("top");
581 if (itemBase->viewLayerPlacement() == ViewLayer::NewBottom) {
582 value = values.at(0);
583 }
584 else {
585 value = values.at(1);
586 }
587 }
588 }
589 return values;
590 }
591 }
592
593 return PaletteItemBase::collectValues(family, prop, value);
594 }
595
596
openPinLabelDialog()597 void PaletteItem::openPinLabelDialog() {
598 InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
599 if (infoGraphicsView == NULL) {
600 QMessageBox::warning(
601 NULL,
602 tr("Fritzing"),
603 tr("Unable to proceed; unable to find top level view.")
604 );
605 return;
606 }
607
608 QStringList labels;
609 QList<Connector *> sortedConnectors = sortConnectors();
610 if (sortedConnectors.count() == 0) {
611 QMessageBox::warning(
612 NULL,
613 tr("Fritzing"),
614 tr("Unable to proceed; part connectors do no have standard IDs.")
615 );
616 return;
617 }
618
619 foreach (Connector * connector, sortedConnectors) {
620 labels.append(connector->connectorSharedName());
621 }
622
623 QString chipLabel = modelPart()->localProp("chip label").toString();
624 if (chipLabel.isEmpty()) {
625 chipLabel = instanceTitle();
626 }
627
628 bool singleRow = isSingleRow(cachedConnectorItems());
629 PinLabelDialog pinLabelDialog(labels, singleRow, chipLabel, modelPart()->isCore(), NULL);
630 int result = pinLabelDialog.exec();
631 if (result != QDialog::Accepted) return;
632
633 QStringList newLabels = pinLabelDialog.labels();
634 if (newLabels.count() != sortedConnectors.count()) {
635 QMessageBox::warning(
636 NULL,
637 tr("Fritzing"),
638 tr("Label mismatch. Nothing was saved.")
639 );
640 return;
641 }
642
643 infoGraphicsView->renamePins(this, labels, newLabels, singleRow);
644 }
645
renamePins(const QStringList & labels,bool singleRow)646 void PaletteItem::renamePins(const QStringList & labels, bool singleRow)
647 {
648 QList<Connector *> sortedConnectors = sortConnectors();
649 for (int i = 0; i < labels.count(); i++) {
650 Connector * connector = sortedConnectors.at(i);
651 connector->setConnectorLocalName(labels.at(i));
652 }
653
654 InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
655 infoGraphicsView->changePinLabels(this, singleRow);
656 }
657
isSingleRow(const QList<ConnectorItem * > & connectorItems)658 bool PaletteItem::isSingleRow(const QList<ConnectorItem *> & connectorItems) {
659 if (connectorItems.count() == 2) {
660 // no way to tell? so default to double
661 return false;
662 }
663 else if (connectorItems.count() % 2 == 0) {
664 QPointF p = connectorItems.at(0)->sceneAdjustedTerminalPoint(NULL);
665 double slope = 0;
666 for (int i = 1; i < connectorItems.count(); i++) {
667 QPointF q = connectorItems.at(i)->sceneAdjustedTerminalPoint(NULL);
668 if (p == q) continue;
669
670 double newSlope = q.x() == p.x() ? std::numeric_limits<double>::max() : (q.y() - p.y()) / (q.x() - p.x());
671 if (i == 1) {
672 slope = newSlope;
673 }
674 else {
675 double d = qAbs(newSlope - slope);
676 if (d != 0 && d / qMax(qAbs(slope), qAbs(newSlope)) > 0.01) {
677 return false;
678 }
679 }
680 }
681 }
682
683 return true;
684 }
685
sortConnectors()686 QList<Connector *> PaletteItem::sortConnectors() {
687 QList<Connector *> sortedConnectors;
688 foreach (Connector * connector, modelPart()->connectors().values()) {
689 sortedConnectors.append(connector);
690 }
691 ByIDParseSuccessful = true;
692 qSort(sortedConnectors.begin(), sortedConnectors.end(), byID);
693 if (!ByIDParseSuccessful || sortedConnectors.count() == 0) {
694 sortedConnectors.clear();
695 }
696
697 return sortedConnectors;
698 }
699
changePinLabels(bool singleRow,bool sip)700 bool PaletteItem::changePinLabels(bool singleRow, bool sip) {
701 Q_UNUSED(singleRow);
702 Q_UNUSED(sip);
703 if (m_viewID != ViewLayer::SchematicView) return true;
704
705 return false;
706 }
707
getPinLabels(bool & hasLocal)708 QStringList PaletteItem::getPinLabels(bool & hasLocal) {
709 hasLocal = false;
710 QStringList labels;
711 QList<Connector *> sortedConnectors = sortConnectors();
712 if (sortedConnectors.count() == 0) return labels;
713
714 foreach (Connector * connector, sortedConnectors) {
715 labels.append(connector->connectorSharedName());
716 if (!connector->connectorLocalName().isEmpty()) {
717 hasLocal = true;
718 }
719 }
720
721 return labels;
722 }
723
resetConnectors()724 void PaletteItem::resetConnectors() {
725 if (m_viewID != ViewLayer::SchematicView) return;
726
727 FSvgRenderer * renderer = fsvgRenderer();
728 if (renderer == NULL) return;
729
730 QSizeF size = renderer->defaultSizeF(); // pixels
731 QRectF viewBox = renderer->viewBoxF();
732 foreach (ConnectorItem * connectorItem, cachedConnectorItems()) {
733 SvgIdLayer * svgIdLayer = connectorItem->connector()->fullPinInfo(m_viewID, m_viewLayerID);
734 if (svgIdLayer == NULL) continue;
735
736 QRectF bounds = renderer->boundsOnElement(svgIdLayer->m_svgId);
737 QPointF p(bounds.left() * size.width() / viewBox.width(), bounds.top() * size.height() / viewBox.height());
738 QRectF r = connectorItem->rect();
739 r.moveTo(p.x(), p.y());
740 connectorItem->setRect(r);
741 }
742 }
743
744
resetConnectors(ItemBase * otherLayer,FSvgRenderer * otherLayerRenderer)745 void PaletteItem::resetConnectors(ItemBase * otherLayer, FSvgRenderer * otherLayerRenderer)
746 {
747 // there's only one connector
748 foreach (Connector * connector, m_modelPart->connectors().values()) {
749 if (connector == NULL) continue;
750
751 connector->unprocess(m_viewID, m_viewLayerID);
752 SvgIdLayer * svgIdLayer = connector->fullPinInfo(m_viewID, m_viewLayerID);
753 if (svgIdLayer == NULL) continue;
754
755 bool result = fsvgRenderer()->setUpConnector(svgIdLayer, false, viewLayerPlacement());
756 if (!result) continue;
757
758 resetConnector(this, svgIdLayer);
759 }
760
761 if (otherLayer) {
762 foreach (Connector * connector, m_modelPart->connectors().values()) {
763 if (connector == NULL) continue;
764
765 connector->unprocess(m_viewID, otherLayer->viewLayerID());
766 SvgIdLayer * svgIdLayer = connector->fullPinInfo(m_viewID, otherLayer->viewLayerID());
767 if (svgIdLayer == NULL) continue;
768
769 bool result = otherLayerRenderer->setUpConnector(svgIdLayer, false, viewLayerPlacement());
770 if (!result) continue;
771
772 resetConnector(otherLayer, svgIdLayer);
773 }
774 }
775
776
777 }
778
resetConnector(ItemBase * itemBase,SvgIdLayer * svgIdLayer)779 void PaletteItem::resetConnector(ItemBase * itemBase, SvgIdLayer * svgIdLayer)
780 {
781 QList<ConnectorItem *> already;
782 foreach (ConnectorItem * connectorItem, itemBase->cachedConnectorItems()) {
783 //DebugDialog::debug(QString("via set rect %1").arg(itemBase->viewID()), svgIdLayer->m_rect);
784
785 connectorItem->setRect(svgIdLayer->rect(viewLayerPlacement()));
786 connectorItem->setTerminalPoint(svgIdLayer->point(viewLayerPlacement()));
787 connectorItem->setRadius(svgIdLayer->m_radius, svgIdLayer->m_strokeWidth);
788 connectorItem->setIsPath(svgIdLayer->m_path);
789 connectorItem->attachedMoved(false, already);
790 break;
791 }
792 }
793
collectHoleSizeInfo(const QString & defaultHoleSizeValue,QWidget * parent,bool swappingEnabled,QString & returnProp,QString & returnValue,QWidget * & returnWidget)794 bool PaletteItem::collectHoleSizeInfo(const QString & defaultHoleSizeValue, QWidget * parent, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget)
795 {
796 returnProp = tr("hole size");
797
798 returnValue = m_modelPart->localProp("hole size").toString();
799 if (returnValue.isEmpty()) {
800 returnValue = defaultHoleSizeValue;
801 }
802 QWidget * frame = createHoleSettings(parent, m_holeSettings, swappingEnabled, returnValue, true);
803
804 connect(m_holeSettings.sizesComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(changeHoleSize(const QString &)));
805 connect(m_holeSettings.mmRadioButton, SIGNAL(toggled(bool)), this, SLOT(changeUnits(bool)));
806 connect(m_holeSettings.inRadioButton, SIGNAL(toggled(bool)), this, SLOT(changeUnits(bool)));
807 connect(m_holeSettings.diameterEdit, SIGNAL(editingFinished()), this, SLOT(changeDiameter()));
808 connect(m_holeSettings.thicknessEdit, SIGNAL(editingFinished()), this, SLOT(changeThickness()));
809
810 returnWidget = frame;
811 return true;
812 }
813
setUpHoleSizes(const QString & type,HoleClassThing & holeThing)814 void PaletteItem::setUpHoleSizes(const QString & type, HoleClassThing & holeThing)
815 {
816 if (holeThing.holeSizes.count() == 0) {
817 setUpHoleSizesAux(holeThing, type);
818 }
819
820 initHoleSettings(m_holeSettings, &holeThing);
821 QStringList localHoleSize = modelPart()->localProp("hole size").toString().split(",");
822 if (localHoleSize.count() == 2) {
823 m_holeSettings.ringThickness = localHoleSize.at(1);
824 m_holeSettings.holeDiameter = localHoleSize.at(0);
825 }
826 else {
827 QString hs = modelPart()->properties().value("hole size");
828 localHoleSize = hs.split(",");
829 if (localHoleSize.count() == 2) {
830 modelPart()->setLocalProp("hole size", hs);
831 m_holeSettings.ringThickness = localHoleSize.at(1);
832 m_holeSettings.holeDiameter = localHoleSize.at(0);
833 }
834 else {
835 m_holeSettings.ringThickness = holeThing.ringThickness;
836 m_holeSettings.holeDiameter = holeThing.holeSize;
837 }
838 }
839 }
setUpHoleSizesAux(HoleClassThing & holeThing,const QString & type)840 void PaletteItem::setUpHoleSizesAux(HoleClassThing & holeThing, const QString & type) {
841 QFile file(":/resources/vias.xml");
842
843 QString errorStr;
844 int errorLine;
845 int errorColumn;
846
847 QDomDocument domDocument;
848 if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
849 DebugDialog::debug(QString("failed loading properties %1 line:%2 col:%3").arg(errorStr).arg(errorLine).arg(errorColumn));
850 return;
851 }
852
853 QDomElement root = domDocument.documentElement();
854 if (root.isNull()) return;
855 if (root.tagName() != "vias") return;
856
857 QDomElement ve = root.firstChildElement("via");
858 while (!ve.isNull()) {
859 QString rt = ve.attribute("ringthickness");
860 QString hs = ve.attribute("holesize");
861 QString name = ve.attribute("name");
862 QString isOK = ve.attribute(type);
863 if (isOK.toInt() == 1) {
864 if (ve.attribute(type + "default").compare("yes") == 0) {
865 if (holeThing.ringThickness.isEmpty()) {
866 holeThing.ringThickness = rt;
867 }
868 if (holeThing.holeSize.isEmpty()) {
869 holeThing.holeSize = hs;
870 }
871 }
872
873 bool ok;
874 double val = TextUtils::convertToInches(ve.attribute(type + "ringthicknesslow"), &ok, false);
875 if (ok) {
876 holeThing.ringThicknessRange.setX(val);
877 }
878 val = TextUtils::convertToInches(ve.attribute(type + "ringthicknesshigh"), &ok, false);
879 if (ok) {
880 holeThing.ringThicknessRange.setY(val);
881 }
882 val = TextUtils::convertToInches(ve.attribute(type + "holediameterlow"), &ok, false);
883 if (ok) {
884 holeThing.holeDiameterRange.setX(val);
885 }
886 val = TextUtils::convertToInches(ve.attribute(type + "holediameterhigh"), &ok, false);
887 if (ok) {
888 holeThing.holeDiameterRange.setY(val);
889 }
890
891 holeThing.holeSizes.insert(name, QString("%1,%2").arg(hs).arg(rt));
892 holeThing.holeSizeKeys.append(name);
893 }
894 ve = ve.nextSiblingElement("via");
895 }
896
897 holeThing.holeSizeValue = QString("%1,%2").arg(holeThing.holeSize).arg(holeThing.ringThickness);
898
899 }
900
createHoleSettings(QWidget * parent,HoleSettings & holeSettings,bool swappingEnabled,const QString & currentHoleSize,bool advanced)901 QWidget * PaletteItem::createHoleSettings(QWidget * parent, HoleSettings & holeSettings, bool swappingEnabled, const QString & currentHoleSize, bool advanced) {
902 static const int RowHeight = 21;
903
904 holeSettings.diameterEdit = NULL;
905 holeSettings.thicknessEdit = NULL;
906 holeSettings.mmRadioButton = NULL;
907 holeSettings.inRadioButton = NULL;
908 holeSettings.diameterValidator = NULL;
909 holeSettings.thicknessValidator = NULL;
910
911 QFrame * frame = new QFrame(parent);
912 frame->setObjectName("infoViewPartFrame");
913
914 QVBoxLayout * vBoxLayout = new QVBoxLayout(frame);
915 vBoxLayout->setMargin(0);
916 vBoxLayout->setContentsMargins(0, 0, 0, 0);
917 vBoxLayout->setSpacing(0);
918
919 holeSettings.sizesComboBox = new QComboBox(frame);
920 holeSettings.sizesComboBox->setEditable(false);
921 holeSettings.sizesComboBox->setObjectName("infoViewComboBox");
922 foreach (QString key, holeSettings.holeThing->holeSizeKeys) {
923 holeSettings.sizesComboBox->addItem(key);
924 }
925 holeSettings.sizesComboBox->setEnabled(swappingEnabled);
926
927 vBoxLayout->addWidget(holeSettings.sizesComboBox);
928
929 if (advanced) {
930 vBoxLayout->addSpacing(4);
931
932 QFrame * hFrame = new QFrame(frame);
933 QHBoxLayout * hLayout = new QHBoxLayout(hFrame);
934 hLayout->setMargin(0);
935
936 QGroupBox * subFrame = new QGroupBox(tr("advanced settings"), frame);
937 subFrame->setObjectName("infoViewGroupBox");
938
939 QGridLayout * gridLayout = new QGridLayout(subFrame);
940 gridLayout->setMargin(0);
941
942 QGroupBox * rbFrame = new QGroupBox("", subFrame);
943 rbFrame->setObjectName("infoViewGroupBox");
944 QVBoxLayout * vbl = new QVBoxLayout(rbFrame);
945 vbl->setMargin(0);
946
947 holeSettings.inRadioButton = new QRadioButton(tr("in"), subFrame);
948 gridLayout->addWidget(holeSettings.inRadioButton, 0, 2);
949 holeSettings.inRadioButton->setObjectName("infoViewRadioButton");
950
951 holeSettings.mmRadioButton = new QRadioButton(tr("mm"), subFrame);
952 gridLayout->addWidget(holeSettings.mmRadioButton, 1, 2);
953 holeSettings.inRadioButton->setObjectName("infoViewRadioButton");
954
955 vbl->addWidget(holeSettings.inRadioButton);
956 vbl->addWidget(holeSettings.mmRadioButton);
957 rbFrame->setLayout(vbl);
958
959 gridLayout->addWidget(rbFrame, 0, 2, 2, 1, Qt::AlignVCenter);
960
961 holeSettings.diameterEdit = new QLineEdit(subFrame);
962 holeSettings.diameterEdit->setMinimumHeight(RowHeight);
963 holeSettings.diameterValidator = new QDoubleValidator(holeSettings.diameterEdit);
964 holeSettings.diameterValidator->setNotation(QDoubleValidator::StandardNotation);
965 holeSettings.diameterEdit->setValidator(holeSettings.diameterValidator);
966 gridLayout->addWidget(holeSettings.diameterEdit, 0, 1);
967 holeSettings.diameterEdit->setObjectName("infoViewLineEdit");
968
969 QLabel * label = new QLabel(tr("Hole Diameter"));
970 label->setMinimumHeight(RowHeight);
971 label->setObjectName("infoViewGroupBoxLabel");
972 gridLayout->addWidget(label, 0, 0);
973
974 holeSettings.thicknessEdit = new QLineEdit(subFrame);
975 holeSettings.thicknessEdit->setMinimumHeight(RowHeight);
976 holeSettings.thicknessValidator = new QDoubleValidator(holeSettings.thicknessEdit);
977 holeSettings.thicknessValidator->setNotation(QDoubleValidator::StandardNotation);
978 holeSettings.thicknessEdit->setValidator(holeSettings.thicknessValidator);
979 gridLayout->addWidget(holeSettings.thicknessEdit, 1, 1);
980 holeSettings.thicknessEdit->setObjectName("infoViewLineEdit");
981
982 label = new QLabel(tr("Ring Thickness"));
983 label->setMinimumHeight(RowHeight);
984 gridLayout->addWidget(label, 1, 0);
985 label->setObjectName("infoViewLabel");
986
987 gridLayout->setContentsMargins(10, 2, 0, 2);
988 gridLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 3);
989 gridLayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum), 1, 3);
990
991 hLayout->addWidget(subFrame);
992 hLayout->addSpacerItem(new QSpacerItem(1,1,QSizePolicy::Expanding,QSizePolicy::Minimum));
993 vBoxLayout->addWidget(hFrame);
994
995
996 holeSettings.mmRadioButton->setEnabled(swappingEnabled);
997 holeSettings.inRadioButton->setEnabled(swappingEnabled);
998 holeSettings.diameterEdit->setEnabled(swappingEnabled);
999 holeSettings.thicknessEdit->setEnabled(swappingEnabled);
1000
1001
1002 if (currentHoleSize.contains("mm")) {
1003 holeSettings.mmRadioButton->setChecked(true);
1004 }
1005 else {
1006 holeSettings.inRadioButton->setChecked(true);
1007 }
1008 }
1009
1010 updateEditTexts(holeSettings);
1011 updateValidators(holeSettings);
1012 updateSizes(holeSettings);
1013
1014 return frame;
1015 }
1016
updateEditTexts(HoleSettings & holeSettings)1017 void PaletteItem::updateEditTexts(HoleSettings & holeSettings) {
1018 if (holeSettings.diameterEdit == NULL) return;
1019 if (holeSettings.thicknessEdit == NULL) return;
1020 if (holeSettings.mmRadioButton == NULL) return;
1021
1022 double hd = TextUtils::convertToInches(holeSettings.holeDiameter);
1023 double rt = TextUtils::convertToInches(holeSettings.ringThickness);
1024
1025 QString newVal;
1026 if (holeSettings.currentUnits() == "in") {
1027 newVal = QString("%1,%2").arg(hd).arg(rt);
1028 }
1029 else {
1030 newVal = QString("%1,%2").arg(hd * 25.4).arg(rt * 25.4);
1031 }
1032
1033 QStringList sizes = newVal.split(",");
1034 holeSettings.diameterEdit->setText(sizes.at(0));
1035 holeSettings.thicknessEdit->setText(sizes.at(1));
1036 }
1037
updateSizes(HoleSettings & holeSettings)1038 void PaletteItem::updateSizes(HoleSettings & holeSettings) {
1039 if (holeSettings.sizesComboBox == NULL) return;
1040
1041 int newIndex = -1;
1042
1043 QPointF current(TextUtils::convertToInches(holeSettings.holeDiameter), TextUtils::convertToInches(holeSettings.ringThickness));
1044 for (int ix = 0; ix < holeSettings.sizesComboBox->count(); ix++) {
1045 QString key = holeSettings.sizesComboBox->itemText(ix);
1046 QString value = holeSettings.holeThing->holeSizes.value(key, "");
1047 QStringList sizes;
1048 if (value.isEmpty()) {
1049 sizes = key.split(",");
1050 }
1051 else {
1052 sizes = value.split(",");
1053 }
1054 if (sizes.count() < 2) continue;
1055
1056 QPointF p(TextUtils::convertToInches(sizes.at(0)), TextUtils::convertToInches(sizes.at(1)));
1057 if (p == current) {
1058 newIndex = ix;
1059 break;
1060 }
1061 }
1062
1063 if (newIndex < 0) {
1064 QString newItem = holeSettings.holeDiameter + "," + holeSettings.ringThickness;
1065 holeSettings.sizesComboBox->addItem(newItem);
1066 newIndex = holeSettings.sizesComboBox->findText(newItem);
1067
1068 holeSettings.holeThing->holeSizes.insert(newItem, newItem);
1069 holeSettings.holeThing->holeSizeKeys.prepend(newItem);
1070 }
1071
1072 // don't want to trigger another undo command
1073 bool wasBlocked = holeSettings.sizesComboBox->blockSignals(true);
1074 holeSettings.sizesComboBox->setCurrentIndex(newIndex);
1075 holeSettings.sizesComboBox->blockSignals(wasBlocked);
1076 }
1077
updateValidators(HoleSettings & holeSettings)1078 void PaletteItem::updateValidators(HoleSettings & holeSettings)
1079 {
1080 if (holeSettings.diameterValidator == NULL) return;
1081 if (holeSettings.thicknessValidator == NULL) return;
1082 if (holeSettings.mmRadioButton == NULL) return;
1083
1084 QString units = holeSettings.currentUnits();
1085 double multiplier = (units == "mm") ? 25.4 : 1.0;
1086 holeSettings.diameterValidator->setRange(holeSettings.holeThing->holeDiameterRange.x() * multiplier, holeSettings.holeThing->holeDiameterRange.y() * multiplier, 3);
1087 holeSettings.thicknessValidator->setRange(holeSettings.holeThing->ringThicknessRange.x() * multiplier, holeSettings.holeThing->ringThicknessRange.y() * multiplier, 3);
1088 }
1089
initHoleSettings(HoleSettings & holeSettings,HoleClassThing * holeThing)1090 void PaletteItem::initHoleSettings(HoleSettings & holeSettings, HoleClassThing * holeThing)
1091 {
1092 holeSettings.holeThing = holeThing;
1093 holeSettings.diameterEdit = holeSettings.thicknessEdit = NULL;
1094 holeSettings.diameterValidator = holeSettings.thicknessValidator = NULL;
1095 holeSettings.inRadioButton = holeSettings.mmRadioButton = NULL;
1096 holeSettings.sizesComboBox = NULL;
1097 }
1098
1099
setHoleSize(QString & holeSize,bool force,HoleSettings & holeSettings)1100 bool PaletteItem::setHoleSize(QString & holeSize, bool force, HoleSettings & holeSettings)
1101 {
1102 QStringList sizes = getSizes(holeSize, holeSettings);
1103 if (sizes.count() < 2) return false;
1104
1105 if (!force && (holeSettings.holeDiameter.compare(sizes.at(0)) == 0) && (holeSettings.ringThickness.compare(sizes.at(1)) == 0))
1106 {
1107 return false;
1108 }
1109
1110 holeSettings.holeDiameter = sizes.at(0);
1111 holeSettings.ringThickness = sizes.at(1);
1112 updateEditTexts(holeSettings);
1113 updateValidators(holeSettings);
1114 updateSizes(holeSettings);
1115 return true;
1116 }
1117
getSizes(QString & holeSize,HoleSettings & holeSettings)1118 QStringList PaletteItem::getSizes(QString & holeSize, HoleSettings & holeSettings)
1119 {
1120 QStringList sizes;
1121 QString hashedHoleSize = holeSettings.holeThing->holeSizes.value(holeSize);
1122 if (hashedHoleSize.isEmpty()) {
1123 sizes = holeSize.split(",");
1124 }
1125 else {
1126 sizes = hashedHoleSize.split(",");
1127 holeSize = sizes[0] + "," + sizes[1];
1128 }
1129 return sizes;
1130 }
1131
changeHoleSize(const QString & newSize)1132 void PaletteItem::changeHoleSize(const QString & newSize) {
1133 if (this->m_viewID != ViewLayer::PCBView) {
1134 PaletteItem * paletteItem = qobject_cast<PaletteItem *>(modelPart()->viewItem(ViewLayer::PCBView));
1135 if (paletteItem == NULL) return;
1136
1137 paletteItem->changeHoleSize(newSize);
1138 return;
1139 }
1140
1141 QString holeSize = newSize;
1142 QStringList sizes = getSizes(holeSize, m_holeSettings);
1143 if (sizes.count() != 2) return;
1144
1145 QString svg = hackSvgHoleSize(sizes.at(0), sizes.at(1));
1146 if (svg.isEmpty()) return;
1147
1148 // figure out the new filename
1149 QString newModuleID = appendHoleSize(moduleID(), sizes.at(0), sizes.at(1));
1150 QString newFzpFilename = newModuleID + ".fzp";
1151 QString newSvgFilename = "pcb/" + newModuleID + ".svg";
1152
1153 QString fzp = hackFzpHoleSize(newModuleID, newSvgFilename, sizes.at(0) + "," + sizes.at(1));
1154 if (fzp.isEmpty()) return;
1155
1156 if (!TextUtils::writeUtf8(PartFactory::fzpPath() + newFzpFilename, fzp)) {
1157 return;
1158 }
1159
1160 if (!TextUtils::writeUtf8(PartFactory::partPath() + newSvgFilename, svg)) {
1161 return;
1162 }
1163
1164 m_propsMap.insert("hole size", newSize);
1165 m_propsMap.insert("moduleID", newModuleID);
1166
1167 InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
1168 if (infoGraphicsView != NULL) {
1169 infoGraphicsView->swap(family(), newModuleID, m_propsMap, this);
1170 }
1171 }
1172
hackFzpHoleSize(const QString & fzp,const QString & moduleid,int hsix)1173 QString PaletteItem::hackFzpHoleSize(const QString & fzp, const QString & moduleid, int hsix)
1174 {
1175 QString errorStr;
1176 int errorLine;
1177 int errorColumn;
1178 QDomDocument document;
1179 bool result = document.setContent(fzp, &errorStr, &errorLine, &errorColumn);
1180 if (!result) {
1181 DebugDialog::debug(QString("bad fzp in %1:%2").arg(moduleid).arg(fzp));
1182 }
1183 QStringList strings = moduleid.mid(hsix).split("_");
1184 return hackFzpHoleSize(document, moduleid, "pcb/" + moduleid + ".svg", strings.at(2) + "," + strings.at(3));
1185 }
1186
1187
hackFzpHoleSize(const QString & newModuleID,const QString & pcbFilename,const QString & newSize)1188 QString PaletteItem::hackFzpHoleSize(const QString & newModuleID, const QString & pcbFilename, const QString & newSize) {
1189 QFile file(modelPart()->path());
1190 QString errorStr;
1191 int errorLine;
1192 int errorColumn;
1193 QDomDocument document;
1194 bool result = document.setContent(&file, &errorStr, &errorLine, &errorColumn);
1195 if (!result) {
1196 DebugDialog::debug(QString("bad doc fzp in %1:%2 %3 %4").arg(newModuleID).arg(errorStr).arg(errorLine).arg(errorColumn));
1197 }
1198
1199 return hackFzpHoleSize(document, newModuleID, pcbFilename, newSize);
1200 }
1201
hackFzpHoleSize(QDomDocument & document,const QString & newModuleID,const QString & pcbFilename,const QString & newSize)1202 QString PaletteItem::hackFzpHoleSize(QDomDocument & document, const QString & newModuleID, const QString & pcbFilename, const QString & newSize)
1203 {
1204 QDomElement root = document.documentElement();
1205 root.setAttribute("moduleId", newModuleID);
1206
1207 QDomElement views = root.firstChildElement("views");
1208 QDomElement pcbView = views.firstChildElement("pcbView");
1209 QDomElement layers = pcbView.firstChildElement("layers");
1210 if (layers.isNull()) return "";
1211
1212 layers.setAttribute("image", pcbFilename);
1213
1214 QDomElement properties = root.firstChildElement("properties");
1215 QDomElement prop = properties.firstChildElement("property");
1216 bool gotProp = false;
1217 while (!prop.isNull()) {
1218 QString name = prop.attribute("name");
1219 if (name.compare("hole size", Qt::CaseInsensitive) == 0) {
1220 gotProp = true;
1221 TextUtils::replaceChildText(prop, newSize);
1222 break;
1223 }
1224
1225 prop = prop.nextSiblingElement("property");
1226 }
1227
1228 if (!gotProp) return "";
1229
1230
1231 return TextUtils::removeXMLEntities(document.toString());
1232 }
1233
1234
hackSvgHoleSizeAux(const QString & svg,const QString & expectedFileName)1235 QString PaletteItem::hackSvgHoleSizeAux(const QString & svg, const QString & expectedFileName)
1236 {
1237 QDomDocument document;
1238 document.setContent(svg);
1239 QFileInfo info(expectedFileName);
1240 QString baseName = info.completeBaseName();
1241 int hsix = baseName.indexOf(HoleSizePrefix);
1242 QStringList strings = baseName.mid(hsix).split("_");
1243 return hackSvgHoleSize(document, strings.at(2), strings.at(3));
1244 }
1245
hackSvgHoleSize(const QString & holeDiameter,const QString & ringThickness)1246 QString PaletteItem::hackSvgHoleSize(const QString & holeDiameter, const QString & ringThickness) {
1247 QFile file(filename());
1248 QString errorStr;
1249 int errorLine;
1250 int errorColumn;
1251
1252 QDomDocument domDocument;
1253 if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
1254 DebugDialog::debug(QString("unable to parse pcb svg xml: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn));
1255 return "";
1256 }
1257
1258 return hackSvgHoleSize(domDocument, holeDiameter, ringThickness);
1259 }
1260
hackSvgHoleSize(QDomDocument & domDocument,const QString & holeDiameter,const QString & ringThickness)1261 QString PaletteItem::hackSvgHoleSize(QDomDocument & domDocument, const QString & holeDiameter, const QString & ringThickness)
1262 {
1263 QDomElement root = domDocument.documentElement();
1264 double w = TextUtils::convertToInches(root.attribute("width"));
1265 QStringList vb = root.attribute("viewBox").split(" ");
1266 if (vb.count() != 4) return "";
1267
1268 double wp = vb.at(2).toDouble();
1269 if (wp == 0) return "";
1270
1271 double dpi = wp / w;
1272 double rt = TextUtils::convertToInches(ringThickness) * dpi;
1273 double hs = TextUtils::convertToInches(holeDiameter) * dpi;
1274 double rad = (hs + rt) / 2;
1275
1276 QDomNodeList circles = root.elementsByTagName("circle");
1277 for (int i = 0; i < circles.count(); i++) {
1278 QDomElement circle = circles.at(i).toElement();
1279 QString id = circle.attribute("id");
1280 if (ConnectorFinder.indexIn(id) == 0) {
1281 circle.setAttribute("r", QString::number(rad));
1282 circle.setAttribute("stroke-width", QString::number(rt));
1283 }
1284 }
1285
1286 QDomNodeList rects = root.elementsByTagName("rect");
1287 for (int i = 0; i < rects.count(); i++) {
1288 QDomElement rect = rects.at(i).toElement();
1289 QString id = rect.attribute("id");
1290 if (id.compare("square") == 0) {
1291 double oldWidth = rect.attribute("width").toDouble();
1292 double oldX = rect.attribute("x").toDouble();
1293 double oldY = rect.attribute("y").toDouble();
1294
1295 rect.setAttribute("width", QString::number(rad * 2));
1296 rect.setAttribute("height", QString::number(rad * 2));
1297 rect.setAttribute("x", QString::number(oldX + (oldWidth / 2) - rad));
1298 rect.setAttribute("y", QString::number(oldY + (oldWidth / 2) - rad));
1299 rect.setAttribute("stroke-width", QString::number(rt));
1300 }
1301 }
1302
1303
1304 return TextUtils::removeXMLEntities(domDocument.toString());
1305 }
1306
appendHoleSize(const QString & moduleid,const QString & holeSize,const QString & ringThickness)1307 QString PaletteItem::appendHoleSize(const QString & moduleid, const QString & holeSize, const QString & ringThickness)
1308 {
1309 QString baseName = moduleid;
1310 int ix = baseName.lastIndexOf(HoleSizePrefix);
1311 if (ix >= 0) {
1312 baseName.truncate(ix);
1313 }
1314
1315 return baseName + QString("%1%2_%3").arg(HoleSizePrefix).arg(holeSize).arg(ringThickness);
1316 }
1317
generateSwap(const QString & text,GenModuleID genModuleID,GenFzp genFzp,GenSvg makeBreadboardSvg,GenSvg makeSchematicSvg,GenSvg makePcbSvg)1318 void PaletteItem::generateSwap(const QString & text, GenModuleID genModuleID, GenFzp genFzp, GenSvg makeBreadboardSvg, GenSvg makeSchematicSvg, GenSvg makePcbSvg)
1319 {
1320 FamilyPropertyComboBox * comboBox = qobject_cast<FamilyPropertyComboBox *>(sender());
1321 if (comboBox == NULL) return;
1322
1323 QMap<QString, QString> propsMap(m_propsMap);
1324 propsMap.insert(comboBox->prop(), text);
1325 QString newModuleID = genModuleID(propsMap);
1326 if (!newModuleID.contains("smd", Qt::CaseInsensitive)) {
1327 // add hole size
1328 int ix = moduleID().indexOf(HoleSizePrefix);
1329 if (ix >= 0) {
1330 newModuleID.append(moduleID().mid(ix));
1331 }
1332 }
1333
1334 QString path;
1335 if (!PartFactory::fzpFileExists(newModuleID, path)) {
1336 QString fzp = genFzp(newModuleID);
1337 TextUtils::writeUtf8(path, fzp);
1338
1339 QDomDocument doc;
1340 doc.setContent(fzp);
1341
1342 QHash<QString, QString> viewNames;
1343
1344 QDomElement root = doc.documentElement();
1345 QDomElement views = root.firstChildElement("views");
1346 QDomElement view = views.firstChildElement();
1347 while (!view.isNull()) {
1348 viewNames.insert(view.tagName(), view.attribute("image", ""));
1349 view = view.nextSiblingElement();
1350 }
1351
1352 QString name = viewNames.value("breadboardView", "");
1353 if (!PartFactory::svgFileExists(name, path)) {
1354 QString svg = makeBreadboardSvg(name);
1355 TextUtils::writeUtf8(path, svg);
1356 }
1357
1358 name = viewNames.value("schematicView", "");
1359 if (!PartFactory::svgFileExists(name, path)) {
1360 QString svg = makeSchematicSvg(name);
1361 TextUtils::writeUtf8(path, svg);
1362 }
1363
1364 name = viewNames.value("pcbView", "");
1365 if (!PartFactory::svgFileExists(name, path)) {
1366 QString svg = makePcbSvg(name);
1367 TextUtils::writeUtf8(path, svg);
1368 }
1369 }
1370
1371 m_propsMap.insert("moduleID", newModuleID);
1372
1373 }
1374
changeUnits(bool)1375 void PaletteItem::changeUnits(bool)
1376 {
1377 QString newVal = changeUnits(m_holeSettings);
1378 changeHoleSize(newVal);
1379 }
1380
changeUnits(HoleSettings & holeSettings)1381 QString PaletteItem::changeUnits(HoleSettings & holeSettings)
1382 {
1383 double hd = TextUtils::convertToInches(holeSettings.holeDiameter);
1384 double rt = TextUtils::convertToInches(holeSettings.ringThickness);
1385 QString newVal;
1386 if (holeSettings.currentUnits() == "in") {
1387 newVal = QString("%1in,%2in").arg(hd).arg(rt);
1388 }
1389 else {
1390 newVal = QString("%1mm,%2mm").arg(hd * 25.4).arg(rt * 25.4);
1391 }
1392
1393 QStringList sizes = newVal.split(",");
1394 holeSettings.ringThickness = sizes.at(1);
1395 holeSettings.holeDiameter = sizes.at(0);
1396
1397 updateValidators(holeSettings);
1398 updateSizes(holeSettings);
1399 updateEditTexts(holeSettings);
1400
1401 return newVal;
1402 }
1403
changeThickness()1404 void PaletteItem::changeThickness()
1405 {
1406 if (changeThickness(m_holeSettings, sender())) {
1407 QLineEdit * edit = qobject_cast<QLineEdit *>(sender());
1408 changeHoleSize(m_holeSettings.holeDiameter + "," + edit->text() + m_holeSettings.currentUnits());
1409 }
1410 }
1411
changeThickness(HoleSettings & holeSettings,QObject * sender)1412 bool PaletteItem::changeThickness(HoleSettings & holeSettings, QObject * sender)
1413 {
1414 QLineEdit * edit = qobject_cast<QLineEdit *>(sender);
1415 if (edit == NULL) return false;
1416
1417 double newValue = edit->text().toDouble();
1418 QString temp = holeSettings.ringThickness;
1419 temp.chop(2);
1420 double oldValue = temp.toDouble();
1421 return (newValue != oldValue);
1422 }
1423
changeDiameter()1424 void PaletteItem::changeDiameter()
1425 {
1426 if (changeDiameter(m_holeSettings, sender())) {
1427 QLineEdit * edit = qobject_cast<QLineEdit *>(sender());
1428 changeHoleSize(edit->text() + m_holeSettings.currentUnits() + "," + m_holeSettings.ringThickness);
1429 }
1430 }
1431
changeDiameter(HoleSettings & holeSettings,QObject * sender)1432 bool PaletteItem::changeDiameter(HoleSettings & holeSettings, QObject * sender)
1433 {
1434 QLineEdit * edit = qobject_cast<QLineEdit *>(sender);
1435 if (edit == NULL) return false;
1436
1437 double newValue = edit->text().toDouble();
1438 QString temp = holeSettings.holeDiameter;
1439 temp.chop(2);
1440 double oldValue = temp.toDouble();
1441 return (newValue != oldValue);
1442 }
1443
makeLocalModifications(QByteArray & svg,const QString & filename)1444 bool PaletteItem::makeLocalModifications(QByteArray & svg, const QString & filename) {
1445 // a bottleneck for modifying part svg xml at setupImage time
1446
1447 // for saved-as-new-part parts (i.e. that are no longer MysteryParts) that still have a chip-label or custom pin names
1448 // also handles adding a title if there is a label id in the
1449 switch (m_viewID) {
1450 case ViewLayer::PCBView:
1451 return false;
1452
1453 default:
1454 if (itemType() != ModelPart::Part) return false;
1455 if (filename.startsWith("icon")) return false;
1456
1457 break;
1458 }
1459
1460 bool gotChipLabel = false;
1461 QString chipLabel = modelPart()->properties().value("chip label", "");
1462 if (!chipLabel.isEmpty()) {
1463 svg = TextUtils::replaceTextElement(svg, "label", chipLabel);
1464 gotChipLabel = true;
1465 }
1466
1467 bool modified = false;
1468 if (m_viewID == ViewLayer::SchematicView) {
1469 QString value = modelPart()->properties().value("editable pin labels", "");
1470 if (value.compare("true") == 0) {
1471 bool hasLayout, sip;
1472 QStringList labels = sipOrDipOrLabels(hasLayout, sip);
1473 if (labels.count() > 0) {
1474 svg = PartFactory::makeSchematicSipOrDipOr(labels, hasLayout, sip).toUtf8();
1475 modified = true;
1476 }
1477 }
1478 gotChipLabel = true;
1479 }
1480
1481 if (gotChipLabel) return modified;
1482
1483 int rix = svg.indexOf("label");
1484 if (rix >= 0) {
1485 rix = qMax(0, rix - 4); // backup for id="
1486 int ix = svg.indexOf("id=\"label\"", rix);
1487 if (ix < 0) {
1488 ix = svg.indexOf("id='label'", rix);
1489 }
1490 if (ix >= 0) {
1491 int tix = svg.lastIndexOf("<text", ix);
1492 int lix = svg.lastIndexOf("<", ix);
1493 if (tix == lix) {
1494 svg = TextUtils::replaceTextElement(svg, "label", modelPart()->title());
1495 modified = true;
1496 }
1497 }
1498 }
1499
1500 return modified;
1501 }
1502
sipOrDipOrLabels(bool & hasLayout,bool & sip)1503 QStringList PaletteItem::sipOrDipOrLabels(bool & hasLayout, bool & sip) {
1504 hasLayout = sip = false;
1505 bool hasLocal = false;
1506 QStringList labels = getPinLabels(hasLocal);
1507 if (labels.count() == 0) return labels;
1508
1509 // part was formerly a mystery part or generic ic ...
1510 QHash<QString, QString> properties = modelPart()->properties();
1511 foreach (QString key, properties.keys()) {
1512 QString value = properties.value(key);
1513 if (key.compare("layout", Qt::CaseInsensitive) == 0) {
1514 // was a mystery part
1515 hasLayout = true;
1516 break;
1517 }
1518
1519 if (key.compare("package") == 0) {
1520 // was a generic ic
1521 sip = value.contains("sip", Qt::CaseInsensitive);
1522 }
1523 }
1524
1525 return labels;
1526 }
1527
resetLayerKin(const QString & svg)1528 void PaletteItem::resetLayerKin(const QString & svg) {
1529 QString svgNoText = SvgFileSplitter::hideText3(svg);
1530
1531 resetRenderer(svgNoText);
1532
1533 foreach (ItemBase * lkpi, layerKin()) {
1534 if (lkpi->viewLayerID() == ViewLayer::SchematicText) {
1535 bool hasText;
1536 QString svgText = SvgFileSplitter::showText3(svg, hasText);
1537 lkpi->resetRenderer(svgText);
1538 lkpi->setProperty("textSvg", svgText);
1539 qobject_cast<SchematicTextLayerKinPaletteItem *>(lkpi)->clearTextThings();
1540 break;
1541 }
1542 }
1543 }
1544
untransform()1545 QTransform PaletteItem::untransform() {
1546 //DebugDialog::debug("untransform");
1547 QTransform chiefTransform = this->transform();
1548 chiefTransform.setMatrix(chiefTransform.m11(), chiefTransform.m12(), chiefTransform.m13(), chiefTransform.m21(), chiefTransform.m22(), chiefTransform.m23(), 0, 0, chiefTransform.m33());
1549 bool identity = chiefTransform.isIdentity();
1550 if (!identity) {
1551 QTransform invert = chiefTransform.inverted();
1552 transformItem(invert, false);
1553 foreach (ItemBase * lkpi, layerKin()) {
1554 lkpi->transformItem(invert, false);
1555 }
1556 }
1557
1558 return chiefTransform;
1559 }
1560
retransform(const QTransform & chiefTransform)1561 void PaletteItem::retransform(const QTransform & chiefTransform) {
1562 //DebugDialog::debug("retransform");
1563 if (!chiefTransform.isIdentity()) {
1564 transformItem(chiefTransform, false);
1565 foreach (ItemBase * lkpi, layerKin()) {
1566 lkpi->transformItem(chiefTransform, false);
1567 }
1568 }
1569 }
1570
1571
1572