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