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