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: 6984 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-22 23:44:56 +0200 (Mo, 22. Apr 2013) $
24 
25 ********************************************************************/
26 
27 #include "ruler.h"
28 #include "../utils/graphicsutils.h"
29 #include "../fsvgrenderer.h"
30 #include "../sketch/infographicsview.h"
31 #include "../svg/svgfilesplitter.h"
32 #include "moduleidnames.h"
33 #include "../utils/textutils.h"
34 #include "../utils/boundedregexpvalidator.h"
35 
36 #include <QHBoxLayout>
37 #include <QVBoxLayout>
38 #include <QFrame>
39 #include <QLabel>
40 #include <QLineEdit>
41 #include <QRegExp>
42 #include <qmath.h>
43 
44 static const int IndexCm = 0;
45 static const int IndexIn = 1;
46 
47 static QString DefaultWidth = "";
48 
Ruler(ModelPart * modelPart,ViewLayer::ViewID viewID,const ViewGeometry & viewGeometry,long id,QMenu * itemMenu,bool doLabel)49 Ruler::Ruler( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel)
50 	: PaletteItem(modelPart, viewID, viewGeometry, id, itemMenu, doLabel)
51 {
52 	m_widthEditor = NULL;
53 	m_unitsEditor = NULL;
54 	m_widthValidator = NULL;
55 	QString w = modelPart->localProp("width").toString();
56 	if (w.isEmpty()) {
57 		if (DefaultWidth.isEmpty()) {
58 			DefaultWidth = modelPart->properties().value("width", "10cm");
59 		}
60 		m_modelPart->setLocalProp("width", DefaultWidth);
61 	}
62 }
63 
~Ruler()64 Ruler::~Ruler() {
65 }
66 
resizeMM(double magnitude,double unitsFlag,const LayerHash & viewLayers)67 void Ruler::resizeMM(double magnitude, double unitsFlag, const LayerHash & viewLayers) {
68 
69 	// note this really isn't resizeMM but resizeUnits
70 
71 	Q_UNUSED(viewLayers);
72 
73 	double w = TextUtils::convertToInches(prop("width"));
74 	QString units((unitsFlag == IndexCm) ? "cm" : "in");
75 	double newW = TextUtils::convertToInches(QString::number(magnitude) + units);
76 	if (w == newW) return;
77 
78 	QString s = makeSvg(newW);
79 
80 	bool result = resetRenderer(s);
81 	if (result) {
82         modelPart()->setLocalProp("width", QVariant(QString::number(magnitude) + units));
83 	}
84 	//	DebugDialog::debug(QString("fast load result %1 %2").arg(result).arg(s));
85 
86 }
87 
retrieveSvg(ViewLayer::ViewLayerID viewLayerID,QHash<QString,QString> & svgHash,bool blackOnly,double dpi,double & factor)88 QString Ruler::retrieveSvg(ViewLayer::ViewLayerID viewLayerID, QHash<QString, QString> & svgHash, bool blackOnly, double dpi, double & factor)
89 {
90 	double w = TextUtils::convertToInches(m_modelPart->localProp("width").toString());
91 	if (w != 0) {
92 		QString xml;
93 		switch (viewLayerID) {
94 			case ViewLayer::BreadboardRuler:
95 			case ViewLayer::SchematicRuler:
96 			case ViewLayer::PcbRuler:
97 				xml = makeSvg(w);
98 				break;
99 			default:
100 				break;
101 		}
102 
103 		if (!xml.isEmpty()) {
104 			 return PaletteItemBase::normalizeSvg(xml, viewLayerID, blackOnly, dpi, factor);
105 		}
106 	}
107 
108 	return PaletteItemBase::retrieveSvg(viewLayerID, svgHash, blackOnly, dpi, factor);
109 }
110 
makeSvg(double inches)111 QString Ruler::makeSvg(double inches) {
112 	double cm = 1 / 2.54;
113 	double offset = 0.125;
114 	double mmW = inches * 25.4;
115 	QString svg = TextUtils::makeSVGHeader(GraphicsUtils::SVGDPI, GraphicsUtils::StandardFritzingDPI, (inches + offset + offset) * GraphicsUtils::SVGDPI, GraphicsUtils::SVGDPI);
116 	svg += "<g font-family='Droid Sans' text-anchor='middle' font-size='100' stroke-width='1' stroke='black'>";
117 	int counter = 0;
118 	for (int i = 0; i <= qCeil(mmW); i++) {
119 		double h = cm / 4;
120 		double x = (offset + (i / 25.4)) * GraphicsUtils::StandardFritzingDPI;
121 		if (i % 10 == 0) {
122 			h = cm / 2;
123 			double y = (h + .1) * GraphicsUtils::StandardFritzingDPI;
124 			svg += QString("<text x='%1' y='%2'>%3</text>")
125 					.arg(x)
126 					.arg(y)
127 					.arg(QString::number(counter++));
128 			if (counter == 1) {
129 				svg += QString("<text x='%1' y='%2'>cm</text>").arg(x + 103).arg(y);
130 			}
131 		}
132 		else if (i % 5 == 0) {
133 			h = 3 * cm / 8;
134 		}
135 		svg += QString("<line x1='%1' y1='0' x2='%1' y2='%2' />\n")
136 			.arg(x)
137 			.arg(h * GraphicsUtils::StandardFritzingDPI);
138 	}
139 	counter = 0;
140 	for (int i = 0; i <= inches * 16; i++) {
141 		double h = 0.125;
142 		double x = (offset + (i / 16.0)) * GraphicsUtils::StandardFritzingDPI;
143 		if (i % 16 == 0) {
144 			h = .125 +  (3.0 / 16);
145 			double y = 1000 - ((h + .015) * GraphicsUtils::StandardFritzingDPI);
146 			svg += QString("<text x='%1' y='%2'>%3</text>")
147 					.arg(x)
148 					.arg(y)
149 					.arg(QString::number(counter++));
150 			if (counter == 1) {
151 				svg += QString("<text x='%1' y='%2'>in</text>").arg(x + 81).arg(y);
152 			}
153 		}
154 		else if (i % 8 == 0) {
155 			h = .125 +  (2.0 / 16);
156 		}
157 		else if (i % 4 == 0) {
158 			h = .125 +  (1.0 / 16);
159 		}
160 		svg += QString("<line x1='%1' y1='%2' x2='%1' y2='1000' />\n")
161 			.arg(x)
162 			.arg(1000 - (h * GraphicsUtils::StandardFritzingDPI));
163 	}
164 
165 	for (int i = 0; i <= inches * 10; i++) {
166 		double x = (offset + (i / 10.0)) * GraphicsUtils::StandardFritzingDPI;
167 		double h = .125 + (3.0 / 16);
168 		double h2 = h - (cm / 4);
169 		if (i % 10 != 0) {
170 			if (i % 5 == 0) {
171 				h2 = .125 +  (2.0 / 16);
172 			}
173 			svg += QString("<line x1='%1' y1='%2' x2='%1' y2='%3' />\n")
174 				.arg(x)
175 				.arg(1000 - (h * GraphicsUtils::StandardFritzingDPI))
176 				.arg(1000 - (h2 * GraphicsUtils::StandardFritzingDPI));
177 		}
178 	}
179 
180     svg += QString("<line x1='%1' y1='%2' x2='%3' y2='%2' stroke-width='1' stroke='black' />\n")
181         .arg(offset * GraphicsUtils::StandardFritzingDPI)
182         .arg((GraphicsUtils::StandardFritzingDPI / 2) - 40)
183         .arg((inches + offset) * GraphicsUtils::StandardFritzingDPI);
184 	svg += "<g font-size='40'>\n";
185 	svg += QString("<text x='%1' y='%2'>1/10</text>").arg((GraphicsUtils::StandardFritzingDPI * offset / 2.0) + 7).arg(780);
186 	svg += QString("<text x='%1' y='%2'>1/16</text>").arg((GraphicsUtils::StandardFritzingDPI * offset / 2.0) + 7).arg(990);
187 	svg += "</g>";
188 
189 
190 	svg += "</g></svg>";
191 	return svg;
192 }
193 
hasCustomSVG()194 bool Ruler::hasCustomSVG() {
195 	switch (m_viewID) {
196 		case ViewLayer::PCBView:
197 		case ViewLayer::SchematicView:
198 		case ViewLayer::BreadboardView:
199 			return true;
200 		default:
201 			return ItemBase::hasCustomSVG();
202 	}
203 }
204 
collectExtraInfo(QWidget * parent,const QString & family,const QString & prop,const QString & value,bool swappingEnabled,QString & returnProp,QString & returnValue,QWidget * & returnWidget,bool & hide)205 bool Ruler::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide)
206 {
207 	bool result = PaletteItem::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide);
208 
209 	if (prop.compare("width", Qt::CaseInsensitive) == 0) {
210 		returnProp = tr("width");
211 
212 		int units = m_modelPart->localProp("width").toString().contains("cm") ? IndexCm : IndexIn;
213 		QLineEdit * e1 = new QLineEdit();
214 		QDoubleValidator * validator = new QDoubleValidator(e1);
215 		validator->setRange(1.0, 20 * ((units == IndexCm) ? 2.54 : 1), 2);
216 		validator->setNotation(QDoubleValidator::StandardNotation);
217         validator->setLocale(QLocale::C);
218 		e1->setValidator(validator);
219 		e1->setEnabled(swappingEnabled);
220 		QString temp = m_modelPart->localProp("width").toString();
221 		temp.chop(2);
222 		e1->setText(temp);
223 		e1->setObjectName("infoViewLineEdit");
224         e1->setMaximumWidth(80);
225 
226 		m_widthEditor = e1;
227 		m_widthValidator = validator;
228 
229 		QComboBox * comboBox = new QComboBox(parent);
230 		comboBox->setEditable(false);
231 		comboBox->setEnabled(swappingEnabled);
232 		comboBox->addItem("cm");
233 		comboBox->addItem("in");
234 		comboBox->setCurrentIndex(units);
235 		m_unitsEditor = comboBox;
236 		comboBox->setObjectName("infoViewComboBox");
237         comboBox->setMinimumWidth(60);
238 
239 
240 		QHBoxLayout * hboxLayout = new QHBoxLayout();
241 		hboxLayout->setAlignment(Qt::AlignLeft);
242 		hboxLayout->setContentsMargins(0, 0, 0, 0);
243 		hboxLayout->setSpacing(0);
244 		hboxLayout->setMargin(0);
245 
246 
247 		hboxLayout->addWidget(e1);
248 		hboxLayout->addWidget(comboBox);
249 
250 		QFrame * frame = new QFrame();
251 		frame->setLayout(hboxLayout);
252 		frame->setObjectName("infoViewPartFrame");
253 
254 		connect(e1, SIGNAL(editingFinished()), this, SLOT(widthEntry()));
255 		connect(comboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(unitsEntry(const QString &)));
256 
257         returnValue = temp + QString::number(units);
258 		returnWidget = frame;
259 
260 		return true;
261 	}
262 
263 	return result;
264 }
265 
widthEntry()266 void Ruler::widthEntry() {
267 	QLineEdit * edit = qobject_cast<QLineEdit *>(sender());
268 	if (edit == NULL) return;
269 
270 	QString t = edit->text();
271 	QString w = prop("width");
272 	w.chop(2);
273 	if (t.compare(w) == 0) {
274 		return;
275 	}
276 
277 	InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
278 	if (infoGraphicsView != NULL) {
279 		int units = (m_unitsEditor->currentText() == "cm") ? IndexCm : IndexIn;
280 		DefaultWidth = edit->text() + m_unitsEditor->currentText();
281 		infoGraphicsView->resizeBoard(edit->text().toDouble(), units, false);
282 	}
283 }
284 
unitsEntry(const QString & units)285 void Ruler::unitsEntry(const QString & units) {
286 	double inches = TextUtils::convertToInches(prop("width"));
287 	if (units == "in") {
288         modelPart()->setLocalProp("width", QVariant(QString::number(inches) + "in"));
289 		m_widthEditor->setText(QString::number(inches));
290 		m_widthValidator->setTop(20);
291 	}
292 	else {
293         modelPart()->setLocalProp("width", QVariant(QString::number(inches * 2.54) + "cm"));
294 		m_widthEditor->setText(QString::number(inches * 2.54));
295 		m_widthValidator->setTop(20 * 2.54);
296 	}
297 	DefaultWidth = prop("width");
298 }
299 
stickyEnabled()300 bool Ruler::stickyEnabled() {
301 	return false;
302 }
303 
hasPartLabel()304 bool Ruler::hasPartLabel() {
305     return false;
306 }
307 
isPlural()308 ItemBase::PluralType Ruler::isPlural() {
309 	return Singular;
310 }
311 
312 
addedToScene(bool temporary)313 void Ruler::addedToScene(bool temporary)
314 {
315 	if (this->scene()) {
316 		LayerHash viewLayers;
317 		QString w = prop("width");
318 		modelPart()->setLocalProp("width", "");							// makes sure resizeMM will do the work
319 		double inches = TextUtils::convertToInches(w);
320 		if (w.endsWith("cm")) {
321 			resizeMM(inches * 2.54, IndexCm, viewLayers);
322 		}
323 		else {
324 			resizeMM(inches, IndexIn, viewLayers);
325 		}
326 	}
327 
328     return PaletteItem::addedToScene(temporary);
329 }
330 
hasPartNumberProperty()331 bool Ruler::hasPartNumberProperty()
332 {
333 	return false;
334 }
335 
canFindConnectorsUnder()336 bool Ruler::canFindConnectorsUnder() {
337 	return false;
338 }
339 
useViewIDForPixmap(ViewLayer::ViewID,bool)340 ViewLayer::ViewID Ruler::useViewIDForPixmap(ViewLayer::ViewID, bool)
341 {
342     return ViewLayer::IconView;
343 }
344