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