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