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