1 /*******************************************************************
2 
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5 
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with Fritzing.  If not, see <http://www.gnu.org/licenses/>.
18 
19 ********************************************************************
20 
21 $Revision: 6998 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-28 13:51:10 +0200 (So, 28. Apr 2013) $
24 
25 ********************************************************************/
26 
27 #include "resistor.h"
28 #include "../utils/graphicsutils.h"
29 #include "../utils/textutils.h"
30 #include "../utils/focusoutcombobox.h"
31 #include "../utils/boundedregexpvalidator.h"
32 #include "../fsvgrenderer.h"
33 #include "../sketch/infographicsview.h"
34 #include "../svg/svgfilesplitter.h"
35 #include "../commands.h"
36 #include "../layerattributes.h"
37 #include "moduleidnames.h"
38 #include "partlabel.h"
39 #include "../debugdialog.h"
40 
41 #include <qmath.h>
42 #include <QRegExpValidator>
43 
44 static QStringList Resistances;
45 static QHash<QString, QString> PinSpacings;
46 static QHash<int, QColor> ColorBands;
47 static QString OhmSymbol(QChar(0x03A9));
48 static QString PlusMinusSymbol(QChar(0x0B1));
49 static QHash<QString, QColor> Tolerances;
50 
51 // TODO
52 //	save into parts bin
53 //	other manifestations of "220"?
54 
Resistor(ModelPart * modelPart,ViewLayer::ViewID viewID,const ViewGeometry & viewGeometry,long id,QMenu * itemMenu,bool doLabel)55 Resistor::Resistor( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel)
56 	: Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel)
57 {
58 	m_changingPinSpacing = false;
59 	if (Resistances.count() == 0) {
60 		Resistances
61 		 << QString("0") + OhmSymbol
62 		 << QString("1") + OhmSymbol << QString("1.5") + OhmSymbol << QString("2.2") + OhmSymbol << QString("3.3") + OhmSymbol << QString("4.7") + OhmSymbol << QString("6.8") + OhmSymbol
63 		 << QString("10") + OhmSymbol << QString("15") + OhmSymbol << QString("22") + OhmSymbol << QString("33") + OhmSymbol << QString("47") + OhmSymbol << QString("68") + OhmSymbol
64 		 << QString("100") + OhmSymbol << QString("150") + OhmSymbol << QString("220") + OhmSymbol << QString("330") + OhmSymbol << QString("470") + OhmSymbol << QString("680") + OhmSymbol
65 		 << QString("1k") + OhmSymbol << QString("1.5k") + OhmSymbol << QString("2.2k") + OhmSymbol << QString("3.3k") + OhmSymbol << QString("4.7k") + OhmSymbol << QString("6.8k") + OhmSymbol
66 		 << QString("10k") + OhmSymbol << QString("15k") + OhmSymbol << QString("22k") + OhmSymbol << QString("33k") + OhmSymbol << QString("47k") + OhmSymbol << QString("68k") + OhmSymbol
67 		 << QString("100k") + OhmSymbol << QString("150k") + OhmSymbol << QString("220k") + OhmSymbol << QString("330k") + OhmSymbol << QString("470k") + OhmSymbol << QString("680k") + OhmSymbol
68 		 << QString("1M") + OhmSymbol;
69 	}
70 
71 	if (PinSpacings.count() == 0) {
72 		PinSpacings.insert("100 mil (stand-up right)", "pcb/axial_stand0_2_100mil_pcb.svg");
73 		PinSpacings.insert("100 mil (stand-up left)", "pcb/axial_stand1_2_100mil_pcb.svg");
74 		PinSpacings.insert("200 mil", "pcb/axial_lay_2_200mil_pcb.svg");
75 		PinSpacings.insert("300 mil", "pcb/axial_lay_2_300mil_pcb.svg");
76 		PinSpacings.insert("400 mil", "pcb/axial_lay_2_400mil_pcb.svg");
77 		PinSpacings.insert("500 mil", "pcb/axial_lay_2_500mil_pcb.svg");
78 		PinSpacings.insert("600 mil", "pcb/axial_lay_2_600mil_pcb.svg");
79 		PinSpacings.insert("800 mil", "pcb/axial_lay_2_800mil_pcb.svg");
80 	}
81 
82 	if (ColorBands.count() == 0) {
83 		ColorBands.insert(0, QColor(0, 0, 0));
84 		ColorBands.insert(1, QColor(138, 61, 6));
85 		ColorBands.insert(2, QColor(196, 8, 8));
86 		ColorBands.insert(3, QColor(255, 77, 0));
87 		ColorBands.insert(4, QColor(255, 213, 0));
88 		ColorBands.insert(5, QColor(0, 163, 61));
89 		ColorBands.insert(6, QColor(0, 96, 182));
90 		ColorBands.insert(7, QColor(130, 16, 210));
91 		ColorBands.insert(8, QColor(140, 140, 140));
92 		ColorBands.insert(9, QColor(255, 255, 255));
93 		ColorBands.insert(-1, QColor(173, 159, 78));
94 		ColorBands.insert(-2, QColor(192, 192, 192));
95 	}
96 
97 	if (Tolerances.count() == 0) {
98 		// TODO: move this into properties.xml
99 		Tolerances.insert(PlusMinusSymbol + "0.05%", QColor(140, 140, 140));
100 		Tolerances.insert(PlusMinusSymbol + "0.1%", QColor(130, 16, 210));
101 		Tolerances.insert(PlusMinusSymbol + "0.25%", QColor(0, 96, 182));
102 		Tolerances.insert(PlusMinusSymbol + "0.5%", QColor(0, 163, 61));
103 		Tolerances.insert(PlusMinusSymbol + "1%", QColor(138, 61, 6));
104 		Tolerances.insert(PlusMinusSymbol + "2%", QColor(196, 8, 8));
105 		Tolerances.insert(PlusMinusSymbol + "5%", QColor(173, 159, 78));
106 		Tolerances.insert(PlusMinusSymbol + "10%", QColor(192, 192, 192));
107 		Tolerances.insert(PlusMinusSymbol + "20%", QColor(0xdb, 0xb4, 0x77));
108 	}
109 
110 	m_ohms = modelPart->localProp("resistance").toString();
111 	if (m_ohms.isEmpty()) {
112 		m_ohms = modelPart->properties().value("resistance", "220");
113 		modelPart->setLocalProp("resistance", m_ohms);
114 	}
115 
116 	m_pinSpacing = modelPart->localProp("pin spacing").toString();
117 	if (m_pinSpacing.isEmpty()) {
118 		m_pinSpacing = modelPart->properties().value("pin spacing", "400 mil");
119 		modelPart->setLocalProp("pin spacing", m_pinSpacing);
120 	}
121 
122 	updateResistances(m_ohms);
123 }
124 
~Resistor()125 Resistor::~Resistor() {
126 }
127 
setResistance(QString resistance,QString pinSpacing,bool force)128 void Resistor::setResistance(QString resistance, QString pinSpacing, bool force) {
129 
130 	QString tolerance = prop("tolerance");
131 
132 	if (resistance.endsWith(OhmSymbol)) {
133 		resistance.chop(1);
134 	}
135 
136 	modelPart()->setLocalTitle(resistance + " " + OhmSymbol + " " + tr("Resistor"));
137 
138 	switch (this->m_viewID) {
139 		case ViewLayer::BreadboardView:
140 			if (force || resistance.compare(m_ohms) != 0) {
141 				QString svg = makeSvg(resistance, m_viewLayerID);
142 				//DebugDialog::debug(svg);
143 				reloadRenderer(svg, false);
144 			}
145 			break;
146 		case ViewLayer::PCBView:
147 			if (force || pinSpacing.compare(m_pinSpacing) != 0) {
148 
149 				InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
150 				if (infoGraphicsView == NULL) break;
151 
152 				if (modelPart()->properties().value("package").compare("tht", Qt::CaseInsensitive) == 0)
153 				{
154                     // pinspacing is irrelevant for SMD resistors
155 					QString filename = PinSpacings.value(pinSpacing, "");
156 					if (filename.isEmpty()) break;
157 
158                     QString original = modelPart()->imageFileName(m_viewID);
159                     modelPart()->setImageFileName(m_viewID, filename);
160 					m_changingPinSpacing = true;
161 					resetImage(infoGraphicsView);
162 					m_changingPinSpacing = false;
163                     modelPart()->setImageFileName(m_viewID, original);
164                     QList<ConnectorItem *> already;
165 					updateConnections(false, already);
166 				}
167 
168 			}
169 			break;
170 		default:
171 			break;
172 	}
173 
174 	m_ohms = resistance;
175 	m_pinSpacing = pinSpacing;
176 	modelPart()->setLocalProp("resistance", resistance);
177 	modelPart()->setLocalProp("pin spacing", pinSpacing);
178 	modelPart()->setLocalProp("tolerance", tolerance);
179 
180 	updateResistances(m_ohms);
181     if (m_partLabel) m_partLabel->displayTextsIf();
182 }
183 
retrieveSvg(ViewLayer::ViewLayerID viewLayerID,QHash<QString,QString> & svgHash,bool blackOnly,double dpi,double & factor)184 QString Resistor::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash<QString, QString> & svgHash, bool blackOnly, double dpi, double & factor)
185 {
186 	switch (viewLayerID) {
187 		case ViewLayer::Breadboard:
188 		case ViewLayer::Icon:
189 			break;
190 		default:
191 			return Capacitor::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor);
192 	}
193 
194 	QString svg = makeSvg(m_ohms, viewLayerID);
195     return PaletteItemBase::normalizeSvg(svg, viewLayerID, blackOnly, dpi, factor);
196 }
197 
makeSvg(const QString & resistance,ViewLayer::ViewLayerID viewLayerID)198 QString Resistor::makeSvg(const QString & resistance, ViewLayer::ViewLayerID viewLayerID) {
199 
200 	QString moduleID = this->moduleID();
201 	double ohms = TextUtils::convertFromPowerPrefix(resistance, OhmSymbol);
202 	QString sohms = QString::number(ohms, 'e', 3);
203 	int firstband = sohms.at(0).toLatin1() - '0';
204 	int secondband = sohms.at(2).toLatin1() - '0';
205 	int thirdband = sohms.at(3).toLatin1() - '0';
206 	int temp = (firstband * 10) + secondband;
207 	if (moduleID.contains("5Band")) {
208 		temp = (temp * 10) + thirdband;
209 	}
210 	int multiplier = (temp == 0) ? 0 : log10(ohms / temp);
211 
212 	QString tolerance = prop("tolerance");
213 
214 	QString errorStr;
215 	int errorLine;
216 	int errorColumn;
217 	QDomDocument domDocument;
218 	QString fn = (viewLayerID == ViewLayer::Breadboard) ? m_breadboardSvgFile : m_iconSvgFile;
219 	QFile file(fn);
220 	if (!domDocument.setContent(&file, &errorStr, &errorLine, &errorColumn)) {
221 		DebugDialog::debug(QString("makesvg failed %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn));
222 		return "";
223 	}
224 
225 	QDomElement root = domDocument.documentElement();
226 	setBands(root, firstband, secondband, thirdband, multiplier, tolerance);
227 	return domDocument.toString();
228 
229 }
230 
setBands(QDomElement & element,int firstband,int secondband,int thirdband,int multiplier,const QString & tolerance)231 void Resistor::setBands(QDomElement & element, int firstband, int secondband, int thirdband, int multiplier, const QString & tolerance)
232 {
233 
234 	QString id = element.attribute("id");
235 	if (!id.isEmpty()) {
236 		if (id.compare("band_1_st") == 0) {
237 			element.setAttribute("fill", ColorBands.value(firstband, Qt::black).name());
238 		}
239 		else if (id.compare("band_2_nd") == 0) {
240 			element.setAttribute("fill", ColorBands.value(secondband, Qt::black).name());
241 		}
242 		else if (id.compare("band_3") == 0) {
243 			element.setAttribute("fill", ColorBands.value(thirdband, Qt::black).name());
244 		}
245 		else if (id.compare("band_rd_multiplier") == 0) {
246 			element.setAttribute("fill", ColorBands.value(multiplier, Qt::black).name());
247 		}
248 		else if (id.compare("gold_band") == 0) {
249 			element.setAttribute("fill", Tolerances.value(tolerance, QColor(173, 159, 78)).name());
250 		}
251 	}
252 
253 	QDomElement child = element.firstChildElement();
254 	while (!child.isNull()) {
255 		setBands(child, firstband, secondband, thirdband, multiplier, tolerance);
256 		child = child.nextSiblingElement();
257 	}
258 }
259 
collectExtraInfo(QWidget * parent,const QString & family,const QString & prop,const QString & value,bool swappingEnabled,QString & returnProp,QString & returnValue,QWidget * & returnWidget,bool & hide)260 bool Resistor::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide)
261 {
262 	if (prop.compare("resistance", Qt::CaseInsensitive) == 0) {
263 		returnProp = tr("resistance");
264 
265 		FocusOutComboBox * focusOutComboBox = new FocusOutComboBox();
266 		focusOutComboBox->setEnabled(swappingEnabled);
267 		focusOutComboBox->setEditable(true);
268 		QString current = m_ohms + OhmSymbol;
269 		focusOutComboBox->addItems(Resistances);
270 		focusOutComboBox->setCurrentIndex(focusOutComboBox->findText(current));
271 		BoundedRegExpValidator * validator = new BoundedRegExpValidator(focusOutComboBox);
272 		validator->setSymbol(OhmSymbol);
273 		validator->setConverter(TextUtils::convertFromPowerPrefix);
274 		validator->setBounds(0, 9900000000.0);
275 		validator->setRegExp(QRegExp(QString("((\\d{1,3})|(\\d{1,3}\\.)|(\\d{1,3}\\.\\d{1,2}))[\\x%1umkMG]{0,1}[\\x03A9]{0,1}").arg(TextUtils::MicroSymbolCode, 4, 16, QChar('0'))));
276 		focusOutComboBox->setValidator(validator);
277 		connect(focusOutComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(resistanceEntry(const QString &)));
278 
279 		focusOutComboBox->setObjectName("infoViewComboBox");
280         focusOutComboBox->setToolTip(tr("You can either type in a resistance value, or select one from the drop down. Format nnn.dP where P is one of 'umkMG'"));
281 
282 		returnValue = current;
283 		returnWidget = focusOutComboBox;
284 
285 		return true;
286 	}
287 
288 	return Capacitor::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide);
289 }
290 
getProperty(const QString & key)291 QString Resistor::getProperty(const QString & key) {
292 	if (key.compare("resistance", Qt::CaseInsensitive) == 0) {
293 		return m_ohms + OhmSymbol;
294 	}
295 
296 	if (key.compare("pin spacing", Qt::CaseInsensitive) == 0) {
297 		return m_pinSpacing;
298 	}
299 
300 	return Capacitor::getProperty(key);
301 }
302 
resistance()303 QString Resistor::resistance() {
304 	return m_ohms;
305 }
306 
pinSpacing()307 QString Resistor::pinSpacing() {
308 	return m_pinSpacing;
309 }
310 
addedToScene(bool temporary)311 void Resistor::addedToScene(bool temporary)
312 {
313 	if (this->scene()) {
314 		setResistance(m_ohms, m_pinSpacing, true);
315 	}
316 
317     return Capacitor::addedToScene(temporary);
318 }
319 
title()320 const QString & Resistor::title() {
321 	m_title = QString("%1%2 Resistor").arg(m_ohms).arg(OhmSymbol);
322 	return m_title;
323 }
324 
updateResistances(QString r)325 void Resistor::updateResistances(QString r) {
326 	if (!Resistances.contains(r + OhmSymbol)) {
327 		Resistances.append(r + OhmSymbol);
328 	}
329 }
330 
newConnectorItem(Connector * connector)331 ConnectorItem* Resistor::newConnectorItem(Connector *connector) {
332 	if (m_changingPinSpacing) {
333 		return connector->connectorItemByViewLayerID(viewID(), viewLayerID());
334 	}
335 
336 	return Capacitor::newConnectorItem(connector);
337 }
338 
newConnectorItem(ItemBase * layerKin,Connector * connector)339 ConnectorItem* Resistor::newConnectorItem(ItemBase * layerKin, Connector *connector) {
340 	if (m_changingPinSpacing) {
341 		return connector->connectorItemByViewLayerID(viewID(), layerKin->viewLayerID());
342 	}
343 
344 	return Capacitor::newConnectorItem(layerKin, connector);
345 }
346 
hasCustomSVG()347 bool Resistor::hasCustomSVG() {
348 	switch (m_viewID) {
349 		case ViewLayer::BreadboardView:
350 		case ViewLayer::IconView:
351 			return true;
352 		default:
353 			return ItemBase::hasCustomSVG();
354 	}
355 }
356 
canEditPart()357 bool Resistor::canEditPart() {
358 	return true;
359 }
360 
collectValues(const QString & family,const QString & prop,QString & value)361 QStringList Resistor::collectValues(const QString & family, const QString & prop, QString & value) {
362 	if (prop.compare("pin spacing", Qt::CaseInsensitive) == 0) {
363 		QStringList values;
364 		foreach (QString f, PinSpacings.keys()) {
365 			values.append(f);
366 		}
367 		value = m_pinSpacing;
368 		return values;
369 	}
370 
371 	return Capacitor::collectValues(family, prop, value);
372 }
373 
resistanceEntry(const QString & text)374 void Resistor::resistanceEntry(const QString & text) {
375     //DebugDialog::debug(QString("resistance entry %1").arg(text));
376 
377     InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
378     if (infoGraphicsView != NULL) {
379             infoGraphicsView->setResistance(text, "");
380     }
381 }
382 
isPlural()383 ItemBase::PluralType Resistor::isPlural() {
384 	return Plural;
385 }
386 
setProp(const QString & prop,const QString & value)387 void Resistor::setProp(const QString & prop, const QString & value)
388 {
389 	Capacitor::setProp(prop, value);
390 
391 	if (prop.compare("tolerance") == 0) {
392 		setResistance(m_ohms, m_pinSpacing, true);
393 	}
394 }
395 
setUpImage(ModelPart * modelPart,const LayerHash & viewLayers,LayerAttributes & layerAttributes)396 bool Resistor::setUpImage(ModelPart * modelPart, const LayerHash & viewLayers, LayerAttributes & layerAttributes)
397 {
398 	bool result = Capacitor::setUpImage(modelPart, viewLayers, layerAttributes);
399 	if (layerAttributes.viewLayerID == ViewLayer::Breadboard) {
400 		if (result && m_breadboardSvgFile.isEmpty()) m_breadboardSvgFile = layerAttributes.filename();
401 	}
402 	else if (layerAttributes.viewLayerID == ViewLayer::Icon) {
403 		if (result && m_iconSvgFile.isEmpty()) m_iconSvgFile = layerAttributes.filename();
404 	}
405 	return result;
406 }
407 
useViewIDForPixmap(ViewLayer::ViewID vid,bool swappingEnabled)408 ViewLayer::ViewID Resistor::useViewIDForPixmap(ViewLayer::ViewID vid, bool swappingEnabled) {
409     if (swappingEnabled && vid == ViewLayer::BreadboardView) {
410         return vid;
411     }
412 
413     return ItemBase::useViewIDForPixmap(vid, swappingEnabled);
414 }
415