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: 6904 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $
24 
25 ********************************************************************/
26 
27 
28 ///////////////////////////////////////
29 
30 // todo:
31 //	save and reload as settings
32 //	enable/disable custom on radio presses
33 //	change wording on custom via
34 //	actually modify the autorouter
35 //	enable single vs. double-sided settings
36 
37 
38 ///////////////////////////////////////
39 
40 #include "autoroutersettingsdialog.h"
41 
42 #include <QLabel>
43 #include <QGroupBox>
44 #include <QDialogButtonBox>
45 #include <QHBoxLayout>
46 #include <QVBoxLayout>
47 #include <QPushButton>
48 #include <QSettings>
49 #include <QComboBox>
50 
51 #include "../items/tracewire.h"
52 #include "../items/via.h"
53 #include "../fsvgrenderer.h"
54 #include "../sketch/pcbsketchwidget.h"
55 #include "../utils/textutils.h"
56 #include "../utils/graphicsutils.h"
57 #include "drc.h"
58 
59 
60 const QString AutorouterSettingsDialog::AutorouteTraceWidth = "autorouteTraceWidth";
61 
AutorouterSettingsDialog(QHash<QString,QString> & settings,QWidget * parent)62 AutorouterSettingsDialog::AutorouterSettingsDialog(QHash<QString, QString> & settings, QWidget *parent) : QDialog(parent)
63 {
64     m_traceWidth = settings.value(AutorouteTraceWidth).toInt();
65 
66 	Via::initHoleSettings(m_holeSettings);
67     m_holeSettings.ringThickness = settings.value(Via::AutorouteViaRingThickness);
68     m_holeSettings.holeDiameter = settings.value(Via::AutorouteViaHoleSize);
69 
70 	this->setWindowTitle(QObject::tr("Autorouter Settings"));
71 
72 	QVBoxLayout * windowLayout = new QVBoxLayout();
73 	this->setLayout(windowLayout);
74 
75 	QGroupBox * prodGroupBox = new QGroupBox(tr("Production type"), this);
76 	QVBoxLayout * prodLayout = new QVBoxLayout();
77 	prodGroupBox->setLayout(prodLayout);
78 
79 	m_homebrewButton = new QRadioButton(tr("homebrew"), this);
80 	connect(m_homebrewButton, SIGNAL(clicked(bool)), this, SLOT(production(bool)));
81 
82 	m_professionalButton = new QRadioButton(tr("professional"), this);
83 	connect(m_professionalButton, SIGNAL(clicked(bool)), this, SLOT(production(bool)));
84 
85 	m_customButton = new QRadioButton(tr("custom"), this);
86 	connect(m_customButton, SIGNAL(clicked(bool)), this, SLOT(production(bool)));
87 
88 	m_customFrame = new QFrame(this);
89 	QHBoxLayout * customFrameLayout = new QHBoxLayout(this);
90 	m_customFrame->setLayout(customFrameLayout);
91 
92 	customFrameLayout->addSpacing(5);
93 
94 	QFrame * innerFrame = new QFrame(this);
95 	QVBoxLayout * innerFrameLayout = new QVBoxLayout(this);
96 	innerFrame->setLayout(innerFrameLayout);
97 
98     QWidget * traceWidget = createTraceWidget();
99     QWidget * keepoutWidget = createKeepoutWidget(settings.value(DRC::KeepoutSettingName));
100     QWidget * viaWidget = createViaWidget();
101 
102     QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
103 	buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
104 	buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
105 
106     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
107     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
108 
109 	innerFrameLayout->addWidget(traceWidget);
110 	innerFrameLayout->addWidget(keepoutWidget);
111 	innerFrameLayout->addWidget(viaWidget);
112 
113 	customFrameLayout->addWidget(innerFrame);
114 
115 	prodLayout->addWidget(m_homebrewButton);
116 	prodLayout->addWidget(m_professionalButton);
117 	prodLayout->addWidget(m_customButton);
118 	prodLayout->addWidget(m_customFrame);
119 
120 	windowLayout->addWidget(prodGroupBox);
121 
122 	windowLayout->addSpacerItem(new QSpacerItem(1, 10, QSizePolicy::Preferred, QSizePolicy::Expanding));
123 
124 	windowLayout->addWidget(buttonBox);
125 
126 	enableCustom(initProductionType());
127 }
128 
~AutorouterSettingsDialog()129 AutorouterSettingsDialog::~AutorouterSettingsDialog() {
130 }
131 
production(bool checked)132 void AutorouterSettingsDialog::production(bool checked) {
133 	Q_UNUSED(checked);
134 
135 	QString units;
136 	if (sender() == m_homebrewButton) {
137 		enableCustom(false);
138 		changeHoleSize(sender()->property("holesize").toString() + "," + sender()->property("ringthickness").toString());
139 		setTraceWidth(16);
140         setDefaultKeepout();
141 	}
142 	else if (sender() == m_professionalButton) {
143 		enableCustom(false);
144 		changeHoleSize(sender()->property("holesize").toString() + "," + sender()->property("ringthickness").toString());
145 		setTraceWidth(24);
146         setDefaultKeepout();
147 	}
148 	else if (sender() == m_customButton) {
149 		enableCustom(true);
150 	}
151 }
152 
enableCustom(bool enable)153 void AutorouterSettingsDialog::enableCustom(bool enable)
154 {
155 	m_customFrame->setVisible(enable);
156 }
157 
initProductionType()158 bool AutorouterSettingsDialog::initProductionType()
159 {
160     m_homebrewButton->setChecked(false);
161     m_professionalButton->setChecked(false);
162 
163     int custom = 0;
164 
165     QString keepoutString = getKeepoutString();
166     double mils = TextUtils::convertToInches(keepoutString) * 1000;
167     if (qAbs(mils - DRC::KeepoutDefaultMils) >= 1) {
168         custom++;
169     }
170 
171     double standard = GraphicsUtils::pixels2mils(Wire::STANDARD_TRACE_WIDTH, GraphicsUtils::SVGDPI);
172     if (qAbs(m_traceWidth - standard) >= 1) {
173         custom++;
174     }
175 
176     custom++;  // assume the holesize/ringthickness won't match
177     double rt = TextUtils::convertToInches(m_holeSettings.ringThickness);
178     double hs = TextUtils::convertToInches(m_holeSettings.holeDiameter);
179 	foreach (QString name, m_holeSettings.holeThing->holeSizeKeys) {
180         // have to loop through all values to set up the two buttons
181 		QStringList values = m_holeSettings.holeThing->holeSizes.value(name).split(",");
182 		QString ringThickness = values[1];
183 		QString holeSize = values[0];
184 		if (!name.isEmpty() && !ringThickness.isEmpty() && !holeSize.isEmpty()) {
185 			QRadioButton * button = NULL;
186 			if (name.contains("home", Qt::CaseInsensitive)) button = m_homebrewButton;
187 			else if (name.contains("standard", Qt::CaseInsensitive)) button = m_professionalButton;
188 			if (button) {
189 				button->setProperty("ringthickness", ringThickness);
190 				button->setProperty("holesize", holeSize);
191                 double krt = TextUtils::convertToInches(ringThickness);
192                 double khs = TextUtils::convertToInches(holeSize);
193 				if (qAbs(rt - krt) < 0.001 && qAbs(hs - khs) < 0.001) {
194                     // holesize/ringthickness match after all
195                     if (--custom == 0) {
196 					    button->setChecked(true);
197                     }
198 				}
199 			}
200 		}
201 	}
202 
203     m_customButton->setChecked(custom > 0);
204 	return custom > 0;
205 }
206 
widthEntry(const QString & text)207 void AutorouterSettingsDialog::widthEntry(const QString & text) {
208 	int w = TraceWire::widthEntry(text, sender());
209 	if (w == 0) return;
210 
211 	m_traceWidth = w;
212 }
213 
changeHoleSize(const QString & newSize)214 void AutorouterSettingsDialog::changeHoleSize(const QString & newSize) {
215 	QString s = newSize;
216 	PaletteItem::setHoleSize(s, false, m_holeSettings);
217 }
218 
changeUnits(bool)219 void AutorouterSettingsDialog::changeUnits(bool)
220 {
221 	QString newVal = PaletteItem::changeUnits(m_holeSettings);
222 }
223 
changeDiameter()224 void AutorouterSettingsDialog::changeDiameter()
225 {
226 	if (PaletteItem::changeDiameter(m_holeSettings, sender())) {
227 		QLineEdit * edit = qobject_cast<QLineEdit *>(sender());
228 		changeHoleSize(edit->text() + m_holeSettings.currentUnits() + "," + m_holeSettings.ringThickness);
229 	}
230 }
231 
changeThickness()232 void AutorouterSettingsDialog::changeThickness()
233 {
234 	if (PaletteItem::changeThickness(m_holeSettings, sender())) {
235 		QLineEdit * edit = qobject_cast<QLineEdit *>(sender());
236 		changeHoleSize(m_holeSettings.holeDiameter + "," + edit->text() + m_holeSettings.currentUnits());
237 	}
238 }
239 
240 
setTraceWidth(int width)241 void AutorouterSettingsDialog::setTraceWidth(int width)
242 {
243 	for (int i = 0; i > m_traceWidthComboBox->count(); i++) {
244 		if (m_traceWidthComboBox->itemData(i).toInt() == width) {
245 			m_traceWidthComboBox->setCurrentIndex(i);
246 			return;
247 		}
248 	}
249 }
250 
createKeepoutWidget(const QString & keepoutString)251 QWidget * AutorouterSettingsDialog::createKeepoutWidget(const QString & keepoutString) {
252 	QGroupBox * keepoutGroupBox = new QGroupBox(tr("Keepout"), this);
253 	QVBoxLayout * vLayout = new QVBoxLayout();
254 
255 	QLabel * label = new QLabel(tr("<b>Keepout</b> is the minimum distance between copper elements on different nets."));
256     //label->setWordWrap(true);  // setting wordwrap here seems to break the layout
257 	vLayout->addWidget(label);
258 
259     label = new QLabel(tr("A keepout of 0.01 inch (0.254 mm) is a good default."));
260 	vLayout->addWidget(label);
261 
262 	label = new QLabel(tr("Note: the smaller the keepout, the slower the DRC and Autorouter will run."));
263 	vLayout->addWidget(label);
264 
265     QFrame * frame = new QFrame;
266     QHBoxLayout * frameLayout = new QHBoxLayout;
267 
268     m_keepoutSpinBox = new QDoubleSpinBox;
269     m_keepoutSpinBox->setDecimals(4);
270     m_keepoutSpinBox->setLocale(QLocale::C);
271     connect(m_keepoutSpinBox, SIGNAL(valueChanged(double)), this, SLOT(keepoutEntry()));
272     frameLayout->addWidget(m_keepoutSpinBox);
273 
274     m_inRadio = new QRadioButton("in");
275     frameLayout->addWidget(m_inRadio);
276     connect(m_inRadio, SIGNAL(clicked()), this, SLOT(toInches()));
277 
278     m_mmRadio = new QRadioButton("mm");
279     frameLayout->addWidget(m_mmRadio);
280     connect(m_mmRadio, SIGNAL(clicked()), this, SLOT(toMM()));
281 
282     m_keepoutMils = TextUtils::convertToInches(keepoutString) * 1000;
283     if (keepoutString.endsWith("mm")) {
284         toMM();
285         m_mmRadio->setChecked(true);
286     }
287     else {
288         toInches();
289         m_inRadio->setChecked(true);
290     }
291 
292     frame->setLayout(frameLayout);
293     vLayout->addWidget(frame);
294 
295 	keepoutGroupBox->setLayout(vLayout);
296 
297     return keepoutGroupBox;
298 }
299 
createTraceWidget()300 QWidget * AutorouterSettingsDialog::createTraceWidget() {
301 	QGroupBox * traceGroupBox = new QGroupBox(tr("Trace width"), this);
302 	QBoxLayout * traceLayout = new QVBoxLayout();
303 
304 	m_traceWidthComboBox = TraceWire::createWidthComboBox(m_traceWidth, traceGroupBox);
305     connect(m_traceWidthComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(widthEntry(const QString &)));
306 
307     traceLayout->addWidget(m_traceWidthComboBox);
308 	traceGroupBox->setLayout(traceLayout);
309 
310     return traceGroupBox;
311 }
312 
createViaWidget()313 QWidget * AutorouterSettingsDialog::createViaWidget() {
314 	QGroupBox * viaGroupBox = new QGroupBox("Via size", this);
315 	QVBoxLayout * viaLayout = new QVBoxLayout();
316 
317 	QWidget * viaWidget = Hole::createHoleSettings(viaGroupBox, m_holeSettings, true, "", true);
318 
319 	connect(m_holeSettings.sizesComboBox, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(changeHoleSize(const QString &)));
320 	connect(m_holeSettings.mmRadioButton, SIGNAL(toggled(bool)), this, SLOT(changeUnits(bool)));
321 	connect(m_holeSettings.inRadioButton, SIGNAL(toggled(bool)), this, SLOT(changeUnits(bool)));
322 	connect(m_holeSettings.diameterEdit, SIGNAL(editingFinished()), this, SLOT(changeDiameter()));
323 	connect(m_holeSettings.thicknessEdit, SIGNAL(editingFinished()), this, SLOT(changeThickness()));
324 
325     viaLayout->addWidget(viaWidget);
326 	viaGroupBox->setLayout(viaLayout);
327 
328     return viaGroupBox;
329 }
330 
toInches()331 void AutorouterSettingsDialog::toInches() {
332     m_keepoutSpinBox->blockSignals(true);
333     m_keepoutSpinBox->setRange(.001, 1);
334     m_keepoutSpinBox->setSingleStep(.001);
335     m_keepoutSpinBox->setValue(m_keepoutMils / 1000);
336     m_keepoutSpinBox->blockSignals(false);
337 }
338 
toMM()339 void AutorouterSettingsDialog::toMM() {
340     m_keepoutSpinBox->blockSignals(true);
341     m_keepoutSpinBox->setRange(.001 * 25.4, 1);
342     m_keepoutSpinBox->setSingleStep(.01);
343     m_keepoutSpinBox->setValue(m_keepoutMils * 25.4 / 1000);
344     m_keepoutSpinBox->blockSignals(false);
345 }
346 
keepoutEntry()347 void AutorouterSettingsDialog::keepoutEntry() {
348     double k = m_keepoutSpinBox->value();
349     if (m_inRadio->isChecked()) {
350         m_keepoutMils = k * 1000;
351     }
352     else {
353         m_keepoutMils = k * 1000 / 25.4;
354     }
355 }
356 
setDefaultKeepout()357 void AutorouterSettingsDialog::setDefaultKeepout() {
358     m_keepoutMils = DRC::KeepoutDefaultMils;
359     double inches = DRC::KeepoutDefaultMils / 1000;
360     if (m_inRadio->isChecked()) {
361         m_keepoutSpinBox->setValue(inches);
362     }
363     else {
364         m_keepoutSpinBox->setValue(inches * 25.4);
365     }
366 }
367 
getSettings()368 QHash<QString, QString> AutorouterSettingsDialog::getSettings() {
369     QHash<QString, QString> settings;
370     settings.insert(DRC::KeepoutSettingName, getKeepoutString());
371 	settings.insert(Via::AutorouteViaHoleSize, m_holeSettings.holeDiameter);
372 	settings.insert(Via::AutorouteViaRingThickness, m_holeSettings.ringThickness);
373 	settings.insert(AutorouteTraceWidth, QString::number(m_traceWidth));
374 
375     return settings;
376 }
377 
getKeepoutString()378 QString AutorouterSettingsDialog::getKeepoutString()
379 {
380     double k = m_keepoutSpinBox->value();
381     if (m_inRadio->isChecked()) {
382         return QString("%1in").arg(k);
383     }
384     else {
385         return QString("%1mm").arg(k);
386     }
387 }
388