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