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