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