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