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