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