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: 6984 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $
24 
25 ********************************************************************/
26 
27 #include "mysterypart.h"
28 #include "../utils/graphicsutils.h"
29 #include "../utils/familypropertycombobox.h"
30 #include "../utils/schematicrectconstants.h"
31 #include "../fsvgrenderer.h"
32 #include "../sketch/infographicsview.h"
33 #include "../commands.h"
34 #include "../utils/textutils.h"
35 #include "../layerattributes.h"
36 #include "partlabel.h"
37 #include "pinheader.h"
38 #include "partfactory.h"
39 #include "../connectors/connectoritem.h"
40 #include "../svg/svgfilesplitter.h"
41 
42 #include <QDomNodeList>
43 #include <QDomDocument>
44 #include <QDomElement>
45 #include <QLineEdit>
46 
47 static QStringList Spacings;
48 static QRegExp Digits("(\\d)+");
49 static QRegExp DigitsMil("(\\d)+mil");
50 
51 static const int MinSipPins = 1;
52 static const int MaxSipPins = 128;
53 static const int MinDipPins = 4;
54 static const int MaxDipPins = 128;
55 
56 static HoleClassThing TheHoleThing;
57 
58 // TODO
59 //	save into parts bin
60 
MysteryPart(ModelPart * modelPart,ViewLayer::ViewID viewID,const ViewGeometry & viewGeometry,long id,QMenu * itemMenu,bool doLabel)61 MysteryPart::MysteryPart( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel)
62 	: PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel)
63 {
64 	m_chipLabel = modelPart->localProp("chip label").toString();
65 	if (m_chipLabel.isEmpty()) {
66 		m_chipLabel = modelPart->properties().value("chip label", "?");
67 		modelPart->setLocalProp("chip label", m_chipLabel);
68 	}
69 
70     setUpHoleSizes("mystery", TheHoleThing);
71 
72 }
73 
~MysteryPart()74 MysteryPart::~MysteryPart() {
75 }
76 
setProp(const QString & prop,const QString & value)77 void MysteryPart::setProp(const QString & prop, const QString & value) {
78 	if (prop.compare("chip label", Qt::CaseInsensitive) == 0) {
79 		setChipLabel(value, false);
80 		return;
81 	}
82 
83 	PaletteItem::setProp(prop, value);
84 }
85 
setChipLabel(QString chipLabel,bool force)86 void MysteryPart::setChipLabel(QString chipLabel, bool force) {
87 
88 	if (!force && m_chipLabel.compare(chipLabel) == 0) return;
89 
90 	m_chipLabel = chipLabel;
91 
92 	QString svg;
93 	switch (this->m_viewID) {
94 		case ViewLayer::BreadboardView:
95 			svg = makeSvg(chipLabel, true);
96             if (!svg.isEmpty()) {
97 	            resetRenderer(svg);
98             }
99             break;
100 		case ViewLayer::SchematicView:
101             {
102                 QTransform  transform = untransform();
103 			    svg = makeSvg(chipLabel, false);
104 			    svg = retrieveSchematicSvg(svg);
105                 resetLayerKin(svg);
106                 retransform(transform);
107             }
108 			break;
109 		default:
110 			break;
111 	}
112 
113 	modelPart()->setLocalProp("chip label", chipLabel);
114 
115     if (m_partLabel) m_partLabel->displayTextsIf();
116 }
117 
retrieveSvg(ViewLayer::ViewLayerID viewLayerID,QHash<QString,QString> & svgHash,bool blackOnly,double dpi,double & factor)118 QString MysteryPart::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash<QString, QString> & svgHash, bool blackOnly, double dpi, double & factor)
119 {
120 	QString svg = PaletteItem::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor);
121 	switch (viewLayerID) {
122 		case ViewLayer::Breadboard:
123 		case ViewLayer::Icon:
124 			return TextUtils::replaceTextElement(svg, "label", m_chipLabel);
125 
126 		case ViewLayer::Schematic:
127 			svg = retrieveSchematicSvg(svg);
128 			return TextUtils::removeSVGHeader(svg);
129 
130 		default:
131 			break;
132 	}
133 
134 	return svg;
135 }
136 
retrieveSchematicSvg(QString & svg)137 QString MysteryPart::retrieveSchematicSvg(QString & svg) {
138 
139 	bool hasLocal = false;
140 	QStringList labels = getPinLabels(hasLocal);
141 
142 	if (hasLocal) {
143 		svg = makeSchematicSvg(labels, false);
144 	}
145 
146 	return TextUtils::replaceTextElement(svg, "label", m_chipLabel);
147 }
148 
149 
makeSvg(const QString & chipLabel,bool replace)150 QString MysteryPart::makeSvg(const QString & chipLabel, bool replace) {
151 	QString path = filename();
152 	QFile file(filename());
153 	QString svg;
154 	if (file.open(QFile::ReadOnly)) {
155 		svg = file.readAll();
156 		file.close();
157 		if (!replace) return svg;
158 
159 		return TextUtils::replaceTextElement(svg, "label", chipLabel);
160 	}
161 
162 	return "";
163 }
164 
collectValues(const QString & family,const QString & prop,QString & value)165 QStringList MysteryPart::collectValues(const QString & family, const QString & prop, QString & value) {
166 	if (prop.compare("layout", Qt::CaseInsensitive) == 0) {
167         // TODO: translate these
168         QStringList values;
169         values << "Single Row" << "Double Row";
170         value = values.at(moduleID().contains("sip", Qt::CaseInsensitive) ? 0 : 1);
171         return values;
172     }
173 
174 	if (prop.compare("pin spacing", Qt::CaseInsensitive) == 0) {
175 		QStringList values;
176         QString spacing;
177         TextUtils::getPinsAndSpacing(moduleID(), spacing);
178 		if (isDIP()) {
179 			foreach (QString f, spacings()) {
180 				values.append(f);
181 			}
182 		}
183 		else {
184 			values.append(spacing);
185 		}
186 
187 		value = spacing;
188 		return values;
189 	}
190 
191 	if (prop.compare("pins", Qt::CaseInsensitive) == 0) {
192 		QStringList values;
193 		value = modelPart()->properties().value("pins");
194 
195 		if (moduleID().contains("dip")) {
196 			for (int i = MinDipPins; i <= MaxDipPins; i += 2) {
197 				values << QString::number(i);
198 			}
199 		}
200 		else {
201 			for (int i = MinSipPins; i <= MaxSipPins; i++) {
202 				values << QString::number(i);
203 			}
204 		}
205 
206 		return values;
207 	}
208 
209 
210 	return PaletteItem::collectValues(family, prop, value);
211 }
212 
collectExtraInfo(QWidget * parent,const QString & family,const QString & prop,const QString & value,bool swappingEnabled,QString & returnProp,QString & returnValue,QWidget * & returnWidget,bool & hide)213 bool MysteryPart::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide)
214 {
215 	if (prop.compare("chip label", Qt::CaseInsensitive) == 0) {
216 		returnProp = tr("label");
217 		returnValue = m_chipLabel;
218 
219 		QLineEdit * e1 = new QLineEdit(parent);
220 		e1->setEnabled(swappingEnabled);
221 		e1->setText(m_chipLabel);
222 		connect(e1, SIGNAL(editingFinished()), this, SLOT(chipLabelEntry()));
223 		e1->setObjectName("infoViewLineEdit");
224 
225 
226 		returnWidget = e1;
227 
228 		return true;
229 	}
230 
231     if (prop.compare("hole size", Qt::CaseInsensitive) == 0) {
232         return collectHoleSizeInfo(TheHoleThing.holeSizeValue, parent, swappingEnabled, returnProp, returnValue, returnWidget);
233 	}
234 
235 	return PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide);
236 }
237 
getProperty(const QString & key)238 QString MysteryPart::getProperty(const QString & key) {
239 	if (key.compare("chip label", Qt::CaseInsensitive) == 0) {
240 		return m_chipLabel;
241 	}
242 
243 	return PaletteItem::getProperty(key);
244 }
245 
chipLabel()246 QString MysteryPart::chipLabel() {
247 	return m_chipLabel;
248 }
249 
addedToScene(bool temporary)250 void MysteryPart::addedToScene(bool temporary)
251 {
252 	if (this->scene()) {
253 		setChipLabel(m_chipLabel, true);
254 	}
255 
256     PaletteItem::addedToScene(temporary);
257 }
258 
title()259 const QString & MysteryPart::title() {
260 	return m_chipLabel;
261 }
262 
hasCustomSVG()263 bool MysteryPart::hasCustomSVG() {
264 	switch (m_viewID) {
265 		case ViewLayer::BreadboardView:
266 		case ViewLayer::SchematicView:
267 		case ViewLayer::IconView:
268 		case ViewLayer::PCBView:
269 			return true;
270 		default:
271 			return ItemBase::hasCustomSVG();
272 	}
273 }
274 
chipLabelEntry()275 void MysteryPart::chipLabelEntry() {
276 	QLineEdit * edit = qobject_cast<QLineEdit *>(sender());
277 	if (edit == NULL) return;
278 
279 	if (edit->text().compare(this->chipLabel()) == 0) return;
280 
281 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
282 	if (infoGraphicsView != NULL) {
283 		infoGraphicsView->setProp(this, "chip label", tr("chip label"), this->chipLabel(), edit->text(), true);
284 	}
285 }
286 
isDIP()287 bool MysteryPart::isDIP() {
288 	QString layout = modelPart()->properties().value("layout", "");
289 	return (layout.indexOf("double", 0, Qt::CaseInsensitive) >= 0);
290 }
291 
otherPropsChange(const QMap<QString,QString> & propsMap)292 bool MysteryPart::otherPropsChange(const QMap<QString, QString> & propsMap) {
293 	QString layout = modelPart()->properties().value("layout", "");
294 	return (layout.compare(propsMap.value("layout", "")) != 0);
295 }
296 
spacings()297 const QStringList & MysteryPart::spacings() {
298 	if (Spacings.count() == 0) {
299 		Spacings << "100mil" << "200mil" << "300mil" << "400mil" << "500mil" << "600mil" << "700mil" << "800mil" << "900mil";
300 	}
301 	return Spacings;
302 }
303 
isPlural()304 ItemBase::PluralType MysteryPart::isPlural() {
305 	return Plural;
306 }
307 
genSipFZP(const QString & moduleid)308 QString MysteryPart::genSipFZP(const QString & moduleid)
309 {
310     return genxFZP(moduleid, "mystery_part_sipFzpTemplate", MinSipPins, MaxSipPins, 1);
311 }
312 
genDipFZP(const QString & moduleid)313 QString MysteryPart::genDipFZP(const QString & moduleid)
314 {
315     return genxFZP(moduleid, "mystery_part_dipFzpTemplate", MinDipPins, MaxDipPins, 2);
316 
317 }
318 
genxFZP(const QString & moduleid,const QString & templateName,int minPins,int maxPins,int step)319 QString MysteryPart::genxFZP(const QString & moduleid, const QString & templateName, int minPins, int maxPins, int step) {
320     QString spacingString;
321     TextUtils::getPinsAndSpacing(moduleid, spacingString);
322     QString result = PaletteItem::genFZP(moduleid, templateName, minPins, maxPins, step, false);
323    	result.replace(".percent.", "%");
324 	result = result.arg(spacingString);
325 	return hackFzpHoleSize(result, moduleid);
326 }
327 
hackFzpHoleSize(const QString & fzp,const QString & moduleid)328 QString MysteryPart::hackFzpHoleSize(const QString & fzp, const QString & moduleid) {
329     int hsix = moduleid.lastIndexOf(HoleSizePrefix);
330     if (hsix >= 0) {
331         return PaletteItem::hackFzpHoleSize(fzp, moduleid, hsix);
332     }
333 
334     return fzp;
335 }
336 
genModuleID(QMap<QString,QString> & currPropsMap)337 QString MysteryPart::genModuleID(QMap<QString, QString> & currPropsMap)
338 {
339 	QString value = currPropsMap.value("layout");
340     bool single = value.contains("single", Qt::CaseInsensitive);
341 	QString pins = currPropsMap.value("pins");
342     QString spacing = currPropsMap.value("pin spacing", "300mil");
343 	if (single) {
344 		return QString("mystery_part_sip_%1_100mil").arg(pins);
345 	}
346 	else {
347 		int p = pins.toInt();
348 		if (p < 4) p = 4;
349 		if (p % 2 == 1) p--;
350 		return QString("mystery_part_dip_%1_%2").arg(p).arg(spacing);
351 	}
352 }
353 
makeSchematicSvg(const QString & expectedFileName)354 QString MysteryPart::makeSchematicSvg(const QString & expectedFileName)
355 {
356 	bool sip = expectedFileName.contains("sip", Qt::CaseInsensitive);
357 
358 	QStringList pieces = expectedFileName.split("_");
359 
360 	int pins = pieces.at(2).toInt();
361 	QStringList labels;
362 	for (int i = 0; i < pins; i++) {
363 		labels << QString::number(i + 1);
364 	}
365 
366     if (expectedFileName.contains(PartFactory::OldSchematicPrefix)) {
367         return obsoleteMakeSchematicSvg(labels, sip);
368     }
369 
370 	return makeSchematicSvg(labels, sip);
371 }
372 
makeSchematicSvg(const QStringList & labels,bool sip)373 QString MysteryPart::makeSchematicSvg(const QStringList & labels, bool sip)
374 {
375     QDomDocument fakeDoc;
376 
377     QList<QDomElement> lefts;
378     for (int i = 0; i < labels.count(); i++) {
379         QDomElement element = fakeDoc.createElement("contact");
380         element.setAttribute("connectorIndex", i);
381         element.setAttribute("name", labels.at(i));
382         lefts.append(element);
383     }
384     QList<QDomElement> empty;
385     QStringList busNames;
386 
387     QString titleText = sip ? "IC" : "?";
388     QString svg = SchematicRectConstants::genSchematicDIP(empty, empty, lefts, empty, empty, busNames, titleText, false, false, SchematicRectConstants::simpleGetConnectorName);
389     if (sip) return svg;
390 
391     // add the mystery part graphic
392     QDomDocument doc;
393     if (!doc.setContent(svg)) return svg;
394 
395     QRectF viewBox;
396     double w, h;
397     TextUtils::ensureViewBox(doc, GraphicsUtils::StandardFritzingDPI, viewBox, false, w, h, false);
398 
399     double newUnit = 1000 * SchematicRectConstants::NewUnit / 25.4;
400     double rectStroke = 2 * 1000 * SchematicRectConstants::RectStrokeWidth / 25.4;
401     QString circle = QString("<circle cx='%1' cy='%2' r='%3' fill='black' stroke-width='0' stroke='none' />\n");
402 	circle += QString("<text x='%1' fill='#FFFFFF' y='%4' font-family=\"%5\" text-anchor='middle' font-weight='bold' stroke='none' stroke-width='0' font-size='%6' >?</text>\n");
403     circle = circle
404                 .arg(viewBox.width() - rectStroke - (newUnit / 2))
405                 .arg(rectStroke + (newUnit / 2))
406                 .arg(newUnit / 2)
407 
408                 .arg(rectStroke + (newUnit / 2) + (newUnit / 3))   // offset so the text appears in the center of the circle
409                 .arg(SchematicRectConstants::FontFamily)
410                 .arg(newUnit)
411                 ;
412 
413     QDomDocument temp;
414     temp.setContent(QString("<g>" + circle + "</g>"));
415 
416     QDomElement root = doc.documentElement();
417     QDomElement schematic = TextUtils::findElementWithAttribute(root, "id", "schematic");
418     if (schematic.isNull()) return svg;
419 
420     QDomElement tempRoot = temp.documentElement();
421     schematic.appendChild(tempRoot);
422 
423     return doc.toString(1);
424 }
425 
obsoleteMakeSchematicSvg(const QStringList & labels,bool sip)426 QString MysteryPart::obsoleteMakeSchematicSvg(const QStringList & labels, bool sip)
427 {
428 	int increment = GraphicsUtils::StandardSchematicSeparationMils;   // 7.5mm;
429 	int border = 30;
430 	double totalHeight = (labels.count() * increment) + increment + border;
431 	int textOffset = 50;
432 	int repeatTextOffset = 50;
433 	int fontSize = 255;
434 	int labelFontSize = 130;
435 	int defaultLabelWidth = 30;
436 	QString labelText = "?";
437 	double textMax = defaultLabelWidth;
438 	QFont font("Droid Sans", labelFontSize * 72 / GraphicsUtils::StandardFritzingDPI, QFont::Normal);
439 	QFontMetricsF fm(font);
440 	for (int i = 0; i < labels.count(); i++) {
441 		double w = fm.width(labels.at(i));
442 		if (w > textMax) textMax = w;
443 	}
444 	textMax = textMax * GraphicsUtils::StandardFritzingDPI / 72;
445 
446 	int totalWidth = (5 * increment) + border;
447 	int innerWidth = 4 * increment;
448 	if (textMax > defaultLabelWidth) {
449 		totalWidth += (textMax - defaultLabelWidth);
450 		innerWidth += (textMax - defaultLabelWidth);
451 	}
452 
453 	QString header("<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n"
454 					"<svg xmlns:svg='http://www.w3.org/2000/svg' \n"
455 					"xmlns='http://www.w3.org/2000/svg' \n"
456 					"version='1.2' baseProfile='tiny' \n"
457 					"width='%7in' height='%1in' viewBox='0 0 %8 %2'>\n"
458 					"<g id='schematic'>\n"
459 					"<rect x='315' y='15' fill='none' width='%9' height='%3' stroke='#000000' stroke-linejoin='round' stroke-linecap='round' stroke-width='30' />\n"
460 					"<text id='label' x='%11' y='%4' fill='#000000' stroke='none' font-family='Droid Sans' text-anchor='middle' font-size='%5' >%6</text>\n");
461 
462 	if (!sip) {
463 		header +=	"<circle fill='#000000' cx='%10' cy='200' r='150' stroke-width='0' />\n"
464 					"<text x='%10' fill='#FFFFFF' y='305' font-family='Droid Sans' text-anchor='middle' font-weight='bold' stroke-width='0' font-size='275' >?</text>\n";
465 	}
466 	else {
467 		labelText = "IC";
468 		fontSize = 235;
469 		textOffset = 0;
470 	}
471 
472 	QString svg = header
473 		.arg(totalHeight / GraphicsUtils::StandardFritzingDPI)
474 		.arg(totalHeight)
475 		.arg(totalHeight - border)
476 		.arg((totalHeight / 2) + textOffset)
477 		.arg(fontSize)
478 		.arg(labelText)
479 		.arg(totalWidth / 1000.0)
480 		.arg(totalWidth)
481 		.arg(innerWidth)
482 		.arg(totalWidth - 200)
483 		.arg(increment + textMax + ((totalWidth - increment - textMax) / 2.0))
484 		;
485 
486 
487 	QString repeat("<line fill='none' stroke='#000000' stroke-linejoin='round' stroke-linecap='round' stroke-width='30' x1='15' y1='%1' x2='300' y2='%1'  />\n"
488 					"<rect x='0' y='%2' fill='none' width='300' height='30' id='connector%3pin' stroke-width='0' />\n"
489 					"<rect x='0' y='%2' fill='none' width='30' height='30' id='connector%3terminal' stroke-width='0' />\n"
490 					"<text id='label%3' x='390' y='%4' font-family='Droid Sans' stroke='none' fill='#000000' text-anchor='start' font-size='%6' >%5</text>\n");
491 
492 	for (int i = 0; i < labels.count(); i++) {
493 		svg += repeat
494 			.arg(increment + (border / 2) + (i * increment))
495 			.arg(increment + (i * increment))
496 			.arg(i)
497 			.arg(increment + repeatTextOffset + (i * increment))
498 			.arg(labels.at(i))
499 			.arg(labelFontSize);
500 	}
501 
502 	svg += "</g>\n";
503 	svg += "</svg>\n";
504 	return svg;
505 }
506 
507 
makeBreadboardSvg(const QString & expectedFileName)508 QString MysteryPart::makeBreadboardSvg(const QString & expectedFileName)
509 {
510 	if (expectedFileName.contains("_sip_")) return makeBreadboardSipSvg(expectedFileName);
511 	if (expectedFileName.contains("_dip_")) return makeBreadboardDipSvg(expectedFileName);
512 
513 	return "";
514 }
515 
makeBreadboardDipSvg(const QString & expectedFileName)516 QString MysteryPart::makeBreadboardDipSvg(const QString & expectedFileName)
517 {
518     QString spacingString;
519     int pins = TextUtils::getPinsAndSpacing(expectedFileName, spacingString);
520 	double spacing = TextUtils::convertToInches(spacingString) * 100;
521 
522 	int increment = 10;
523 
524 	QString repeatT("<rect id='connector%1terminal' x='[1.87]' y='1' fill='#8C8C8C' stroke='none' stroke-width='0' width='2.3' height='0'/>\n"
525 					"<rect id='connector%1pin' x='[1.87]' y='0' fill='#8C8C8C' stroke='none' stroke-width='0' width='2.3' height='3.5'/>\n");
526 
527 	QString repeatB("<rect id='connector%1terminal' x='{1.87}' y='[11.0]' fill='#8C8C8C' stroke='none' stroke-width='0' width='2.3' height='0'/>\n"
528 					"<rect id='connector%1pin' x='{1.87}' y='[7.75]' fill='#8C8C8C' stroke='none' stroke-width='0' width='2.3' height='4.25'/>\n");
529 
530 	QString header("<?xml version='1.0' encoding='utf-8'?>\n"
531 					"<svg version='1.2' baseProfile='tiny' xmlns='http://www.w3.org/2000/svg' \n"
532 					"width='.percent.1in' height='%1in' viewBox='0 0 {16.0022} [12.0]'>\n"
533 					"<g id='breadboard'>\n"
534 					".percent.2\n"
535 					"<rect width='{16.0022}' x='0' y='2.5' height='[6.5]' fill='#000000' id='upper' stroke-width='0' />\n"
536 					"<rect width='{16.0022}' x='0' y='[6.5]' fill='#404040' height='3.096' id='lower' stroke-width='0' />\n"
537 					"<text id='label' x='2.5894' y='{{6.0}}' fill='#e6e6e6' stroke='none' font-family='Droid Sans' text-anchor='start' font-size='7.3' >?</text>\n"
538 					"<circle fill='#8C8C8C' cx='11.0022' cy='{{7.5}}' r='3' stroke-width='0' />\n"
539 					"<text x='11.0022' y='{{9.2}}' font-family='Droid Sans' text-anchor='middle' font-weight='bold' stroke-width='0' font-size='5.5' >?</text>\n"
540 					".percent.3\n"
541 					"</g>\n"
542 					"</svg>\n");
543 
544 
545 	header = TextUtils::incrementTemplateString(header, 1, spacing - increment, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL);
546 	header = header.arg(TextUtils::getViewBoxCoord(header, 3) / 100.0);
547 	if (spacing == 10) {
548 		header.replace("{{6.0}}", "8.0");
549 		header.replace("{{7.5}}", "5.5");
550 		header.replace("{{9.2}}", "7.2");
551 	}
552 	else {
553 		header.replace("{{6.0}}", QString::number(6.0 + ((spacing - increment) / 2)));
554 		header.replace("{{7.5}}", "7.5");
555 		header.replace("{{9.2}}", "9.2");
556 	}
557 	header.replace(".percent.", "%");
558 	header.replace("{", "[");
559 	header.replace("}", "]");
560 
561 	QString svg = TextUtils::incrementTemplateString(header, 1, increment * ((pins - 4) / 2), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL);
562 
563 	repeatB = TextUtils::incrementTemplateString(repeatB, 1, spacing - increment, TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL);
564 	repeatB.replace("{", "[");
565 	repeatB.replace("}", "]");
566 
567 	int userData[2];
568 	userData[0] = pins;
569 	userData[1] = 1;
570 	QString repeatTs = TextUtils::incrementTemplateString(repeatT, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::negIncCopyPinFunction, userData);
571 	QString repeatBs = TextUtils::incrementTemplateString(repeatB, pins / 2, increment, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL);
572 
573 	return svg.arg(TextUtils::getViewBoxCoord(svg, 2) / 100).arg(repeatTs).arg(repeatBs);
574 }
575 
makeBreadboardSipSvg(const QString & expectedFileName)576 QString MysteryPart::makeBreadboardSipSvg(const QString & expectedFileName)
577 {
578 	QStringList pieces = expectedFileName.split("_");
579 	if (pieces.count() != 6) return "";
580 
581 	int pins = pieces.at(2).toInt();
582 	int increment = 10;
583 
584 	QString header("<?xml version='1.0' encoding='utf-8'?>\n"
585 					"<svg version='1.2' baseProfile='tiny' id='svg2' xmlns='http://www.w3.org/2000/svg' \n"
586 					"width='%1in' height='0.27586in' viewBox='0 0 [6.0022] 27.586'>\n"
587 					"<g id='breadboard'>\n"
588 					"<rect width='[6.0022]' x='0' y='0' height='24.17675' fill='#000000' id='upper' stroke-width='0' />\n"
589 					"<rect width='[6.0022]' x='0' y='22' fill='#404040' height='3.096' id='lower' stroke-width='0' />\n"
590 					"<text id='label' x='2.5894' y='13' fill='#e6e6e6' stroke='none' font-family='Droid Sans' text-anchor='start' font-size='7.3' >?</text>\n"
591 					"<circle fill='#8C8C8C' cx='[1.0022]' cy='5' r='3' stroke-width='0' />\n"
592 					"<text x='[1.0022]' y='6.7' font-family='Droid Sans' text-anchor='middle' font-weight='bold' stroke-width='0' font-size='5.5' >?</text>\n"
593 					"%2\n"
594 					"</g>\n"
595 					"</svg>\n");
596 
597 	QString svg = TextUtils::incrementTemplateString(header, 1, increment * (pins - 1), TextUtils::incMultiplyPinFunction, TextUtils::noCopyPinFunction, NULL);
598 
599 	QString repeat("<rect id='connector%1terminal' stroke='none' stroke-width='0' x='[1.87]' y='25.586' fill='#8C8C8C' width='2.3' height='2.0'/>\n"
600 					"<rect id='connector%1pin' stroke='none' stroke-width='0' x='[1.87]' y='23.336' fill='#8C8C8C' width='2.3' height='4.25'/>\n");
601 
602 	QString repeats = TextUtils::incrementTemplateString(repeat, pins, increment, TextUtils::standardMultiplyPinFunction, TextUtils::standardCopyPinFunction, NULL);
603 
604 	return svg.arg(TextUtils::getViewBoxCoord(svg, 2) / 100).arg(repeats);
605 }
606 
changePinLabels(bool singleRow,bool sip)607 bool MysteryPart::changePinLabels(bool singleRow, bool sip) {
608 	Q_UNUSED(singleRow);
609 
610 	if (m_viewID != ViewLayer::SchematicView) return true;
611 
612 	bool hasLocal = false;
613 	QStringList labels = getPinLabels(hasLocal);
614 	if (labels.count() == 0) return true;
615 
616     QTransform  transform = untransform();
617 
618 	QString svg = MysteryPart::makeSchematicSvg(labels, sip);
619 
620     QString chipLabel = modelPart()->localProp("chip label").toString();
621     if (!chipLabel.isEmpty()) {
622         svg =TextUtils::replaceTextElement(svg, "label", chipLabel);
623     }
624 
625     resetLayerKin(svg);
626 
627     retransform(transform);
628 
629 	return true;
630 }
631 
swapEntry(const QString & text)632 void MysteryPart::swapEntry(const QString & text) {
633 
634     FamilyPropertyComboBox * comboBox = qobject_cast<FamilyPropertyComboBox *>(sender());
635     if (comboBox == NULL) return;
636 
637     QString layout = m_propsMap.value("layout");
638 
639     if (comboBox->prop().contains("layout", Qt::CaseInsensitive)) {
640         layout = text;
641     }
642 
643     if (layout.isEmpty()) {
644         if (moduleID().contains("sip", Qt::CaseInsensitive)) {
645             layout = "single";
646         }
647     }
648 
649     if (layout.contains("single", Qt::CaseInsensitive)) {
650         generateSwap(text, genModuleID, genSipFZP, makeBreadboardSvg, makeSchematicSvg, PinHeader::makePcbSvg);
651     }
652     else {
653         generateSwap(text, genModuleID, genDipFZP, makeBreadboardSvg, makeSchematicSvg, makePcbDipSvg);
654     }
655     PaletteItem::swapEntry(text);
656 }
657 
makePcbDipSvg(const QString & expectedFileName)658 QString MysteryPart::makePcbDipSvg(const QString & expectedFileName)
659 {
660     QString spacingString;
661 	int pins = TextUtils::getPinsAndSpacing(expectedFileName, spacingString);
662     if (pins == 0) return "";
663 
664 	QString header("<?xml version='1.0' encoding='UTF-8'?>\n"
665 				    "<svg baseProfile='tiny' version='1.2' width='%1in' height='%2in' viewBox='0 0 %3 %4' xmlns='http://www.w3.org/2000/svg'>\n"
666 				    "<desc>Fritzing footprint SVG</desc>\n"
667 					"<g id='silkscreen'>\n"
668 					"<line stroke='white' stroke-width='10' x1='10' x2='10' y1='10' y2='%5'/>\n"
669 					"<line stroke='white' stroke-width='10' x1='10' x2='%6' y1='%5' y2='%5'/>\n"
670 					"<line stroke='white' stroke-width='10' x1='%6' x2='%6' y1='%5' y2='10'/>\n"
671 					"<line stroke='white' stroke-width='10' x1='10' x2='%7' y1='10' y2='10'/>\n"
672 					"<line stroke='white' stroke-width='10' x1='%8' x2='%6' y1='10' y2='10'/>\n"
673 					"</g>\n"
674 					"<g id='copper1'><g id='copper0'>\n"
675 					"<rect id='square' fill='none' height='55' stroke='rgb(255, 191, 0)' stroke-width='20' width='55' x='32.5' y='32.5'/>\n");
676 
677 	double outerBorder = 10;
678 	double silkSplitTop = 100;
679 	double offsetX = 60;
680 	double offsetY = 60;
681 	double spacing = TextUtils::convertToInches(spacingString) * GraphicsUtils::StandardFritzingDPI;
682 	double totalWidth = 120 + spacing;
683 	double totalHeight = (100 * pins / 2) + (outerBorder * 2);
684 	double center = totalWidth / 2;
685 
686 	QString svg = header.arg(totalWidth / GraphicsUtils::StandardFritzingDPI).arg(totalHeight / GraphicsUtils::StandardFritzingDPI).arg(totalWidth).arg(totalHeight)
687 							.arg(totalHeight - outerBorder).arg(totalWidth - outerBorder)
688 							.arg(center - (silkSplitTop / 2)).arg(center + (silkSplitTop / 2));
689 
690 	QString circle("<circle cx='%1' cy='%2' fill='none' id='connector%3pin' r='27.5' stroke='rgb(255, 191, 0)' stroke-width='20'/>\n");
691 
692 	int y = offsetY;
693 	for (int i = 0; i < pins / 2; i++) {
694 		svg += circle.arg(offsetX).arg(y).arg(i);
695 		svg += circle.arg(totalWidth - offsetX).arg(y).arg(pins - 1 - i);
696 		y += 100;
697 	}
698 
699 	svg += "</g></g>\n";
700 	svg += "</svg>\n";
701 
702     int hsix = expectedFileName.indexOf(HoleSizePrefix);
703     if (hsix >= 0) {
704         return hackSvgHoleSizeAux(svg, expectedFileName);
705     }
706 
707 	return svg;
708 }
709