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