src/configDialog.cc

branch
projects
changeset 935
8d98ee0dc917
parent 930
ab77deb851fa
parent 934
be8128aff739
child 936
aee883858c90
equal deleted inserted replaced
930:ab77deb851fa 935:8d98ee0dc917
1 /*
2 * LDForge: LDraw parts authoring CAD
3 * Copyright (C) 2013 - 2015 Teemu Piippo
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * =====================================================================
18 *
19 * configDialog.cxx: Settings dialog and everything related to it.
20 * Actual configuration core is in config.cxx.
21 */
22
23 #include <QGridLayout>
24 #include <QFileDialog>
25 #include <QColorDialog>
26 #include <QBoxLayout>
27 #include <QKeyEvent>
28 #include <QGroupBox>
29 #include <QDoubleSpinBox>
30 #include <QLineEdit>
31 #include <QCheckBox>
32 #include "main.h"
33 #include "configDialog.h"
34 #include "ldDocument.h"
35 #include "configuration.h"
36 #include "miscallenous.h"
37 #include "colors.h"
38 #include "colorSelector.h"
39 #include "glRenderer.h"
40 #include "ui_config.h"
41
42 EXTERN_CFGENTRY (String, YtruderPath)
43 EXTERN_CFGENTRY (String, RectifierPath)
44 EXTERN_CFGENTRY (String, IntersectorPath)
45 EXTERN_CFGENTRY (String, CovererPath)
46 EXTERN_CFGENTRY (String, IsecalcPath)
47 EXTERN_CFGENTRY (String, Edger2Path)
48 EXTERN_CFGENTRY (Bool, YtruderUsesWine)
49 EXTERN_CFGENTRY (Bool, RectifierUsesWine)
50 EXTERN_CFGENTRY (Bool, IntersectorUsesWine)
51 EXTERN_CFGENTRY (Bool, CovererUsesWine)
52 EXTERN_CFGENTRY (Bool, IsecalcUsesWine)
53 EXTERN_CFGENTRY (Bool, Edger2UsesWine)
54 EXTERN_CFGENTRY (String, QuickColorToolbar)
55
56 const char* g_extProgPathFilter =
57 #ifdef _WIN32
58 "Applications (*.exe)(*.exe);;"
59 #endif
60 "All files (*.*)(*.*)";
61
62 //
63 //
64 static struct LDExtProgInfo
65 {
66 QString const name;
67 QString const iconname;
68 QString* const path;
69 QLineEdit* input;
70 QPushButton* setPathButton;
71 bool* const wine;
72 QCheckBox* wineBox;
73 } g_LDExtProgInfo[] =
74 {
75 #ifndef _WIN32
76 # define EXTPROG(NAME, LOWNAME) { #NAME, #LOWNAME, &cfg::NAME##Path, null, null, \
77 &cfg::NAME##UsesWine, null },
78 #else
79 # define EXTPROG(NAME, LOWNAME) { #NAME, #LOWNAME, &cfg::NAME##Path, null, null, null, null },
80 #endif
81 EXTPROG (Ytruder, ytruder)
82 EXTPROG (Rectifier, rectifier)
83 EXTPROG (Intersector, intersector)
84 EXTPROG (Isecalc, isecalc)
85 EXTPROG (Coverer, coverer)
86 EXTPROG (Edger2, edger2)
87 #undef EXTPROG
88 };
89
90 //
91 //
92 ConfigDialog::ConfigDialog (ConfigDialog::Tab deftab, QWidget* parent, Qt::WindowFlags f) :
93 QDialog (parent, f)
94 {
95 assert (g_win != null);
96 ui = new Ui_ConfigUI;
97 ui->setupUi (this);
98
99 // Set defaults
100 m_applyToWidgetOptions ([&](QWidget* wdg, AbstractConfigEntry* conf)
101 {
102 QVariant value (conf->toVariant());
103 QLineEdit* le;
104 QSpinBox* spinbox;
105 QDoubleSpinBox* doublespinbox;
106 QSlider* slider;
107 QCheckBox* checkbox;
108 QPushButton* button;
109
110 if ((le = qobject_cast<QLineEdit*> (wdg)) != null)
111 {
112 le->setText (value.toString());
113 }
114 elif ((spinbox = qobject_cast<QSpinBox*> (wdg)) != null)
115 {
116 spinbox->setValue (value.toInt());
117 }
118 elif ((doublespinbox = qobject_cast<QDoubleSpinBox*> (wdg)) != null)
119 {
120 doublespinbox->setValue (value.toDouble());
121 }
122 elif ((slider = qobject_cast<QSlider*> (wdg)) != null)
123 {
124 slider->setValue (value.toInt());
125 }
126 elif ((checkbox = qobject_cast<QCheckBox*> (wdg)) != null)
127 {
128 checkbox->setChecked (value.toBool());
129 }
130 elif ((button = qobject_cast<QPushButton*> (wdg)) != null)
131 {
132 setButtonBackground (button, value.toString());
133 connect (button, SIGNAL (clicked()), this, SLOT (setButtonColor()));
134 }
135 else
136 {
137 print ("Unknown widget of type %1\n", wdg->metaObject()->className());
138 }
139 });
140
141 g_win->applyToActions ([&](QAction* act)
142 {
143 addShortcut (act);
144 });
145
146 ui->shortcutsList->setSortingEnabled (true);
147 ui->shortcutsList->sortItems();
148 quickColors = LoadQuickColorList();
149 updateQuickColorList();
150 initExtProgs();
151 selectPage (deftab);
152 connect (ui->shortcut_set, SIGNAL (clicked()), this, SLOT (slot_setShortcut()));
153 connect (ui->shortcut_reset, SIGNAL (clicked()), this, SLOT (slot_resetShortcut()));
154 connect (ui->shortcut_clear, SIGNAL (clicked()), this, SLOT (slot_clearShortcut()));
155 connect (ui->quickColor_add, SIGNAL (clicked()), this, SLOT (slot_setColor()));
156 connect (ui->quickColor_remove, SIGNAL (clicked()), this, SLOT (slot_delColor()));
157 connect (ui->quickColor_edit, SIGNAL (clicked()), this, SLOT (slot_setColor()));
158 connect (ui->quickColor_addSep, SIGNAL (clicked()), this, SLOT (slot_addColorSeparator()));
159 connect (ui->quickColor_moveUp, SIGNAL (clicked()), this, SLOT (slot_moveColor()));
160 connect (ui->quickColor_moveDown, SIGNAL (clicked()), this, SLOT (slot_moveColor()));
161 connect (ui->quickColor_clear, SIGNAL (clicked()), this, SLOT (slot_clearColors()));
162 connect (ui->findDownloadPath, SIGNAL (clicked (bool)), this, SLOT (slot_findDownloadFolder()));
163 connect (ui->buttonBox, SIGNAL (clicked (QAbstractButton*)),
164 this, SLOT (buttonClicked (QAbstractButton*)));
165 connect (ui->m_pages, SIGNAL (currentChanged (int)), this, SLOT (selectPage (int)));
166 connect (ui->m_pagelist, SIGNAL (currentRowChanged (int)), this, SLOT (selectPage (int)));
167 }
168
169 //
170 //
171 ConfigDialog::~ConfigDialog()
172 {
173 delete ui;
174 }
175
176 //
177 //
178 void ConfigDialog::selectPage (int row)
179 {
180 ui->m_pagelist->setCurrentRow (row);
181 ui->m_pages->setCurrentIndex (row);
182 }
183
184 //
185 // Adds a shortcut entry to the list of shortcuts.
186 //
187 void ConfigDialog::addShortcut (QAction* act)
188 {
189 ShortcutListItem* item = new ShortcutListItem;
190 item->setIcon (act->icon());
191 item->setAction (act);
192 item->setSequence (act->shortcut());
193 setShortcutText (item);
194
195 // If the action doesn't have a valid icon, use an empty one
196 // so that the list is kept aligned.
197 if (act->icon().isNull())
198 item->setIcon (GetIcon ("empty"));
199
200 ui->shortcutsList->insertItem (ui->shortcutsList->count(), item);
201 }
202
203 //
204 // Initializes the stuff in the ext programs tab
205 //
206 void ConfigDialog::initExtProgs()
207 {
208 QGridLayout* pathsLayout = new QGridLayout;
209 int row = 0;
210
211 for (LDExtProgInfo& info : g_LDExtProgInfo)
212 {
213 QLabel* icon = new QLabel,
214 *progLabel = new QLabel (info.name);
215 QLineEdit* input = new QLineEdit;
216 QPushButton* setPathButton = new QPushButton;
217
218 icon->setPixmap (GetIcon (info.iconname));
219 input->setText (*info.path);
220 setPathButton->setIcon (GetIcon ("folder"));
221 info.input = input;
222 info.setPathButton = setPathButton;
223
224 connect (setPathButton, SIGNAL (clicked()), this, SLOT (slot_setExtProgPath()));
225
226 pathsLayout->addWidget (icon, row, 0);
227 pathsLayout->addWidget (progLabel, row, 1);
228 pathsLayout->addWidget (input, row, 2);
229 pathsLayout->addWidget (setPathButton, row, 3);
230
231 if (info.wine != null)
232 {
233 QCheckBox* wineBox = new QCheckBox ("Wine");
234 wineBox->setChecked (*info.wine);
235 info.wineBox = wineBox;
236 pathsLayout->addWidget (wineBox, row, 4);
237 }
238
239 ++row;
240 }
241
242 ui->extProgs->setLayout (pathsLayout);
243 }
244
245 void ConfigDialog::m_applyToWidgetOptions (std::function<void (QWidget*, AbstractConfigEntry*)> func)
246 {
247 // Apply configuration
248 for (QWidget* widget : findChildren<QWidget*>())
249 {
250 if (not widget->objectName().startsWith ("config"))
251 continue;
252
253 QString confname (widget->objectName().mid (strlen ("config")));
254 AbstractConfigEntry* conf (Config::FindByName (confname));
255
256 if (conf == null)
257 {
258 print ("Couldn't find configuration entry named %1", confname);
259 continue;
260 }
261
262 func (widget, conf);
263 }
264 }
265
266 //
267 // Set the settings based on widget data.
268 //
269 void ConfigDialog::applySettings()
270 {
271 m_applyToWidgetOptions ([&](QWidget* widget, AbstractConfigEntry* conf)
272 {
273 QVariant value (conf->toVariant());
274 QLineEdit* le;
275 QSpinBox* spinbox;
276 QDoubleSpinBox* doublespinbox;
277 QSlider* slider;
278 QCheckBox* checkbox;
279 QPushButton* button;
280
281 if ((le = qobject_cast<QLineEdit*> (widget)) != null)
282 value = le->text();
283 elif ((spinbox = qobject_cast<QSpinBox*> (widget)) != null)
284 value = spinbox->value();
285 elif ((doublespinbox = qobject_cast<QDoubleSpinBox*> (widget)) != null)
286 value = doublespinbox->value();
287 elif ((slider = qobject_cast<QSlider*> (widget)) != null)
288 value = slider->value();
289 elif ((checkbox = qobject_cast<QCheckBox*> (widget)) != null)
290 value = checkbox->isChecked();
291 elif ((button = qobject_cast<QPushButton*> (widget)) != null)
292 value = m_buttonColors[button];
293 else
294 print ("Unknown widget of type %1\n", widget->metaObject()->className());
295
296 conf->loadFromVariant (value);
297 });
298
299 // Rebuild the quick color toolbar
300 g_win->setQuickColors (quickColors);
301 cfg::QuickColorToolbar = quickColorString();
302
303 // Ext program settings
304 for (const LDExtProgInfo& info : g_LDExtProgInfo)
305 {
306 *info.path = info.input->text();
307
308 if (info.wine != null)
309 *info.wine = info.wineBox->isChecked();
310 }
311
312 // Apply shortcuts
313 for (int i = 0; i < ui->shortcutsList->count(); ++i)
314 {
315 auto item = static_cast<ShortcutListItem*> (ui->shortcutsList->item (i));
316 item->action()->setShortcut (item->sequence());
317 }
318
319 Config::Save();
320 LDDocument::current()->reloadAllSubfiles();
321 LoadLogoStuds();
322 g_win->R()->setBackground();
323 g_win->doFullRefresh();
324 g_win->updateDocumentList();
325 }
326
327 //
328 // A dialog button was clicked
329 //
330 void ConfigDialog::buttonClicked (QAbstractButton* button)
331 {
332 QDialogButtonBox* dbb = ui->buttonBox;
333
334 if (button == dbb->button (QDialogButtonBox::Ok))
335 {
336 applySettings();
337 accept();
338 }
339 elif (button == dbb->button (QDialogButtonBox::Apply))
340 {
341 applySettings();
342 }
343 elif (button == dbb->button (QDialogButtonBox::Cancel))
344 {
345 reject();
346 }
347 }
348
349 //
350 // Update the list of color toolbar items in the quick color tab.
351 //
352 void ConfigDialog::updateQuickColorList (LDQuickColor* sel)
353 {
354 for (QListWidgetItem * item : quickColorItems)
355 delete item;
356
357 quickColorItems.clear();
358
359 // Init table items
360 for (LDQuickColor& entry : quickColors)
361 {
362 QListWidgetItem* item = new QListWidgetItem;
363
364 if (entry.isSeparator())
365 {
366 item->setText ("<hr />");
367 item->setIcon (GetIcon ("empty"));
368 }
369 else
370 {
371 LDColor col (entry.color());
372
373 if (col == null)
374 {
375 item->setText ("[[unknown color]]");
376 item->setIcon (GetIcon ("error"));
377 }
378 else
379 {
380 item->setText (col.name());
381 item->setIcon (MakeColorIcon (col, 16));
382 }
383 }
384
385 ui->quickColorList->addItem (item);
386 quickColorItems << item;
387
388 if (sel and &entry == sel)
389 {
390 ui->quickColorList->setCurrentItem (item);
391 ui->quickColorList->scrollToItem (item);
392 }
393 }
394 }
395
396 //
397 // Quick colors: add or edit button was clicked.
398 //
399 void ConfigDialog::slot_setColor()
400 {
401 LDQuickColor* entry = null;
402 QListWidgetItem* item = null;
403 const bool isNew = static_cast<QPushButton*> (sender()) == ui->quickColor_add;
404
405 if (not isNew)
406 {
407 item = getSelectedQuickColor();
408
409 if (not item)
410 return;
411
412 int i = getItemRow (item, quickColorItems);
413 entry = &quickColors[i];
414
415 if (entry->isSeparator() == true)
416 return; // don't color separators
417 }
418
419 LDColor defval = entry ? entry->color() : null;
420 LDColor val;
421
422 if (not ColorSelector::selectColor (val, defval, this))
423 return;
424
425 if (entry != null)
426 {
427 entry->setColor (val);
428 }
429 else
430 {
431 LDQuickColor entry (val, null);
432 item = getSelectedQuickColor();
433 int idx = (item) ? getItemRow (item, quickColorItems) + 1 : quickColorItems.size();
434 quickColors.insert (idx, entry);
435 entry = quickColors[idx];
436 }
437
438 updateQuickColorList (entry);
439 }
440
441 //
442 // Remove a quick color
443 //
444 void ConfigDialog::slot_delColor()
445 {
446 if (ui->quickColorList->selectedItems().isEmpty())
447 return;
448
449 QListWidgetItem* item = ui->quickColorList->selectedItems() [0];
450 quickColors.removeAt (getItemRow (item, quickColorItems));
451 updateQuickColorList();
452 }
453
454 //
455 // Move a quick color up/down
456 //
457 void ConfigDialog::slot_moveColor()
458 {
459 const bool up = (static_cast<QPushButton*> (sender()) == ui->quickColor_moveUp);
460
461 if (ui->quickColorList->selectedItems().isEmpty())
462 return;
463
464 QListWidgetItem* item = ui->quickColorList->selectedItems() [0];
465 int idx = getItemRow (item, quickColorItems);
466 int dest = up ? (idx - 1) : (idx + 1);
467
468 if (dest < 0 or dest >= quickColorItems.size())
469 return; // destination out of bounds
470
471 qSwap (quickColors[dest], quickColors[idx]);
472 updateQuickColorList (&quickColors[dest]);
473 }
474
475 //
476 //
477 // Add a separator to quick colors
478 //
479 void ConfigDialog::slot_addColorSeparator()
480 {
481 quickColors << LDQuickColor::getSeparator();
482 updateQuickColorList (&quickColors[quickColors.size() - 1]);
483 }
484
485 //
486 //
487 // Clear all quick colors
488 //
489 void ConfigDialog::slot_clearColors()
490 {
491 quickColors.clear();
492 updateQuickColorList();
493 }
494
495 //
496 //
497 void ConfigDialog::setButtonColor()
498 {
499 QPushButton* button = qobject_cast<QPushButton*> (sender());
500
501 if (button == null)
502 {
503 print ("setButtonColor: null sender!\n");
504 return;
505 }
506
507 QColor color = QColorDialog::getColor (m_buttonColors[button]);
508
509 if (color.isValid())
510 {
511 QString colorname;
512 colorname.sprintf ("#%.2X%.2X%.2X", color.red(), color.green(), color.blue());
513 setButtonBackground (button, colorname);
514 }
515 }
516
517 //
518 // Sets background color of a given button.
519 //
520 void ConfigDialog::setButtonBackground (QPushButton* button, QString value)
521 {
522 button->setIcon (GetIcon ("colorselect"));
523 button->setAutoFillBackground (true);
524 button->setStyleSheet (format ("background-color: %1", value));
525 m_buttonColors[button] = QColor (value);
526 }
527
528 //
529 // Finds the given list widget item in the list of widget items given.
530 //
531 int ConfigDialog::getItemRow (QListWidgetItem* item, QList<QListWidgetItem*>& haystack)
532 {
533 int i = 0;
534
535 for (QListWidgetItem* it : haystack)
536 {
537 if (it == item)
538 return i;
539
540 ++i;
541 }
542
543 return -1;
544 }
545
546 //
547 // Which quick color is currently selected?
548 //
549 QListWidgetItem* ConfigDialog::getSelectedQuickColor()
550 {
551 if (ui->quickColorList->selectedItems().isEmpty())
552 return null;
553
554 return ui->quickColorList->selectedItems() [0];
555 }
556
557 //
558 // Get the list of shortcuts selected
559 //
560 QList<ShortcutListItem*> ConfigDialog::getShortcutSelection()
561 {
562 QList<ShortcutListItem*> out;
563
564 for (QListWidgetItem* entry : ui->shortcutsList->selectedItems())
565 out << static_cast<ShortcutListItem*> (entry);
566
567 return out;
568 }
569
570 //
571 // Edit the shortcut of a given action.
572 //
573 void ConfigDialog::slot_setShortcut()
574 {
575 QList<ShortcutListItem*> sel = getShortcutSelection();
576
577 if (sel.size() < 1)
578 return;
579
580 ShortcutListItem* item = sel[0];
581
582 if (KeySequenceDialog::staticDialog (item, this))
583 setShortcutText (item);
584 }
585
586 //
587 // Reset a shortcut to defaults
588 //
589 void ConfigDialog::slot_resetShortcut()
590 {
591 QList<ShortcutListItem*> sel = getShortcutSelection();
592
593 for (ShortcutListItem* item : sel)
594 {
595 item->setSequence (MainWindow::defaultShortcut (item->action()));
596 setShortcutText (item);
597 }
598 }
599
600 //
601 // Remove the shortcut of an action.
602 //
603 void ConfigDialog::slot_clearShortcut()
604 {
605 QList<ShortcutListItem*> sel = getShortcutSelection();
606
607 for (ShortcutListItem* item : sel)
608 {
609 item->setSequence (QKeySequence());
610 setShortcutText (item);
611 }
612 }
613
614 //
615 // Set the path of an external program
616 //
617 void ConfigDialog::slot_setExtProgPath()
618 {
619 const LDExtProgInfo* info = null;
620
621 for (const LDExtProgInfo& it : g_LDExtProgInfo)
622 {
623 if (it.setPathButton == sender())
624 {
625 info = &it;
626 break;
627 }
628 }
629
630 assert (info != null);
631 QString fpath = QFileDialog::getOpenFileName (this, format ("Path to %1", info->name), *info->path, g_extProgPathFilter);
632
633 if (fpath.isEmpty())
634 return;
635
636 info->input->setText (fpath);
637 }
638
639 //
640 // '...' button pressed for the download path
641 //
642 void ConfigDialog::slot_findDownloadFolder()
643 {
644 QString dpath = QFileDialog::getExistingDirectory();
645
646 if (not dpath.isEmpty())
647 ui->configDownloadFilePath->setText (dpath);
648 }
649
650 //
651 //
652 // Updates the text string for a given shortcut list item
653 //
654 void ConfigDialog::setShortcutText (ShortcutListItem* item)
655 {
656 QAction* act = item->action();
657 QString label = act->iconText();
658 QString keybind = item->sequence().toString();
659 item->setText (format ("%1 (%2)", label, keybind));
660 }
661
662 //
663 // Gets the configuration string of the quick color toolbar
664 //
665 QString ConfigDialog::quickColorString()
666 {
667 QString val;
668
669 for (const LDQuickColor& entry : quickColors)
670 {
671 if (val.length() > 0)
672 val += ':';
673
674 if (entry.isSeparator())
675 val += '|';
676 else
677 val += format ("%1", entry.color().index());
678 }
679
680 return val;
681 }
682
683 //
684 //
685 KeySequenceDialog::KeySequenceDialog (QKeySequence seq, QWidget* parent, Qt::WindowFlags f) :
686 QDialog (parent, f), seq (seq)
687 {
688 lb_output = new QLabel;
689 IMPLEMENT_DIALOG_BUTTONS
690
691 setWhatsThis (tr ("Into this dialog you can input a key sequence for use as a "
692 "shortcut in LDForge. Use OK to confirm the new shortcut and Cancel to "
693 "dismiss."));
694
695 QVBoxLayout* layout = new QVBoxLayout;
696 layout->addWidget (lb_output);
697 layout->addWidget (bbx_buttons);
698 setLayout (layout);
699
700 updateOutput();
701 }
702
703 //
704 //
705 bool KeySequenceDialog::staticDialog (ShortcutListItem* item, QWidget* parent)
706 {
707 KeySequenceDialog dlg (item->sequence(), parent);
708
709 if (dlg.exec() == QDialog::Rejected)
710 return false;
711
712 item->setSequence (dlg.seq);
713 return true;
714 }
715
716 //
717 //
718 void KeySequenceDialog::updateOutput()
719 {
720 QString shortcut = seq.toString();
721
722 if (seq == QKeySequence())
723 shortcut = "&lt;empty&gt;";
724
725 QString text = format ("<center><b>%1</b></center>", shortcut);
726 lb_output->setText (text);
727 }
728
729 //
730 //
731 void KeySequenceDialog::keyPressEvent (QKeyEvent* ev)
732 {
733 seq = ev->key() + ev->modifiers();
734 updateOutput();
735 }

mercurial