]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/GuiPrefs.cpp
adjust text display width in windowed mode
[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         screenWidthLA->setEnabled(state);
2837         screenWidthUnitCO->setEnabled(state);
2838         changed();
2839 }
2840
2841
2842 void PrefEdit::on_citationSearchCB_toggled(bool const state)
2843 {
2844         citationSearchLE->setEnabled(state);
2845         citationSearchLA->setEnabled(state);
2846         changed();
2847 }
2848
2849
2850 void PrefEdit::applyRC(LyXRC & rc) const
2851 {
2852         rc.cursor_follows_scrollbar = cursorFollowsCB->isChecked();
2853         rc.scroll_below_document = scrollBelowCB->isChecked();
2854         rc.mac_like_cursor_movement = macLikeCursorMovementCB->isChecked();
2855         rc.ct_markup_copied = copyCTMarkupCB->isChecked();
2856         rc.sort_layouts = sortEnvironmentsCB->isChecked();
2857         rc.group_layouts = groupEnvironmentsCB->isChecked();
2858         switch (macroEditStyleCO->currentIndex()) {
2859                 case 0: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE_BOX; break;
2860                 case 1: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE; break;
2861                 case 2: rc.macro_edit_style = LyXRC::MACRO_EDIT_LIST;   break;
2862         }
2863         rc.cursor_width = cursorWidthSB->value();
2864         rc.citation_search = citationSearchCB->isChecked();
2865         rc.citation_search_pattern = fromqstr(citationSearchLE->text());
2866         rc.full_screen_toolbars = toggleToolbarsCB->isChecked();
2867         rc.full_screen_scrollbar = toggleScrollbarCB->isChecked();
2868         rc.full_screen_statusbar = toggleStatusbarCB->isChecked();
2869         rc.full_screen_tabbar = toggleTabbarCB->isChecked();
2870         rc.full_screen_menubar = toggleMenubarCB->isChecked();
2871     rc.screen_width = Length(widgetsToLength(screenWidthLE, screenWidthUnitCO)); 
2872     rc.screen_limit = screenLimitCB->isChecked(); 
2873 }
2874
2875
2876 void PrefEdit::updateRC(LyXRC const & rc)
2877 {
2878         cursorFollowsCB->setChecked(rc.cursor_follows_scrollbar);
2879         scrollBelowCB->setChecked(rc.scroll_below_document);
2880         macLikeCursorMovementCB->setChecked(rc.mac_like_cursor_movement);
2881         copyCTMarkupCB->setChecked(rc.ct_markup_copied);
2882         sortEnvironmentsCB->setChecked(rc.sort_layouts);
2883         groupEnvironmentsCB->setChecked(rc.group_layouts);
2884         macroEditStyleCO->setCurrentIndex(rc.macro_edit_style);
2885         cursorWidthSB->setValue(rc.cursor_width);
2886         citationSearchCB->setChecked(rc.citation_search);
2887         citationSearchLE->setText(toqstr(rc.citation_search_pattern));
2888         citationSearchLE->setEnabled(rc.citation_search);
2889         citationSearchLA->setEnabled(rc.citation_search);
2890         toggleScrollbarCB->setChecked(rc.full_screen_scrollbar);
2891         toggleStatusbarCB->setChecked(rc.full_screen_statusbar);
2892         toggleToolbarsCB->setChecked(rc.full_screen_toolbars);
2893         toggleTabbarCB->setChecked(rc.full_screen_tabbar);
2894         toggleMenubarCB->setChecked(rc.full_screen_menubar);
2895         lengthToWidgets(screenWidthLE, screenWidthUnitCO, rc.screen_width, Length::defaultUnit());
2896         screenWidthUnitCO->setEnabled(rc.screen_limit);
2897         screenLimitCB->setChecked(rc.screen_limit);
2898         screenWidthLE->setEnabled(rc.screen_limit);
2899         screenWidthLA->setEnabled(rc.screen_limit); 
2900 }
2901
2902
2903 /////////////////////////////////////////////////////////////////////
2904 //
2905 // PrefShortcuts
2906 //
2907 /////////////////////////////////////////////////////////////////////
2908
2909
2910 GuiShortcutDialog::GuiShortcutDialog(QWidget * parent) : QDialog(parent)
2911 {
2912         Ui::shortcutUi::setupUi(this);
2913         QDialog::setModal(true);
2914         lfunLE->setValidator(new NoNewLineValidator(lfunLE));
2915 }
2916
2917
2918 PrefShortcuts::PrefShortcuts(GuiPreferences * form)
2919         : PrefModule(catEditing, N_("Shortcuts"), form),
2920           editItem_(nullptr), mathItem_(nullptr), bufferItem_(nullptr), layoutItem_(nullptr),
2921           systemItem_(nullptr)
2922 {
2923         setupUi(this);
2924
2925         shortcutsTW->setColumnCount(2);
2926         shortcutsTW->headerItem()->setText(0, qt_("Function"));
2927         shortcutsTW->headerItem()->setText(1, qt_("Shortcut"));
2928         shortcutsTW->setSortingEnabled(true);
2929         // Multi-selection can be annoying.
2930         // shortcutsTW->setSelectionMode(QAbstractItemView::MultiSelection);
2931
2932         connect(bindFilePB, SIGNAL(clicked()),
2933                 this, SLOT(selectBind()));
2934         connect(bindFileED, SIGNAL(textChanged(QString)),
2935                 this, SIGNAL(changed()));
2936
2937         shortcut_ = new GuiShortcutDialog(this);
2938         shortcut_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
2939         shortcut_bc_.setOK(shortcut_->buttonBox->button(QDialogButtonBox::Ok));
2940         shortcut_bc_.setCancel(shortcut_->buttonBox->button(QDialogButtonBox::Cancel));
2941
2942         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2943                 this, SIGNAL(changed()));
2944         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2945                 shortcut_, SLOT(reject()));
2946         connect(shortcut_->clearPB, SIGNAL(clicked()),
2947                 this, SLOT(shortcutClearPressed()));
2948         connect(shortcut_->removePB, SIGNAL(clicked()),
2949                 this, SLOT(shortcutRemovePressed()));
2950         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2951                 this, SLOT(shortcutOkPressed()));
2952         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2953                 this, SLOT(shortcutCancelPressed()));
2954 }
2955
2956
2957 void PrefShortcuts::applyRC(LyXRC & rc) const
2958 {
2959         rc.bind_file = internal_path(fromqstr(bindFileED->text()));
2960         // write user_bind and user_unbind to .lyx/bind/user.bind
2961         FileName bind_dir(addPath(package().user_support().absFileName(), "bind"));
2962         if (!bind_dir.exists() && !bind_dir.createDirectory(0777)) {
2963                 lyxerr << "LyX could not create the user bind directory '"
2964                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2965                 return;
2966         }
2967         if (!bind_dir.isDirWritable()) {
2968                 lyxerr << "LyX could not write to the user bind directory '"
2969                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2970                 return;
2971         }
2972         FileName user_bind_file(bind_dir.absFileName() + "/user.bind");
2973         user_unbind_.write(user_bind_file.toFilesystemEncoding(), false, true);
2974         user_bind_.write(user_bind_file.toFilesystemEncoding(), true, false);
2975         // immediately apply the keybindings. Why this is not done before?
2976         // The good thing is that the menus are updated automatically.
2977         theTopLevelKeymap().clear();
2978         theTopLevelKeymap().read("site");
2979         theTopLevelKeymap().read(rc.bind_file, nullptr, KeyMap::Fallback);
2980         theTopLevelKeymap().read("user", nullptr, KeyMap::MissingOK);
2981 }
2982
2983
2984 void PrefShortcuts::updateRC(LyXRC const & rc)
2985 {
2986         bindFileED->setText(toqstr(external_path(rc.bind_file)));
2987         //
2988         system_bind_.clear();
2989         user_bind_.clear();
2990         user_unbind_.clear();
2991         system_bind_.read("site");
2992         system_bind_.read(rc.bind_file);
2993         // \unbind in user.bind is added to user_unbind_
2994         user_bind_.read("user", &user_unbind_, KeyMap::MissingOK);
2995         updateShortcutsTW();
2996 }
2997
2998
2999 void PrefShortcuts::updateShortcutsTW()
3000 {
3001         shortcutsTW->clear();
3002
3003         editItem_ = new QTreeWidgetItem(shortcutsTW);
3004         editItem_->setText(0, qt_("Cursor, Mouse and Editing Functions"));
3005         editItem_->setFlags(editItem_->flags() & ~Qt::ItemIsSelectable);
3006
3007         mathItem_ = new QTreeWidgetItem(shortcutsTW);
3008         mathItem_->setText(0, qt_("Mathematical Symbols"));
3009         mathItem_->setFlags(mathItem_->flags() & ~Qt::ItemIsSelectable);
3010
3011         bufferItem_ = new QTreeWidgetItem(shortcutsTW);
3012         bufferItem_->setText(0, qt_("Document and Window"));
3013         bufferItem_->setFlags(bufferItem_->flags() & ~Qt::ItemIsSelectable);
3014
3015         layoutItem_ = new QTreeWidgetItem(shortcutsTW);
3016         layoutItem_->setText(0, qt_("Font, Layouts and Textclasses"));
3017         layoutItem_->setFlags(layoutItem_->flags() & ~Qt::ItemIsSelectable);
3018
3019         systemItem_ = new QTreeWidgetItem(shortcutsTW);
3020         systemItem_->setText(0, qt_("System and Miscellaneous"));
3021         systemItem_->setFlags(systemItem_->flags() & ~Qt::ItemIsSelectable);
3022
3023         // listBindings(unbound=true) lists all bound and unbound lfuns
3024         // Items in this list is tagged by its source.
3025         KeyMap::BindingList bindinglist = system_bind_.listBindings(true,
3026                 KeyMap::System);
3027         KeyMap::BindingList user_bindinglist = user_bind_.listBindings(false,
3028                 KeyMap::UserBind);
3029         KeyMap::BindingList user_unbindinglist = user_unbind_.listBindings(false,
3030                 KeyMap::UserUnbind);
3031         bindinglist.insert(bindinglist.end(), user_bindinglist.begin(),
3032                         user_bindinglist.end());
3033         bindinglist.insert(bindinglist.end(), user_unbindinglist.begin(),
3034                         user_unbindinglist.end());
3035
3036         KeyMap::BindingList::const_iterator it = bindinglist.begin();
3037         KeyMap::BindingList::const_iterator it_end = bindinglist.end();
3038         for (; it != it_end; ++it)
3039                 insertShortcutItem(it->request, it->sequence, it->tag);
3040
3041         shortcutsTW->sortItems(0, Qt::AscendingOrder);
3042         on_shortcutsTW_itemSelectionChanged();
3043         on_searchLE_textEdited();
3044         shortcutsTW->resizeColumnToContents(0);
3045 }
3046
3047
3048 //static
3049 KeyMap::ItemType PrefShortcuts::itemType(QTreeWidgetItem & item)
3050 {
3051         return static_cast<KeyMap::ItemType>(item.data(0, Qt::UserRole).toInt());
3052 }
3053
3054
3055 //static
3056 bool PrefShortcuts::isAlwaysHidden(QTreeWidgetItem & item)
3057 {
3058         // Hide rebound system settings that are empty
3059         return itemType(item) == KeyMap::UserUnbind && item.text(1).isEmpty();
3060 }
3061
3062
3063 void PrefShortcuts::setItemType(QTreeWidgetItem * item, KeyMap::ItemType tag)
3064 {
3065         item->setData(0, Qt::UserRole, QVariant(tag));
3066         QFont font;
3067
3068         switch (tag) {
3069         case KeyMap::System:
3070                 break;
3071         case KeyMap::UserBind:
3072                 font.setBold(true);
3073                 break;
3074         case KeyMap::UserUnbind:
3075                 font.setStrikeOut(true);
3076                 break;
3077         // this item is not displayed now.
3078         case KeyMap::UserExtraUnbind:
3079                 font.setStrikeOut(true);
3080                 break;
3081         }
3082         item->setHidden(isAlwaysHidden(*item));
3083         item->setFont(1, font);
3084 }
3085
3086
3087 QTreeWidgetItem * PrefShortcuts::insertShortcutItem(FuncRequest const & lfun,
3088                 KeySequence const & seq, KeyMap::ItemType tag)
3089 {
3090         FuncCode const action = lfun.action();
3091         string const action_name = lyxaction.getActionName(action);
3092         QString const lfun_name = toqstr(from_utf8(action_name)
3093                         + ' ' + lfun.argument());
3094         QString const shortcut = toqstr(seq.print(KeySequence::ForGui));
3095
3096         QTreeWidgetItem * newItem = nullptr;
3097         // for unbind items, try to find an existing item in the system bind list
3098         if (tag == KeyMap::UserUnbind) {
3099                 QList<QTreeWidgetItem*> const items = shortcutsTW->findItems(shortcut,
3100                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 1);
3101                 for (auto const & item : items) {
3102                         if (item->text(0) == lfun_name || lfun == FuncRequest::unknown) {
3103                                 newItem = item;
3104                                 break;
3105                         }
3106                 }
3107                 // if not found, this unbind item is KeyMap::UserExtraUnbind
3108                 // Such an item is not displayed to avoid confusion (what is
3109                 // unmatched removed?).
3110                 if (!newItem) {
3111                         return nullptr;
3112                 }
3113         }
3114         if (!newItem) {
3115                 switch(lyxaction.getActionType(action)) {
3116                 case LyXAction::Hidden:
3117                         return nullptr;
3118                 case LyXAction::Edit:
3119                         newItem = new QTreeWidgetItem(editItem_);
3120                         break;
3121                 case LyXAction::Math:
3122                         newItem = new QTreeWidgetItem(mathItem_);
3123                         break;
3124                 case LyXAction::Buffer:
3125                         newItem = new QTreeWidgetItem(bufferItem_);
3126                         break;
3127                 case LyXAction::Layout:
3128                         newItem = new QTreeWidgetItem(layoutItem_);
3129                         break;
3130                 case LyXAction::System:
3131                         newItem = new QTreeWidgetItem(systemItem_);
3132                         break;
3133                 default:
3134                         // this should not happen
3135                         newItem = new QTreeWidgetItem(shortcutsTW);
3136                 }
3137                 newItem->setText(0, lfun_name);
3138                 newItem->setText(1, shortcut);
3139         }
3140
3141         // record BindFile representation to recover KeySequence when needed.
3142         newItem->setData(1, Qt::UserRole, toqstr(seq.print(KeySequence::BindFile)));
3143         setItemType(newItem, tag);
3144         return newItem;
3145 }
3146
3147
3148 void PrefShortcuts::on_shortcutsTW_itemSelectionChanged()
3149 {
3150         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3151         removePB->setEnabled(!items.isEmpty() && !items[0]->text(1).isEmpty());
3152         modifyPB->setEnabled(!items.isEmpty());
3153         if (items.isEmpty())
3154                 return;
3155
3156         if (itemType(*items[0]) == KeyMap::UserUnbind)
3157                 removePB->setText(qt_("Res&tore"));
3158         else
3159                 removePB->setText(qt_("Remo&ve"));
3160 }
3161
3162
3163 void PrefShortcuts::on_shortcutsTW_itemDoubleClicked()
3164 {
3165         modifyShortcut();
3166 }
3167
3168
3169 void PrefShortcuts::modifyShortcut()
3170 {
3171         QTreeWidgetItem * item = shortcutsTW->currentItem();
3172         if (item->flags() & Qt::ItemIsSelectable) {
3173                 shortcut_->lfunLE->setText(item->text(0));
3174                 save_lfun_ = item->text(0).trimmed();
3175                 shortcut_->shortcutWG->setText(item->text(1));
3176                 KeySequence seq;
3177                 seq.parse(fromqstr(item->data(1, Qt::UserRole).toString()));
3178                 shortcut_->shortcutWG->setKeySequence(seq);
3179                 shortcut_->shortcutWG->setFocus();
3180                 shortcut_->exec();
3181         }
3182 }
3183
3184
3185 void PrefShortcuts::unhideEmpty(QString const & lfun, bool select)
3186 {
3187         // list of items that match lfun
3188         QList<QTreeWidgetItem*> items = shortcutsTW->findItems(lfun,
3189              Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
3190         for (auto const & item : items) {
3191                 if (isAlwaysHidden(*item)) {
3192                         setItemType(item, KeyMap::System);
3193                         if (select)
3194                                 shortcutsTW->setCurrentItem(item);
3195                         return;
3196                 }
3197         }
3198 }
3199
3200
3201 void PrefShortcuts::removeShortcut()
3202 {
3203         // it seems that only one item can be selected, but I am
3204         // removing all selected items anyway.
3205         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3206         for (auto & item : items) {
3207                 string shortcut = fromqstr(item->data(1, Qt::UserRole).toString());
3208                 string lfun = fromqstr(item->text(0));
3209                 FuncRequest const func = lyxaction.lookupFunc(lfun);
3210
3211                 switch (itemType(*item)) {
3212                 case KeyMap::System: {
3213                         // for system bind, we do not touch the item
3214                         // but add an user unbind item
3215                         user_unbind_.bind(shortcut, func);
3216                         setItemType(item, KeyMap::UserUnbind);
3217                         removePB->setText(qt_("Res&tore"));
3218                         break;
3219                 }
3220                 case KeyMap::UserBind: {
3221                         // for user_bind, we remove this bind
3222                         QTreeWidgetItem * parent = item->parent();
3223                         int itemIdx = parent->indexOfChild(item);
3224                         parent->takeChild(itemIdx);
3225                         if (itemIdx > 0)
3226                                 shortcutsTW->scrollToItem(parent->child(itemIdx - 1));
3227                         else
3228                                 shortcutsTW->scrollToItem(parent);
3229                         user_bind_.unbind(shortcut, func);
3230                         // If this user binding hid an empty system binding, unhide the
3231                         // latter and select it.
3232                         unhideEmpty(item->text(0), true);
3233                         break;
3234                 }
3235                 case KeyMap::UserUnbind: {
3236                         // for user_unbind, we remove the unbind, and the item
3237                         // become KeyMap::System again.
3238                         KeySequence seq;
3239                         seq.parse(shortcut);
3240                         // Ask the user to replace current binding
3241                         if (!validateNewShortcut(func, seq, QString()))
3242                                 break;
3243                         user_unbind_.unbind(shortcut, func);
3244                         setItemType(item, KeyMap::System);
3245                         removePB->setText(qt_("Remo&ve"));
3246                         break;
3247                 }
3248                 case KeyMap::UserExtraUnbind: {
3249                         // for user unbind that is not in system bind file,
3250                         // remove this unbind file
3251                         QTreeWidgetItem * parent = item->parent();
3252                         parent->takeChild(parent->indexOfChild(item));
3253                         user_unbind_.unbind(shortcut, func);
3254                 }
3255                 }
3256         }
3257 }
3258
3259
3260 void PrefShortcuts::deactivateShortcuts(QList<QTreeWidgetItem*> const & items)
3261 {
3262         for (auto item : items) {
3263                 string shortcut = fromqstr(item->data(1, Qt::UserRole).toString());
3264                 string lfun = fromqstr(item->text(0));
3265                 FuncRequest const func = lyxaction.lookupFunc(lfun);
3266
3267                 switch (itemType(*item)) {
3268                 case KeyMap::System:
3269                         // for system bind, we do not touch the item
3270                         // but add an user unbind item
3271                         user_unbind_.bind(shortcut, func);
3272                         setItemType(item, KeyMap::UserUnbind);
3273                         break;
3274
3275                 case KeyMap::UserBind: {
3276                         // for user_bind, we remove this bind
3277                         QTreeWidgetItem * parent = item->parent();
3278                         int itemIdx = parent->indexOfChild(item);
3279                         parent->takeChild(itemIdx);
3280                         user_bind_.unbind(shortcut, func);
3281                         unhideEmpty(item->text(0), false);
3282                         break;
3283                 }
3284                 default:
3285                         break;
3286                 }
3287         }
3288 }
3289
3290
3291 void PrefShortcuts::selectBind()
3292 {
3293         QString file = form_->browsebind(internalPath(bindFileED->text()));
3294         if (!file.isEmpty()) {
3295                 bindFileED->setText(file);
3296                 system_bind_ = KeyMap();
3297                 system_bind_.read(fromqstr(file));
3298                 updateShortcutsTW();
3299         }
3300 }
3301
3302
3303 void PrefShortcuts::on_modifyPB_pressed()
3304 {
3305         modifyShortcut();
3306 }
3307
3308
3309 void PrefShortcuts::on_newPB_pressed()
3310 {
3311         shortcut_->lfunLE->clear();
3312         shortcut_->shortcutWG->reset();
3313         save_lfun_ = QString();
3314         shortcut_->exec();
3315 }
3316
3317
3318 void PrefShortcuts::on_removePB_pressed()
3319 {
3320         changed();
3321         removeShortcut();
3322 }
3323
3324
3325 void PrefShortcuts::on_searchLE_textEdited()
3326 {
3327         if (searchLE->text().isEmpty()) {
3328                 // show all hidden items
3329                 QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Hidden);
3330                 for (; *it; ++it)
3331                         (*it)->setHidden(isAlwaysHidden(**it));
3332                 // close all categories
3333                 for (int i = 0; i < shortcutsTW->topLevelItemCount(); ++i)
3334                         shortcutsTW->collapseItem(shortcutsTW->topLevelItem(i));
3335                 return;
3336         }
3337         // search both columns
3338         QList<QTreeWidgetItem *> matched = shortcutsTW->findItems(searchLE->text(),
3339                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 0);
3340         matched += shortcutsTW->findItems(searchLE->text(),
3341                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 1);
3342
3343         // hide everyone (to avoid searching in matched QList repeatedly
3344         QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Selectable);
3345         while (*it)
3346                 (*it++)->setHidden(true);
3347         // show matched items
3348         for (auto & item : matched)
3349                 if (!isAlwaysHidden(*item)) {
3350                         item->setHidden(false);
3351                         if (item->parent())
3352                                 item->parent()->setExpanded(true);
3353                 }
3354 }
3355
3356
3357 docstring makeCmdString(FuncRequest const & f)
3358 {
3359         docstring actionStr = from_ascii(lyxaction.getActionName(f.action()));
3360         if (!f.argument().empty())
3361                 actionStr += " " + f.argument();
3362         return actionStr;
3363 }
3364
3365
3366 FuncRequest PrefShortcuts::currentBinding(KeySequence const & k)
3367 {
3368         FuncRequest res = user_bind_.getBinding(k);
3369         if (res.action() != LFUN_UNKNOWN_ACTION)
3370                 return res;
3371         res = system_bind_.getBinding(k);
3372         // Check if it is unbound. Note: user_unbind_ can only unbind one
3373         // FuncRequest per key sequence.
3374         if (user_unbind_.getBinding(k) == res)
3375                 return FuncRequest::unknown;
3376         return res;
3377 }
3378
3379
3380 bool PrefShortcuts::validateNewShortcut(FuncRequest const & func,
3381                                         KeySequence const & k,
3382                                         QString const & lfun_to_modify)
3383 {
3384         if (func.action() == LFUN_UNKNOWN_ACTION) {
3385                 Alert::error(_("Failed to create shortcut"),
3386                         _("Unknown or invalid LyX function"));
3387                 return false;
3388         }
3389
3390         // It is not currently possible to bind Hidden lfuns such as self-insert. In
3391         // the future, to remove this limitation, see GuiPrefs::insertShortcutItem
3392         // and how it is used in GuiPrefs::shortcutOkPressed.
3393         if (lyxaction.getActionType(func.action()) == LyXAction::Hidden) {
3394                 Alert::error(_("Failed to create shortcut"),
3395                         _("This LyX function is hidden and cannot be bound."));
3396                 return false;
3397         }
3398
3399         if (k.length() == 0) {
3400                 Alert::error(_("Failed to create shortcut"),
3401                         _("Invalid or empty key sequence"));
3402                 return false;
3403         }
3404
3405         FuncRequest oldBinding = currentBinding(k);
3406         if (oldBinding == func)
3407                 // nothing to change
3408                 return false;
3409
3410         // make sure this key isn't already bound---and, if so, prompt user
3411         // (exclude the lfun the user already wants to modify)
3412         docstring const action_string = makeCmdString(oldBinding);
3413         if (oldBinding.action() != LFUN_UNKNOWN_ACTION
3414             && lfun_to_modify != toqstr(action_string)) {
3415                 docstring const new_action_string = makeCmdString(func);
3416                 docstring const text = bformat(_("Shortcut `%1$s' is already bound to "
3417                                                  "%2$s.\n"
3418                                                  "Are you sure you want to unbind the "
3419                                                  "current shortcut and bind it to %3$s?"),
3420                                                k.print(KeySequence::ForGui), action_string,
3421                                                new_action_string);
3422                 int ret = Alert::prompt(_("Redefine shortcut?"),
3423                                         text, 0, 1, _("&Redefine"), _("&Cancel"));
3424                 if (ret != 0)
3425                         return false;
3426                 QString const sequence_text = toqstr(k.print(KeySequence::ForGui));
3427                 QList<QTreeWidgetItem*> items = shortcutsTW->findItems(sequence_text,
3428                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 1);
3429                 deactivateShortcuts(items);
3430         }
3431         return true;
3432 }
3433
3434
3435 void PrefShortcuts::shortcutOkPressed()
3436 {
3437         QString const new_lfun = shortcut_->lfunLE->text();
3438         FuncRequest const func = lyxaction.lookupFunc(fromqstr(new_lfun));
3439         KeySequence k = shortcut_->shortcutWG->getKeySequence();
3440
3441         // save_lfun_ contains the text of the lfun to modify, if the user clicked
3442         // "modify", or is empty if they clicked "new" (which I do not really like)
3443         if (!validateNewShortcut(func, k, save_lfun_))
3444                 return;
3445
3446         if (!save_lfun_.isEmpty()) {
3447                 // real modification of the lfun's shortcut,
3448                 // so remove the previous one
3449                 QList<QTreeWidgetItem*> to_modify = shortcutsTW->selectedItems();
3450                 deactivateShortcuts(to_modify);
3451         }
3452
3453         shortcut_->accept();
3454
3455         QTreeWidgetItem * item = insertShortcutItem(func, k, KeyMap::UserBind);
3456         if (item) {
3457                 user_bind_.bind(&k, func);
3458                 shortcutsTW->sortItems(0, Qt::AscendingOrder);
3459                 if (item->parent())
3460                         item->parent()->setExpanded(true);
3461                 shortcutsTW->setCurrentItem(item);
3462                 shortcutsTW->scrollToItem(item);
3463         } else {
3464                 Alert::error(_("Failed to create shortcut"),
3465                         _("Can not insert shortcut to the list"));
3466                 return;
3467         }
3468 }
3469
3470
3471 void PrefShortcuts::shortcutCancelPressed()
3472 {
3473         shortcut_->shortcutWG->reset();
3474 }
3475
3476
3477 void PrefShortcuts::shortcutClearPressed()
3478 {
3479         shortcut_->shortcutWG->reset();
3480 }
3481
3482
3483 void PrefShortcuts::shortcutRemovePressed()
3484 {
3485         shortcut_->shortcutWG->removeFromSequence();
3486 }
3487
3488
3489 /////////////////////////////////////////////////////////////////////
3490 //
3491 // PrefIdentity
3492 //
3493 /////////////////////////////////////////////////////////////////////
3494
3495 PrefIdentity::PrefIdentity(GuiPreferences * form)
3496         : PrefModule(QString(), N_("Identity"), form)
3497 {
3498         setupUi(this);
3499
3500         connect(nameED, SIGNAL(textChanged(QString)),
3501                 this, SIGNAL(changed()));
3502         connect(emailED, SIGNAL(textChanged(QString)),
3503                 this, SIGNAL(changed()));
3504         connect(initialsED, SIGNAL(textChanged(QString)),
3505                 this, SIGNAL(changed()));
3506
3507         nameED->setValidator(new NoNewLineValidator(nameED));
3508         emailED->setValidator(new NoNewLineValidator(emailED));
3509         initialsED->setValidator(new NoNewLineValidator(initialsED));
3510 }
3511
3512
3513 void PrefIdentity::applyRC(LyXRC & rc) const
3514 {
3515         rc.user_name = fromqstr(nameED->text());
3516         rc.user_email = fromqstr(emailED->text());
3517         rc.user_initials = fromqstr(initialsED->text());
3518 }
3519
3520
3521 void PrefIdentity::updateRC(LyXRC const & rc)
3522 {
3523         nameED->setText(toqstr(rc.user_name));
3524         emailED->setText(toqstr(rc.user_email));
3525         initialsED->setText(toqstr(rc.user_initials));
3526 }
3527
3528
3529
3530 /////////////////////////////////////////////////////////////////////
3531 //
3532 // GuiPreferences
3533 //
3534 /////////////////////////////////////////////////////////////////////
3535
3536 GuiPreferences::GuiPreferences(GuiView & lv)
3537         : GuiDialog(lv, "prefs", qt_("Preferences"))
3538 {
3539         setupUi(this);
3540
3541         QDialog::setModal(false);
3542
3543         connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
3544                 this, SLOT(slotButtonBox(QAbstractButton *)));
3545
3546         addModule(new PrefUserInterface(this));
3547         addModule(new PrefDocHandling(this));
3548         addModule(new PrefEdit(this));
3549         addModule(new PrefShortcuts(this));
3550         PrefScreenFonts * screenfonts = new PrefScreenFonts(this);
3551         connect(this, SIGNAL(prefsApplied(LyXRC const &)),
3552                         screenfonts, SLOT(updateScreenFontSizes(LyXRC const &)));
3553         addModule(screenfonts);
3554         addModule(new PrefColors(this));
3555         addModule(new PrefDisplay(this));
3556         addModule(new PrefInput(this));
3557         addModule(new PrefCompletion(this));
3558
3559         addModule(new PrefPaths(this));
3560
3561         addModule(new PrefIdentity(this));
3562
3563         addModule(new PrefLanguage(this));
3564         addModule(new PrefSpellchecker(this));
3565
3566         PrefOutput * output = new PrefOutput(this);
3567         addModule(output);
3568         addModule(new PrefLatex(this));
3569
3570         PrefConverters * converters = new PrefConverters(this);
3571         PrefFileformats * formats = new PrefFileformats(this);
3572         connect(formats, SIGNAL(formatsChanged()),
3573                         converters, SLOT(updateGui()));
3574         addModule(converters);
3575         addModule(formats);
3576
3577         prefsPS->setCurrentPanel("User Interface");
3578 // FIXME: hack to work around resizing bug in Qt >= 4.2
3579 // bug verified with Qt 4.2.{0-3} (JSpitzm)
3580 #if QT_VERSION >= 0x040200
3581         prefsPS->updateGeometry();
3582 #endif
3583
3584         bc().setPolicy(ButtonPolicy::PreferencesPolicy);
3585         bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
3586         bc().setApply(buttonBox->button(QDialogButtonBox::Apply));
3587         bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
3588         bc().setRestore(buttonBox->button(QDialogButtonBox::Reset));
3589
3590         guilyxfiles_ = new GuiLyXFiles(lv);
3591         connect(guilyxfiles_, SIGNAL(fileSelected(QString)),
3592                         this, SLOT(slotFileSelected(QString)));
3593 }
3594
3595
3596 void GuiPreferences::addModule(PrefModule * module)
3597 {
3598         LASSERT(module, return);
3599         if (module->category().isEmpty())
3600                 prefsPS->addPanel(module, module->title());
3601         else
3602                 prefsPS->addPanel(module, module->title(), module->category());
3603         connect(module, SIGNAL(changed()), this, SLOT(change_adaptor()));
3604         modules_.push_back(module);
3605 }
3606
3607
3608 void GuiPreferences::change_adaptor()
3609 {
3610         changed();
3611 }
3612
3613
3614 void GuiPreferences::applyRC(LyXRC & rc) const
3615 {
3616         size_t end = modules_.size();
3617         for (size_t i = 0; i != end; ++i)
3618                 modules_[i]->applyRC(rc);
3619 }
3620
3621
3622 void GuiPreferences::updateRC(LyXRC const & rc)
3623 {
3624         size_t const end = modules_.size();
3625         for (size_t i = 0; i != end; ++i)
3626                 modules_[i]->updateRC(rc);
3627 }
3628
3629
3630 void GuiPreferences::applyView()
3631 {
3632         applyRC(rc());
3633 }
3634
3635
3636 bool GuiPreferences::initialiseParams(string const &)
3637 {
3638         rc_ = lyxrc;
3639         formats_ = theFormats();
3640         converters_ = theConverters();
3641         converters_.update(formats_);
3642         movers_ = theMovers();
3643         colors_.clear();
3644
3645         updateRC(rc_);
3646         // Make sure that the bc is in the INITIAL state
3647         if (bc().policy().buttonStatus(ButtonPolicy::RESTORE))
3648                 bc().restore();
3649
3650         return true;
3651 }
3652
3653
3654 void GuiPreferences::dispatchParams()
3655 {
3656         ostringstream ss;
3657         rc_.write(ss, true);
3658         dispatch(FuncRequest(LFUN_LYXRC_APPLY, ss.str()));
3659         // issue prefsApplied signal. This will update the
3660         // localized screen font sizes.
3661         prefsApplied(rc_);
3662         // FIXME: these need lfuns
3663         // FIXME UNICODE
3664         Author const & author =
3665                 Author(from_utf8(rc_.user_name), from_utf8(rc_.user_email),
3666                        from_utf8(rc_.user_initials));
3667         theBufferList().recordCurrentAuthor(author);
3668
3669         theFormats() = formats_;
3670
3671         theConverters() = converters_;
3672         theConverters().update(formats_);
3673         theConverters().buildGraph();
3674         theBufferList().invalidateConverterCache();
3675
3676         theMovers() = movers_;
3677
3678         for (string const & color : colors_)
3679                 dispatch(FuncRequest(LFUN_SET_COLOR, color));
3680         colors_.clear();
3681
3682         // Save permanently
3683         if (!tempSaveCB->isChecked())
3684                 dispatch(FuncRequest(LFUN_PREFERENCES_SAVE));
3685 }
3686
3687
3688 void GuiPreferences::setColor(ColorCode col, QString const & hex)
3689 {
3690         colors_.push_back(lcolor.getLyXName(col) + ' ' + fromqstr(hex));
3691 }
3692
3693
3694 void GuiPreferences::slotFileSelected(QString const file)
3695 {
3696         uifile_ = file;
3697 }
3698
3699
3700 QString GuiPreferences::browseLibFile(QString const & dir,
3701         QString const & name, QString const & ext)
3702 {
3703         uifile_.clear();
3704
3705         guilyxfiles_->passParams(fromqstr(dir));
3706         guilyxfiles_->selectItem(name);
3707         guilyxfiles_->exec();
3708
3709         QString const result = uifile_;
3710
3711         // remove the extension if it is the default one
3712         QString noextresult;
3713         if (getExtension(result) == ext)
3714                 noextresult = removeExtension(result);
3715         else
3716                 noextresult = result;
3717
3718         // remove the directory, if it is the default one
3719         QString const file = onlyFileName(noextresult);
3720         if (toqstr(libFileSearch(dir, file, ext).absFileName()) == result)
3721                 return file;
3722         else
3723                 return noextresult;
3724 }
3725
3726
3727 QString GuiPreferences::browsebind(QString const & file)
3728 {
3729         return browseLibFile("bind", file, "bind");
3730 }
3731
3732
3733 QString GuiPreferences::browseUI(QString const & file)
3734 {
3735         return browseLibFile("ui", file, "ui");
3736 }
3737
3738
3739 QString GuiPreferences::browsekbmap(QString const & file)
3740 {
3741         return browseLibFile("kbd", file, "kmap");
3742 }
3743
3744
3745 QString GuiPreferences::browse(QString const & file,
3746         QString const & title) const
3747 {
3748         return browseFile(file, title, QStringList(), true);
3749 }
3750
3751
3752 } // namespace frontend
3753 } // namespace lyx
3754
3755 #include "moc_GuiPrefs.cpp"