1 /*******************************************************************
2
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2015 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: 6972 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-18 07:18:04 +0200 (Do, 18. Apr 2013) $
24
25 ********************************************************************/
26
27 #include "prefsdialog.h"
28 #include "../debugdialog.h"
29 #include "translatorlistmodel.h"
30 #include "../items/itembase.h"
31 #include "../utils/clickablelabel.h"
32 #include "setcolordialog.h"
33 #include "../sketch/zoomablegraphicsview.h"
34 #include "../mainwindow/mainwindow.h"
35 #include "../utils/folderutils.h"
36
37 #include <QFormLayout>
38 #include <QLabel>
39 #include <QComboBox>
40 #include <QPushButton>
41 #include <QLocale>
42 #include <QDialogButtonBox>
43 #include <QGroupBox>
44 #include <QRadioButton>
45 #include <QSpinBox>
46 #include <QSettings>
47 #include <QLineEdit>
48
49 #define MARGIN 5
50 #define FORMLABELWIDTH 195
51 #define SPACING 15
52
PrefsDialog(const QString & language,QWidget * parent)53 PrefsDialog::PrefsDialog(const QString & language, QWidget *parent)
54 : QDialog(parent)
55 {
56 this->setWindowTitle(QObject::tr("Preferences"));
57
58 m_name = language;
59 m_cleared = false;
60 m_wheelMapping = (int) ZoomableGraphicsView::wheelMapping();
61 }
62
~PrefsDialog()63 PrefsDialog::~PrefsDialog()
64 {
65 }
66
initViewInfo(int index,const QString & viewName,const QString & shortName,bool curvy)67 void PrefsDialog::initViewInfo(int index, const QString & viewName, const QString & shortName, bool curvy)
68 {
69 m_viewInfoThings[index].index = index;
70 m_viewInfoThings[index].viewName = viewName;
71 m_viewInfoThings[index].shortName = shortName;
72 m_viewInfoThings[index].curvy = curvy;
73 }
74
initLayout(QFileInfoList & languages,QList<Platform * > platforms)75 void PrefsDialog::initLayout(QFileInfoList & languages, QList<Platform *> platforms)
76 {
77 m_tabWidget = new QTabWidget();
78 m_general = new QWidget();
79 m_breadboard = new QWidget();
80 m_schematic = new QWidget();
81 m_pcb = new QWidget();
82 m_code = new QWidget();
83 m_tabWidget->setObjectName("preDia_tabs");
84
85 m_tabWidget->addTab(m_general, tr("General"));
86 m_tabWidget->addTab(m_breadboard, m_viewInfoThings[0].viewName);
87 m_tabWidget->addTab(m_schematic, m_viewInfoThings[1].viewName);
88 m_tabWidget->addTab(m_pcb, m_viewInfoThings[2].viewName);
89 m_tabWidget->addTab(m_code, tr("Code View"));
90
91 QVBoxLayout * vLayout = new QVBoxLayout();
92 vLayout->addWidget(m_tabWidget);
93
94 initGeneral(m_general, languages);
95
96 initBreadboard(m_breadboard, &m_viewInfoThings[0]);
97 initSchematic(m_schematic, &m_viewInfoThings[1]);
98 initPCB(m_pcb, &m_viewInfoThings[2]);
99
100 initCode(m_code, platforms);
101 m_platforms = platforms;
102
103 QDialogButtonBox * buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
104 buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
105 buttonBox->button(QDialogButtonBox::Ok)->setText(tr("OK"));
106
107 connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
108 connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
109
110 vLayout->addWidget(buttonBox);
111
112 this->setLayout(vLayout);
113
114 }
115
initGeneral(QWidget * widget,QFileInfoList & languages)116 void PrefsDialog::initGeneral(QWidget * widget, QFileInfoList & languages)
117 {
118 QVBoxLayout * vLayout = new QVBoxLayout();
119
120 // TODO: if no translation files found, don't put up the translation part of this dialog
121
122 vLayout->addWidget(createLanguageForm(languages));
123 vLayout->addWidget(createColorForm());
124 vLayout->addWidget(createZoomerForm());
125 vLayout->addWidget(createAutosaveForm());
126
127 vLayout->addWidget(createOtherForm());
128
129 widget->setLayout(vLayout);
130 }
131
initBreadboard(QWidget * widget,ViewInfoThing * viewInfoThing)132 void PrefsDialog::initBreadboard(QWidget * widget, ViewInfoThing * viewInfoThing)
133 {
134 QVBoxLayout * vLayout = new QVBoxLayout();
135
136 vLayout->addWidget(createCurvyForm(viewInfoThing));
137 vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding));
138
139 widget->setLayout(vLayout);
140 }
141
initSchematic(QWidget * widget,ViewInfoThing * viewInfoThing)142 void PrefsDialog::initSchematic(QWidget * widget, ViewInfoThing * viewInfoThing)
143 {
144 QVBoxLayout * vLayout = new QVBoxLayout();
145 vLayout->addWidget(createCurvyForm(viewInfoThing));
146 vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding));
147
148 widget->setLayout(vLayout);
149 }
150
initPCB(QWidget * widget,ViewInfoThing * viewInfoThing)151 void PrefsDialog::initPCB(QWidget * widget, ViewInfoThing * viewInfoThing)
152 {
153 QVBoxLayout * vLayout = new QVBoxLayout();
154 vLayout->addWidget(createCurvyForm(viewInfoThing));
155 vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding));
156
157 widget->setLayout(vLayout);
158 }
159
initCode(QWidget * widget,QList<Platform * > platforms)160 void PrefsDialog::initCode(QWidget * widget, QList<Platform *> platforms)
161 {
162 QVBoxLayout * vLayout = new QVBoxLayout();
163 vLayout->addWidget(createProgrammerForm(platforms));
164 vLayout->addSpacerItem(new QSpacerItem(1, 1, QSizePolicy::Preferred, QSizePolicy::Expanding));
165 widget->setLayout(vLayout);
166 }
167
createZoomerForm()168 QWidget * PrefsDialog::createZoomerForm() {
169 QGroupBox * zoomer = new QGroupBox(tr("Mouse Wheel Behavior"), this );
170
171 QHBoxLayout * zhlayout = new QHBoxLayout();
172 zhlayout->setSpacing(SPACING);
173
174 QFrame * frame = new QFrame();
175 frame->setFixedWidth(FORMLABELWIDTH);
176
177 QVBoxLayout * vLayout = new QVBoxLayout();
178 vLayout->setSpacing(0);
179 vLayout->setMargin(0);
180
181 for (int i = 0; i < 3; i++) {
182 m_wheelLabel[i] = new QLabel();
183 vLayout->addWidget(m_wheelLabel[i]);
184 }
185
186 updateWheelText();
187
188 frame->setLayout(vLayout);
189 zhlayout->addWidget(frame);
190
191 QPushButton * pushButton = new QPushButton(tr("Change Wheel Behavior"), this);
192 connect(pushButton, SIGNAL(clicked()), this, SLOT(changeWheelBehavior()));
193 zhlayout->addWidget(pushButton);
194
195 zoomer->setLayout(zhlayout);
196
197 return zoomer;
198 }
199
createAutosaveForm()200 QWidget * PrefsDialog::createAutosaveForm() {
201 QGroupBox * autosave = new QGroupBox(tr("Autosave"), this );
202
203 QHBoxLayout * zhlayout = new QHBoxLayout();
204 zhlayout->setSpacing(SPACING);
205
206 QCheckBox * box = new QCheckBox(tr("Autosave every:"));
207 box->setChecked(MainWindow::AutosaveEnabled);
208 zhlayout->addWidget(box);
209
210 zhlayout->addSpacerItem(new QSpacerItem(0,0,QSizePolicy::Expanding));
211
212 QSpinBox * spinBox = new QSpinBox;
213 spinBox->setMinimum(1);
214 spinBox->setMaximum(60);
215 spinBox->setValue(MainWindow::AutosaveTimeoutMinutes);
216 spinBox->setMaximumWidth(80);
217 zhlayout->addWidget(spinBox);
218
219 QLabel * label = new QLabel(tr("minutes"));
220 zhlayout->addWidget(label);
221
222 autosave->setLayout(zhlayout);
223
224 connect(box, SIGNAL(clicked(bool)), this, SLOT(toggleAutosave(bool)));
225 connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(changeAutosavePeriod(int)));
226
227
228 return autosave;
229 }
230
createLanguageForm(QFileInfoList & languages)231 QWidget * PrefsDialog::createLanguageForm(QFileInfoList & languages)
232 {
233 QGroupBox * formGroupBox = new QGroupBox(tr("Language"));
234 QVBoxLayout *layout = new QVBoxLayout();
235
236 QComboBox* comboBox = new QComboBox(this);
237 m_translatorListModel = new TranslatorListModel(languages, this);
238 comboBox->setModel(m_translatorListModel);
239 comboBox->setCurrentIndex(m_translatorListModel->findIndex(m_name));
240 connect(comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeLanguage(int)));
241
242 layout->addWidget(comboBox);
243
244 QLabel * ll = new QLabel();
245 ll->setMinimumHeight(45);
246 ll->setWordWrap(true);
247 ll->setText(QObject::tr("Please note that a new language setting will not take effect "
248 "until the next time you run Fritzing."));
249 layout->addWidget(ll);
250
251 formGroupBox->setLayout(layout);
252 return formGroupBox;
253 }
254
createColorForm()255 QWidget* PrefsDialog::createColorForm()
256 {
257 QGroupBox * formGroupBox = new QGroupBox(tr("Colors"));
258 QVBoxLayout *layout = new QVBoxLayout();
259 layout->setSpacing(0);
260 layout->setMargin(0);
261
262 QFrame * f1 = new QFrame();
263 QHBoxLayout * h1 = new QHBoxLayout();
264 h1->setSpacing(SPACING);
265
266 QLabel * c1 = new QLabel(QObject::tr("Connected highlight color"));
267 c1->setWordWrap(true);
268 c1->setFixedWidth(FORMLABELWIDTH);
269 h1->addWidget(c1);
270
271 QColor connectedColor = ItemBase::connectedColor();
272 ClickableLabel * cl1 = new ClickableLabel(tr("%1 (click to change...)").arg(connectedColor.name()), this);
273 connect(cl1, SIGNAL(clicked()), this, SLOT(setConnectedColor()));
274 cl1->setPalette(QPalette(connectedColor));
275 cl1->setAutoFillBackground(true);
276 cl1->setMargin(MARGIN);
277 h1->addWidget(cl1);
278
279 f1->setLayout(h1);
280 layout->addWidget(f1);
281
282 QFrame * f2 = new QFrame();
283 QHBoxLayout * h2 = new QHBoxLayout();
284 h2->setSpacing(SPACING);
285
286 QLabel * c2 = new QLabel(QObject::tr("Unconnected highlight color"));
287 c2->setWordWrap(true);
288 c2->setFixedWidth(FORMLABELWIDTH);
289 h2->addWidget(c2);
290
291 QColor unconnectedColor = ItemBase::unconnectedColor();
292 ClickableLabel * cl2 = new ClickableLabel(tr("%1 (click to change...)").arg(unconnectedColor.name()), this);
293 connect(cl2, SIGNAL(clicked()), this, SLOT(setUnconnectedColor()));
294 cl2->setPalette(QPalette(unconnectedColor));
295 cl2->setAutoFillBackground(true);
296 cl2->setMargin(MARGIN);
297 h2->addWidget(cl2);
298
299 f2->setLayout(h2);
300 layout->addWidget(f2);
301
302 formGroupBox->setLayout(layout);
303 return formGroupBox;
304 }
305
createOtherForm()306 QWidget* PrefsDialog::createOtherForm()
307 {
308 QGroupBox * formGroupBox = new QGroupBox(tr("Clear Settings"));
309 QHBoxLayout *layout = new QHBoxLayout();
310 layout->setSpacing(SPACING);
311
312 QFrame * frame = new QFrame;
313 QVBoxLayout * vlayout = new QVBoxLayout();
314 vlayout->setMargin(0);
315 vlayout->setSpacing(0);
316
317 QLabel * clearLabel = new QLabel(QObject::tr("Clear all saved settings and close this dialog immediately."));
318 clearLabel->setWordWrap(true);
319 clearLabel->setFixedWidth(FORMLABELWIDTH);
320 vlayout->addWidget(clearLabel);
321
322 vlayout->addSpacing(SPACING);
323
324 clearLabel = new QLabel(QObject::tr("This action does not delete any files; it restores settings to their default values."));
325 clearLabel->setWordWrap(true);
326 clearLabel->setFixedWidth(FORMLABELWIDTH);
327 vlayout->addWidget(clearLabel);
328
329 vlayout->addSpacing(SPACING);
330
331 clearLabel = new QLabel(QObject::tr("There is no undo for this action, and no further warning!!!!"));
332 clearLabel->setWordWrap(true);
333 clearLabel->setFixedWidth(FORMLABELWIDTH);
334 vlayout->addWidget(clearLabel);
335
336 frame->setLayout(vlayout);
337 layout->addWidget(frame);
338
339 QPushButton * clear = new QPushButton(QObject::tr("Clear Settings"), this);
340 clear->setMaximumWidth(220);
341 connect(clear, SIGNAL(clicked()), this, SLOT(clear()));
342
343 layout->addWidget(clear);
344
345 formGroupBox->setLayout(layout);
346 return formGroupBox;
347 }
348
createProgrammerForm(QList<Platform * > platforms)349 QWidget* PrefsDialog::createProgrammerForm(QList<Platform *> platforms) {
350 QGroupBox * formGroupBox = new QGroupBox(tr("Platform Support"));
351 QVBoxLayout *layout = new QVBoxLayout();
352 layout->setSpacing(SPACING);
353
354 foreach (Platform * platform, platforms) {
355 QLabel *platformLb = new QLabel("");
356 platformLb->setTextFormat(Qt::RichText);
357 platformLb->setText(tr("<b>%1</b>").arg(platform->getName()));
358 layout->addWidget(platformLb);
359
360 QFrame * locationFrame = new QFrame(formGroupBox);
361 QHBoxLayout * locationLayout = new QHBoxLayout();
362 locationLayout->setMargin(0);
363 locationLayout->setSpacing(0);
364 locationFrame->setLayout(locationLayout);
365
366 QLabel *locationLb = new QLabel(tr("Location:"));
367 locationLayout->addWidget(locationLb);
368 locationLayout->addSpacing(SPACING);
369
370 QLineEdit * locationLE = new QLineEdit(locationFrame);
371 locationLE->setText(platform->getCommandLocation());
372 locationLayout->addWidget(locationLE);
373 m_programmerLEs.insert(platform->getName(), locationLE);
374
375 QPushButton * locateBtn = new QPushButton(tr("..."),locationFrame);
376 locationLayout->addWidget(locateBtn);
377 locateBtn->setProperty("platform", platform->getName());
378 connect(locateBtn, SIGNAL(clicked()), this, SLOT(chooseProgrammer()));
379
380 layout->addWidget(locationFrame);
381
382 QLabel *hintLb = new QLabel("");
383 hintLb->setTextFormat(Qt::RichText);
384 hintLb->setOpenExternalLinks(true);
385 hintLb->setText(tr("You need to have <a href='%1'>%2</a> (version %3 or newer) installed.")
386 .arg(platform->getDownloadUrl().toString())
387 .arg(platform->getIdeName())
388 .arg(platform->getMinVersion()));
389 layout->addWidget(hintLb);
390
391 layout->addSpacing(SPACING);
392 }
393
394 formGroupBox->setLayout(layout);
395 return formGroupBox;
396 }
397
chooseProgrammer()398 void PrefsDialog::chooseProgrammer()
399 {
400 QObject * button = sender();
401 if (button == NULL) return;
402
403 QString platformName = sender()->property("platform").toString();
404 Platform * platform;
405 foreach (Platform *p, m_platforms) {
406 if (p->getName().compare(platformName) == 0) {
407 platform = p;
408 break;
409 }
410 }
411
412 QString filename = FolderUtils::getOpenFileName(
413 this,
414 tr("Select a programmer (executable) for %1").arg(platform->getName()),
415 FolderUtils::openSaveFolder(),
416 "(Programmer *)"
417 );
418 if (!filename.isEmpty()) {
419 m_programmerLEs.value(platformName)->setText(filename);
420 platform->setCommandLocation(filename);
421 }
422 }
423
changeLanguage(int index)424 void PrefsDialog::changeLanguage(int index)
425 {
426 const QLocale * locale = m_translatorListModel->locale(index);
427 if (locale) {
428 m_name = locale->name();
429 m_settings.insert("language", m_name);
430 }
431 }
432
clear()433 void PrefsDialog::clear() {
434 m_cleared = true;
435 accept();
436 }
437
cleared()438 bool PrefsDialog::cleared() {
439 return m_cleared;
440 }
441
setConnectedColor()442 void PrefsDialog::setConnectedColor() {
443 QColor cc = ItemBase::connectedColor();
444 QColor scc = ItemBase::standardConnectedColor();
445
446 SetColorDialog setColorDialog(tr("Connected Highlight"), cc, scc, false, this);
447 int result = setColorDialog.exec();
448 if (result == QDialog::Rejected) return;
449
450 QColor c = setColorDialog.selectedColor();
451 m_settings.insert("connectedColor", c.name());
452 ClickableLabel * cl = qobject_cast<ClickableLabel *>(sender());
453 if (cl) {
454 cl->setPalette(QPalette(c));
455 }
456 }
457
setUnconnectedColor()458 void PrefsDialog::setUnconnectedColor() {
459 QColor cc = ItemBase::unconnectedColor();
460 QColor scc = ItemBase::standardUnconnectedColor();
461
462 SetColorDialog setColorDialog(tr("Unconnected Highlight"), cc, scc, false, this);
463 int result = setColorDialog.exec();
464 if (result == QDialog::Rejected) return;
465
466 QColor c = setColorDialog.selectedColor();
467 m_settings.insert("unconnectedColor", c.name());
468 ClickableLabel * cl = qobject_cast<ClickableLabel *>(sender());
469 if (cl) {
470 cl->setPalette(QPalette(c));
471 }
472 }
473
settings()474 QHash<QString, QString> & PrefsDialog::settings() {
475 return m_settings;
476 }
477
tempSettings()478 QHash<QString, QString> & PrefsDialog::tempSettings() {
479 return m_tempSettings;
480 }
481
changeWheelBehavior()482 void PrefsDialog::changeWheelBehavior() {
483 if (++m_wheelMapping >= ZoomableGraphicsView::WheelMappingCount) {
484 m_wheelMapping = 0;
485 }
486
487 m_settings.insert("wheelMapping", QString("%1").arg(m_wheelMapping));
488 updateWheelText();
489 }
490
updateWheelText()491 void PrefsDialog::updateWheelText() {
492 QString text;
493 #ifdef Q_OS_MAC
494 QString cKey = tr("Command");
495 #else
496 QString cKey = tr("Control");
497 #endif
498
499 switch((ZoomableGraphicsView::WheelMapping) m_wheelMapping) {
500 case ZoomableGraphicsView::ScrollPrimary:
501 default:
502 text = tr("no keys down = scroll\nshift key swaps scroll axis\nAlt or %1 key = zoom").arg(cKey);
503 break;
504 case ZoomableGraphicsView::ZoomPrimary:
505 text = tr("no keys down = zoom\nAlt or %1 key = scroll\nshift key swaps scroll axis").arg(cKey);
506 break;
507 }
508
509 QStringList strings = text.split('\n');
510 for (int i = 0; i < 3; i++) {
511 m_wheelLabel[i]->setText(strings.at(i));
512 }
513 }
514
toggleAutosave(bool checked)515 void PrefsDialog::toggleAutosave(bool checked) {
516 m_settings.insert("autosaveEnabled", QString("%1").arg(checked));
517 }
518
changeAutosavePeriod(int value)519 void PrefsDialog::changeAutosavePeriod(int value) {
520 m_settings.insert("autosavePeriod", QString("%1").arg(value));
521 }
522
createCurvyForm(ViewInfoThing * viewInfoThing)523 QWidget* PrefsDialog::createCurvyForm(ViewInfoThing * viewInfoThing)
524 {
525 QGroupBox * groupBox = new QGroupBox(tr("Curvy vs. straight wires"));
526 QVBoxLayout *layout = new QVBoxLayout;
527
528 QLabel * label1 = new QLabel(tr("When you mouse-down and drag on a wire or the leg of a part (as opposed to a connector or a bendpoint) "
529 "do you want to change the curvature of the wire (or leg) or drag out a new bendpoint?"));
530 label1->setWordWrap(true);
531 layout->addWidget(label1);
532
533 QLabel * label2 = new QLabel(tr("This checkbox sets the default behavior. "
534 "You can switch back to the non-default behavior by holding down the Control key (Mac: Command key) when you drag."));
535 label2->setWordWrap(true);
536 layout->addWidget(label2);
537
538 layout->addSpacing(10);
539
540 QCheckBox * checkbox = new QCheckBox(tr("Curvy wires and legs"));
541 checkbox->setProperty("index", viewInfoThing->index);
542 checkbox->setChecked(viewInfoThing->curvy);
543 connect(checkbox, SIGNAL(clicked()), this, SLOT(curvyChanged()));
544 layout->addWidget(checkbox);
545
546 groupBox->setLayout(layout);
547 return groupBox;
548 }
549
curvyChanged()550 void PrefsDialog::curvyChanged() {
551 QCheckBox * checkBox = qobject_cast<QCheckBox *>(sender());
552 if (checkBox == NULL) return;
553
554 ViewInfoThing * viewInfoThing = &m_viewInfoThings[sender()->property("index").toInt()];
555 m_settings.insert(QString("%1CurvyWires").arg(viewInfoThing->shortName), checkBox->isChecked() ? "1" : "0");
556 }
557