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