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 "perfboard.h"
28 #include "../utils/graphicsutils.h"
29 #include "../utils/textutils.h"
30 #include "../utils/boundedregexpvalidator.h"
31 #include "../fsvgrenderer.h"
32 #include "../sketch/infographicsview.h"
33 #include "../svg/svgfilesplitter.h"
34 #include "../commands.h"
35 #include "../debugdialog.h"
36 #include "moduleidnames.h"
37 #include "partlabel.h"
38 
39 #include <qmath.h>
40 #include <QRegExpValidator>
41 #include <QHBoxLayout>
42 #include <QVBoxLayout>
43 #include <QLabel>
44 #include <QMessageBox>
45 
46 
47 static const int ConnectorIDJump = 1000;
48 static const int MaxXDimension = 199;
49 static const int MinXDimension = 5;
50 static const int MaxYDimension = 199;
51 static const int MinYDimension = 5;
52 static const int WarningSize = 2000;
53 
54 static const QString OneHole("M%1,%2a%3,%3 0 1 %5 %4,0 %3,%3 0 1 %5 -%4,0z\n");
55 
56 bool Perfboard::m_gotWarning = false;
57 
58 /////////////////////////////////////////////////////////////////////
59 
Perfboard(ModelPart * modelPart,ViewLayer::ViewID viewID,const ViewGeometry & viewGeometry,long id,QMenu * itemMenu,bool doLabel)60 Perfboard::Perfboard( ModelPart * modelPart, ViewLayer::ViewID viewID, const ViewGeometry & viewGeometry, long id, QMenu * itemMenu, bool doLabel)
61 	: Capacitor(modelPart, viewID, viewGeometry, id, itemMenu, doLabel)
62 {
63 	m_size = modelPart->localProp("size").toString();
64 	if (m_size.isEmpty()) {
65 		m_size = modelPart->properties().value("size", "20.20");
66 		modelPart->setLocalProp("size", m_size);
67 	}
68 }
69 
~Perfboard()70 Perfboard::~Perfboard() {
71 }
72 
setProp(const QString & prop,const QString & value)73 void Perfboard::setProp(const QString & prop, const QString & value)
74 {
75 	if (prop.compare("size") != 0) {
76 		Capacitor::setProp(prop, value);
77 		return;
78 	}
79 	switch (this->m_viewID) {
80 		case ViewLayer::BreadboardView:
81 			if (value.compare(m_size) != 0) {
82                 QString temp = value;
83 				QString svg = makeBreadboardSvg(temp);
84 				reloadRenderer(svg, false);
85 				//DebugDialog::debug(svg);
86 			}
87 			break;
88 
89 		default:
90 			break;
91 	}
92 
93 	m_size = value;
94 	modelPart()->setLocalProp("size", value);
95 
96     if (m_partLabel) m_partLabel->displayTextsIf();
97 }
98 
makeBreadboardSvg(const QString & size)99 QString Perfboard::makeBreadboardSvg(const QString & size)
100 {
101 	QString BreadboardLayerTemplate = "";
102 	QString ConnectorTemplate = "";
103 
104 	if (BreadboardLayerTemplate.isEmpty()) {
105 		QFile file(":/resources/templates/perfboard_boardLayerTemplate.txt");
106 		file.open(QFile::ReadOnly);
107 		BreadboardLayerTemplate = file.readAll();
108 		file.close();
109 	}
110 	if (ConnectorTemplate.isEmpty()) {
111 		QFile file(":/resources/templates/perfboard_connectorTemplate.txt");
112 		file.open(QFile::ReadOnly);
113 		ConnectorTemplate = file.readAll();
114 		file.close();
115 	}
116 
117 	int x, y;
118 	getXY(x, y, size);
119 
120 	QString middle;
121 	QString holes;
122 	double radius = 17.5;
123 	int sweepflag = 0;
124 
125 	int top = 100;
126 	for (int iy = 0; iy < y; iy++) {
127 		int left = 100;
128 		for (int jx = 0; jx < x; jx++) {
129 			middle += ConnectorTemplate.arg(left).arg(top).arg(jx).arg(iy).arg(QString::number((iy * ConnectorIDJump) + jx));
130 			holes += OneHole
131 				.arg(left - radius)
132 				.arg(top)
133 				.arg(radius)
134 				.arg(2 * radius)
135 				.arg(sweepflag);
136 
137 			left += 100;
138 		}
139 		top += 100;
140 	}
141 
142 	QString svg = BreadboardLayerTemplate
143 					.arg((x / 10.0) + 0.1)
144 					.arg((y / 10.0) + 0.1)
145 					.arg((x * 100) + 100)
146 					.arg((y * 100) + 100)
147 					.arg(holes)
148 					.arg(x * 100 - 8 + 100)
149 					.arg(y * 100 - 8 + 100)
150 					.arg(middle);
151 
152 	return svg;
153 }
154 
genFZP(const QString & moduleid)155 QString Perfboard::genFZP(const QString & moduleid)
156 {
157 	QString ConnectorFzpTemplate = "";
158 	QString FzpTemplate = "";
159 
160 	if (ConnectorFzpTemplate.isEmpty()) {
161 		QFile file(":/resources/templates/perfboard_connectorFzpTemplate.txt");
162 		file.open(QFile::ReadOnly);
163 		ConnectorFzpTemplate = file.readAll();
164 		file.close();
165 	}
166 	if (FzpTemplate.isEmpty()) {
167 		QFile file(":/resources/templates/perfboard_fzpTemplate.txt");
168 		file.open(QFile::ReadOnly);
169 		FzpTemplate = file.readAll();
170 		file.close();
171 	}
172 
173 	int x, y;
174 	getXY(x, y, moduleid);
175 
176 	QString middle;
177 
178 	for (int iy = 0; iy < y; iy++) {
179 		for (int jx = 0; jx < x; jx++) {
180 			middle += ConnectorFzpTemplate.arg(jx).arg(iy).arg(QString::number((iy * ConnectorIDJump) + jx));
181 		}
182 	}
183 
184 	return FzpTemplate.arg(x).arg(y).arg(middle);
185 }
186 
collectExtraInfo(QWidget * parent,const QString & family,const QString & prop,const QString & value,bool swappingEnabled,QString & returnProp,QString & returnValue,QWidget * & returnWidget,bool & hide)187 bool Perfboard::collectExtraInfo(QWidget * parent, const QString & family, const QString & prop, const QString & value, bool swappingEnabled, QString & returnProp, QString & returnValue, QWidget * & returnWidget, bool & hide)
188 {
189 	if (prop.compare("size", Qt::CaseInsensitive) == 0) {
190 		returnProp = tr("size");
191 		returnValue = m_size;
192 		m_propsMap.insert("size", m_size);
193 
194 		int x, y;
195 		getXY(x, y, m_size);
196 
197 		QFrame * frame = new QFrame();
198 		QVBoxLayout * vboxLayout = new QVBoxLayout();
199 		vboxLayout->setAlignment(Qt::AlignLeft);
200 		vboxLayout->setSpacing(1);
201 		vboxLayout->setContentsMargins(0, 3, 0, 0);
202 		vboxLayout->setMargin(0);
203 
204 		QFrame * subframe1 = new QFrame();
205 		QHBoxLayout * hboxLayout1 = new QHBoxLayout();
206 		hboxLayout1->setAlignment(Qt::AlignLeft);
207 		hboxLayout1->setContentsMargins(0, 0, 0, 0);
208 		hboxLayout1->setSpacing(2);
209 
210 		QLabel * l1 = new QLabel(getColumnLabel());
211 		l1->setMargin(0);
212 		l1->setObjectName("infoViewLabel");
213 		m_xEdit = new QLineEdit();
214 		m_xEdit->setEnabled(swappingEnabled);
215 		QIntValidator * validator = new QIntValidator(m_xEdit);
216 		validator->setRange(MinXDimension, MaxXDimension);
217 		m_xEdit->setObjectName("infoViewLineEdit");
218 		m_xEdit->setValidator(validator);
219 		m_xEdit->setMaxLength(5);
220 		m_xEdit->setText(QString::number(x));
221 
222 		QFrame * subframe2 = new QFrame();
223 		QHBoxLayout * hboxLayout2 = new QHBoxLayout();
224 		hboxLayout2->setAlignment(Qt::AlignLeft);
225 		hboxLayout2->setContentsMargins(0, 0, 0, 0);
226 		hboxLayout2->setSpacing(2);
227 
228 		QLabel * l2 = new QLabel(getRowLabel());
229 		l2->setMargin(0);
230 		l2->setObjectName("infoViewLabel");
231 		m_yEdit = new QLineEdit();
232 		m_yEdit->setEnabled(swappingEnabled);
233 		validator = new QIntValidator(m_yEdit);
234 		validator->setRange(MinYDimension, MaxYDimension);
235 		m_yEdit->setObjectName("infoViewLineEdit");
236 		m_yEdit->setValidator(validator);
237 		m_yEdit->setMaxLength(5);
238 		m_yEdit->setText(QString::number(y));
239 
240 		hboxLayout1->addWidget(l1);
241 		hboxLayout1->addWidget(m_xEdit);
242 
243 		hboxLayout2->addWidget(l2);
244 		hboxLayout2->addWidget(m_yEdit);
245 
246 		subframe1->setLayout(hboxLayout1);
247 		subframe2->setLayout(hboxLayout2);
248 
249 		if (returnWidget != NULL) vboxLayout->addWidget(qobject_cast<QWidget *>(returnWidget));
250 		vboxLayout->addWidget(subframe1);
251 		vboxLayout->addWidget(subframe2);
252 
253 		m_setButton = new QPushButton (tr("set board size"));
254 		m_setButton->setObjectName("infoViewButton");
255 		connect(m_setButton, SIGNAL(pressed()), this, SLOT(changeBoardSize()));
256 		m_setButton->setEnabled(false);
257 
258 		vboxLayout->addWidget(m_setButton);
259 
260 		connect(m_xEdit, SIGNAL(editingFinished()), this, SLOT(enableSetButton()));
261 		connect(m_yEdit, SIGNAL(editingFinished()), this, SLOT(enableSetButton()));
262 
263 		frame->setLayout(vboxLayout);
264 
265 		returnWidget = frame;
266 
267 		return true;
268 	}
269 
270 	return Capacitor::collectExtraInfo(parent, family, prop, value, swappingEnabled, returnProp, returnValue, returnWidget, hide);
271 }
272 
273 
addedToScene(bool temporary)274 void Perfboard::addedToScene(bool temporary)
275 {
276 	if (this->scene()) {
277 		QString temp = m_size;
278 		m_size = "";
279 		setProp("size", temp);
280 	}
281     return Capacitor::addedToScene(temporary);
282 }
283 
canEditPart()284 bool Perfboard::canEditPart() {
285 	return false;
286 }
287 
changeBoardSize()288 void Perfboard::changeBoardSize()
289 {
290 	if (!m_gotWarning) {
291 		int x = m_xEdit->text().toInt();
292 		int y = m_yEdit->text().toInt();
293 		if (x * y >= WarningSize) {
294 			m_gotWarning = true;
295 			QMessageBox messageBox(NULL);
296 			messageBox.setWindowTitle(tr("Performance Warning"));
297 			messageBox.setText(tr("Performance of perfboards and stripboards with more than approximately 2000 holes can be slow. Are you sure ?\n"
298 				"\nNote: this warning will not be repeated during this session."
299 				));
300 			messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
301 			messageBox.setDefaultButton(QMessageBox::Cancel);
302 			messageBox.setIcon(QMessageBox::Warning);
303 			messageBox.setWindowModality(Qt::WindowModal);
304 			messageBox.setButtonText(QMessageBox::Ok, tr("Set new size"));
305 			messageBox.setButtonText(QMessageBox::Cancel, tr("Cancel"));
306 			QMessageBox::StandardButton answer = (QMessageBox::StandardButton) messageBox.exec();
307 
308 			if (answer != QMessageBox::Ok) {
309 				getXY(x, y, m_size);
310 				m_xEdit->setText(QString::number(x));
311 				m_yEdit->setText(QString::number(y));
312 				return;
313 			}
314 		}
315 	}
316 
317 	QString newSize = QString("%1.%2").arg(m_xEdit->text()).arg(m_yEdit->text());
318     m_propsMap.insert("size", newSize);
319 
320     foreach (QString key, m_propsMap.keys()) {
321         DebugDialog::debug("prop " + key + " " + m_propsMap.value(key));
322     }
323 
324     InfoGraphicsView * infoGraphicsView = InfoGraphicsView::getInfoGraphicsView(this);
325     if (infoGraphicsView != NULL) {
326         infoGraphicsView->swap(family(), "size", m_propsMap, this);
327     }
328 }
329 
isPlural()330 ItemBase::PluralType Perfboard::isPlural() {
331 	return Plural;
332 }
333 
enableSetButton()334 void Perfboard::enableSetButton() {
335 	QLineEdit * edit = qobject_cast<QLineEdit *>(sender());
336 	if (edit == NULL) return;
337 
338 	int x, y;
339 	getXY(x, y, m_size);
340 
341 	int vx = m_xEdit->text().toInt();
342 	int vy = m_yEdit->text().toInt();
343 
344 	m_setButton->setEnabled(vx != x || vy != y);
345 }
346 
genModuleID(QMap<QString,QString> & currPropsMap)347 QString Perfboard::genModuleID(QMap<QString, QString> & currPropsMap)
348 {
349 	QString size = currPropsMap.value("size");
350 	return size + ModuleIDNames::PerfboardModuleIDName;
351 }
352 
getXY(int & x,int & y,const QString & s)353 bool Perfboard::getXY(int & x, int & y, const QString & s)
354 {
355 	QRegExp re("(\\d+)\\.(\\d+)");
356 
357 	int ix = re.indexIn(s);
358 	if (ix < 0) return false;
359 
360 	bool ok;
361 	x = re.cap(1).toInt(&ok);
362 	if (!ok) return false;
363 
364 	y = re.cap(2).toInt(&ok);
365 	return ok;
366 }
367 
rotation45Allowed()368 bool Perfboard::rotation45Allowed() {
369 	return false;
370 }
371 
hoverUpdate()372 void Perfboard::hoverUpdate()
373 {
374 }
375 
paintHover(QPainter * painter,const QStyleOptionGraphicsItem * option,QWidget * widget)376 void Perfboard::paintHover(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
377 {
378 	Q_UNUSED(painter);
379 	Q_UNUSED(option);
380 	Q_UNUSED(widget);
381 }
382 
stickyEnabled()383 bool Perfboard::stickyEnabled() {
384 	return false;
385 }
386 
canFindConnectorsUnder()387 bool Perfboard::canFindConnectorsUnder() {
388 	return false;
389 }
390 
getRowLabel()391 QString Perfboard::getRowLabel() {
392 	return tr("rows");
393 }
394 
getColumnLabel()395 QString Perfboard::getColumnLabel() {
396 	return tr("columns");
397 }
398