]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/GuiPrefs.cpp
Add dark theme colors to semantic colors (#8325)
[lyx.git] / src / frontends / qt / GuiPrefs.cpp
1 /**
2  * \file GuiPrefs.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Bo Peng
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "GuiPrefs.h"
15
16 #include "ColorCache.h"
17 #include "FileDialog.h"
18 #include "GuiApplication.h"
19 #include "GuiFontExample.h"
20 #include "GuiFontLoader.h"
21 #include "GuiKeySymbol.h"
22 #include "GuiLyXFiles.h"
23 #include "GuiView.h"
24 #include "qt_helpers.h"
25 #include "Validator.h"
26
27 #include "Author.h"
28 #include "BufferList.h"
29 #include "Color.h"
30 #include "ColorSet.h"
31 #include "ConverterCache.h"
32 #include "FontEnums.h"
33 #include "FuncRequest.h"
34 #include "KeySequence.h"
35 #include "Language.h"
36 #include "LyXAction.h"
37 #include "LyX.h"
38 #include "PanelStack.h"
39 #include "Session.h"
40 #include "SpellChecker.h"
41
42 #include "support/debug.h"
43 #include "support/FileName.h"
44 #include "support/filetools.h"
45 #include "support/gettext.h"
46 #include "support/lassert.h"
47 #include "support/lstrings.h"
48 #include "support/Messages.h"
49 #include "support/os.h"
50 #include "support/Package.h"
51
52 #include "frontends/alert.h"
53 #include "frontends/Application.h"
54 #include "frontends/FontLoader.h"
55
56 #include <QAbstractItemModel>
57 #include <QCheckBox>
58 #include <QColorDialog>
59 #include <QFontDatabase>
60 #include <QHeaderView>
61 #include <QLineEdit>
62 #include <QMessageBox>
63 #include <QPushButton>
64 #include <QSpinBox>
65 #include <QString>
66 #include <QTreeWidget>
67 #include <QTreeWidgetItem>
68 #include <QValidator>
69
70 #include <iomanip>
71 #include <sstream>
72 #include <algorithm>
73 #include <math.h>
74
75 using namespace Ui;
76
77 using namespace std;
78 using namespace lyx::support;
79 using namespace lyx::support::os;
80
81 namespace lyx {
82 namespace frontend {
83
84 /////////////////////////////////////////////////////////////////////
85 //
86 // Browser Helpers
87 //
88 /////////////////////////////////////////////////////////////////////
89
90 /** Launch a file dialog and return the chosen file.
91         filename: a suggested filename.
92         title: the title of the dialog.
93         filters: *.ps etc.
94         dir1 = (name, dir), dir2 = (name, dir): extra buttons on the dialog.
95 */
96 QString browseFile(QString const & filename,
97         QString const & title,
98         QStringList const & filters,
99         bool save = false,
100         QString const & label1 = QString(),
101         QString const & dir1 = QString(),
102         QString const & label2 = QString(),
103         QString const & dir2 = QString(),
104         QString const & fallback_dir = QString())
105 {
106         QString lastPath = ".";
107         if (!filename.isEmpty())
108                 lastPath = onlyPath(filename);
109         else if(!fallback_dir.isEmpty())
110                 lastPath = fallback_dir;
111
112         FileDialog dlg(title);
113         dlg.setButton1(label1, dir1);
114         dlg.setButton2(label2, dir2);
115
116         FileDialog::Result result;
117
118         if (save)
119                 result = dlg.save(lastPath, filters, onlyFileName(filename));
120         else
121                 result = dlg.open(lastPath, filters, onlyFileName(filename));
122
123         return result.second;
124 }
125
126
127 /** Launch a file dialog and return the chosen directory.
128         pathname: a suggested pathname.
129         title: the title of the dialog.
130         dir1 = (name, dir), dir2 = (name, dir): extra buttons on the dialog.
131 */
132 QString browseDir(QString const & pathname,
133         QString const & title,
134         QString const & label1 = QString(),
135         QString const & dir1 = QString(),
136         QString const & label2 = QString(),
137         QString const & dir2 = QString())
138 {
139         QString lastPath = ".";
140         if (!pathname.isEmpty())
141                 lastPath = onlyPath(pathname);
142
143         FileDialog dlg(title);
144         dlg.setButton1(label1, dir1);
145         dlg.setButton2(label2, dir2);
146
147         FileDialog::Result const result =
148                 dlg.opendir(lastPath, onlyFileName(pathname));
149
150         return result.second;
151 }
152
153
154 } // namespace frontend
155
156
157 QString browseRelToParent(QString const & filename, QString const & relpath,
158         QString const & title, QStringList const & filters, bool save,
159         QString const & label1, QString const & dir1,
160         QString const & label2, QString const & dir2)
161 {
162         QString const fname = makeAbsPath(filename, relpath);
163
164         QString const outname =
165                 frontend::browseFile(fname, title, filters, save, label1, dir1, label2, dir2);
166
167         QString const reloutname =
168                 toqstr(makeRelPath(qstring_to_ucs4(outname), qstring_to_ucs4(relpath)));
169
170         if (reloutname.startsWith("../"))
171                 return outname;
172         else
173                 return reloutname;
174 }
175
176
177 QString browseRelToSub(QString const & filename, QString const & relpath,
178         QString const & title, QStringList const & filters, bool save,
179         QString const & label1, QString const & dir1,
180         QString const & label2, QString const & dir2)
181 {
182         QString const fname = makeAbsPath(filename, relpath);
183
184         QString const outname =
185                 frontend::browseFile(fname, title, filters, save, label1, dir1, label2, dir2);
186
187         QString const reloutname =
188                 toqstr(makeRelPath(qstring_to_ucs4(outname), qstring_to_ucs4(relpath)));
189
190         QString testname = reloutname;
191         testname.remove(QRegExp("^(\\.\\./)+"));
192
193         if (testname.contains("/"))
194                 return outname;
195         else
196                 return reloutname;
197 }
198
199
200
201 /////////////////////////////////////////////////////////////////////
202 //
203 // Helpers
204 //
205 /////////////////////////////////////////////////////////////////////
206
207 namespace frontend {
208
209 QString const catLookAndFeel = N_("Look & Feel");
210 QString const catEditing = N_("Editing");
211 QString const catLanguage = N_("Language Settings");
212 QString const catOutput = N_("Output");
213 QString const catFiles = N_("File Handling");
214
215 static void parseFontName(QString const & mangled0,
216         string & name, string & foundry)
217 {
218         string mangled = fromqstr(mangled0);
219         size_t const idx = mangled.find('[');
220         if (idx == string::npos || idx == 0) {
221                 name = mangled;
222                 foundry.clear();
223         } else {
224                 name = mangled.substr(0, idx - 1);
225                 foundry = mangled.substr(idx + 1, mangled.size() - idx - 2);
226         }
227 }
228
229
230 static void setComboxFont(QComboBox * cb, string const & family,
231         string const & foundry)
232 {
233         QString fontname = toqstr(family);
234         if (!foundry.empty())
235                 fontname += " [" + toqstr(foundry) + ']';
236
237         for (int i = 0; i != cb->count(); ++i) {
238                 if (cb->itemText(i) == fontname) {
239                         cb->setCurrentIndex(i);
240                         return;
241                 }
242         }
243
244         // Try matching without foundry name
245
246         // We count in reverse in order to prefer the Xft foundry
247         for (int i = cb->count(); --i >= 0;) {
248                 string name, fnt_foundry;
249                 parseFontName(cb->itemText(i), name, fnt_foundry);
250                 if (compare_ascii_no_case(name, family) == 0) {
251                         cb->setCurrentIndex(i);
252                         return;
253                 }
254         }
255
256         // family alone can contain e.g. "Helvetica [Adobe]"
257         string tmpname, tmpfoundry;
258         parseFontName(toqstr(family), tmpname, tmpfoundry);
259
260         // We count in reverse in order to prefer the Xft foundry
261         for (int i = cb->count(); --i >= 0; ) {
262                 string name, fnt_foundry;
263                 parseFontName(cb->itemText(i), name, fnt_foundry);
264                 if (compare_ascii_no_case(name, fnt_foundry) == 0) {
265                         cb->setCurrentIndex(i);
266                         return;
267                 }
268         }
269
270         // Bleh, default fonts, and the names couldn't be found. Hack
271         // for bug 1063.
272
273         QFont font;
274
275         QString const font_family = toqstr(family);
276         if (font_family == guiApp->romanFontName()) {
277                 font.setStyleHint(QFont::Serif);
278                 font.setFamily(font_family);
279         } else if (font_family == guiApp->sansFontName()) {
280                 font.setStyleHint(QFont::SansSerif);
281                 font.setFamily(font_family);
282         } else if (font_family == guiApp->typewriterFontName()) {
283                 font.setStyleHint(QFont::TypeWriter);
284                 font.setFamily(font_family);
285         } else {
286                 LYXERR0("FAILED to find the default font: '"
287                        << foundry << "', '" << family << '\'');
288                 return;
289         }
290
291         QFontInfo info(font);
292         string default_font_name, dummyfoundry;
293         parseFontName(info.family(), default_font_name, dummyfoundry);
294         LYXERR0("Apparent font is " << default_font_name);
295
296         for (int i = 0; i < cb->count(); ++i) {
297                 LYXERR0("Looking at " << cb->itemText(i));
298                 if (compare_ascii_no_case(fromqstr(cb->itemText(i)),
299                                     default_font_name) == 0) {
300                         cb->setCurrentIndex(i);
301                         return;
302                 }
303         }
304
305         LYXERR0("FAILED to find the font: '"
306                << foundry << "', '" << family << '\'');
307 }
308
309
310 /////////////////////////////////////////////////////////////////////
311 //
312 // PrefOutput
313 //
314 /////////////////////////////////////////////////////////////////////
315
316 PrefOutput::PrefOutput(GuiPreferences * form)
317         : PrefModule(catOutput, N_("General[[settings]]"), form)
318 {
319         setupUi(this);
320
321         dviCB->setValidator(new NoNewLineValidator(dviCB));
322         pdfCB->setValidator(new NoNewLineValidator(pdfCB));
323
324         connect(plaintextLinelengthSB, SIGNAL(valueChanged(int)),
325                 this, SIGNAL(changed()));
326         connect(overwriteCO, SIGNAL(activated(int)),
327                 this, SIGNAL(changed()));
328         connect(dviCB, SIGNAL(editTextChanged(QString)),
329                 this, SIGNAL(changed()));
330         connect(pdfCB, SIGNAL(editTextChanged(QString)),
331                 this, SIGNAL(changed()));
332         connect(printerPaperTypeED, SIGNAL(textChanged(QString)),
333                 this, SIGNAL(changed()));
334         connect(printerLandscapeED, SIGNAL(textChanged(QString)),
335                 this, SIGNAL(changed()));
336         connect(printerPaperSizeED, SIGNAL(textChanged(QString)),
337                 this, SIGNAL(changed()));
338
339         printerPaperTypeED->setValidator(new NoNewLineValidator(printerPaperTypeED));
340         printerLandscapeED->setValidator(new NoNewLineValidator(printerLandscapeED));
341         printerPaperSizeED->setValidator(new NoNewLineValidator(printerPaperSizeED));
342
343         dviCB->addItem("");
344         dviCB->addItem("xdvi -sourceposition '$$n:\\ $$t' $$o");
345         dviCB->addItem("yap -1 -s \"$$n $$t\" $$o");
346         dviCB->addItem("okular --unique \"$$o#src:$$n $$f\"");
347         dviCB->addItem("synctex view -i $$n:0:$$t -o $$o -x \"evince -i %{page+1} $$o\"");
348         pdfCB->addItem("");
349         pdfCB->addItem("CMCDDE SUMATRA control [ForwardSearch(\\\"$$o\\\",\\\"$$t\\\",$$n,0,0,1)]");
350         pdfCB->addItem("SumatraPDF -reuse-instance \"$$o\" -forward-search \"$$t\" $$n");
351         pdfCB->addItem("synctex view -i $$n:0:$$t -o $$o -x \"xpdf -raise -remote $$t.tmp $$o %{page+1}\"");
352         pdfCB->addItem("okular --unique \"$$o#src:$$n $$f\"");
353         pdfCB->addItem("qpdfview --unique \"$$o#src:$$f:$$n:0\"");
354         pdfCB->addItem("synctex view -i $$n:0:$$t -o $$o -x \"evince -i %{page+1} $$o\"");
355         pdfCB->addItem("/Applications/Skim.app/Contents/SharedSupport/displayline $$n $$o $$t");
356 }
357
358
359 void PrefOutput::applyRC(LyXRC & rc) const
360 {
361         rc.plaintext_linelen = plaintextLinelengthSB->value();
362         rc.forward_search_dvi = fromqstr(dviCB->currentText());
363         rc.forward_search_pdf = fromqstr(pdfCB->currentText());
364
365         switch (overwriteCO->currentIndex()) {
366         case 0:
367                 rc.export_overwrite = NO_FILES;
368                 break;
369         case 1:
370                 rc.export_overwrite = MAIN_FILE;
371                 break;
372         case 2:
373                 rc.export_overwrite = ALL_FILES;
374                 break;
375         }
376
377         rc.print_paper_flag = fromqstr(printerPaperTypeED->text());
378         rc.print_landscape_flag = fromqstr(printerLandscapeED->text());
379         rc.print_paper_dimension_flag = fromqstr(printerPaperSizeED->text());
380 }
381
382
383 void PrefOutput::updateRC(LyXRC const & rc)
384 {
385         plaintextLinelengthSB->setValue(rc.plaintext_linelen);
386         dviCB->setEditText(toqstr(rc.forward_search_dvi));
387         pdfCB->setEditText(toqstr(rc.forward_search_pdf));
388
389         switch (rc.export_overwrite) {
390         case NO_FILES:
391                 overwriteCO->setCurrentIndex(0);
392                 break;
393         case MAIN_FILE:
394                 overwriteCO->setCurrentIndex(1);
395                 break;
396         case ALL_FILES:
397                 overwriteCO->setCurrentIndex(2);
398                 break;
399         }
400
401         printerPaperTypeED->setText(toqstr(rc.print_paper_flag));
402         printerLandscapeED->setText(toqstr(rc.print_landscape_flag));
403         printerPaperSizeED->setText(toqstr(rc.print_paper_dimension_flag));
404 }
405
406
407 /////////////////////////////////////////////////////////////////////
408 //
409 // PrefInput
410 //
411 /////////////////////////////////////////////////////////////////////
412
413 PrefInput::PrefInput(GuiPreferences * form)
414         : PrefModule(catEditing, N_("Keyboard/Mouse"), form)
415 {
416         setupUi(this);
417
418         connect(keymapCB, SIGNAL(clicked()),
419                 this, SIGNAL(changed()));
420         connect(firstKeymapED, SIGNAL(textChanged(QString)),
421                 this, SIGNAL(changed()));
422         connect(secondKeymapED, SIGNAL(textChanged(QString)),
423                 this, SIGNAL(changed()));
424         connect(mouseWheelSpeedSB, SIGNAL(valueChanged(double)),
425                 this, SIGNAL(changed()));
426         connect(scrollzoomEnableCB, SIGNAL(clicked()),
427                 this, SIGNAL(changed()));
428         connect(scrollzoomValueCO, SIGNAL(activated(int)),
429                 this, SIGNAL(changed()));
430         connect(dontswapCB, SIGNAL(toggled(bool)),
431                 this, SIGNAL(changed()));
432         connect(mmPasteCB, SIGNAL(toggled(bool)),
433                 this, SIGNAL(changed()));
434
435         // reveal checkbox for switching Ctrl and Meta on Mac:
436         bool swapcb = false;
437 #ifdef Q_OS_MAC
438 #if QT_VERSION > 0x040600
439         swapcb = true;
440 #endif
441 #endif
442         dontswapCB->setVisible(swapcb);
443 }
444
445
446 void PrefInput::applyRC(LyXRC & rc) const
447 {
448         // FIXME: can derive CB from the two EDs
449         rc.use_kbmap = keymapCB->isChecked();
450         rc.primary_kbmap = internal_path(fromqstr(firstKeymapED->text()));
451         rc.secondary_kbmap = internal_path(fromqstr(secondKeymapED->text()));
452         rc.mouse_wheel_speed = mouseWheelSpeedSB->value();
453         if (scrollzoomEnableCB->isChecked()) {
454                 switch (scrollzoomValueCO->currentIndex()) {
455                 case 0:
456                         rc.scroll_wheel_zoom = LyXRC::SCROLL_WHEEL_ZOOM_CTRL;
457                         break;
458                 case 1:
459                         rc.scroll_wheel_zoom = LyXRC::SCROLL_WHEEL_ZOOM_SHIFT;
460                         break;
461                 case 2:
462                         rc.scroll_wheel_zoom = LyXRC::SCROLL_WHEEL_ZOOM_ALT;
463                         break;
464                 }
465         } else {
466                 rc.scroll_wheel_zoom = LyXRC::SCROLL_WHEEL_ZOOM_OFF;
467         }
468         rc.mac_dontswap_ctrl_meta  = dontswapCB->isChecked();
469         rc.mouse_middlebutton_paste = mmPasteCB->isChecked();
470 }
471
472
473 void PrefInput::updateRC(LyXRC const & rc)
474 {
475         // FIXME: can derive CB from the two EDs
476         keymapCB->setChecked(rc.use_kbmap);
477         firstKeymapED->setText(toqstr(external_path(rc.primary_kbmap)));
478         secondKeymapED->setText(toqstr(external_path(rc.secondary_kbmap)));
479         mouseWheelSpeedSB->setValue(rc.mouse_wheel_speed);
480         switch (rc.scroll_wheel_zoom) {
481         case LyXRC::SCROLL_WHEEL_ZOOM_OFF:
482                 scrollzoomEnableCB->setChecked(false);
483                 break;
484         case LyXRC::SCROLL_WHEEL_ZOOM_CTRL:
485                 scrollzoomEnableCB->setChecked(true);
486                 scrollzoomValueCO->setCurrentIndex(0);
487                 break;
488         case LyXRC::SCROLL_WHEEL_ZOOM_SHIFT:
489                 scrollzoomEnableCB->setChecked(true);
490                 scrollzoomValueCO->setCurrentIndex(1);
491                 break;
492         case LyXRC::SCROLL_WHEEL_ZOOM_ALT:
493                 scrollzoomEnableCB->setChecked(true);
494                 scrollzoomValueCO->setCurrentIndex(2);
495                 break;
496         }
497         dontswapCB->setChecked(rc.mac_dontswap_ctrl_meta);
498         mmPasteCB->setChecked(rc.mouse_middlebutton_paste);
499 }
500
501
502 QString PrefInput::testKeymap(QString const & keymap)
503 {
504         return form_->browsekbmap(internalPath(keymap));
505 }
506
507
508 void PrefInput::on_firstKeymapPB_clicked(bool)
509 {
510         QString const file = testKeymap(firstKeymapED->text());
511         if (!file.isEmpty())
512                 firstKeymapED->setText(file);
513 }
514
515
516 void PrefInput::on_secondKeymapPB_clicked(bool)
517 {
518         QString const file = testKeymap(secondKeymapED->text());
519         if (!file.isEmpty())
520                 secondKeymapED->setText(file);
521 }
522
523
524 void PrefInput::on_keymapCB_toggled(bool keymap)
525 {
526         firstKeymapLA->setEnabled(keymap);
527         secondKeymapLA->setEnabled(keymap);
528         firstKeymapED->setEnabled(keymap);
529         secondKeymapED->setEnabled(keymap);
530         firstKeymapPB->setEnabled(keymap);
531         secondKeymapPB->setEnabled(keymap);
532 }
533
534
535 void PrefInput::on_scrollzoomEnableCB_toggled(bool enabled)
536 {
537         scrollzoomValueCO->setEnabled(enabled);
538 }
539
540
541 /////////////////////////////////////////////////////////////////////
542 //
543 // PrefCompletion
544 //
545 /////////////////////////////////////////////////////////////////////
546
547 PrefCompletion::PrefCompletion(GuiPreferences * form)
548         : PrefModule(catEditing, N_("Input Completion"), form)
549 {
550         setupUi(this);
551
552         connect(inlineDelaySB, SIGNAL(valueChanged(double)),
553                 this, SIGNAL(changed()));
554         connect(inlineMathCB, SIGNAL(clicked()),
555                 this, SIGNAL(changed()));
556         connect(inlineTextCB, SIGNAL(clicked()),
557                 this, SIGNAL(changed()));
558         connect(inlineDotsCB, SIGNAL(clicked()),
559                 this, SIGNAL(changed()));
560         connect(popupDelaySB, SIGNAL(valueChanged(double)),
561                 this, SIGNAL(changed()));
562         connect(popupMathCB, SIGNAL(clicked()),
563                 this, SIGNAL(changed()));
564         connect(autocorrectionCB, SIGNAL(clicked()),
565                 this, SIGNAL(changed()));
566         connect(popupTextCB, SIGNAL(clicked()),
567                 this, SIGNAL(changed()));
568         connect(popupAfterCompleteCB, SIGNAL(clicked()),
569                 this, SIGNAL(changed()));
570         connect(cursorTextCB, SIGNAL(clicked()),
571                 this, SIGNAL(changed()));
572         connect(minlengthSB, SIGNAL(valueChanged(int)),
573                         this, SIGNAL(changed()));
574 }
575
576
577 void PrefCompletion::on_inlineTextCB_clicked()
578 {
579         enableCB();
580 }
581
582
583 void PrefCompletion::on_popupTextCB_clicked()
584 {
585         enableCB();
586 }
587
588
589 void PrefCompletion::enableCB()
590 {
591         cursorTextCB->setEnabled(
592                 popupTextCB->isChecked() || inlineTextCB->isChecked());
593 }
594
595
596 void PrefCompletion::applyRC(LyXRC & rc) const
597 {
598         rc.completion_inline_delay = inlineDelaySB->value();
599         rc.completion_inline_math = inlineMathCB->isChecked();
600         rc.completion_inline_text = inlineTextCB->isChecked();
601         rc.completion_inline_dots = inlineDotsCB->isChecked() ? 13 : -1;
602         rc.completion_popup_delay = popupDelaySB->value();
603         rc.completion_popup_math = popupMathCB->isChecked();
604         rc.autocorrection_math = autocorrectionCB->isChecked();
605         rc.completion_popup_text = popupTextCB->isChecked();
606         rc.completion_cursor_text = cursorTextCB->isChecked();
607         rc.completion_popup_after_complete =
608                 popupAfterCompleteCB->isChecked();
609         rc.completion_minlength = minlengthSB->value();
610 }
611
612
613 void PrefCompletion::updateRC(LyXRC const & rc)
614 {
615         inlineDelaySB->setValue(rc.completion_inline_delay);
616         inlineMathCB->setChecked(rc.completion_inline_math);
617         inlineTextCB->setChecked(rc.completion_inline_text);
618         inlineDotsCB->setChecked(rc.completion_inline_dots != -1);
619         popupDelaySB->setValue(rc.completion_popup_delay);
620         popupMathCB->setChecked(rc.completion_popup_math);
621         autocorrectionCB->setChecked(rc.autocorrection_math);
622         popupTextCB->setChecked(rc.completion_popup_text);
623         cursorTextCB->setChecked(rc.completion_cursor_text);
624         popupAfterCompleteCB->setChecked(rc.completion_popup_after_complete);
625         enableCB();
626         minlengthSB->setValue(rc.completion_minlength);
627 }
628
629
630
631 /////////////////////////////////////////////////////////////////////
632 //
633 // PrefLatex
634 //
635 /////////////////////////////////////////////////////////////////////
636
637 PrefLatex::PrefLatex(GuiPreferences * form)
638         : PrefModule(catOutput, N_("LaTeX"), form)
639 {
640         setupUi(this);
641
642         latexDviPaperED->setValidator(new NoNewLineValidator(latexDviPaperED));
643         latexBibtexED->setValidator(new NoNewLineValidator(latexBibtexED));
644         latexJBibtexED->setValidator(new NoNewLineValidator(latexJBibtexED));
645         latexIndexED->setValidator(new NoNewLineValidator(latexIndexED));
646         latexJIndexED->setValidator(new NoNewLineValidator(latexJIndexED));
647         latexNomenclED->setValidator(new NoNewLineValidator(latexNomenclED));
648         latexChecktexED->setValidator(new NoNewLineValidator(latexChecktexED));
649
650         connect(latexChecktexED, SIGNAL(textChanged(QString)),
651                 this, SIGNAL(changed()));
652         connect(latexBibtexCO, SIGNAL(activated(int)),
653                 this, SIGNAL(changed()));
654         connect(latexBibtexED, SIGNAL(textChanged(QString)),
655                 this, SIGNAL(changed()));
656         connect(latexJBibtexCO, SIGNAL(activated(int)),
657                 this, SIGNAL(changed()));
658         connect(latexJBibtexED, SIGNAL(textChanged(QString)),
659                 this, SIGNAL(changed()));
660         connect(latexIndexCO, SIGNAL(activated(int)),
661                 this, SIGNAL(changed()));
662         connect(latexIndexED, SIGNAL(textChanged(QString)),
663                 this, SIGNAL(changed()));
664         connect(latexJIndexED, SIGNAL(textChanged(QString)),
665                 this, SIGNAL(changed()));
666         connect(latexAutoresetCB, SIGNAL(clicked()),
667                 this, SIGNAL(changed()));
668         connect(latexDviPaperED, SIGNAL(textChanged(QString)),
669                 this, SIGNAL(changed()));
670         connect(latexNomenclED, SIGNAL(textChanged(QString)),
671                 this, SIGNAL(changed()));
672
673 #if defined(__CYGWIN__) || defined(_WIN32)
674         pathCB->setVisible(true);
675         connect(pathCB, SIGNAL(clicked()),
676                 this, SIGNAL(changed()));
677 #else
678         pathCB->setVisible(false);
679 #endif
680 }
681
682
683 void PrefLatex::on_latexBibtexCO_activated(int n)
684 {
685         QString const bibtex = latexBibtexCO->itemData(n).toString();
686         if (bibtex.isEmpty()) {
687                 latexBibtexED->clear();
688                 latexBibtexOptionsLA->setText(qt_("C&ommand:"));
689                 return;
690         }
691         for (LyXRC::CommandSet::const_iterator it = bibtex_alternatives.begin();
692              it != bibtex_alternatives.end(); ++it) {
693                 QString const bib = toqstr(*it);
694                 int ind = bib.indexOf(" ");
695                 QString sel_command = bib.left(ind);
696                 QString sel_options = ind < 0 ? QString() : bib.mid(ind + 1);
697                 if (bibtex == sel_command) {
698                         if (ind < 0)
699                                 latexBibtexED->clear();
700                         else
701                                 latexBibtexED->setText(sel_options.trimmed());
702                 }
703         }
704         latexBibtexOptionsLA->setText(qt_("&Options:"));
705 }
706
707
708 void PrefLatex::on_latexJBibtexCO_activated(int n)
709 {
710         QString const jbibtex = latexJBibtexCO->itemData(n).toString();
711         if (jbibtex.isEmpty()) {
712                 latexJBibtexED->clear();
713                 latexJBibtexOptionsLA->setText(qt_("Co&mmand:"));
714                 return;
715         }
716         for (LyXRC::CommandSet::const_iterator it = jbibtex_alternatives.begin();
717              it != jbibtex_alternatives.end(); ++it) {
718                 QString const bib = toqstr(*it);
719                 int ind = bib.indexOf(" ");
720                 QString sel_command = bib.left(ind);
721                 QString sel_options = ind < 0 ? QString() : bib.mid(ind + 1);
722                 if (jbibtex == sel_command) {
723                         if (ind < 0)
724                                 latexJBibtexED->clear();
725                         else
726                                 latexJBibtexED->setText(sel_options.trimmed());
727                 }
728         }
729         latexJBibtexOptionsLA->setText(qt_("Opt&ions:"));
730 }
731
732
733 void PrefLatex::on_latexIndexCO_activated(int n)
734 {
735         QString const index = latexIndexCO->itemData(n).toString();
736         if (index.isEmpty()) {
737                 latexIndexED->clear();
738                 latexIndexOptionsLA->setText(qt_("Co&mmand:"));
739                 return;
740         }
741         for (LyXRC::CommandSet::const_iterator it = index_alternatives.begin();
742              it != index_alternatives.end(); ++it) {
743                 QString const idx = toqstr(*it);
744                 int ind = idx.indexOf(" ");
745                 QString sel_command = idx.left(ind);
746                 QString sel_options = ind < 0 ? QString() : idx.mid(ind + 1);
747                 if (index == sel_command) {
748                         if (ind < 0)
749                                 latexIndexED->clear();
750                         else
751                                 latexIndexED->setText(sel_options.trimmed());
752                 }
753         }
754         latexIndexOptionsLA->setText(qt_("Op&tions:"));
755 }
756
757
758 void PrefLatex::applyRC(LyXRC & rc) const
759 {
760         // If bibtex is not empty, bibopt contains the options, otherwise
761         // it is a customized bibtex command with options.
762         QString const bibtex = latexBibtexCO->itemData(
763                 latexBibtexCO->currentIndex()).toString();
764         QString const bibopt = latexBibtexED->text();
765         if (bibtex.isEmpty())
766                 rc.bibtex_command = fromqstr(bibopt);
767         else if (bibopt.isEmpty())
768                 rc.bibtex_command = fromqstr(bibtex);
769         else
770                 rc.bibtex_command = fromqstr(bibtex) + " " + fromqstr(bibopt);
771
772         // If jbibtex is not empty, jbibopt contains the options, otherwise
773         // it is a customized bibtex command with options.
774         QString const jbibtex = latexJBibtexCO->itemData(
775                 latexJBibtexCO->currentIndex()).toString();
776         QString const jbibopt = latexJBibtexED->text();
777         if (jbibtex.isEmpty())
778                 rc.jbibtex_command = fromqstr(jbibopt);
779         else if (jbibopt.isEmpty())
780                 rc.jbibtex_command = fromqstr(jbibtex);
781         else
782                 rc.jbibtex_command = fromqstr(jbibtex) + " " + fromqstr(jbibopt);
783
784         // If index is not empty, idxopt contains the options, otherwise
785         // it is a customized index command with options.
786         QString const index = latexIndexCO->itemData(
787                 latexIndexCO->currentIndex()).toString();
788         QString const idxopt = latexIndexED->text();
789         if (index.isEmpty())
790                 rc.index_command = fromqstr(idxopt);
791         else if (idxopt.isEmpty())
792                 rc.index_command = fromqstr(index);
793         else
794                 rc.index_command = fromqstr(index) + " " + fromqstr(idxopt);
795
796         rc.chktex_command = fromqstr(latexChecktexED->text());
797         rc.jindex_command = fromqstr(latexJIndexED->text());
798         rc.nomencl_command = fromqstr(latexNomenclED->text());
799         rc.auto_reset_options = latexAutoresetCB->isChecked();
800         rc.view_dvi_paper_option = fromqstr(latexDviPaperED->text());
801 #if defined(__CYGWIN__) || defined(_WIN32)
802         rc.windows_style_tex_paths = pathCB->isChecked();
803 #endif
804 }
805
806
807 void PrefLatex::updateRC(LyXRC const & rc)
808 {
809         latexBibtexCO->clear();
810
811         latexBibtexCO->addItem(qt_("Automatic"), "automatic");
812         latexBibtexCO->addItem(qt_("Custom"), QString());
813         for (LyXRC::CommandSet::const_iterator it = rc.bibtex_alternatives.begin();
814                              it != rc.bibtex_alternatives.end(); ++it) {
815                 QString const command = toqstr(*it).left(toqstr(*it).indexOf(" "));
816                 latexBibtexCO->addItem(command, command);
817         }
818
819         bibtex_alternatives = rc.bibtex_alternatives;
820
821         QString const bib = toqstr(rc.bibtex_command);
822         int ind = bib.indexOf(" ");
823         QString sel_command = bib.left(ind);
824         QString sel_options = ind < 0 ? QString() : bib.mid(ind + 1);
825
826         int pos = latexBibtexCO->findData(sel_command);
827         if (pos != -1) {
828                 latexBibtexCO->setCurrentIndex(pos);
829                 latexBibtexED->setText(sel_options.trimmed());
830                 latexBibtexOptionsLA->setText(qt_("&Options:"));
831         } else {
832                 latexBibtexED->setText(toqstr(rc.bibtex_command));
833                 latexBibtexCO->setCurrentIndex(0);
834                 latexBibtexOptionsLA->setText(qt_("C&ommand:"));
835         }
836
837         latexJBibtexCO->clear();
838
839         latexJBibtexCO->addItem(qt_("Automatic"), "automatic");
840         latexJBibtexCO->addItem(qt_("Custom"), QString());
841         for (LyXRC::CommandSet::const_iterator it = rc.jbibtex_alternatives.begin();
842                              it != rc.jbibtex_alternatives.end(); ++it) {
843                 QString const command = toqstr(*it).left(toqstr(*it).indexOf(" "));
844                 latexJBibtexCO->addItem(command, command);
845         }
846
847         jbibtex_alternatives = rc.jbibtex_alternatives;
848
849         QString const jbib = toqstr(rc.jbibtex_command);
850         ind = jbib.indexOf(" ");
851         sel_command = jbib.left(ind);
852         sel_options = ind < 0 ? QString() : jbib.mid(ind + 1);
853
854         pos = latexJBibtexCO->findData(sel_command);
855         if (pos != -1) {
856                 latexJBibtexCO->setCurrentIndex(pos);
857                 latexJBibtexED->setText(sel_options.trimmed());
858                 latexJBibtexOptionsLA->setText(qt_("Opt&ions:"));
859         } else {
860                 latexJBibtexED->setText(toqstr(rc.bibtex_command));
861                 latexJBibtexCO->setCurrentIndex(0);
862                 latexJBibtexOptionsLA->setText(qt_("Co&mmand:"));
863         }
864
865         latexIndexCO->clear();
866
867         latexIndexCO->addItem(qt_("Custom"), QString());
868         for (LyXRC::CommandSet::const_iterator it = rc.index_alternatives.begin();
869                              it != rc.index_alternatives.end(); ++it) {
870                 QString const command = toqstr(*it).left(toqstr(*it).indexOf(" "));
871                 latexIndexCO->addItem(command, command);
872         }
873
874         index_alternatives = rc.index_alternatives;
875
876         QString const idx = toqstr(rc.index_command);
877         ind = idx.indexOf(" ");
878         sel_command = idx.left(ind);
879         sel_options = ind < 0 ? QString() : idx.mid(ind + 1);
880
881         pos = latexIndexCO->findData(sel_command);
882         if (pos != -1) {
883                 latexIndexCO->setCurrentIndex(pos);
884                 latexIndexED->setText(sel_options.trimmed());
885                 latexIndexOptionsLA->setText(qt_("Op&tions:"));
886         } else {
887                 latexIndexED->setText(toqstr(rc.index_command));
888                 latexIndexCO->setCurrentIndex(0);
889                 latexIndexOptionsLA->setText(qt_("Co&mmand:"));
890         }
891
892         latexChecktexED->setText(toqstr(rc.chktex_command));
893         latexJIndexED->setText(toqstr(rc.jindex_command));
894         latexNomenclED->setText(toqstr(rc.nomencl_command));
895         latexAutoresetCB->setChecked(rc.auto_reset_options);
896         latexDviPaperED->setText(toqstr(rc.view_dvi_paper_option));
897 #if defined(__CYGWIN__) || defined(_WIN32)
898         pathCB->setChecked(rc.windows_style_tex_paths);
899 #endif
900 }
901
902
903 /////////////////////////////////////////////////////////////////////
904 //
905 // PrefScreenFonts
906 //
907 /////////////////////////////////////////////////////////////////////
908
909 PrefScreenFonts::PrefScreenFonts(GuiPreferences * form)
910         : PrefModule(catLookAndFeel, N_("Screen Fonts"), form)
911 {
912         setupUi(this);
913
914         connect(screenRomanCO, SIGNAL(activated(QString)),
915                 this, SLOT(selectRoman(QString)));
916         connect(screenSansCO, SIGNAL(activated(QString)),
917                 this, SLOT(selectSans(QString)));
918         connect(screenTypewriterCO, SIGNAL(activated(QString)),
919                 this, SLOT(selectTypewriter(QString)));
920
921         QFontDatabase fontdb;
922         QStringList families(fontdb.families());
923         for (auto const & family : families) {
924                 screenRomanCO->addItem(family);
925                 screenSansCO->addItem(family);
926                 screenTypewriterCO->addItem(family);
927         }
928         connect(screenRomanCO, SIGNAL(activated(QString)),
929                 this, SIGNAL(changed()));
930         connect(screenSansCO, SIGNAL(activated(QString)),
931                 this, SIGNAL(changed()));
932         connect(screenTypewriterCO, SIGNAL(activated(QString)),
933                 this, SIGNAL(changed()));
934         connect(screenZoomSB, SIGNAL(valueChanged(int)),
935                 this, SIGNAL(changed()));
936         connect(screenTinyED, SIGNAL(textChanged(QString)),
937                 this, SIGNAL(changed()));
938         connect(screenSmallestED, SIGNAL(textChanged(QString)),
939                 this, SIGNAL(changed()));
940         connect(screenSmallerED, SIGNAL(textChanged(QString)),
941                 this, SIGNAL(changed()));
942         connect(screenSmallED, SIGNAL(textChanged(QString)),
943                 this, SIGNAL(changed()));
944         connect(screenNormalED, SIGNAL(textChanged(QString)),
945                 this, SIGNAL(changed()));
946         connect(screenLargeED, SIGNAL(textChanged(QString)),
947                 this, SIGNAL(changed()));
948         connect(screenLargerED, SIGNAL(textChanged(QString)),
949                 this, SIGNAL(changed()));
950         connect(screenLargestED, SIGNAL(textChanged(QString)),
951                 this, SIGNAL(changed()));
952         connect(screenHugeED, SIGNAL(textChanged(QString)),
953                 this, SIGNAL(changed()));
954         connect(screenHugerED, SIGNAL(textChanged(QString)),
955                 this, SIGNAL(changed()));
956
957         screenTinyED->setValidator(new QDoubleValidator(screenTinyED));
958         screenSmallestED->setValidator(new QDoubleValidator(screenSmallestED));
959         screenSmallerED->setValidator(new QDoubleValidator(screenSmallerED));
960         screenSmallED->setValidator(new QDoubleValidator(screenSmallED));
961         screenNormalED->setValidator(new QDoubleValidator(screenNormalED));
962         screenLargeED->setValidator(new QDoubleValidator(screenLargeED));
963         screenLargerED->setValidator(new QDoubleValidator(screenLargerED));
964         screenLargestED->setValidator(new QDoubleValidator(screenLargestED));
965         screenHugeED->setValidator(new QDoubleValidator(screenHugeED));
966         screenHugerED->setValidator(new QDoubleValidator(screenHugerED));
967 }
968
969
970 void PrefScreenFonts::applyRC(LyXRC & rc) const
971 {
972         LyXRC const oldrc = rc;
973
974         parseFontName(screenRomanCO->currentText(),
975                 rc.roman_font_name, rc.roman_font_foundry);
976         parseFontName(screenSansCO->currentText(),
977                 rc.sans_font_name, rc.sans_font_foundry);
978         parseFontName(screenTypewriterCO->currentText(),
979                 rc.typewriter_font_name, rc.typewriter_font_foundry);
980
981         rc.defaultZoom = screenZoomSB->value();
982         rc.font_sizes[TINY_SIZE] = widgetToDoubleStr(screenTinyED);
983         rc.font_sizes[SCRIPT_SIZE] = widgetToDoubleStr(screenSmallestED);
984         rc.font_sizes[FOOTNOTE_SIZE] = widgetToDoubleStr(screenSmallerED);
985         rc.font_sizes[SMALL_SIZE] = widgetToDoubleStr(screenSmallED);
986         rc.font_sizes[NORMAL_SIZE] = widgetToDoubleStr(screenNormalED);
987         rc.font_sizes[LARGE_SIZE] = widgetToDoubleStr(screenLargeED);
988         rc.font_sizes[LARGER_SIZE] = widgetToDoubleStr(screenLargerED);
989         rc.font_sizes[LARGEST_SIZE] = widgetToDoubleStr(screenLargestED);
990         rc.font_sizes[HUGE_SIZE] = widgetToDoubleStr(screenHugeED);
991         rc.font_sizes[HUGER_SIZE] = widgetToDoubleStr(screenHugerED);
992 }
993
994
995 void PrefScreenFonts::updateRC(LyXRC const & rc)
996 {
997         setComboxFont(screenRomanCO, rc.roman_font_name,
998                         rc.roman_font_foundry);
999         setComboxFont(screenSansCO, rc.sans_font_name,
1000                         rc.sans_font_foundry);
1001         setComboxFont(screenTypewriterCO, rc.typewriter_font_name,
1002                         rc.typewriter_font_foundry);
1003
1004         selectRoman(screenRomanCO->currentText());
1005         selectSans(screenSansCO->currentText());
1006         selectTypewriter(screenTypewriterCO->currentText());
1007
1008         screenZoomSB->setValue(rc.defaultZoom);
1009         updateScreenFontSizes(rc);
1010 }
1011
1012
1013 void PrefScreenFonts::updateScreenFontSizes(LyXRC const & rc)
1014 {
1015         doubleToWidget(screenTinyED, rc.font_sizes[TINY_SIZE]);
1016         doubleToWidget(screenSmallestED, rc.font_sizes[SCRIPT_SIZE]);
1017         doubleToWidget(screenSmallerED, rc.font_sizes[FOOTNOTE_SIZE]);
1018         doubleToWidget(screenSmallED, rc.font_sizes[SMALL_SIZE]);
1019         doubleToWidget(screenNormalED, rc.font_sizes[NORMAL_SIZE]);
1020         doubleToWidget(screenLargeED, rc.font_sizes[LARGE_SIZE]);
1021         doubleToWidget(screenLargerED, rc.font_sizes[LARGER_SIZE]);
1022         doubleToWidget(screenLargestED, rc.font_sizes[LARGEST_SIZE]);
1023         doubleToWidget(screenHugeED, rc.font_sizes[HUGE_SIZE]);
1024         doubleToWidget(screenHugerED, rc.font_sizes[HUGER_SIZE]);
1025 }
1026
1027
1028 void PrefScreenFonts::selectRoman(const QString & name)
1029 {
1030         screenRomanFE->set(QFont(name), name);
1031 }
1032
1033
1034 void PrefScreenFonts::selectSans(const QString & name)
1035 {
1036         screenSansFE->set(QFont(name), name);
1037 }
1038
1039
1040 void PrefScreenFonts::selectTypewriter(const QString & name)
1041 {
1042         screenTypewriterFE->set(QFont(name), name);
1043 }
1044
1045
1046 /////////////////////////////////////////////////////////////////////
1047 //
1048 // PrefColors
1049 //
1050 /////////////////////////////////////////////////////////////////////
1051
1052
1053 PrefColors::PrefColors(GuiPreferences * form)
1054         : PrefModule(catLookAndFeel, N_("Colors"), form)
1055 {
1056         setupUi(this);
1057
1058         // FIXME: all of this initialization should be put into the controller.
1059         // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg113301.html
1060         // for some discussion of why that is not trivial.
1061         QPixmap icon(32, 32);
1062         for (int i = 0; i < Color_ignore; ++i) {
1063                 ColorCode lc = static_cast<ColorCode>(i);
1064                 if (lc == Color_none
1065                     || lc == Color_black
1066                     || lc == Color_white
1067                     || lc == Color_blue
1068                     || lc == Color_brown
1069                     || lc == Color_cyan
1070                     || lc == Color_darkgray
1071                     || lc == Color_gray
1072                     || lc == Color_green
1073                     || lc == Color_lightgray
1074                     || lc == Color_lime
1075                     || lc == Color_magenta
1076                     || lc == Color_olive
1077                     || lc == Color_orange
1078                     || lc == Color_pink
1079                     || lc == Color_purple
1080                     || lc == Color_red
1081                     || lc == Color_teal
1082                     || lc == Color_violet
1083                     || lc == Color_yellow
1084                     || lc == Color_inherit
1085                     || lc == Color_ignore)
1086                         continue;
1087                 lcolors_.push_back(lc);
1088         }
1089         sort(lcolors_.begin(), lcolors_.end(), ColorSorter);
1090         vector<ColorCode>::const_iterator cit = lcolors_.begin();
1091         vector<ColorCode>::const_iterator const end = lcolors_.end();
1092         for (; cit != end; ++cit) {
1093                 (void) new QListWidgetItem(QIcon(icon),
1094                         toqstr(lcolor.getGUIName(*cit)), lyxObjectsLW);
1095         }
1096         curcolors_.resize(lcolors_.size());
1097         newcolors_.resize(lcolors_.size());
1098         // End initialization
1099
1100         connect(colorChangePB, SIGNAL(clicked()),
1101                 this, SLOT(changeColor()));
1102         connect(colorResetPB, SIGNAL(clicked()),
1103                 this, SLOT(resetColor()));
1104         connect(colorResetAllPB, SIGNAL(clicked()),
1105                 this, SLOT(resetAllColor()));
1106         connect(lyxObjectsLW, SIGNAL(itemSelectionChanged()),
1107                 this, SLOT(changeLyxObjectsSelection()));
1108         connect(lyxObjectsLW, SIGNAL(itemActivated(QListWidgetItem*)),
1109                 this, SLOT(changeColor()));
1110         connect(syscolorsCB, SIGNAL(toggled(bool)),
1111                 this, SIGNAL(changed()));
1112         connect(syscolorsCB, SIGNAL(toggled(bool)),
1113                 this, SLOT(changeSysColor()));
1114 }
1115
1116
1117 void PrefColors::applyRC(LyXRC & rc) const
1118 {
1119         LyXRC oldrc = rc;
1120
1121         for (unsigned int i = 0; i < lcolors_.size(); ++i)
1122                 if (curcolors_[i] != newcolors_[i])
1123                         form_->setColor(lcolors_[i], newcolors_[i]);
1124         rc.use_system_colors = syscolorsCB->isChecked();
1125
1126         if (oldrc.use_system_colors != rc.use_system_colors)
1127                 guiApp->colorCache().clear();
1128 }
1129
1130
1131 void PrefColors::updateRC(LyXRC const & rc)
1132 {
1133         for (size_type i = 0; i < lcolors_.size(); ++i) {
1134                 QColor color = QColor(guiApp->colorCache().get(lcolors_[i], false));
1135                 QPixmap coloritem(32, 32);
1136                 coloritem.fill(color);
1137                 lyxObjectsLW->item(int(i))->setIcon(QIcon(coloritem));
1138                 newcolors_[i] = curcolors_[i] = color.name();
1139         }
1140         syscolorsCB->setChecked(rc.use_system_colors);
1141         changeLyxObjectsSelection();
1142
1143         setDisabledResets();
1144 }
1145
1146
1147 void PrefColors::changeColor()
1148 {
1149         int const row = lyxObjectsLW->currentRow();
1150
1151         // just to be sure
1152         if (row < 0)
1153                 return;
1154
1155         QString const color = newcolors_[size_t(row)];
1156         QColor const c = QColorDialog::getColor(QColor(color), qApp->focusWidget());
1157
1158         if (setColor(row, c, color)) {
1159                 setDisabledResets();
1160                 // emit signal
1161                 changed();
1162         }
1163 }
1164
1165
1166 void PrefColors::resetColor()
1167 {
1168         int const row = lyxObjectsLW->currentRow();
1169
1170         // just to be sure
1171         if (row < 0)
1172                 return;
1173
1174         QString const color = newcolors_[size_t(row)];
1175         QColor const c = getDefaultColorByRow(row);
1176
1177         if (setColor(row, c, color)) {
1178                 setDisabledResets();
1179                 // emit signal
1180                 changed();
1181         }
1182 }
1183
1184
1185 void PrefColors::resetAllColor()
1186 {
1187         bool isChanged = false;
1188
1189         colorResetAllPB->setDisabled(true);
1190
1191         for (int irow = 0, count = lyxObjectsLW->count(); irow < count; ++irow) {
1192                 QString const color = newcolors_[size_t(irow)];
1193                 QColor const c = getDefaultColorByRow(irow);
1194
1195                 if (setColor(irow, c, color))
1196                         isChanged = true;
1197         }
1198
1199         if (isChanged) {
1200                 setDisabledResets();
1201                 // emit signal
1202                 changed();
1203         }
1204 }
1205
1206
1207 bool PrefColors::setColor(int const row, QColor const & new_color,
1208                           QString const & old_color)
1209 {
1210         if (new_color.isValid() && new_color.name() != old_color) {
1211                 newcolors_[size_t(row)] = new_color.name();
1212                 QPixmap coloritem(32, 32);
1213                 coloritem.fill(new_color);
1214                 lyxObjectsLW->item(row)->setIcon(QIcon(coloritem));
1215                 return true;
1216         }
1217         return false;
1218 }
1219
1220
1221 void PrefColors::setDisabledResets()
1222 {
1223         int const row = lyxObjectsLW->currentRow();
1224         // set disable reset buttons ...
1225         if (row >= 0)
1226                 colorResetPB->setDisabled(isDefaultColor(row, newcolors_[size_t(row)]));
1227
1228         colorResetAllPB->setDisabled(true);
1229
1230         // ... in between process qt events to give quicker visual feedback to the user ...
1231         guiApp->processEvents();
1232
1233         // ... set disable Reset All button
1234         for (int irow = 0, count = lyxObjectsLW->count(); irow < count; ++irow) {
1235                 if (!isDefaultColor(irow, newcolors_[size_t(irow)])) {
1236                         colorResetAllPB->setDisabled(false);
1237                         // the break condition might hide performance issues
1238                         // if a non-default color is at the top of the list
1239                         break;
1240                 }
1241         }
1242 }
1243
1244
1245 bool PrefColors::isDefaultColor(int const row, QString const & color)
1246 {
1247         return color == getDefaultColorByRow(row).name();
1248 }
1249
1250
1251 QColor PrefColors::getDefaultColorByRow(int const row)
1252 {
1253         ColorSet const defaultcolor;
1254         return defaultcolor.getX11HexName(lcolors_[size_t(row)],
1255                         guiApp->colorCache().isDarkMode()).c_str();
1256 }
1257
1258
1259 void PrefColors::changeSysColor()
1260 {
1261         for (int row = 0 ; row < lyxObjectsLW->count() ; ++row) {
1262                 // skip colors that are taken from system palette
1263                 bool const disable = syscolorsCB->isChecked()
1264                         && guiApp->colorCache().isSystem(lcolors_[size_t(row)]);
1265
1266                 QListWidgetItem * const item = lyxObjectsLW->item(row);
1267                 Qt::ItemFlags const flags = item->flags();
1268
1269                 if (disable)
1270                         item->setFlags(flags & ~Qt::ItemIsEnabled);
1271                 else
1272                         item->setFlags(flags | Qt::ItemIsEnabled);
1273         }
1274 }
1275
1276
1277 void PrefColors::changeLyxObjectsSelection()
1278 {
1279         int currentRow = lyxObjectsLW->currentRow();
1280         colorChangePB->setDisabled(currentRow < 0);
1281
1282         if (currentRow < 0)
1283                 colorResetPB->setDisabled(true);
1284         else
1285                 colorResetPB->setDisabled(
1286                         isDefaultColor(currentRow, newcolors_[size_t(currentRow)]));
1287 }
1288
1289
1290 /////////////////////////////////////////////////////////////////////
1291 //
1292 // PrefDisplay
1293 //
1294 /////////////////////////////////////////////////////////////////////
1295
1296 PrefDisplay::PrefDisplay(GuiPreferences * form)
1297         : PrefModule(catLookAndFeel, N_("Display"), form)
1298 {
1299         setupUi(this);
1300         connect(displayGraphicsCB, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
1301         connect(instantPreviewCO, SIGNAL(activated(int)), this, SIGNAL(changed()));
1302         connect(previewSizeSB, SIGNAL(valueChanged(double)), this, SIGNAL(changed()));
1303         connect(paragraphMarkerCB, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
1304         connect(ctAdditionsUnderlinedCB, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
1305 }
1306
1307
1308 void PrefDisplay::on_instantPreviewCO_currentIndexChanged(int index)
1309 {
1310         previewSizeSB->setEnabled(index != 0);
1311 }
1312
1313
1314 void PrefDisplay::applyRC(LyXRC & rc) const
1315 {
1316         switch (instantPreviewCO->currentIndex()) {
1317                 case 0:
1318                         rc.preview = LyXRC::PREVIEW_OFF;
1319                         break;
1320                 case 1:
1321                         rc.preview = LyXRC::PREVIEW_NO_MATH;
1322                         break;
1323                 case 2:
1324                         rc.preview = LyXRC::PREVIEW_ON;
1325                         break;
1326         }
1327
1328         rc.display_graphics = displayGraphicsCB->isChecked();
1329         rc.preview_scale_factor = previewSizeSB->value();
1330         rc.paragraph_markers = paragraphMarkerCB->isChecked();
1331         rc.ct_additions_underlined = ctAdditionsUnderlinedCB->isChecked();
1332
1333         // FIXME!! The graphics cache no longer has a changeDisplay method.
1334 #if 0
1335         if (old_value != rc.display_graphics) {
1336                 graphics::GCache & gc = graphics::GCache::get();
1337                 gc.changeDisplay();
1338         }
1339 #endif
1340 }
1341
1342
1343 void PrefDisplay::updateRC(LyXRC const & rc)
1344 {
1345         switch (rc.preview) {
1346         case LyXRC::PREVIEW_OFF:
1347                 instantPreviewCO->setCurrentIndex(0);
1348                 break;
1349         case LyXRC::PREVIEW_NO_MATH :
1350                 instantPreviewCO->setCurrentIndex(1);
1351                 break;
1352         case LyXRC::PREVIEW_ON :
1353                 instantPreviewCO->setCurrentIndex(2);
1354                 break;
1355         }
1356
1357         displayGraphicsCB->setChecked(rc.display_graphics);
1358         previewSizeSB->setValue(rc.preview_scale_factor);
1359         paragraphMarkerCB->setChecked(rc.paragraph_markers);
1360         ctAdditionsUnderlinedCB->setChecked(rc.ct_additions_underlined);
1361         previewSizeSB->setEnabled(
1362                 rc.display_graphics
1363                 && rc.preview != LyXRC::PREVIEW_OFF);
1364 }
1365
1366
1367 /////////////////////////////////////////////////////////////////////
1368 //
1369 // PrefPaths
1370 //
1371 /////////////////////////////////////////////////////////////////////
1372
1373 PrefPaths::PrefPaths(GuiPreferences * form)
1374         : PrefModule(QString(), N_("Paths"), form)
1375 {
1376         setupUi(this);
1377
1378         connect(workingDirPB, SIGNAL(clicked()), this, SLOT(selectWorkingdir()));
1379         connect(workingDirED, SIGNAL(textChanged(QString)),
1380                 this, SIGNAL(changed()));
1381
1382         connect(templateDirPB, SIGNAL(clicked()), this, SLOT(selectTemplatedir()));
1383         connect(templateDirED, SIGNAL(textChanged(QString)),
1384                 this, SIGNAL(changed()));
1385
1386         connect(exampleDirPB, SIGNAL(clicked()), this, SLOT(selectExampledir()));
1387         connect(exampleDirED, SIGNAL(textChanged(QString)),
1388                 this, SIGNAL(changed()));
1389
1390         connect(backupDirPB, SIGNAL(clicked()), this, SLOT(selectBackupdir()));
1391         connect(backupDirED, SIGNAL(textChanged(QString)),
1392                 this, SIGNAL(changed()));
1393
1394         connect(lyxserverDirPB, SIGNAL(clicked()), this, SLOT(selectLyxPipe()));
1395         connect(lyxserverDirED, SIGNAL(textChanged(QString)),
1396                 this, SIGNAL(changed()));
1397
1398         connect(thesaurusDirPB, SIGNAL(clicked()), this, SLOT(selectThesaurusdir()));
1399         connect(thesaurusDirED, SIGNAL(textChanged(QString)),
1400                 this, SIGNAL(changed()));
1401
1402         connect(tempDirPB, SIGNAL(clicked()), this, SLOT(selectTempdir()));
1403         connect(tempDirED, SIGNAL(textChanged(QString)),
1404                 this, SIGNAL(changed()));
1405
1406 #if defined(USE_HUNSPELL)
1407         connect(hunspellDirPB, SIGNAL(clicked()), this, SLOT(selectHunspelldir()));
1408         connect(hunspellDirED, SIGNAL(textChanged(QString)),
1409                 this, SIGNAL(changed()));
1410 #else
1411         hunspellDirPB->setEnabled(false);
1412         hunspellDirED->setEnabled(false);
1413 #endif
1414
1415         connect(pathPrefixED, SIGNAL(textChanged(QString)),
1416                 this, SIGNAL(changed()));
1417
1418         connect(texinputsPrefixED, SIGNAL(textChanged(QString)),
1419                 this, SIGNAL(changed()));
1420
1421         pathPrefixED->setValidator(new NoNewLineValidator(pathPrefixED));
1422         texinputsPrefixED->setValidator(new NoNewLineValidator(texinputsPrefixED));
1423 }
1424
1425
1426 void PrefPaths::applyRC(LyXRC & rc) const
1427 {
1428         rc.document_path = internal_path(fromqstr(workingDirED->text()));
1429         rc.example_path = internal_path(fromqstr(exampleDirED->text()));
1430         rc.template_path = internal_path(fromqstr(templateDirED->text()));
1431         rc.backupdir_path = internal_path(fromqstr(backupDirED->text()));
1432         rc.tempdir_path = internal_path(fromqstr(tempDirED->text()));
1433         rc.thesaurusdir_path = internal_path(fromqstr(thesaurusDirED->text()));
1434         rc.hunspelldir_path = internal_path(fromqstr(hunspellDirED->text()));
1435         rc.path_prefix = internal_path_list(fromqstr(pathPrefixED->text()));
1436         rc.texinputs_prefix = internal_path_list(fromqstr(texinputsPrefixED->text()));
1437         // FIXME: should be a checkbox only
1438         rc.lyxpipes = internal_path(fromqstr(lyxserverDirED->text()));
1439 }
1440
1441
1442 void PrefPaths::updateRC(LyXRC const & rc)
1443 {
1444         workingDirED->setText(toqstr(external_path(rc.document_path)));
1445         exampleDirED->setText(toqstr(external_path(rc.example_path)));
1446         templateDirED->setText(toqstr(external_path(rc.template_path)));
1447         backupDirED->setText(toqstr(external_path(rc.backupdir_path)));
1448         tempDirED->setText(toqstr(external_path(rc.tempdir_path)));
1449         thesaurusDirED->setText(toqstr(external_path(rc.thesaurusdir_path)));
1450         hunspellDirED->setText(toqstr(external_path(rc.hunspelldir_path)));
1451         pathPrefixED->setText(toqstr(external_path_list(rc.path_prefix)));
1452         texinputsPrefixED->setText(toqstr(external_path_list(rc.texinputs_prefix)));
1453         // FIXME: should be a checkbox only
1454         lyxserverDirED->setText(toqstr(external_path(rc.lyxpipes)));
1455 }
1456
1457
1458 void PrefPaths::selectExampledir()
1459 {
1460         QString file = browseDir(internalPath(exampleDirED->text()),
1461                 qt_("Select directory for example files"));
1462         if (!file.isEmpty())
1463                 exampleDirED->setText(file);
1464 }
1465
1466
1467 void PrefPaths::selectTemplatedir()
1468 {
1469         QString file = browseDir(internalPath(templateDirED->text()),
1470                 qt_("Select a document templates directory"));
1471         if (!file.isEmpty())
1472                 templateDirED->setText(file);
1473 }
1474
1475
1476 void PrefPaths::selectTempdir()
1477 {
1478         QString file = browseDir(internalPath(tempDirED->text()),
1479                 qt_("Select a temporary directory"));
1480         if (!file.isEmpty())
1481                 tempDirED->setText(file);
1482 }
1483
1484
1485 void PrefPaths::selectBackupdir()
1486 {
1487         QString file = browseDir(internalPath(backupDirED->text()),
1488                 qt_("Select a backups directory"));
1489         if (!file.isEmpty())
1490                 backupDirED->setText(file);
1491 }
1492
1493
1494 void PrefPaths::selectWorkingdir()
1495 {
1496         QString file = browseDir(internalPath(workingDirED->text()),
1497                 qt_("Select a document directory"));
1498         if (!file.isEmpty())
1499                 workingDirED->setText(file);
1500 }
1501
1502
1503 void PrefPaths::selectThesaurusdir()
1504 {
1505         QString file = browseDir(internalPath(thesaurusDirED->text()),
1506                 qt_("Set the path to the thesaurus dictionaries"));
1507         if (!file.isEmpty())
1508                 thesaurusDirED->setText(file);
1509 }
1510
1511
1512 void PrefPaths::selectHunspelldir()
1513 {
1514         QString file = browseDir(internalPath(hunspellDirED->text()),
1515                 qt_("Set the path to the Hunspell dictionaries"));
1516         if (!file.isEmpty())
1517                 hunspellDirED->setText(file);
1518 }
1519
1520
1521 void PrefPaths::selectLyxPipe()
1522 {
1523         QString file = form_->browse(internalPath(lyxserverDirED->text()),
1524                 qt_("Give a filename for the LyX server pipe"));
1525         if (!file.isEmpty())
1526                 lyxserverDirED->setText(file);
1527 }
1528
1529
1530 /////////////////////////////////////////////////////////////////////
1531 //
1532 // PrefSpellchecker
1533 //
1534 /////////////////////////////////////////////////////////////////////
1535
1536 PrefSpellchecker::PrefSpellchecker(GuiPreferences * form)
1537         : PrefModule(catLanguage, N_("Spellchecker"), form)
1538 {
1539         setupUi(this);
1540
1541 // FIXME: this check should test the target platform (darwin)
1542 #if defined(USE_MACOSX_PACKAGING)
1543         spellcheckerCB->addItem(qt_("Native"), QString("native"));
1544 #define CONNECT_APPLESPELL
1545 #else
1546 #undef CONNECT_APPLESPELL
1547 #endif
1548 #if defined(USE_ASPELL)
1549         spellcheckerCB->addItem(qt_("Aspell"), QString("aspell"));
1550 #endif
1551 #if defined(USE_ENCHANT)
1552         spellcheckerCB->addItem(qt_("Enchant"), QString("enchant"));
1553 #endif
1554 #if defined(USE_HUNSPELL)
1555         spellcheckerCB->addItem(qt_("Hunspell"), QString("hunspell"));
1556 #endif
1557
1558         #if defined(CONNECT_APPLESPELL) || defined(USE_ASPELL) || defined(USE_ENCHANT) || defined(USE_HUNSPELL)
1559                 connect(spellcheckerCB, SIGNAL(currentIndexChanged(int)),
1560                         this, SIGNAL(changed()));
1561                 connect(altLanguageED, SIGNAL(textChanged(QString)),
1562                         this, SIGNAL(changed()));
1563                 connect(escapeCharactersED, SIGNAL(textChanged(QString)),
1564                         this, SIGNAL(changed()));
1565                 connect(compoundWordCB, SIGNAL(clicked()),
1566                         this, SIGNAL(changed()));
1567                 connect(spellcheckContinuouslyCB, SIGNAL(clicked()),
1568                         this, SIGNAL(changed()));
1569                 connect(spellcheckNotesCB, SIGNAL(clicked()),
1570                         this, SIGNAL(changed()));
1571
1572                 altLanguageED->setValidator(new NoNewLineValidator(altLanguageED));
1573                 escapeCharactersED->setValidator(new NoNewLineValidator(escapeCharactersED));
1574         #else
1575                 spellcheckerCB->setEnabled(false);
1576                 altLanguageED->setEnabled(false);
1577                 escapeCharactersED->setEnabled(false);
1578                 compoundWordCB->setEnabled(false);
1579                 spellcheckContinuouslyCB->setEnabled(false);
1580                 spellcheckNotesCB->setEnabled(false);
1581         #endif
1582 }
1583
1584
1585 void PrefSpellchecker::applyRC(LyXRC & rc) const
1586 {
1587         string const speller = fromqstr(spellcheckerCB->
1588                 itemData(spellcheckerCB->currentIndex()).toString());
1589         if (!speller.empty())
1590                 rc.spellchecker = speller;
1591         rc.spellchecker_alt_lang = fromqstr(altLanguageED->text());
1592         rc.spellchecker_esc_chars = fromqstr(escapeCharactersED->text());
1593         rc.spellchecker_accept_compound = compoundWordCB->isChecked();
1594         rc.spellcheck_continuously = spellcheckContinuouslyCB->isChecked();
1595         rc.spellcheck_notes = spellcheckNotesCB->isChecked();
1596 }
1597
1598
1599 void PrefSpellchecker::updateRC(LyXRC const & rc)
1600 {
1601         spellcheckerCB->setCurrentIndex(
1602                 spellcheckerCB->findData(toqstr(rc.spellchecker)));
1603         altLanguageED->setText(toqstr(rc.spellchecker_alt_lang));
1604         escapeCharactersED->setText(toqstr(rc.spellchecker_esc_chars));
1605         compoundWordCB->setChecked(rc.spellchecker_accept_compound);
1606         spellcheckContinuouslyCB->setChecked(rc.spellcheck_continuously);
1607         spellcheckNotesCB->setChecked(rc.spellcheck_notes);
1608 }
1609
1610
1611 void PrefSpellchecker::on_spellcheckerCB_currentIndexChanged(int index)
1612 {
1613         QString spellchecker = spellcheckerCB->itemData(index).toString();
1614
1615         compoundWordCB->setEnabled(spellchecker == QString("aspell"));
1616 }
1617
1618
1619
1620 /////////////////////////////////////////////////////////////////////
1621 //
1622 // PrefConverters
1623 //
1624 /////////////////////////////////////////////////////////////////////
1625
1626
1627 PrefConverters::PrefConverters(GuiPreferences * form)
1628         : PrefModule(catFiles, N_("Converters"), form)
1629 {
1630         setupUi(this);
1631
1632         connect(converterNewPB, SIGNAL(clicked()),
1633                 this, SLOT(updateConverter()));
1634         connect(converterRemovePB, SIGNAL(clicked()),
1635                 this, SLOT(removeConverter()));
1636         connect(converterModifyPB, SIGNAL(clicked()),
1637                 this, SLOT(updateConverter()));
1638         connect(convertersLW, SIGNAL(currentRowChanged(int)),
1639                 this, SLOT(switchConverter()));
1640         connect(converterFromCO, SIGNAL(activated(QString)),
1641                 this, SLOT(changeConverter()));
1642         connect(converterToCO, SIGNAL(activated(QString)),
1643                 this, SLOT(changeConverter()));
1644         connect(converterED, SIGNAL(textEdited(QString)),
1645                 this, SLOT(changeConverter()));
1646         connect(converterFlagED, SIGNAL(textEdited(QString)),
1647                 this, SLOT(changeConverter()));
1648         connect(converterNewPB, SIGNAL(clicked()),
1649                 this, SIGNAL(changed()));
1650         connect(converterRemovePB, SIGNAL(clicked()),
1651                 this, SIGNAL(changed()));
1652         connect(converterModifyPB, SIGNAL(clicked()),
1653                 this, SIGNAL(changed()));
1654         connect(maxAgeLE, SIGNAL(textEdited(QString)),
1655                 this, SIGNAL(changed()));
1656         connect(needauthForbiddenCB, SIGNAL(toggled(bool)),
1657                 this, SIGNAL(changed()));
1658
1659         converterED->setValidator(new NoNewLineValidator(converterED));
1660         converterFlagED->setValidator(new NoNewLineValidator(converterFlagED));
1661         maxAgeLE->setValidator(new QDoubleValidator(0, HUGE_VAL, 6, maxAgeLE));
1662         //converterDefGB->setFocusProxy(convertersLW);
1663 }
1664
1665
1666 void PrefConverters::applyRC(LyXRC & rc) const
1667 {
1668         rc.use_converter_cache = cacheCB->isChecked();
1669         rc.use_converter_needauth_forbidden = needauthForbiddenCB->isChecked();
1670         rc.use_converter_needauth = needauthCB->isChecked();
1671         rc.converter_cache_maxage = int(widgetToDouble(maxAgeLE) * 86400.0);
1672 }
1673
1674
1675 static void setCheckboxBlockSignals(QCheckBox *cb, bool checked) {
1676         cb->blockSignals(true);
1677         cb->setChecked(checked);
1678         cb->blockSignals(false);
1679 }
1680
1681
1682 void PrefConverters::updateRC(LyXRC const & rc)
1683 {
1684         cacheCB->setChecked(rc.use_converter_cache);
1685         needauthForbiddenCB->setChecked(rc.use_converter_needauth_forbidden);
1686         setCheckboxBlockSignals(needauthCB, rc.use_converter_needauth);
1687         QString max_age;
1688         doubleToWidget(maxAgeLE, (double(rc.converter_cache_maxage) / 86400.0), 'g', 6);
1689         updateGui();
1690 }
1691
1692
1693 void PrefConverters::updateGui()
1694 {
1695         QString const pattern("%1 -> %2");
1696         form_->formats().sort();
1697         form_->converters().update(form_->formats());
1698         // save current selection
1699         QString current =
1700                 pattern
1701                 .arg(converterFromCO->currentText())
1702                 .arg(converterToCO->currentText());
1703
1704         converterFromCO->clear();
1705         converterToCO->clear();
1706
1707         for (Format const & f : form_->formats()) {
1708                 QString const name = toqstr(translateIfPossible(f.prettyname()));
1709                 converterFromCO->addItem(name);
1710                 converterToCO->addItem(name);
1711         }
1712
1713         // currentRowChanged(int) is also triggered when updating the listwidget
1714         // block signals to avoid unnecessary calls to switchConverter()
1715         convertersLW->blockSignals(true);
1716         convertersLW->clear();
1717
1718         for (Converter const & c : form_->converters()) {
1719                 QString const name =
1720                         pattern
1721                         .arg(toqstr(translateIfPossible(c.From()->prettyname())))
1722                         .arg(toqstr(translateIfPossible(c.To()->prettyname())));
1723                 int type = form_->converters().getNumber(c.From()->name(),
1724                                                          c.To()->name());
1725                 new QListWidgetItem(name, convertersLW, type);
1726         }
1727         convertersLW->sortItems(Qt::AscendingOrder);
1728         convertersLW->blockSignals(false);
1729
1730         // restore selection
1731         if (current != pattern.arg(QString()).arg(QString())) {
1732                 QList<QListWidgetItem *> const item =
1733                         convertersLW->findItems(current, Qt::MatchExactly);
1734                 if (!item.isEmpty())
1735                         convertersLW->setCurrentItem(item.at(0));
1736         }
1737
1738         // select first element if restoring failed
1739         if (convertersLW->currentRow() == -1)
1740                 convertersLW->setCurrentRow(0);
1741
1742         updateButtons();
1743 }
1744
1745
1746 void PrefConverters::switchConverter()
1747 {
1748         int const cnr = convertersLW->currentItem()->type();
1749         Converter const & c(form_->converters().get(cnr));
1750         converterFromCO->setCurrentIndex(form_->formats().getNumber(c.from()));
1751         converterToCO->setCurrentIndex(form_->formats().getNumber(c.to()));
1752         converterED->setText(toqstr(c.command()));
1753         converterFlagED->setText(toqstr(c.flags()));
1754
1755         updateButtons();
1756 }
1757
1758
1759 void PrefConverters::changeConverter()
1760 {
1761         updateButtons();
1762 }
1763
1764
1765 void PrefConverters::updateButtons()
1766 {
1767         if (form_->formats().empty())
1768                 return;
1769         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1770         Format const & to = form_->formats().get(converterToCO->currentIndex());
1771         int const sel = form_->converters().getNumber(from.name(), to.name());
1772         bool const known = sel >= 0;
1773         bool const valid = !(converterED->text().isEmpty()
1774                 || from.name() == to.name());
1775
1776         string old_command;
1777         string old_flag;
1778
1779         if (convertersLW->count() > 0) {
1780                 int const cnr = convertersLW->currentItem()->type();
1781                 Converter const & c = form_->converters().get(cnr);
1782                 old_command = c.command();
1783                 old_flag = c.flags();
1784         }
1785
1786         string const new_command = fromqstr(converterED->text());
1787         string const new_flag = fromqstr(converterFlagED->text());
1788
1789         bool modified = (old_command != new_command || old_flag != new_flag);
1790
1791         converterModifyPB->setEnabled(valid && known && modified);
1792         converterNewPB->setEnabled(valid && !known);
1793         converterRemovePB->setEnabled(known);
1794
1795         maxAgeLE->setEnabled(cacheCB->isChecked());
1796         maxAgeLA->setEnabled(cacheCB->isChecked());
1797 }
1798
1799
1800 // FIXME: user must
1801 // specify unique from/to or it doesn't appear. This is really bad UI
1802 // this is why we can use the same function for both new and modify
1803 void PrefConverters::updateConverter()
1804 {
1805         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1806         Format const & to = form_->formats().get(converterToCO->currentIndex());
1807         string const flags = fromqstr(converterFlagED->text());
1808         string const command = fromqstr(converterED->text());
1809
1810         Converter const * old =
1811                 form_->converters().getConverter(from.name(), to.name());
1812         form_->converters().add(from.name(), to.name(), command, flags);
1813
1814         if (!old)
1815                 form_->converters().updateLast(form_->formats());
1816
1817         updateGui();
1818
1819         // Remove all files created by this converter from the cache, since
1820         // the modified converter might create different files.
1821         ConverterCache::get().remove_all(from.name(), to.name());
1822 }
1823
1824
1825 void PrefConverters::removeConverter()
1826 {
1827         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1828         Format const & to = form_->formats().get(converterToCO->currentIndex());
1829         form_->converters().erase(from.name(), to.name());
1830
1831         updateGui();
1832
1833         // Remove all files created by this converter from the cache, since
1834         // a possible new converter might create different files.
1835         ConverterCache::get().remove_all(from.name(), to.name());
1836 }
1837
1838
1839 void PrefConverters::on_cacheCB_stateChanged(int state)
1840 {
1841         maxAgeLE->setEnabled(state == Qt::Checked);
1842         maxAgeLA->setEnabled(state == Qt::Checked);
1843         changed();
1844 }
1845
1846
1847 void PrefConverters::on_needauthForbiddenCB_toggled(bool checked)
1848 {
1849         needauthCB->setEnabled(!checked);
1850 }
1851
1852
1853 void PrefConverters::on_needauthCB_toggled(bool checked)
1854 {
1855         if (checked) {
1856                 changed();
1857                 return;
1858         }
1859
1860         int ret = frontend::Alert::prompt(
1861                 _("SECURITY WARNING!"), _("Unchecking this option has the effect that potentially harmful converters would be run without asking your permission first. This is UNSAFE and NOT recommended, unless you know what you are doing. Are you sure you would like to proceed ? The recommended and safe answer is NO!"),
1862                 0, 0, _("&No"), _("&Yes"));
1863         if (ret == 1)
1864                 changed();
1865         else
1866                 setCheckboxBlockSignals(needauthCB, true);
1867 }
1868
1869
1870 /////////////////////////////////////////////////////////////////////
1871 //
1872 // FormatValidator
1873 //
1874 /////////////////////////////////////////////////////////////////////
1875
1876 class FormatValidator : public QValidator
1877 {
1878 public:
1879         FormatValidator(QWidget *, Formats const & f);
1880         void fixup(QString & input) const override;
1881         QValidator::State validate(QString & input, int & pos) const override;
1882 private:
1883         virtual QString toString(Format const & format) const = 0;
1884         int nr() const;
1885         Formats const & formats_;
1886 };
1887
1888
1889 FormatValidator::FormatValidator(QWidget * parent, Formats const & f)
1890         : QValidator(parent), formats_(f)
1891 {
1892 }
1893
1894
1895 void FormatValidator::fixup(QString & input) const
1896 {
1897         Formats::const_iterator cit = formats_.begin();
1898         Formats::const_iterator end = formats_.end();
1899         for (; cit != end; ++cit) {
1900                 QString const name = toString(*cit);
1901                 if (distance(formats_.begin(), cit) == nr()) {
1902                         input = name;
1903                         return;
1904                 }
1905         }
1906 }
1907
1908
1909 QValidator::State FormatValidator::validate(QString & input, int & /*pos*/) const
1910 {
1911         Formats::const_iterator cit = formats_.begin();
1912         Formats::const_iterator end = formats_.end();
1913         bool unknown = true;
1914         for (; unknown && cit != end; ++cit) {
1915                 QString const name = toString(*cit);
1916                 if (distance(formats_.begin(), cit) != nr())
1917                         unknown = name != input;
1918         }
1919
1920         if (unknown && !input.isEmpty())
1921                 return QValidator::Acceptable;
1922         else
1923                 return QValidator::Intermediate;
1924 }
1925
1926
1927 int FormatValidator::nr() const
1928 {
1929         QComboBox * p = qobject_cast<QComboBox *>(parent());
1930         return p->itemData(p->currentIndex()).toInt();
1931 }
1932
1933
1934 /////////////////////////////////////////////////////////////////////
1935 //
1936 // FormatNameValidator
1937 //
1938 /////////////////////////////////////////////////////////////////////
1939
1940 class FormatNameValidator : public FormatValidator
1941 {
1942 public:
1943         FormatNameValidator(QWidget * parent, Formats const & f)
1944                 : FormatValidator(parent, f)
1945         {}
1946 private:
1947         QString toString(Format const & format) const override
1948         {
1949                 return toqstr(format.name());
1950         }
1951 };
1952
1953
1954 /////////////////////////////////////////////////////////////////////
1955 //
1956 // FormatPrettynameValidator
1957 //
1958 /////////////////////////////////////////////////////////////////////
1959
1960 class FormatPrettynameValidator : public FormatValidator
1961 {
1962 public:
1963         FormatPrettynameValidator(QWidget * parent, Formats const & f)
1964                 : FormatValidator(parent, f)
1965         {}
1966 private:
1967         QString toString(Format const & format) const override
1968         {
1969                 return toqstr(translateIfPossible(format.prettyname()));
1970         }
1971 };
1972
1973
1974 /////////////////////////////////////////////////////////////////////
1975 //
1976 // PrefFileformats
1977 //
1978 /////////////////////////////////////////////////////////////////////
1979
1980 PrefFileformats::PrefFileformats(GuiPreferences * form)
1981         : PrefModule(catFiles, N_("File Formats"), form)
1982 {
1983         setupUi(this);
1984
1985         formatED->setValidator(new FormatNameValidator(formatsCB, form_->formats()));
1986         formatsCB->setValidator(new FormatPrettynameValidator(formatsCB, form_->formats()));
1987         extensionsED->setValidator(new NoNewLineValidator(extensionsED));
1988         shortcutED->setValidator(new NoNewLineValidator(shortcutED));
1989         editorED->setValidator(new NoNewLineValidator(editorED));
1990         viewerED->setValidator(new NoNewLineValidator(viewerED));
1991         copierED->setValidator(new NoNewLineValidator(copierED));
1992
1993         connect(documentCB, SIGNAL(clicked()),
1994                 this, SLOT(setFlags()));
1995         connect(vectorCB, SIGNAL(clicked()),
1996                 this, SLOT(setFlags()));
1997         connect(exportMenuCB, SIGNAL(clicked()),
1998                 this, SLOT(setFlags()));
1999         connect(formatsCB->lineEdit(), SIGNAL(editingFinished()),
2000                 this, SLOT(updatePrettyname()));
2001         connect(formatsCB->lineEdit(), SIGNAL(textEdited(QString)),
2002                 this, SIGNAL(changed()));
2003         connect(defaultFormatCB, SIGNAL(activated(QString)),
2004                 this, SIGNAL(changed()));
2005         connect(defaultOTFFormatCB, SIGNAL(activated(QString)),
2006                 this, SIGNAL(changed()));
2007         connect(defaultPlatexFormatCB, SIGNAL(activated(QString)),
2008                 this, SIGNAL(changed()));
2009         connect(viewerCO, SIGNAL(activated(int)),
2010                 this, SIGNAL(changed()));
2011         connect(editorCO, SIGNAL(activated(int)),
2012                 this, SIGNAL(changed()));
2013 }
2014
2015
2016 namespace {
2017
2018 string const l10n_shortcut(docstring const & prettyname, string const & shortcut)
2019 {
2020         if (shortcut.empty())
2021                 return string();
2022
2023         string l10n_format =
2024                 to_utf8(_(to_utf8(prettyname) + '|' + shortcut));
2025         return split(l10n_format, '|');
2026 }
2027
2028 } // namespace
2029
2030
2031 void PrefFileformats::applyRC(LyXRC & rc) const
2032 {
2033         QString const default_format = defaultFormatCB->itemData(
2034                 defaultFormatCB->currentIndex()).toString();
2035         rc.default_view_format = fromqstr(default_format);
2036         QString const default_otf_format = defaultOTFFormatCB->itemData(
2037                 defaultOTFFormatCB->currentIndex()).toString();
2038         rc.default_otf_view_format = fromqstr(default_otf_format);
2039         QString const default_platex_format = defaultPlatexFormatCB->itemData(
2040                 defaultPlatexFormatCB->currentIndex()).toString();
2041         rc.default_platex_view_format = fromqstr(default_platex_format);
2042 }
2043
2044
2045 void PrefFileformats::updateRC(LyXRC const & rc)
2046 {
2047         viewer_alternatives = rc.viewer_alternatives;
2048         editor_alternatives = rc.editor_alternatives;
2049         bool const init = defaultFormatCB->currentText().isEmpty();
2050         updateView();
2051         if (init) {
2052                 int pos =
2053                         defaultFormatCB->findData(toqstr(rc.default_view_format));
2054                 defaultFormatCB->setCurrentIndex(pos);
2055                 pos = defaultOTFFormatCB->findData(toqstr(rc.default_otf_view_format));
2056                                 defaultOTFFormatCB->setCurrentIndex(pos);
2057                 defaultOTFFormatCB->setCurrentIndex(pos);
2058                 pos = defaultPlatexFormatCB->findData(toqstr(rc.default_platex_view_format));
2059                                 defaultPlatexFormatCB->setCurrentIndex(pos);
2060                 defaultPlatexFormatCB->setCurrentIndex(pos);
2061         }
2062 }
2063
2064
2065 void PrefFileformats::updateView()
2066 {
2067         QString const current = formatsCB->currentText();
2068         QString const current_def = defaultFormatCB->currentText();
2069         QString const current_def_otf = defaultOTFFormatCB->currentText();
2070         QString const current_def_platex = defaultPlatexFormatCB->currentText();
2071
2072         // update comboboxes with formats
2073         formatsCB->blockSignals(true);
2074         defaultFormatCB->blockSignals(true);
2075         defaultOTFFormatCB->blockSignals(true);
2076         defaultPlatexFormatCB->blockSignals(true);
2077         formatsCB->clear();
2078         defaultFormatCB->clear();
2079         defaultOTFFormatCB->clear();
2080         defaultPlatexFormatCB->clear();
2081         form_->formats().sort();
2082         for (Format const & f : form_->formats()) {
2083                 QString const prettyname = toqstr(translateIfPossible(f.prettyname()));
2084                 formatsCB->addItem(prettyname,
2085                                    QVariant(form_->formats().getNumber(f.name())));
2086                 if (f.viewer().empty())
2087                         continue;
2088                 if (form_->converters().isReachable("xhtml", f.name())
2089                     || form_->converters().isReachable("dviluatex", f.name())
2090                     || form_->converters().isReachable("luatex", f.name())
2091                     || form_->converters().isReachable("xetex", f.name())) {
2092                         defaultFormatCB->addItem(prettyname,
2093                                         QVariant(toqstr(f.name())));
2094                         defaultOTFFormatCB->addItem(prettyname,
2095                                         QVariant(toqstr(f.name())));
2096                 } else {
2097                         if (form_->converters().isReachable("latex", f.name())
2098                             || form_->converters().isReachable("pdflatex", f.name()))
2099                                 defaultFormatCB->addItem(prettyname,
2100                                         QVariant(toqstr(f.name())));
2101                         if (form_->converters().isReachable("platex", f.name()))
2102                                         defaultPlatexFormatCB->addItem(prettyname,
2103                                                 QVariant(toqstr(f.name())));
2104                 }
2105         }
2106
2107         // restore selections
2108         int item = formatsCB->findText(current, Qt::MatchExactly);
2109         formatsCB->setCurrentIndex(item < 0 ? 0 : item);
2110         on_formatsCB_currentIndexChanged(item < 0 ? 0 : item);
2111         item = defaultFormatCB->findText(current_def, Qt::MatchExactly);
2112         defaultFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2113         item = defaultOTFFormatCB->findText(current_def_otf, Qt::MatchExactly);
2114         defaultOTFFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2115         item = defaultPlatexFormatCB->findText(current_def_platex, Qt::MatchExactly);
2116         defaultPlatexFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2117         formatsCB->blockSignals(false);
2118         defaultFormatCB->blockSignals(false);
2119         defaultOTFFormatCB->blockSignals(false);
2120         defaultPlatexFormatCB->blockSignals(false);
2121 }
2122
2123
2124 void PrefFileformats::on_formatsCB_currentIndexChanged(int i)
2125 {
2126         if (form_->formats().empty())
2127                 return;
2128         int const nr = formatsCB->itemData(i).toInt();
2129         Format const f = form_->formats().get(nr);
2130
2131         formatED->setText(toqstr(f.name()));
2132         copierED->setText(toqstr(form_->movers().command(f.name())));
2133         extensionsED->setText(toqstr(f.extensions()));
2134         mimeED->setText(toqstr(f.mime()));
2135         shortcutED->setText(
2136                 toqstr(l10n_shortcut(f.prettyname(), f.shortcut())));
2137         documentCB->setChecked((f.documentFormat()));
2138         vectorCB->setChecked((f.vectorFormat()));
2139         exportMenuCB->setChecked((f.inExportMenu()));
2140         exportMenuCB->setEnabled((f.documentFormat()));
2141         updateViewers();
2142         updateEditors();
2143 }
2144
2145
2146 void PrefFileformats::setFlags()
2147 {
2148         int flags = Format::none;
2149         if (documentCB->isChecked())
2150                 flags |= Format::document;
2151         if (vectorCB->isChecked())
2152                 flags |= Format::vector;
2153         if (exportMenuCB->isChecked())
2154                 flags |= Format::export_menu;
2155         currentFormat().setFlags(flags);
2156         exportMenuCB->setEnabled(documentCB->isChecked());
2157         changed();
2158 }
2159
2160
2161 void PrefFileformats::on_copierED_textEdited(const QString & s)
2162 {
2163         string const fmt = fromqstr(formatED->text());
2164         form_->movers().set(fmt, fromqstr(s));
2165         changed();
2166 }
2167
2168
2169 void PrefFileformats::on_extensionsED_textEdited(const QString & s)
2170 {
2171         currentFormat().setExtensions(fromqstr(s));
2172         changed();
2173 }
2174
2175
2176 void PrefFileformats::on_viewerED_textEdited(const QString & s)
2177 {
2178         currentFormat().setViewer(fromqstr(s));
2179         changed();
2180 }
2181
2182
2183 void PrefFileformats::on_editorED_textEdited(const QString & s)
2184 {
2185         currentFormat().setEditor(fromqstr(s));
2186         changed();
2187 }
2188
2189
2190 void PrefFileformats::on_mimeED_textEdited(const QString & s)
2191 {
2192         currentFormat().setMime(fromqstr(s));
2193         changed();
2194 }
2195
2196
2197 void PrefFileformats::on_shortcutED_textEdited(const QString & s)
2198 {
2199         string const new_shortcut = fromqstr(s);
2200         if (new_shortcut == l10n_shortcut(currentFormat().prettyname(),
2201                                           currentFormat().shortcut()))
2202                 return;
2203         currentFormat().setShortcut(new_shortcut);
2204         changed();
2205 }
2206
2207
2208 void PrefFileformats::on_formatED_editingFinished()
2209 {
2210         string const newname = fromqstr(formatED->displayText());
2211         string const oldname = currentFormat().name();
2212         if (newname == oldname)
2213                 return;
2214         if (form_->converters().formatIsUsed(oldname)) {
2215                 Alert::error(_("Format in use"),
2216                              _("You cannot change a format's short name "
2217                                "if the format is used by a converter. "
2218                                "Please remove the converter first."));
2219                 updateView();
2220                 return;
2221         }
2222
2223         currentFormat().setName(newname);
2224         changed();
2225 }
2226
2227
2228 void PrefFileformats::on_formatED_textChanged(const QString &)
2229 {
2230         QString t = formatED->text();
2231         int p = 0;
2232         bool valid = formatED->validator()->validate(t, p) == QValidator::Acceptable;
2233         setValid(formatLA, valid);
2234 }
2235
2236
2237 void PrefFileformats::on_formatsCB_editTextChanged(const QString &)
2238 {
2239         QString t = formatsCB->currentText();
2240         int p = 0;
2241         bool valid = formatsCB->validator()->validate(t, p) == QValidator::Acceptable;
2242         setValid(formatsLA, valid);
2243 }
2244
2245
2246 void PrefFileformats::updatePrettyname()
2247 {
2248         QString const newname = formatsCB->currentText();
2249         if (newname == toqstr(translateIfPossible(currentFormat().prettyname())))
2250                 return;
2251
2252         currentFormat().setPrettyname(qstring_to_ucs4(newname));
2253         formatsChanged();
2254         updateView();
2255         changed();
2256 }
2257
2258
2259 namespace {
2260         void updateComboBox(LyXRC::Alternatives const & alts,
2261                             string const & fmt, QComboBox * combo)
2262         {
2263                 LyXRC::Alternatives::const_iterator it =
2264                                 alts.find(fmt);
2265                 if (it != alts.end()) {
2266                         LyXRC::CommandSet const & cmds = it->second;
2267                         LyXRC::CommandSet::const_iterator sit =
2268                                         cmds.begin();
2269                         LyXRC::CommandSet::const_iterator const sen =
2270                                         cmds.end();
2271                         for (; sit != sen; ++sit) {
2272                                 QString const qcmd = toqstr(*sit);
2273                                 combo->addItem(qcmd, qcmd);
2274                         }
2275                 }
2276         }
2277 } // namespace
2278
2279
2280 void PrefFileformats::updateViewers()
2281 {
2282         Format const f = currentFormat();
2283         viewerCO->blockSignals(true);
2284         viewerCO->clear();
2285         viewerCO->addItem(qt_("None"), QString());
2286         updateComboBox(viewer_alternatives, f.name(), viewerCO);
2287         viewerCO->addItem(qt_("Custom"), QString("custom viewer"));
2288         viewerCO->blockSignals(false);
2289
2290         int pos = viewerCO->findData(toqstr(f.viewer()));
2291         if (pos != -1) {
2292                 viewerED->clear();
2293                 viewerED->setEnabled(false);
2294                 viewerCO->setCurrentIndex(pos);
2295         } else {
2296                 viewerED->setEnabled(true);
2297                 viewerED->setText(toqstr(f.viewer()));
2298                 viewerCO->setCurrentIndex(viewerCO->findData(toqstr("custom viewer")));
2299         }
2300 }
2301
2302
2303 void PrefFileformats::updateEditors()
2304 {
2305         Format const f = currentFormat();
2306         editorCO->blockSignals(true);
2307         editorCO->clear();
2308         editorCO->addItem(qt_("None"), QString());
2309         updateComboBox(editor_alternatives, f.name(), editorCO);
2310         editorCO->addItem(qt_("Custom"), QString("custom editor"));
2311         editorCO->blockSignals(false);
2312
2313         int pos = editorCO->findData(toqstr(f.editor()));
2314         if (pos != -1) {
2315                 editorED->clear();
2316                 editorED->setEnabled(false);
2317                 editorCO->setCurrentIndex(pos);
2318         } else {
2319                 editorED->setEnabled(true);
2320                 editorED->setText(toqstr(f.editor()));
2321                 editorCO->setCurrentIndex(editorCO->findData(toqstr("custom editor")));
2322         }
2323 }
2324
2325
2326 void PrefFileformats::on_viewerCO_currentIndexChanged(int i)
2327 {
2328         bool const custom = viewerCO->itemData(i).toString() == "custom viewer";
2329         viewerED->setEnabled(custom);
2330         if (!custom)
2331                 currentFormat().setViewer(fromqstr(viewerCO->itemData(i).toString()));
2332 }
2333
2334
2335 void PrefFileformats::on_editorCO_currentIndexChanged(int i)
2336 {
2337         bool const custom = editorCO->itemData(i).toString() == "custom editor";
2338         editorED->setEnabled(custom);
2339         if (!custom)
2340                 currentFormat().setEditor(fromqstr(editorCO->itemData(i).toString()));
2341 }
2342
2343
2344 Format & PrefFileformats::currentFormat()
2345 {
2346         int const i = formatsCB->currentIndex();
2347         int const nr = formatsCB->itemData(i).toInt();
2348         return form_->formats().get(nr);
2349 }
2350
2351
2352 void PrefFileformats::on_formatNewPB_clicked()
2353 {
2354         form_->formats().add("", "", docstring(), "", "", "", "", Format::none);
2355         updateView();
2356         formatsCB->setCurrentIndex(0);
2357         formatsCB->setFocus(Qt::OtherFocusReason);
2358 }
2359
2360
2361 void PrefFileformats::on_formatRemovePB_clicked()
2362 {
2363         int const i = formatsCB->currentIndex();
2364         int const nr = formatsCB->itemData(i).toInt();
2365         string const current_text = form_->formats().get(nr).name();
2366         if (form_->converters().formatIsUsed(current_text)) {
2367                 Alert::error(_("Format in use"),
2368                              _("Cannot remove a Format used by a Converter. "
2369                                             "Remove the converter first."));
2370                 return;
2371         }
2372
2373         form_->formats().erase(current_text);
2374         formatsChanged();
2375         updateView();
2376         on_formatsCB_editTextChanged(formatsCB->currentText());
2377         changed();
2378 }
2379
2380
2381 /////////////////////////////////////////////////////////////////////
2382 //
2383 // PrefLanguage
2384 //
2385 /////////////////////////////////////////////////////////////////////
2386
2387 PrefLanguage::PrefLanguage(GuiPreferences * form)
2388         : PrefModule(catLanguage, N_("Language"), form)
2389 {
2390         setupUi(this);
2391
2392         connect(visualCursorRB, SIGNAL(clicked()),
2393                 this, SIGNAL(changed()));
2394         connect(logicalCursorRB, SIGNAL(clicked()),
2395                 this, SIGNAL(changed()));
2396         connect(markForeignCB, SIGNAL(clicked()),
2397                 this, SIGNAL(changed()));
2398         connect(respectOSkbdCB, SIGNAL(clicked()),
2399                 this, SIGNAL(changed()));
2400         connect(explicitDocLangBeginCB, SIGNAL(clicked()),
2401                 this, SIGNAL(changed()));
2402         connect(explicitDocLangEndCB, SIGNAL(clicked()),
2403                 this, SIGNAL(changed()));
2404         connect(languagePackageCO, SIGNAL(activated(int)),
2405                 this, SIGNAL(changed()));
2406         connect(languagePackageED, SIGNAL(textChanged(QString)),
2407                 this, SIGNAL(changed()));
2408         connect(globalCB, SIGNAL(clicked()),
2409                 this, SIGNAL(changed()));
2410         connect(startCommandED, SIGNAL(textChanged(QString)),
2411                 this, SIGNAL(changed()));
2412         connect(endCommandED, SIGNAL(textChanged(QString)),
2413                 this, SIGNAL(changed()));
2414         connect(uiLanguageCO, SIGNAL(activated(int)),
2415                 this, SIGNAL(changed()));
2416         connect(defaultDecimalSepED, SIGNAL(textChanged(QString)),
2417                 this, SIGNAL(changed()));
2418         connect(defaultDecimalSepCO, SIGNAL(activated(int)),
2419                 this, SIGNAL(changed()));
2420         connect(defaultLengthUnitCO, SIGNAL(activated(int)),
2421                 this, SIGNAL(changed()));
2422
2423         languagePackageED->setValidator(new NoNewLineValidator(languagePackageED));
2424         startCommandED->setValidator(new NoNewLineValidator(startCommandED));
2425         endCommandED->setValidator(new NoNewLineValidator(endCommandED));
2426
2427         defaultDecimalSepED->setValidator(new QRegExpValidator(QRegExp("\\S"), this));
2428         defaultDecimalSepED->setMaxLength(1);
2429
2430         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::CM]), Length::CM);
2431         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::IN]), Length::IN);
2432
2433         QAbstractItemModel * language_model = guiApp->languageModel();
2434         language_model->sort(0);
2435         uiLanguageCO->blockSignals(true);
2436         uiLanguageCO->clear();
2437         uiLanguageCO->addItem(qt_("Default"), toqstr("auto"));
2438         for (int i = 0; i != language_model->rowCount(); ++i) {
2439                 QModelIndex index = language_model->index(i, 0);
2440                 // Filter the list based on the available translation and add
2441                 // each language code only once
2442                 string const name = fromqstr(index.data(Qt::UserRole).toString());
2443                 Language const * lang = languages.getLanguage(name);
2444                 if (!lang)
2445                         continue;
2446                 // never remove the currently selected language
2447                 if (name != form->rc().gui_language
2448                     && name != lyxrc.gui_language
2449                     && (!Messages::available(lang->code())
2450                         || !lang->hasGuiSupport()))
2451                         continue;
2452                 uiLanguageCO->addItem(index.data(Qt::DisplayRole).toString(),
2453                                       index.data(Qt::UserRole).toString());
2454         }
2455         uiLanguageCO->blockSignals(false);
2456 }
2457
2458
2459 void PrefLanguage::on_uiLanguageCO_currentIndexChanged(int)
2460 {
2461          QMessageBox::information(this, qt_("LyX needs to be restarted!"),
2462                  qt_("The change of user interface language will be fully "
2463                  "effective only after a restart."));
2464 }
2465
2466
2467 void PrefLanguage::on_languagePackageCO_currentIndexChanged(int i)
2468 {
2469         if (i == 2)
2470                 languagePackageED->setText(save_langpack_);
2471         else if (!languagePackageED->text().isEmpty()) {
2472                 save_langpack_ = languagePackageED->text();
2473                 languagePackageED->clear();
2474         }
2475         languagePackageED->setEnabled(i == 2);
2476 }
2477
2478
2479 void PrefLanguage::on_defaultDecimalSepCO_currentIndexChanged(int i)
2480 {
2481         defaultDecimalSepED->setEnabled(i == 1);
2482 }
2483
2484
2485 void PrefLanguage::applyRC(LyXRC & rc) const
2486 {
2487         rc.visual_cursor = visualCursorRB->isChecked();
2488         rc.mark_foreign_language = markForeignCB->isChecked();
2489         rc.respect_os_kbd_language = respectOSkbdCB->isChecked();
2490         rc.language_auto_begin = !explicitDocLangBeginCB->isChecked();
2491         rc.language_auto_end = !explicitDocLangEndCB->isChecked();
2492         int const p = languagePackageCO->currentIndex();
2493         if (p == 0)
2494                 rc.language_package_selection = LyXRC::LP_AUTO;
2495         else if (p == 1)
2496                 rc.language_package_selection = LyXRC::LP_BABEL;
2497         else if (p == 2)
2498                 rc.language_package_selection = LyXRC::LP_CUSTOM;
2499         else if (p == 3)
2500                 rc.language_package_selection = LyXRC::LP_NONE;
2501         rc.language_custom_package = fromqstr(languagePackageED->text());
2502         rc.language_global_options = globalCB->isChecked();
2503         rc.language_command_begin = fromqstr(startCommandED->text());
2504         rc.language_command_end = fromqstr(endCommandED->text());
2505         rc.gui_language = fromqstr(
2506                 uiLanguageCO->itemData(uiLanguageCO->currentIndex()).toString());
2507         if (defaultDecimalSepCO->currentIndex() == 0)
2508                 rc.default_decimal_sep = "locale";
2509         else
2510                 rc.default_decimal_sep = fromqstr(defaultDecimalSepED->text());
2511         rc.default_length_unit = (Length::UNIT) defaultLengthUnitCO->itemData(defaultLengthUnitCO->currentIndex()).toInt();
2512 }
2513
2514
2515 void PrefLanguage::updateRC(LyXRC const & rc)
2516 {
2517         if (rc.visual_cursor)
2518                 visualCursorRB->setChecked(true);
2519         else
2520                 logicalCursorRB->setChecked(true);
2521         markForeignCB->setChecked(rc.mark_foreign_language);
2522         respectOSkbdCB->setChecked(rc.respect_os_kbd_language);
2523         explicitDocLangBeginCB->setChecked(!rc.language_auto_begin);
2524         explicitDocLangEndCB->setChecked(!rc.language_auto_end);
2525         languagePackageCO->setCurrentIndex(rc.language_package_selection);
2526         if (languagePackageCO->currentIndex() == 2) {
2527                 languagePackageED->setText(toqstr(rc.language_custom_package));
2528                 languagePackageED->setEnabled(true);
2529         } else {
2530                 languagePackageED->clear();
2531                 save_langpack_ = toqstr(rc.language_custom_package);
2532                 languagePackageED->setEnabled(false);
2533         }
2534         defaultDecimalSepED->setEnabled(defaultDecimalSepCO->currentIndex() == 1);
2535         globalCB->setChecked(rc.language_global_options);
2536         startCommandED->setText(toqstr(rc.language_command_begin));
2537         endCommandED->setText(toqstr(rc.language_command_end));
2538         if (rc.default_decimal_sep == "locale") {
2539                 defaultDecimalSepCO->setCurrentIndex(0);
2540                 defaultDecimalSepED->clear();
2541         } else {
2542                 defaultDecimalSepCO->setCurrentIndex(1);
2543                 defaultDecimalSepED->setText(toqstr(rc.default_decimal_sep));
2544         }
2545         int pos = defaultLengthUnitCO->findData(int(rc.default_length_unit));
2546         defaultLengthUnitCO->setCurrentIndex(pos);
2547
2548         pos = uiLanguageCO->findData(toqstr(rc.gui_language));
2549         uiLanguageCO->blockSignals(true);
2550         uiLanguageCO->setCurrentIndex(pos);
2551         uiLanguageCO->blockSignals(false);
2552 }
2553
2554
2555 /////////////////////////////////////////////////////////////////////
2556 //
2557 // PrefUserInterface
2558 //
2559 /////////////////////////////////////////////////////////////////////
2560
2561 PrefUserInterface::PrefUserInterface(GuiPreferences * form)
2562         : PrefModule(catLookAndFeel, N_("User Interface"), form)
2563 {
2564         setupUi(this);
2565
2566         connect(uiFilePB, SIGNAL(clicked()),
2567                 this, SLOT(selectUi()));
2568         connect(uiFileED, SIGNAL(textChanged(QString)),
2569                 this, SIGNAL(changed()));
2570         connect(iconSetCO, SIGNAL(activated(int)),
2571                 this, SIGNAL(changed()));
2572         connect(useSystemThemeIconsCB, SIGNAL(clicked()),
2573                 this, SIGNAL(changed()));
2574         connect(lastfilesSB, SIGNAL(valueChanged(int)),
2575                 this, SIGNAL(changed()));
2576         connect(tooltipCB, SIGNAL(toggled(bool)),
2577                 this, SIGNAL(changed()));
2578         lastfilesSB->setMaximum(maxlastfiles);
2579
2580         iconSetCO->addItem(qt_("Default"), QString());
2581         iconSetCO->addItem(qt_("Classic"), "classic");
2582         iconSetCO->addItem(qt_("Oxygen"), "oxygen");
2583
2584 #if (!(defined Q_WS_X11 || defined(QPA_XCB)) || QT_VERSION < 0x040600)
2585         useSystemThemeIconsCB->hide();
2586 #endif
2587 }
2588
2589
2590 void PrefUserInterface::applyRC(LyXRC & rc) const
2591 {
2592         rc.icon_set = fromqstr(iconSetCO->itemData(
2593                 iconSetCO->currentIndex()).toString());
2594
2595         rc.ui_file = internal_path(fromqstr(uiFileED->text()));
2596         rc.use_system_theme_icons = useSystemThemeIconsCB->isChecked();
2597         rc.num_lastfiles = lastfilesSB->value();
2598         rc.use_tooltip = tooltipCB->isChecked();
2599 }
2600
2601
2602 void PrefUserInterface::updateRC(LyXRC const & rc)
2603 {
2604         int iconset = iconSetCO->findData(toqstr(rc.icon_set));
2605         if (iconset < 0)
2606                 iconset = 0;
2607         iconSetCO->setCurrentIndex(iconset);
2608         useSystemThemeIconsCB->setChecked(rc.use_system_theme_icons);
2609         uiFileED->setText(toqstr(external_path(rc.ui_file)));
2610         lastfilesSB->setValue(rc.num_lastfiles);
2611         tooltipCB->setChecked(rc.use_tooltip);
2612 }
2613
2614
2615 void PrefUserInterface::selectUi()
2616 {
2617         QString file = form_->browseUI(internalPath(uiFileED->text()));
2618         if (!file.isEmpty())
2619                 uiFileED->setText(file);
2620 }
2621
2622
2623 /////////////////////////////////////////////////////////////////////
2624 //
2625 // PrefDocumentHandling
2626 //
2627 /////////////////////////////////////////////////////////////////////
2628
2629 PrefDocHandling::PrefDocHandling(GuiPreferences * form)
2630         : PrefModule(catLookAndFeel, N_("Document Handling"), form)
2631 {
2632         setupUi(this);
2633
2634         connect(autoSaveCB, SIGNAL(toggled(bool)),
2635                 autoSaveSB, SLOT(setEnabled(bool)));
2636         connect(autoSaveCB, SIGNAL(toggled(bool)),
2637                 TextLabel1, SLOT(setEnabled(bool)));
2638         connect(openDocumentsInTabsCB, SIGNAL(clicked()),
2639                 this, SIGNAL(changed()));
2640         connect(singleInstanceCB, SIGNAL(clicked()),
2641                 this, SIGNAL(changed()));
2642         connect(singleCloseTabButtonCB, SIGNAL(clicked()),
2643                 this, SIGNAL(changed()));
2644         connect(closeLastViewCO, SIGNAL(activated(int)),
2645                 this, SIGNAL(changed()));
2646         connect(restoreCursorCB, SIGNAL(clicked()),
2647                 this, SIGNAL(changed()));
2648         connect(loadSessionCB, SIGNAL(clicked()),
2649                 this, SIGNAL(changed()));
2650         connect(allowGeometrySessionCB, SIGNAL(clicked()),
2651                 this, SIGNAL(changed()));
2652         connect(autoSaveSB, SIGNAL(valueChanged(int)),
2653                 this, SIGNAL(changed()));
2654         connect(autoSaveCB, SIGNAL(clicked()),
2655                 this, SIGNAL(changed()));
2656         connect(backupCB, SIGNAL(clicked()),
2657                 this, SIGNAL(changed()));
2658         connect(saveCompressedCB, SIGNAL(clicked()),
2659                 this, SIGNAL(changed()));
2660         connect(saveOriginCB, SIGNAL(clicked()),
2661                 this, SIGNAL(changed()));
2662 }
2663
2664
2665 void PrefDocHandling::applyRC(LyXRC & rc) const
2666 {
2667         rc.use_lastfilepos = restoreCursorCB->isChecked();
2668         rc.load_session = loadSessionCB->isChecked();
2669         rc.allow_geometry_session = allowGeometrySessionCB->isChecked();
2670         rc.autosave = autoSaveCB->isChecked() ?  autoSaveSB->value() * 60 : 0;
2671         rc.make_backup = backupCB->isChecked();
2672         rc.save_compressed = saveCompressedCB->isChecked();
2673         rc.save_origin = saveOriginCB->isChecked();
2674         rc.open_buffers_in_tabs = openDocumentsInTabsCB->isChecked();
2675         rc.single_instance = singleInstanceCB->isChecked();
2676         rc.single_close_tab_button = singleCloseTabButtonCB->isChecked();
2677
2678         switch (closeLastViewCO->currentIndex()) {
2679         case 0:
2680                 rc.close_buffer_with_last_view = "yes";
2681                 break;
2682         case 1:
2683                 rc.close_buffer_with_last_view = "no";
2684                 break;
2685         case 2:
2686                 rc.close_buffer_with_last_view = "ask";
2687                 break;
2688         default:
2689                 ;
2690         }
2691 }
2692
2693
2694 void PrefDocHandling::updateRC(LyXRC const & rc)
2695 {
2696         restoreCursorCB->setChecked(rc.use_lastfilepos);
2697         loadSessionCB->setChecked(rc.load_session);
2698         allowGeometrySessionCB->setChecked(rc.allow_geometry_session);
2699         // convert to minutes
2700         bool autosave = rc.autosave > 0;
2701         int mins = rc.autosave / 60;
2702         if (!mins)
2703                 mins = 5;
2704         autoSaveSB->setValue(mins);
2705         autoSaveCB->setChecked(autosave);
2706         autoSaveSB->setEnabled(autosave);
2707         backupCB->setChecked(rc.make_backup);
2708         saveCompressedCB->setChecked(rc.save_compressed);
2709         saveOriginCB->setChecked(rc.save_origin);
2710         openDocumentsInTabsCB->setChecked(rc.open_buffers_in_tabs);
2711         singleInstanceCB->setChecked(rc.single_instance && !rc.lyxpipes.empty());
2712         singleInstanceCB->setEnabled(!rc.lyxpipes.empty());
2713         singleCloseTabButtonCB->setChecked(rc.single_close_tab_button);
2714         if (rc.close_buffer_with_last_view == "yes")
2715                 closeLastViewCO->setCurrentIndex(0);
2716         else if (rc.close_buffer_with_last_view == "no")
2717                 closeLastViewCO->setCurrentIndex(1);
2718         else if (rc.close_buffer_with_last_view == "ask")
2719                 closeLastViewCO->setCurrentIndex(2);
2720 }
2721
2722
2723 void PrefDocHandling::on_clearSessionPB_clicked()
2724 {
2725         guiApp->clearSession();
2726 }
2727
2728
2729
2730 /////////////////////////////////////////////////////////////////////
2731 //
2732 // PrefEdit
2733 //
2734 /////////////////////////////////////////////////////////////////////
2735
2736 PrefEdit::PrefEdit(GuiPreferences * form)
2737         : PrefModule(catEditing, N_("Control"), form)
2738 {
2739         setupUi(this);
2740
2741         connect(cursorFollowsCB, SIGNAL(clicked()),
2742                 this, SIGNAL(changed()));
2743         connect(scrollBelowCB, SIGNAL(clicked()),
2744                 this, SIGNAL(changed()));
2745         connect(macLikeCursorMovementCB, SIGNAL(clicked()),
2746                 this, SIGNAL(changed()));
2747         connect(copyCTMarkupCB, SIGNAL(clicked()),
2748                 this, SIGNAL(changed()));
2749         connect(sortEnvironmentsCB, SIGNAL(clicked()),
2750                 this, SIGNAL(changed()));
2751         connect(groupEnvironmentsCB, SIGNAL(clicked()),
2752                 this, SIGNAL(changed()));
2753         connect(macroEditStyleCO, SIGNAL(activated(int)),
2754                 this, SIGNAL(changed()));
2755         connect(cursorWidthSB, SIGNAL(valueChanged(int)),
2756                 this, SIGNAL(changed()));
2757         connect(citationSearchLE, SIGNAL(textChanged(QString)),
2758                 this, SIGNAL(changed()));
2759         connect(fullscreenWidthSB, SIGNAL(valueChanged(int)),
2760                 this, SIGNAL(changed()));
2761         connect(toggleTabbarCB, SIGNAL(toggled(bool)),
2762                 this, SIGNAL(changed()));
2763         connect(toggleMenubarCB, SIGNAL(toggled(bool)),
2764                 this, SIGNAL(changed()));
2765         connect(toggleScrollbarCB, SIGNAL(toggled(bool)),
2766                 this, SIGNAL(changed()));
2767         connect(toggleStatusbarCB, SIGNAL(toggled(bool)),
2768                 this, SIGNAL(changed()));
2769         connect(toggleToolbarsCB, SIGNAL(toggled(bool)),
2770                 this, SIGNAL(changed()));
2771 }
2772
2773
2774 void PrefEdit::on_fullscreenLimitCB_toggled(bool const state)
2775 {
2776         fullscreenWidthSB->setEnabled(state);
2777         fullscreenWidthLA->setEnabled(state);
2778         changed();
2779 }
2780
2781
2782 void PrefEdit::on_citationSearchCB_toggled(bool const state)
2783 {
2784         citationSearchLE->setEnabled(state);
2785         citationSearchLA->setEnabled(state);
2786         changed();
2787 }
2788
2789
2790 void PrefEdit::applyRC(LyXRC & rc) const
2791 {
2792         rc.cursor_follows_scrollbar = cursorFollowsCB->isChecked();
2793         rc.scroll_below_document = scrollBelowCB->isChecked();
2794         rc.mac_like_cursor_movement = macLikeCursorMovementCB->isChecked();
2795         rc.ct_markup_copied = copyCTMarkupCB->isChecked();
2796         rc.sort_layouts = sortEnvironmentsCB->isChecked();
2797         rc.group_layouts = groupEnvironmentsCB->isChecked();
2798         switch (macroEditStyleCO->currentIndex()) {
2799                 case 0: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE_BOX; break;
2800                 case 1: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE; break;
2801                 case 2: rc.macro_edit_style = LyXRC::MACRO_EDIT_LIST;   break;
2802         }
2803         rc.cursor_width = cursorWidthSB->value();
2804         rc.citation_search = citationSearchCB->isChecked();
2805         rc.citation_search_pattern = fromqstr(citationSearchLE->text());
2806         rc.full_screen_toolbars = toggleToolbarsCB->isChecked();
2807         rc.full_screen_scrollbar = toggleScrollbarCB->isChecked();
2808         rc.full_screen_statusbar = toggleStatusbarCB->isChecked();
2809         rc.full_screen_tabbar = toggleTabbarCB->isChecked();
2810         rc.full_screen_menubar = toggleMenubarCB->isChecked();
2811         rc.full_screen_width = fullscreenWidthSB->value();
2812         rc.full_screen_limit = fullscreenLimitCB->isChecked();
2813 }
2814
2815
2816 void PrefEdit::updateRC(LyXRC const & rc)
2817 {
2818         cursorFollowsCB->setChecked(rc.cursor_follows_scrollbar);
2819         scrollBelowCB->setChecked(rc.scroll_below_document);
2820         macLikeCursorMovementCB->setChecked(rc.mac_like_cursor_movement);
2821         copyCTMarkupCB->setChecked(rc.ct_markup_copied);
2822         sortEnvironmentsCB->setChecked(rc.sort_layouts);
2823         groupEnvironmentsCB->setChecked(rc.group_layouts);
2824         macroEditStyleCO->setCurrentIndex(rc.macro_edit_style);
2825         cursorWidthSB->setValue(rc.cursor_width);
2826         citationSearchCB->setChecked(rc.citation_search);
2827         citationSearchLE->setText(toqstr(rc.citation_search_pattern));
2828         citationSearchLE->setEnabled(rc.citation_search);
2829         citationSearchLA->setEnabled(rc.citation_search);
2830         toggleScrollbarCB->setChecked(rc.full_screen_scrollbar);
2831         toggleStatusbarCB->setChecked(rc.full_screen_statusbar);
2832         toggleToolbarsCB->setChecked(rc.full_screen_toolbars);
2833         toggleTabbarCB->setChecked(rc.full_screen_tabbar);
2834         toggleMenubarCB->setChecked(rc.full_screen_menubar);
2835         fullscreenWidthSB->setValue(rc.full_screen_width);
2836         fullscreenLimitCB->setChecked(rc.full_screen_limit);
2837         fullscreenWidthSB->setEnabled(rc.full_screen_limit);
2838         fullscreenWidthLA->setEnabled(rc.full_screen_limit);
2839 }
2840
2841
2842 /////////////////////////////////////////////////////////////////////
2843 //
2844 // PrefShortcuts
2845 //
2846 /////////////////////////////////////////////////////////////////////
2847
2848
2849 GuiShortcutDialog::GuiShortcutDialog(QWidget * parent) : QDialog(parent)
2850 {
2851         Ui::shortcutUi::setupUi(this);
2852         QDialog::setModal(true);
2853         lfunLE->setValidator(new NoNewLineValidator(lfunLE));
2854 }
2855
2856
2857 PrefShortcuts::PrefShortcuts(GuiPreferences * form)
2858         : PrefModule(catEditing, N_("Shortcuts"), form),
2859           editItem_(nullptr), mathItem_(nullptr), bufferItem_(nullptr), layoutItem_(nullptr),
2860           systemItem_(nullptr)
2861 {
2862         setupUi(this);
2863
2864         shortcutsTW->setColumnCount(2);
2865         shortcutsTW->headerItem()->setText(0, qt_("Function"));
2866         shortcutsTW->headerItem()->setText(1, qt_("Shortcut"));
2867         shortcutsTW->setSortingEnabled(true);
2868         // Multi-selection can be annoying.
2869         // shortcutsTW->setSelectionMode(QAbstractItemView::MultiSelection);
2870
2871         connect(bindFilePB, SIGNAL(clicked()),
2872                 this, SLOT(selectBind()));
2873         connect(bindFileED, SIGNAL(textChanged(QString)),
2874                 this, SIGNAL(changed()));
2875
2876         shortcut_ = new GuiShortcutDialog(this);
2877         shortcut_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
2878         shortcut_bc_.setOK(shortcut_->buttonBox->button(QDialogButtonBox::Ok));
2879         shortcut_bc_.setCancel(shortcut_->buttonBox->button(QDialogButtonBox::Cancel));
2880
2881         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2882                 this, SIGNAL(changed()));
2883         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2884                 shortcut_, SLOT(reject()));
2885         connect(shortcut_->clearPB, SIGNAL(clicked()),
2886                 this, SLOT(shortcutClearPressed()));
2887         connect(shortcut_->removePB, SIGNAL(clicked()),
2888                 this, SLOT(shortcutRemovePressed()));
2889         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2890                 this, SLOT(shortcutOkPressed()));
2891         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2892                 this, SLOT(shortcutCancelPressed()));
2893 }
2894
2895
2896 void PrefShortcuts::applyRC(LyXRC & rc) const
2897 {
2898         rc.bind_file = internal_path(fromqstr(bindFileED->text()));
2899         // write user_bind and user_unbind to .lyx/bind/user.bind
2900         FileName bind_dir(addPath(package().user_support().absFileName(), "bind"));
2901         if (!bind_dir.exists() && !bind_dir.createDirectory(0777)) {
2902                 lyxerr << "LyX could not create the user bind directory '"
2903                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2904                 return;
2905         }
2906         if (!bind_dir.isDirWritable()) {
2907                 lyxerr << "LyX could not write to the user bind directory '"
2908                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2909                 return;
2910         }
2911         FileName user_bind_file(bind_dir.absFileName() + "/user.bind");
2912         user_unbind_.write(user_bind_file.toFilesystemEncoding(), false, true);
2913         user_bind_.write(user_bind_file.toFilesystemEncoding(), true, false);
2914         // immediately apply the keybindings. Why this is not done before?
2915         // The good thing is that the menus are updated automatically.
2916         theTopLevelKeymap().clear();
2917         theTopLevelKeymap().read("site");
2918         theTopLevelKeymap().read(rc.bind_file, nullptr, KeyMap::Fallback);
2919         theTopLevelKeymap().read("user", nullptr, KeyMap::MissingOK);
2920 }
2921
2922
2923 void PrefShortcuts::updateRC(LyXRC const & rc)
2924 {
2925         bindFileED->setText(toqstr(external_path(rc.bind_file)));
2926         //
2927         system_bind_.clear();
2928         user_bind_.clear();
2929         user_unbind_.clear();
2930         system_bind_.read("site");
2931         system_bind_.read(rc.bind_file);
2932         // \unbind in user.bind is added to user_unbind_
2933         user_bind_.read("user", &user_unbind_, KeyMap::MissingOK);
2934         updateShortcutsTW();
2935 }
2936
2937
2938 void PrefShortcuts::updateShortcutsTW()
2939 {
2940         shortcutsTW->clear();
2941
2942         editItem_ = new QTreeWidgetItem(shortcutsTW);
2943         editItem_->setText(0, qt_("Cursor, Mouse and Editing Functions"));
2944         editItem_->setFlags(editItem_->flags() & ~Qt::ItemIsSelectable);
2945
2946         mathItem_ = new QTreeWidgetItem(shortcutsTW);
2947         mathItem_->setText(0, qt_("Mathematical Symbols"));
2948         mathItem_->setFlags(mathItem_->flags() & ~Qt::ItemIsSelectable);
2949
2950         bufferItem_ = new QTreeWidgetItem(shortcutsTW);
2951         bufferItem_->setText(0, qt_("Document and Window"));
2952         bufferItem_->setFlags(bufferItem_->flags() & ~Qt::ItemIsSelectable);
2953
2954         layoutItem_ = new QTreeWidgetItem(shortcutsTW);
2955         layoutItem_->setText(0, qt_("Font, Layouts and Textclasses"));
2956         layoutItem_->setFlags(layoutItem_->flags() & ~Qt::ItemIsSelectable);
2957
2958         systemItem_ = new QTreeWidgetItem(shortcutsTW);
2959         systemItem_->setText(0, qt_("System and Miscellaneous"));
2960         systemItem_->setFlags(systemItem_->flags() & ~Qt::ItemIsSelectable);
2961
2962         // listBindings(unbound=true) lists all bound and unbound lfuns
2963         // Items in this list is tagged by its source.
2964         KeyMap::BindingList bindinglist = system_bind_.listBindings(true,
2965                 KeyMap::System);
2966         KeyMap::BindingList user_bindinglist = user_bind_.listBindings(false,
2967                 KeyMap::UserBind);
2968         KeyMap::BindingList user_unbindinglist = user_unbind_.listBindings(false,
2969                 KeyMap::UserUnbind);
2970         bindinglist.insert(bindinglist.end(), user_bindinglist.begin(),
2971                         user_bindinglist.end());
2972         bindinglist.insert(bindinglist.end(), user_unbindinglist.begin(),
2973                         user_unbindinglist.end());
2974
2975         KeyMap::BindingList::const_iterator it = bindinglist.begin();
2976         KeyMap::BindingList::const_iterator it_end = bindinglist.end();
2977         for (; it != it_end; ++it)
2978                 insertShortcutItem(it->request, it->sequence, it->tag);
2979
2980         shortcutsTW->sortItems(0, Qt::AscendingOrder);
2981         on_shortcutsTW_itemSelectionChanged();
2982         on_searchLE_textEdited();
2983         shortcutsTW->resizeColumnToContents(0);
2984 }
2985
2986
2987 //static
2988 KeyMap::ItemType PrefShortcuts::itemType(QTreeWidgetItem & item)
2989 {
2990         return static_cast<KeyMap::ItemType>(item.data(0, Qt::UserRole).toInt());
2991 }
2992
2993
2994 //static
2995 bool PrefShortcuts::isAlwaysHidden(QTreeWidgetItem & item)
2996 {
2997         // Hide rebound system settings that are empty
2998         return itemType(item) == KeyMap::UserUnbind && item.text(1).isEmpty();
2999 }
3000
3001
3002 void PrefShortcuts::setItemType(QTreeWidgetItem * item, KeyMap::ItemType tag)
3003 {
3004         item->setData(0, Qt::UserRole, QVariant(tag));
3005         QFont font;
3006
3007         switch (tag) {
3008         case KeyMap::System:
3009                 break;
3010         case KeyMap::UserBind:
3011                 font.setBold(true);
3012                 break;
3013         case KeyMap::UserUnbind:
3014                 font.setStrikeOut(true);
3015                 break;
3016         // this item is not displayed now.
3017         case KeyMap::UserExtraUnbind:
3018                 font.setStrikeOut(true);
3019                 break;
3020         }
3021         item->setHidden(isAlwaysHidden(*item));
3022         item->setFont(1, font);
3023 }
3024
3025
3026 QTreeWidgetItem * PrefShortcuts::insertShortcutItem(FuncRequest const & lfun,
3027                 KeySequence const & seq, KeyMap::ItemType tag)
3028 {
3029         FuncCode const action = lfun.action();
3030         string const action_name = lyxaction.getActionName(action);
3031         QString const lfun_name = toqstr(from_utf8(action_name)
3032                         + ' ' + lfun.argument());
3033         QString const shortcut = toqstr(seq.print(KeySequence::ForGui));
3034
3035         QTreeWidgetItem * newItem = nullptr;
3036         // for unbind items, try to find an existing item in the system bind list
3037         if (tag == KeyMap::UserUnbind) {
3038                 QList<QTreeWidgetItem*> const items = shortcutsTW->findItems(lfun_name,
3039                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
3040                 for (auto const & item : items) {
3041                         if (item->text(1) == shortcut) {
3042                                 newItem = item;
3043                                 break;
3044                         }
3045                 }
3046                 // if not found, this unbind item is KeyMap::UserExtraUnbind
3047                 // Such an item is not displayed to avoid confusion (what is
3048                 // unmatched removed?).
3049                 if (!newItem) {
3050                         return nullptr;
3051                 }
3052         }
3053         if (!newItem) {
3054                 switch(lyxaction.getActionType(action)) {
3055                 case LyXAction::Hidden:
3056                         return nullptr;
3057                 case LyXAction::Edit:
3058                         newItem = new QTreeWidgetItem(editItem_);
3059                         break;
3060                 case LyXAction::Math:
3061                         newItem = new QTreeWidgetItem(mathItem_);
3062                         break;
3063                 case LyXAction::Buffer:
3064                         newItem = new QTreeWidgetItem(bufferItem_);
3065                         break;
3066                 case LyXAction::Layout:
3067                         newItem = new QTreeWidgetItem(layoutItem_);
3068                         break;
3069                 case LyXAction::System:
3070                         newItem = new QTreeWidgetItem(systemItem_);
3071                         break;
3072                 default:
3073                         // this should not happen
3074                         newItem = new QTreeWidgetItem(shortcutsTW);
3075                 }
3076         }
3077
3078         newItem->setText(0, lfun_name);
3079         newItem->setText(1, shortcut);
3080         // record BindFile representation to recover KeySequence when needed.
3081         newItem->setData(1, Qt::UserRole, toqstr(seq.print(KeySequence::BindFile)));
3082         setItemType(newItem, tag);
3083         return newItem;
3084 }
3085
3086
3087 void PrefShortcuts::on_shortcutsTW_itemSelectionChanged()
3088 {
3089         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3090         removePB->setEnabled(!items.isEmpty() && !items[0]->text(1).isEmpty());
3091         modifyPB->setEnabled(!items.isEmpty());
3092         if (items.isEmpty())
3093                 return;
3094
3095         if (itemType(*items[0]) == KeyMap::UserUnbind)
3096                 removePB->setText(qt_("Res&tore"));
3097         else
3098                 removePB->setText(qt_("Remo&ve"));
3099 }
3100
3101
3102 void PrefShortcuts::on_shortcutsTW_itemDoubleClicked()
3103 {
3104         modifyShortcut();
3105 }
3106
3107
3108 void PrefShortcuts::modifyShortcut()
3109 {
3110         QTreeWidgetItem * item = shortcutsTW->currentItem();
3111         if (item->flags() & Qt::ItemIsSelectable) {
3112                 shortcut_->lfunLE->setText(item->text(0));
3113                 save_lfun_ = item->text(0).trimmed();
3114                 shortcut_->shortcutWG->setText(item->text(1));
3115                 KeySequence seq;
3116                 seq.parse(fromqstr(item->data(1, Qt::UserRole).toString()));
3117                 shortcut_->shortcutWG->setKeySequence(seq);
3118                 shortcut_->shortcutWG->setFocus();
3119                 shortcut_->exec();
3120         }
3121 }
3122
3123
3124 void PrefShortcuts::unhideEmpty(QString const & lfun, bool select)
3125 {
3126         // list of items that match lfun
3127         QList<QTreeWidgetItem*> items = shortcutsTW->findItems(lfun,
3128              Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
3129         for (auto const & item : items) {
3130                 if (isAlwaysHidden(*item)) {
3131                         setItemType(item, KeyMap::System);
3132                         if (select)
3133                                 shortcutsTW->setCurrentItem(item);
3134                         return;
3135                 }
3136         }
3137 }
3138
3139
3140 void PrefShortcuts::removeShortcut()
3141 {
3142         // it seems that only one item can be selected, but I am
3143         // removing all selected items anyway.
3144         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3145         for (auto & item : items) {
3146                 string shortcut = fromqstr(item->data(1, Qt::UserRole).toString());
3147                 string lfun = fromqstr(item->text(0));
3148                 FuncRequest const func = lyxaction.lookupFunc(lfun);
3149
3150                 switch (itemType(*item)) {
3151                 case KeyMap::System: {
3152                         // for system bind, we do not touch the item
3153                         // but add an user unbind item
3154                         user_unbind_.bind(shortcut, func);
3155                         setItemType(item, KeyMap::UserUnbind);
3156                         removePB->setText(qt_("Res&tore"));
3157                         break;
3158                 }
3159                 case KeyMap::UserBind: {
3160                         // for user_bind, we remove this bind
3161                         QTreeWidgetItem * parent = item->parent();
3162                         int itemIdx = parent->indexOfChild(item);
3163                         parent->takeChild(itemIdx);
3164                         if (itemIdx > 0)
3165                                 shortcutsTW->scrollToItem(parent->child(itemIdx - 1));
3166                         else
3167                                 shortcutsTW->scrollToItem(parent);
3168                         user_bind_.unbind(shortcut, func);
3169                         // If this user binding hid an empty system binding, unhide the
3170                         // latter and select it.
3171                         unhideEmpty(item->text(0), true);
3172                         break;
3173                 }
3174                 case KeyMap::UserUnbind: {
3175                         // for user_unbind, we remove the unbind, and the item
3176                         // become KeyMap::System again.
3177                         KeySequence seq;
3178                         seq.parse(shortcut);
3179                         // Ask the user to replace current binding
3180                         if (!validateNewShortcut(func, seq, QString()))
3181                                 break;
3182                         user_unbind_.unbind(shortcut, func);
3183                         setItemType(item, KeyMap::System);
3184                         removePB->setText(qt_("Remo&ve"));
3185                         break;
3186                 }
3187                 case KeyMap::UserExtraUnbind: {
3188                         // for user unbind that is not in system bind file,
3189                         // remove this unbind file
3190                         QTreeWidgetItem * parent = item->parent();
3191                         parent->takeChild(parent->indexOfChild(item));
3192                         user_unbind_.unbind(shortcut, func);
3193                 }
3194                 }
3195         }
3196 }
3197
3198
3199 void PrefShortcuts::deactivateShortcuts(QList<QTreeWidgetItem*> const & items)
3200 {
3201         for (auto item : items) {
3202                 string shortcut = fromqstr(item->data(1, Qt::UserRole).toString());
3203                 string lfun = fromqstr(item->text(0));
3204                 FuncRequest const func = lyxaction.lookupFunc(lfun);
3205
3206                 switch (itemType(*item)) {
3207                 case KeyMap::System:
3208                         // for system bind, we do not touch the item
3209                         // but add an user unbind item
3210                         user_unbind_.bind(shortcut, func);
3211                         setItemType(item, KeyMap::UserUnbind);
3212                         break;
3213
3214                 case KeyMap::UserBind: {
3215                         // for user_bind, we remove this bind
3216                         QTreeWidgetItem * parent = item->parent();
3217                         int itemIdx = parent->indexOfChild(item);
3218                         parent->takeChild(itemIdx);
3219                         user_bind_.unbind(shortcut, func);
3220                         unhideEmpty(item->text(0), false);
3221                         break;
3222                 }
3223                 default:
3224                         break;
3225                 }
3226         }
3227 }
3228
3229
3230 void PrefShortcuts::selectBind()
3231 {
3232         QString file = form_->browsebind(internalPath(bindFileED->text()));
3233         if (!file.isEmpty()) {
3234                 bindFileED->setText(file);
3235                 system_bind_ = KeyMap();
3236                 system_bind_.read(fromqstr(file));
3237                 updateShortcutsTW();
3238         }
3239 }
3240
3241
3242 void PrefShortcuts::on_modifyPB_pressed()
3243 {
3244         modifyShortcut();
3245 }
3246
3247
3248 void PrefShortcuts::on_newPB_pressed()
3249 {
3250         shortcut_->lfunLE->clear();
3251         shortcut_->shortcutWG->reset();
3252         save_lfun_ = QString();
3253         shortcut_->exec();
3254 }
3255
3256
3257 void PrefShortcuts::on_removePB_pressed()
3258 {
3259         changed();
3260         removeShortcut();
3261 }
3262
3263
3264 void PrefShortcuts::on_searchLE_textEdited()
3265 {
3266         if (searchLE->text().isEmpty()) {
3267                 // show all hidden items
3268                 QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Hidden);
3269                 for (; *it; ++it)
3270                         (*it)->setHidden(isAlwaysHidden(**it));
3271                 // close all categories
3272                 for (int i = 0; i < shortcutsTW->topLevelItemCount(); ++i)
3273                         shortcutsTW->collapseItem(shortcutsTW->topLevelItem(i));
3274                 return;
3275         }
3276         // search both columns
3277         QList<QTreeWidgetItem *> matched = shortcutsTW->findItems(searchLE->text(),
3278                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 0);
3279         matched += shortcutsTW->findItems(searchLE->text(),
3280                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 1);
3281
3282         // hide everyone (to avoid searching in matched QList repeatedly
3283         QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Selectable);
3284         while (*it)
3285                 (*it++)->setHidden(true);
3286         // show matched items
3287         for (auto & item : matched)
3288                 if (!isAlwaysHidden(*item)) {
3289                         item->setHidden(false);
3290                         if (item->parent())
3291                                 item->parent()->setExpanded(true);
3292                 }
3293 }
3294
3295
3296 docstring makeCmdString(FuncRequest const & f)
3297 {
3298         docstring actionStr = from_ascii(lyxaction.getActionName(f.action()));
3299         if (!f.argument().empty())
3300                 actionStr += " " + f.argument();
3301         return actionStr;
3302 }
3303
3304
3305 FuncRequest PrefShortcuts::currentBinding(KeySequence const & k)
3306 {
3307         FuncRequest res = user_bind_.getBinding(k);
3308         if (res.action() != LFUN_UNKNOWN_ACTION)
3309                 return res;
3310         res = system_bind_.getBinding(k);
3311         // Check if it is unbound. Note: user_unbind_ can only unbind one
3312         // FuncRequest per key sequence.
3313         if (user_unbind_.getBinding(k) == res)
3314                 return FuncRequest::unknown;
3315         return res;
3316 }
3317
3318
3319 bool PrefShortcuts::validateNewShortcut(FuncRequest const & func,
3320                                         KeySequence const & k,
3321                                         QString const & lfun_to_modify)
3322 {
3323         if (func.action() == LFUN_UNKNOWN_ACTION) {
3324                 Alert::error(_("Failed to create shortcut"),
3325                         _("Unknown or invalid LyX function"));
3326                 return false;
3327         }
3328
3329         // It is not currently possible to bind Hidden lfuns such as self-insert. In
3330         // the future, to remove this limitation, see GuiPrefs::insertShortcutItem
3331         // and how it is used in GuiPrefs::shortcutOkPressed.
3332         if (lyxaction.getActionType(func.action()) == LyXAction::Hidden) {
3333                 Alert::error(_("Failed to create shortcut"),
3334                         _("This LyX function is hidden and cannot be bound."));
3335                 return false;
3336         }
3337
3338         if (k.length() == 0) {
3339                 Alert::error(_("Failed to create shortcut"),
3340                         _("Invalid or empty key sequence"));
3341                 return false;
3342         }
3343
3344         FuncRequest oldBinding = currentBinding(k);
3345         if (oldBinding == func)
3346                 // nothing to change
3347                 return false;
3348
3349         // make sure this key isn't already bound---and, if so, prompt user
3350         // (exclude the lfun the user already wants to modify)
3351         docstring const action_string = makeCmdString(oldBinding);
3352         if (oldBinding.action() != LFUN_UNKNOWN_ACTION
3353             && lfun_to_modify != toqstr(action_string)) {
3354                 docstring const new_action_string = makeCmdString(func);
3355                 docstring const text = bformat(_("Shortcut `%1$s' is already bound to "
3356                                                  "%2$s.\n"
3357                                                  "Are you sure you want to unbind the "
3358                                                  "current shortcut and bind it to %3$s?"),
3359                                                k.print(KeySequence::ForGui), action_string,
3360                                                new_action_string);
3361                 int ret = Alert::prompt(_("Redefine shortcut?"),
3362                                         text, 0, 1, _("&Redefine"), _("&Cancel"));
3363                 if (ret != 0)
3364                         return false;
3365                 QString const sequence_text = toqstr(k.print(KeySequence::ForGui));
3366                 QList<QTreeWidgetItem*> items = shortcutsTW->findItems(sequence_text,
3367                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 1);
3368                 deactivateShortcuts(items);
3369         }
3370         return true;
3371 }
3372
3373
3374 void PrefShortcuts::shortcutOkPressed()
3375 {
3376         QString const new_lfun = shortcut_->lfunLE->text();
3377         FuncRequest const func = lyxaction.lookupFunc(fromqstr(new_lfun));
3378         KeySequence k = shortcut_->shortcutWG->getKeySequence();
3379
3380         // save_lfun_ contains the text of the lfun to modify, if the user clicked
3381         // "modify", or is empty if they clicked "new" (which I do not really like)
3382         if (!validateNewShortcut(func, k, save_lfun_))
3383                 return;
3384
3385         if (!save_lfun_.isEmpty()) {
3386                 // real modification of the lfun's shortcut,
3387                 // so remove the previous one
3388                 QList<QTreeWidgetItem*> to_modify = shortcutsTW->selectedItems();
3389                 deactivateShortcuts(to_modify);
3390         }
3391
3392         shortcut_->accept();
3393
3394         QTreeWidgetItem * item = insertShortcutItem(func, k, KeyMap::UserBind);
3395         if (item) {
3396                 user_bind_.bind(&k, func);
3397                 shortcutsTW->sortItems(0, Qt::AscendingOrder);
3398                 item->parent()->setExpanded(true);
3399                 shortcutsTW->setCurrentItem(item);
3400                 shortcutsTW->scrollToItem(item);
3401         } else {
3402                 Alert::error(_("Failed to create shortcut"),
3403                         _("Can not insert shortcut to the list"));
3404                 return;
3405         }
3406 }
3407
3408
3409 void PrefShortcuts::shortcutCancelPressed()
3410 {
3411         shortcut_->shortcutWG->reset();
3412 }
3413
3414
3415 void PrefShortcuts::shortcutClearPressed()
3416 {
3417         shortcut_->shortcutWG->reset();
3418 }
3419
3420
3421 void PrefShortcuts::shortcutRemovePressed()
3422 {
3423         shortcut_->shortcutWG->removeFromSequence();
3424 }
3425
3426
3427 /////////////////////////////////////////////////////////////////////
3428 //
3429 // PrefIdentity
3430 //
3431 /////////////////////////////////////////////////////////////////////
3432
3433 PrefIdentity::PrefIdentity(GuiPreferences * form)
3434         : PrefModule(QString(), N_("Identity"), form)
3435 {
3436         setupUi(this);
3437
3438         connect(nameED, SIGNAL(textChanged(QString)),
3439                 this, SIGNAL(changed()));
3440         connect(emailED, SIGNAL(textChanged(QString)),
3441                 this, SIGNAL(changed()));
3442         connect(initialsED, SIGNAL(textChanged(QString)),
3443                 this, SIGNAL(changed()));
3444
3445         nameED->setValidator(new NoNewLineValidator(nameED));
3446         emailED->setValidator(new NoNewLineValidator(emailED));
3447         initialsED->setValidator(new NoNewLineValidator(initialsED));
3448 }
3449
3450
3451 void PrefIdentity::applyRC(LyXRC & rc) const
3452 {
3453         rc.user_name = fromqstr(nameED->text());
3454         rc.user_email = fromqstr(emailED->text());
3455         rc.user_initials = fromqstr(initialsED->text());
3456 }
3457
3458
3459 void PrefIdentity::updateRC(LyXRC const & rc)
3460 {
3461         nameED->setText(toqstr(rc.user_name));
3462         emailED->setText(toqstr(rc.user_email));
3463         initialsED->setText(toqstr(rc.user_initials));
3464 }
3465
3466
3467
3468 /////////////////////////////////////////////////////////////////////
3469 //
3470 // GuiPreferences
3471 //
3472 /////////////////////////////////////////////////////////////////////
3473
3474 GuiPreferences::GuiPreferences(GuiView & lv)
3475         : GuiDialog(lv, "prefs", qt_("Preferences"))
3476 {
3477         setupUi(this);
3478
3479         QDialog::setModal(false);
3480
3481         connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
3482                 this, SLOT(slotButtonBox(QAbstractButton *)));
3483
3484         addModule(new PrefUserInterface(this));
3485         addModule(new PrefDocHandling(this));
3486         addModule(new PrefEdit(this));
3487         addModule(new PrefShortcuts(this));
3488         PrefScreenFonts * screenfonts = new PrefScreenFonts(this);
3489         connect(this, SIGNAL(prefsApplied(LyXRC const &)),
3490                         screenfonts, SLOT(updateScreenFontSizes(LyXRC const &)));
3491         addModule(screenfonts);
3492         addModule(new PrefColors(this));
3493         addModule(new PrefDisplay(this));
3494         addModule(new PrefInput(this));
3495         addModule(new PrefCompletion(this));
3496
3497         addModule(new PrefPaths(this));
3498
3499         addModule(new PrefIdentity(this));
3500
3501         addModule(new PrefLanguage(this));
3502         addModule(new PrefSpellchecker(this));
3503
3504         PrefOutput * output = new PrefOutput(this);
3505         addModule(output);
3506         addModule(new PrefLatex(this));
3507
3508         PrefConverters * converters = new PrefConverters(this);
3509         PrefFileformats * formats = new PrefFileformats(this);
3510         connect(formats, SIGNAL(formatsChanged()),
3511                         converters, SLOT(updateGui()));
3512         addModule(converters);
3513         addModule(formats);
3514
3515         prefsPS->setCurrentPanel("User Interface");
3516 // FIXME: hack to work around resizing bug in Qt >= 4.2
3517 // bug verified with Qt 4.2.{0-3} (JSpitzm)
3518 #if QT_VERSION >= 0x040200
3519         prefsPS->updateGeometry();
3520 #endif
3521
3522         bc().setPolicy(ButtonPolicy::PreferencesPolicy);
3523         bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
3524         bc().setApply(buttonBox->button(QDialogButtonBox::Apply));
3525         bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
3526         bc().setRestore(buttonBox->button(QDialogButtonBox::Reset));
3527
3528         guilyxfiles_ = new GuiLyXFiles(lv);
3529         connect(guilyxfiles_, SIGNAL(fileSelected(QString)),
3530                         this, SLOT(slotFileSelected(QString)));
3531 }
3532
3533
3534 void GuiPreferences::addModule(PrefModule * module)
3535 {
3536         LASSERT(module, return);
3537         if (module->category().isEmpty())
3538                 prefsPS->addPanel(module, module->title());
3539         else
3540                 prefsPS->addPanel(module, module->title(), module->category());
3541         connect(module, SIGNAL(changed()), this, SLOT(change_adaptor()));
3542         modules_.push_back(module);
3543 }
3544
3545
3546 void GuiPreferences::change_adaptor()
3547 {
3548         changed();
3549 }
3550
3551
3552 void GuiPreferences::applyRC(LyXRC & rc) const
3553 {
3554         size_t end = modules_.size();
3555         for (size_t i = 0; i != end; ++i)
3556                 modules_[i]->applyRC(rc);
3557 }
3558
3559
3560 void GuiPreferences::updateRC(LyXRC const & rc)
3561 {
3562         size_t const end = modules_.size();
3563         for (size_t i = 0; i != end; ++i)
3564                 modules_[i]->updateRC(rc);
3565 }
3566
3567
3568 void GuiPreferences::applyView()
3569 {
3570         applyRC(rc());
3571 }
3572
3573
3574 bool GuiPreferences::initialiseParams(string const &)
3575 {
3576         rc_ = lyxrc;
3577         formats_ = theFormats();
3578         converters_ = theConverters();
3579         converters_.update(formats_);
3580         movers_ = theMovers();
3581         colors_.clear();
3582
3583         updateRC(rc_);
3584         // Make sure that the bc is in the INITIAL state
3585         if (bc().policy().buttonStatus(ButtonPolicy::RESTORE))
3586                 bc().restore();
3587
3588         return true;
3589 }
3590
3591
3592 void GuiPreferences::dispatchParams()
3593 {
3594         ostringstream ss;
3595         rc_.write(ss, true);
3596         dispatch(FuncRequest(LFUN_LYXRC_APPLY, ss.str()));
3597         // issue prefsApplied signal. This will update the
3598         // localized screen font sizes.
3599         prefsApplied(rc_);
3600         // FIXME: these need lfuns
3601         // FIXME UNICODE
3602         Author const & author =
3603                 Author(from_utf8(rc_.user_name), from_utf8(rc_.user_email),
3604                        from_utf8(rc_.user_initials));
3605         theBufferList().recordCurrentAuthor(author);
3606
3607         theFormats() = formats_;
3608
3609         theConverters() = converters_;
3610         theConverters().update(formats_);
3611         theConverters().buildGraph();
3612         theBufferList().invalidateConverterCache();
3613
3614         theMovers() = movers_;
3615
3616         for (string const & color : colors_)
3617                 dispatch(FuncRequest(LFUN_SET_COLOR, color));
3618         colors_.clear();
3619
3620         // Save permanently
3621         if (!tempSaveCB->isChecked())
3622                 dispatch(FuncRequest(LFUN_PREFERENCES_SAVE));
3623 }
3624
3625
3626 void GuiPreferences::setColor(ColorCode col, QString const & hex)
3627 {
3628         colors_.push_back(lcolor.getLyXName(col) + ' ' + fromqstr(hex));
3629 }
3630
3631
3632 void GuiPreferences::slotFileSelected(QString const file)
3633 {
3634         uifile_ = file;
3635 }
3636
3637
3638 QString GuiPreferences::browseLibFile(QString const & dir,
3639         QString const & name, QString const & ext)
3640 {
3641         uifile_.clear();
3642
3643         guilyxfiles_->passParams(fromqstr(dir));
3644         guilyxfiles_->selectItem(name);
3645         guilyxfiles_->exec();
3646
3647         QString const result = uifile_;
3648
3649         // remove the extension if it is the default one
3650         QString noextresult;
3651         if (getExtension(result) == ext)
3652                 noextresult = removeExtension(result);
3653         else
3654                 noextresult = result;
3655
3656         // remove the directory, if it is the default one
3657         QString const file = onlyFileName(noextresult);
3658         if (toqstr(libFileSearch(dir, file, ext).absFileName()) == result)
3659                 return file;
3660         else
3661                 return noextresult;
3662 }
3663
3664
3665 QString GuiPreferences::browsebind(QString const & file)
3666 {
3667         return browseLibFile("bind", file, "bind");
3668 }
3669
3670
3671 QString GuiPreferences::browseUI(QString const & file)
3672 {
3673         return browseLibFile("ui", file, "ui");
3674 }
3675
3676
3677 QString GuiPreferences::browsekbmap(QString const & file)
3678 {
3679         return browseLibFile("kbd", file, "kmap");
3680 }
3681
3682
3683 QString GuiPreferences::browse(QString const & file,
3684         QString const & title) const
3685 {
3686         return browseFile(file, title, QStringList(), true);
3687 }
3688
3689
3690 } // namespace frontend
3691 } // namespace lyx
3692
3693 #include "moc_GuiPrefs.cpp"