]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/GuiPrefs.cpp
Allow to unbind without specifying the lfun
[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         updateComboBox(viewer_alternatives, f.name(), viewerCO);
2329         viewerCO->addItem(qt_("Custom"), QString("custom viewer"));
2330         viewerCO->blockSignals(false);
2331
2332         int pos = viewerCO->findData(toqstr(f.viewer()));
2333         if (pos != -1) {
2334                 viewerED->clear();
2335                 viewerED->setEnabled(false);
2336                 viewerCO->setCurrentIndex(pos);
2337         } else {
2338                 viewerED->setEnabled(true);
2339                 viewerED->setText(toqstr(f.viewer()));
2340                 viewerCO->setCurrentIndex(viewerCO->findData(toqstr("custom viewer")));
2341         }
2342 }
2343
2344
2345 void PrefFileformats::updateEditors()
2346 {
2347         Format const f = currentFormat();
2348         editorCO->blockSignals(true);
2349         editorCO->clear();
2350         editorCO->addItem(qt_("None"), QString());
2351         updateComboBox(editor_alternatives, f.name(), editorCO);
2352         editorCO->addItem(qt_("Custom"), QString("custom editor"));
2353         editorCO->blockSignals(false);
2354
2355         int pos = editorCO->findData(toqstr(f.editor()));
2356         if (pos != -1) {
2357                 editorED->clear();
2358                 editorED->setEnabled(false);
2359                 editorCO->setCurrentIndex(pos);
2360         } else {
2361                 editorED->setEnabled(true);
2362                 editorED->setText(toqstr(f.editor()));
2363                 editorCO->setCurrentIndex(editorCO->findData(toqstr("custom editor")));
2364         }
2365 }
2366
2367
2368 void PrefFileformats::on_viewerCO_currentIndexChanged(int i)
2369 {
2370         bool const custom = viewerCO->itemData(i).toString() == "custom viewer";
2371         viewerED->setEnabled(custom);
2372         if (!custom)
2373                 currentFormat().setViewer(fromqstr(viewerCO->itemData(i).toString()));
2374 }
2375
2376
2377 void PrefFileformats::on_editorCO_currentIndexChanged(int i)
2378 {
2379         bool const custom = editorCO->itemData(i).toString() == "custom editor";
2380         editorED->setEnabled(custom);
2381         if (!custom)
2382                 currentFormat().setEditor(fromqstr(editorCO->itemData(i).toString()));
2383 }
2384
2385
2386 Format & PrefFileformats::currentFormat()
2387 {
2388         int const i = formatsCB->currentIndex();
2389         int const nr = formatsCB->itemData(i).toInt();
2390         return form_->formats().get(nr);
2391 }
2392
2393
2394 void PrefFileformats::on_formatNewPB_clicked()
2395 {
2396         form_->formats().add("", "", docstring(), "", "", "", "", Format::none);
2397         updateView();
2398         formatsCB->setCurrentIndex(0);
2399         formatsCB->setFocus(Qt::OtherFocusReason);
2400 }
2401
2402
2403 void PrefFileformats::on_formatRemovePB_clicked()
2404 {
2405         int const i = formatsCB->currentIndex();
2406         int const nr = formatsCB->itemData(i).toInt();
2407         string const current_text = form_->formats().get(nr).name();
2408         if (form_->converters().formatIsUsed(current_text)) {
2409                 Alert::error(_("Format in use"),
2410                              _("Cannot remove a Format used by a Converter. "
2411                                             "Remove the converter first."));
2412                 return;
2413         }
2414
2415         form_->formats().erase(current_text);
2416         formatsChanged();
2417         updateView();
2418         on_formatsCB_editTextChanged(formatsCB->currentText());
2419         changed();
2420 }
2421
2422
2423 /////////////////////////////////////////////////////////////////////
2424 //
2425 // PrefLanguage
2426 //
2427 /////////////////////////////////////////////////////////////////////
2428
2429 PrefLanguage::PrefLanguage(GuiPreferences * form)
2430         : PrefModule(catLanguage, N_("Language"), form)
2431 {
2432         setupUi(this);
2433
2434         connect(visualCursorRB, SIGNAL(clicked()),
2435                 this, SIGNAL(changed()));
2436         connect(logicalCursorRB, SIGNAL(clicked()),
2437                 this, SIGNAL(changed()));
2438         connect(markForeignCB, SIGNAL(clicked()),
2439                 this, SIGNAL(changed()));
2440         connect(respectOSkbdCB, SIGNAL(clicked()),
2441                 this, SIGNAL(changed()));
2442         connect(explicitDocLangBeginCB, SIGNAL(clicked()),
2443                 this, SIGNAL(changed()));
2444         connect(explicitDocLangEndCB, SIGNAL(clicked()),
2445                 this, SIGNAL(changed()));
2446         connect(languagePackageCO, SIGNAL(activated(int)),
2447                 this, SIGNAL(changed()));
2448         connect(languagePackageED, SIGNAL(textChanged(QString)),
2449                 this, SIGNAL(changed()));
2450         connect(globalCB, SIGNAL(clicked()),
2451                 this, SIGNAL(changed()));
2452         connect(startCommandED, SIGNAL(textChanged(QString)),
2453                 this, SIGNAL(changed()));
2454         connect(endCommandED, SIGNAL(textChanged(QString)),
2455                 this, SIGNAL(changed()));
2456         connect(uiLanguageCO, SIGNAL(activated(int)),
2457                 this, SIGNAL(changed()));
2458         connect(defaultDecimalSepED, SIGNAL(textChanged(QString)),
2459                 this, SIGNAL(changed()));
2460         connect(defaultDecimalSepCO, SIGNAL(activated(int)),
2461                 this, SIGNAL(changed()));
2462         connect(defaultLengthUnitCO, SIGNAL(activated(int)),
2463                 this, SIGNAL(changed()));
2464
2465         languagePackageED->setValidator(new NoNewLineValidator(languagePackageED));
2466         startCommandED->setValidator(new NoNewLineValidator(startCommandED));
2467         endCommandED->setValidator(new NoNewLineValidator(endCommandED));
2468
2469 #if QT_VERSION < 0x060000
2470         defaultDecimalSepED->setValidator(new QRegExpValidator(QRegExp("\\S"), this));
2471 #else
2472         defaultDecimalSepED->setValidator(new QRegularExpressionValidator(QRegularExpression("\\S"), this));
2473 #endif
2474         defaultDecimalSepED->setMaxLength(1);
2475
2476         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::CM]), Length::CM);
2477         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::IN]), Length::IN);
2478
2479         QAbstractItemModel * language_model = guiApp->languageModel();
2480         language_model->sort(0);
2481         uiLanguageCO->blockSignals(true);
2482         uiLanguageCO->clear();
2483         uiLanguageCO->addItem(qt_("Default"), toqstr("auto"));
2484         for (int i = 0; i != language_model->rowCount(); ++i) {
2485                 QModelIndex index = language_model->index(i, 0);
2486                 // Filter the list based on the available translation and add
2487                 // each language code only once
2488                 string const name = fromqstr(index.data(Qt::UserRole).toString());
2489                 Language const * lang = languages.getLanguage(name);
2490                 if (!lang)
2491                         continue;
2492                 // never remove the currently selected language
2493                 if (name != form->rc().gui_language
2494                     && name != lyxrc.gui_language
2495                     && (!Messages::available(lang->code())
2496                         || !lang->hasGuiSupport()))
2497                         continue;
2498                 uiLanguageCO->addItem(index.data(Qt::DisplayRole).toString(),
2499                                       index.data(Qt::UserRole).toString());
2500         }
2501         uiLanguageCO->blockSignals(false);
2502
2503         // FIXME: restore this when it works (see discussion in #6450).
2504         respectOSkbdCB->hide();
2505 }
2506
2507
2508 void PrefLanguage::on_uiLanguageCO_currentIndexChanged(int)
2509 {
2510          QMessageBox::information(this, qt_("LyX needs to be restarted!"),
2511                  qt_("The change of user interface language will be fully "
2512                  "effective only after a restart."));
2513 }
2514
2515
2516 void PrefLanguage::on_languagePackageCO_currentIndexChanged(int i)
2517 {
2518         if (i == 2)
2519                 languagePackageED->setText(save_langpack_);
2520         else if (!languagePackageED->text().isEmpty()) {
2521                 save_langpack_ = languagePackageED->text();
2522                 languagePackageED->clear();
2523         }
2524         languagePackageED->setEnabled(i == 2);
2525 }
2526
2527
2528 void PrefLanguage::on_defaultDecimalSepCO_currentIndexChanged(int i)
2529 {
2530         defaultDecimalSepED->setEnabled(i == 1);
2531 }
2532
2533
2534 void PrefLanguage::applyRC(LyXRC & rc) const
2535 {
2536         rc.visual_cursor = visualCursorRB->isChecked();
2537         rc.mark_foreign_language = markForeignCB->isChecked();
2538         rc.respect_os_kbd_language = respectOSkbdCB->isChecked();
2539         rc.language_auto_begin = !explicitDocLangBeginCB->isChecked();
2540         rc.language_auto_end = !explicitDocLangEndCB->isChecked();
2541         int const p = languagePackageCO->currentIndex();
2542         if (p == 0)
2543                 rc.language_package_selection = LyXRC::LP_AUTO;
2544         else if (p == 1)
2545                 rc.language_package_selection = LyXRC::LP_BABEL;
2546         else if (p == 2)
2547                 rc.language_package_selection = LyXRC::LP_CUSTOM;
2548         else if (p == 3)
2549                 rc.language_package_selection = LyXRC::LP_NONE;
2550         rc.language_custom_package = fromqstr(languagePackageED->text());
2551         rc.language_global_options = globalCB->isChecked();
2552         rc.language_command_begin = fromqstr(startCommandED->text());
2553         rc.language_command_end = fromqstr(endCommandED->text());
2554         rc.gui_language = fromqstr(
2555                 uiLanguageCO->itemData(uiLanguageCO->currentIndex()).toString());
2556         if (defaultDecimalSepCO->currentIndex() == 0)
2557                 rc.default_decimal_sep = "locale";
2558         else
2559                 rc.default_decimal_sep = fromqstr(defaultDecimalSepED->text());
2560         rc.default_length_unit = (Length::UNIT) defaultLengthUnitCO->itemData(defaultLengthUnitCO->currentIndex()).toInt();
2561 }
2562
2563
2564 void PrefLanguage::updateRC(LyXRC const & rc)
2565 {
2566         if (rc.visual_cursor)
2567                 visualCursorRB->setChecked(true);
2568         else
2569                 logicalCursorRB->setChecked(true);
2570         markForeignCB->setChecked(rc.mark_foreign_language);
2571         respectOSkbdCB->setChecked(rc.respect_os_kbd_language);
2572         explicitDocLangBeginCB->setChecked(!rc.language_auto_begin);
2573         explicitDocLangEndCB->setChecked(!rc.language_auto_end);
2574         languagePackageCO->setCurrentIndex(rc.language_package_selection);
2575         if (languagePackageCO->currentIndex() == 2) {
2576                 languagePackageED->setText(toqstr(rc.language_custom_package));
2577                 languagePackageED->setEnabled(true);
2578         } else {
2579                 languagePackageED->clear();
2580                 save_langpack_ = toqstr(rc.language_custom_package);
2581                 languagePackageED->setEnabled(false);
2582         }
2583         defaultDecimalSepED->setEnabled(defaultDecimalSepCO->currentIndex() == 1);
2584         globalCB->setChecked(rc.language_global_options);
2585         startCommandED->setText(toqstr(rc.language_command_begin));
2586         endCommandED->setText(toqstr(rc.language_command_end));
2587         if (rc.default_decimal_sep == "locale") {
2588                 defaultDecimalSepCO->setCurrentIndex(0);
2589                 defaultDecimalSepED->clear();
2590         } else {
2591                 defaultDecimalSepCO->setCurrentIndex(1);
2592                 defaultDecimalSepED->setText(toqstr(rc.default_decimal_sep));
2593         }
2594         int pos = defaultLengthUnitCO->findData(int(rc.default_length_unit));
2595         defaultLengthUnitCO->setCurrentIndex(pos);
2596
2597         pos = uiLanguageCO->findData(toqstr(rc.gui_language));
2598         uiLanguageCO->blockSignals(true);
2599         uiLanguageCO->setCurrentIndex(pos);
2600         uiLanguageCO->blockSignals(false);
2601 }
2602
2603
2604 /////////////////////////////////////////////////////////////////////
2605 //
2606 // PrefUserInterface
2607 //
2608 /////////////////////////////////////////////////////////////////////
2609
2610 PrefUserInterface::PrefUserInterface(GuiPreferences * form)
2611         : PrefModule(catLookAndFeel, N_("User Interface"), form)
2612 {
2613         setupUi(this);
2614
2615         connect(uiFilePB, SIGNAL(clicked()),
2616                 this, SLOT(selectUi()));
2617         connect(uiFileED, SIGNAL(textChanged(QString)),
2618                 this, SIGNAL(changed()));
2619         connect(iconSetCO, SIGNAL(activated(int)),
2620                 this, SIGNAL(changed()));
2621         connect(useSystemThemeIconsCB, SIGNAL(clicked()),
2622                 this, SIGNAL(changed()));
2623         connect(lastfilesSB, SIGNAL(valueChanged(int)),
2624                 this, SIGNAL(changed()));
2625         connect(tooltipCB, SIGNAL(toggled(bool)),
2626                 this, SIGNAL(changed()));
2627         lastfilesSB->setMaximum(maxlastfiles);
2628
2629         iconSetCO->addItem(qt_("Default"), QString());
2630         iconSetCO->addItem(qt_("Classic"), "classic");
2631         iconSetCO->addItem(qt_("Oxygen"), "oxygen");
2632
2633 #if QT_VERSION >= 0x040600
2634         if (guiApp->platformName() != "qt4x11"
2635             && guiApp->platformName() != "xcb"
2636             && !guiApp->platformName().contains("wayland"))
2637 #endif
2638                 useSystemThemeIconsCB->hide();
2639 }
2640
2641
2642 void PrefUserInterface::applyRC(LyXRC & rc) const
2643 {
2644         rc.icon_set = fromqstr(iconSetCO->itemData(
2645                 iconSetCO->currentIndex()).toString());
2646
2647         rc.ui_file = internal_path(fromqstr(uiFileED->text()));
2648         rc.use_system_theme_icons = useSystemThemeIconsCB->isChecked();
2649         rc.num_lastfiles = lastfilesSB->value();
2650         rc.use_tooltip = tooltipCB->isChecked();
2651 }
2652
2653
2654 void PrefUserInterface::updateRC(LyXRC const & rc)
2655 {
2656         int iconset = iconSetCO->findData(toqstr(rc.icon_set));
2657         if (iconset < 0)
2658                 iconset = 0;
2659         iconSetCO->setCurrentIndex(iconset);
2660         useSystemThemeIconsCB->setChecked(rc.use_system_theme_icons);
2661         uiFileED->setText(toqstr(external_path(rc.ui_file)));
2662         lastfilesSB->setValue(rc.num_lastfiles);
2663         tooltipCB->setChecked(rc.use_tooltip);
2664 }
2665
2666
2667 void PrefUserInterface::selectUi()
2668 {
2669         QString file = form_->browseUI(internalPath(uiFileED->text()));
2670         if (!file.isEmpty())
2671                 uiFileED->setText(file);
2672 }
2673
2674
2675 /////////////////////////////////////////////////////////////////////
2676 //
2677 // PrefDocumentHandling
2678 //
2679 /////////////////////////////////////////////////////////////////////
2680
2681 PrefDocHandling::PrefDocHandling(GuiPreferences * form)
2682         : PrefModule(catLookAndFeel, N_("Document Handling"), form)
2683 {
2684         setupUi(this);
2685
2686         connect(autoSaveCB, SIGNAL(toggled(bool)),
2687                 autoSaveSB, SLOT(setEnabled(bool)));
2688         connect(autoSaveCB, SIGNAL(toggled(bool)),
2689                 TextLabel1, SLOT(setEnabled(bool)));
2690         connect(openDocumentsInTabsCB, SIGNAL(clicked()),
2691                 this, SIGNAL(changed()));
2692         connect(singleInstanceCB, SIGNAL(clicked()),
2693                 this, SIGNAL(changed()));
2694         connect(singleCloseTabButtonCB, SIGNAL(clicked()),
2695                 this, SIGNAL(changed()));
2696         connect(closeLastViewCO, SIGNAL(activated(int)),
2697                 this, SIGNAL(changed()));
2698         connect(restoreCursorCB, SIGNAL(clicked()),
2699                 this, SIGNAL(changed()));
2700         connect(loadSessionCB, SIGNAL(clicked()),
2701                 this, SIGNAL(changed()));
2702         connect(allowGeometrySessionCB, SIGNAL(clicked()),
2703                 this, SIGNAL(changed()));
2704         connect(autoSaveSB, SIGNAL(valueChanged(int)),
2705                 this, SIGNAL(changed()));
2706         connect(autoSaveCB, SIGNAL(clicked()),
2707                 this, SIGNAL(changed()));
2708         connect(backupCB, SIGNAL(clicked()),
2709                 this, SIGNAL(changed()));
2710         connect(saveCompressedCB, SIGNAL(clicked()),
2711                 this, SIGNAL(changed()));
2712         connect(saveOriginCB, SIGNAL(clicked()),
2713                 this, SIGNAL(changed()));
2714 }
2715
2716
2717 void PrefDocHandling::applyRC(LyXRC & rc) const
2718 {
2719         rc.use_lastfilepos = restoreCursorCB->isChecked();
2720         rc.load_session = loadSessionCB->isChecked();
2721         rc.allow_geometry_session = allowGeometrySessionCB->isChecked();
2722         rc.autosave = autoSaveCB->isChecked() ?  autoSaveSB->value() * 60 : 0;
2723         rc.make_backup = backupCB->isChecked();
2724         rc.save_compressed = saveCompressedCB->isChecked();
2725         rc.save_origin = saveOriginCB->isChecked();
2726         rc.open_buffers_in_tabs = openDocumentsInTabsCB->isChecked();
2727         rc.single_instance = singleInstanceCB->isChecked();
2728         rc.single_close_tab_button = singleCloseTabButtonCB->isChecked();
2729
2730         switch (closeLastViewCO->currentIndex()) {
2731         case 0:
2732                 rc.close_buffer_with_last_view = "yes";
2733                 break;
2734         case 1:
2735                 rc.close_buffer_with_last_view = "no";
2736                 break;
2737         case 2:
2738                 rc.close_buffer_with_last_view = "ask";
2739                 break;
2740         default:
2741                 ;
2742         }
2743 }
2744
2745
2746 void PrefDocHandling::updateRC(LyXRC const & rc)
2747 {
2748         restoreCursorCB->setChecked(rc.use_lastfilepos);
2749         loadSessionCB->setChecked(rc.load_session);
2750         allowGeometrySessionCB->setChecked(rc.allow_geometry_session);
2751         // convert to minutes
2752         bool autosave = rc.autosave > 0;
2753         int mins = rc.autosave / 60;
2754         if (!mins)
2755                 mins = 5;
2756         autoSaveSB->setValue(mins);
2757         autoSaveCB->setChecked(autosave);
2758         autoSaveSB->setEnabled(autosave);
2759         backupCB->setChecked(rc.make_backup);
2760         saveCompressedCB->setChecked(rc.save_compressed);
2761         saveOriginCB->setChecked(rc.save_origin);
2762         openDocumentsInTabsCB->setChecked(rc.open_buffers_in_tabs);
2763         singleInstanceCB->setChecked(rc.single_instance && !rc.lyxpipes.empty());
2764         singleInstanceCB->setEnabled(!rc.lyxpipes.empty());
2765         singleCloseTabButtonCB->setChecked(rc.single_close_tab_button);
2766         if (rc.close_buffer_with_last_view == "yes")
2767                 closeLastViewCO->setCurrentIndex(0);
2768         else if (rc.close_buffer_with_last_view == "no")
2769                 closeLastViewCO->setCurrentIndex(1);
2770         else if (rc.close_buffer_with_last_view == "ask")
2771                 closeLastViewCO->setCurrentIndex(2);
2772 }
2773
2774
2775 void PrefDocHandling::on_clearSessionPB_clicked()
2776 {
2777         guiApp->clearSession();
2778 }
2779
2780
2781
2782 /////////////////////////////////////////////////////////////////////
2783 //
2784 // PrefEdit
2785 //
2786 /////////////////////////////////////////////////////////////////////
2787
2788 PrefEdit::PrefEdit(GuiPreferences * form)
2789         : PrefModule(catEditing, N_("Control"), form)
2790 {
2791         setupUi(this);
2792
2793         connect(cursorFollowsCB, SIGNAL(clicked()),
2794                 this, SIGNAL(changed()));
2795         connect(scrollBelowCB, SIGNAL(clicked()),
2796                 this, SIGNAL(changed()));
2797         connect(macLikeCursorMovementCB, SIGNAL(clicked()),
2798                 this, SIGNAL(changed()));
2799         connect(copyCTMarkupCB, SIGNAL(clicked()),
2800                 this, SIGNAL(changed()));
2801         connect(sortEnvironmentsCB, SIGNAL(clicked()),
2802                 this, SIGNAL(changed()));
2803         connect(groupEnvironmentsCB, SIGNAL(clicked()),
2804                 this, SIGNAL(changed()));
2805         connect(macroEditStyleCO, SIGNAL(activated(int)),
2806                 this, SIGNAL(changed()));
2807         connect(cursorWidthSB, SIGNAL(valueChanged(int)),
2808                 this, SIGNAL(changed()));
2809         connect(citationSearchLE, SIGNAL(textChanged(QString)),
2810                 this, SIGNAL(changed()));
2811         connect(fullscreenWidthSB, SIGNAL(valueChanged(int)),
2812                 this, SIGNAL(changed()));
2813         connect(toggleTabbarCB, SIGNAL(toggled(bool)),
2814                 this, SIGNAL(changed()));
2815         connect(toggleMenubarCB, SIGNAL(toggled(bool)),
2816                 this, SIGNAL(changed()));
2817         connect(toggleScrollbarCB, SIGNAL(toggled(bool)),
2818                 this, SIGNAL(changed()));
2819         connect(toggleStatusbarCB, SIGNAL(toggled(bool)),
2820                 this, SIGNAL(changed()));
2821         connect(toggleToolbarsCB, SIGNAL(toggled(bool)),
2822                 this, SIGNAL(changed()));
2823 }
2824
2825
2826 void PrefEdit::on_fullscreenLimitCB_toggled(bool const state)
2827 {
2828         fullscreenWidthSB->setEnabled(state);
2829         fullscreenWidthLA->setEnabled(state);
2830         changed();
2831 }
2832
2833
2834 void PrefEdit::on_citationSearchCB_toggled(bool const state)
2835 {
2836         citationSearchLE->setEnabled(state);
2837         citationSearchLA->setEnabled(state);
2838         changed();
2839 }
2840
2841
2842 void PrefEdit::applyRC(LyXRC & rc) const
2843 {
2844         rc.cursor_follows_scrollbar = cursorFollowsCB->isChecked();
2845         rc.scroll_below_document = scrollBelowCB->isChecked();
2846         rc.mac_like_cursor_movement = macLikeCursorMovementCB->isChecked();
2847         rc.ct_markup_copied = copyCTMarkupCB->isChecked();
2848         rc.sort_layouts = sortEnvironmentsCB->isChecked();
2849         rc.group_layouts = groupEnvironmentsCB->isChecked();
2850         switch (macroEditStyleCO->currentIndex()) {
2851                 case 0: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE_BOX; break;
2852                 case 1: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE; break;
2853                 case 2: rc.macro_edit_style = LyXRC::MACRO_EDIT_LIST;   break;
2854         }
2855         rc.cursor_width = cursorWidthSB->value();
2856         rc.citation_search = citationSearchCB->isChecked();
2857         rc.citation_search_pattern = fromqstr(citationSearchLE->text());
2858         rc.full_screen_toolbars = toggleToolbarsCB->isChecked();
2859         rc.full_screen_scrollbar = toggleScrollbarCB->isChecked();
2860         rc.full_screen_statusbar = toggleStatusbarCB->isChecked();
2861         rc.full_screen_tabbar = toggleTabbarCB->isChecked();
2862         rc.full_screen_menubar = toggleMenubarCB->isChecked();
2863         rc.full_screen_width = fullscreenWidthSB->value();
2864         rc.full_screen_limit = fullscreenLimitCB->isChecked();
2865 }
2866
2867
2868 void PrefEdit::updateRC(LyXRC const & rc)
2869 {
2870         cursorFollowsCB->setChecked(rc.cursor_follows_scrollbar);
2871         scrollBelowCB->setChecked(rc.scroll_below_document);
2872         macLikeCursorMovementCB->setChecked(rc.mac_like_cursor_movement);
2873         copyCTMarkupCB->setChecked(rc.ct_markup_copied);
2874         sortEnvironmentsCB->setChecked(rc.sort_layouts);
2875         groupEnvironmentsCB->setChecked(rc.group_layouts);
2876         macroEditStyleCO->setCurrentIndex(rc.macro_edit_style);
2877         cursorWidthSB->setValue(rc.cursor_width);
2878         citationSearchCB->setChecked(rc.citation_search);
2879         citationSearchLE->setText(toqstr(rc.citation_search_pattern));
2880         citationSearchLE->setEnabled(rc.citation_search);
2881         citationSearchLA->setEnabled(rc.citation_search);
2882         toggleScrollbarCB->setChecked(rc.full_screen_scrollbar);
2883         toggleStatusbarCB->setChecked(rc.full_screen_statusbar);
2884         toggleToolbarsCB->setChecked(rc.full_screen_toolbars);
2885         toggleTabbarCB->setChecked(rc.full_screen_tabbar);
2886         toggleMenubarCB->setChecked(rc.full_screen_menubar);
2887         fullscreenWidthSB->setValue(rc.full_screen_width);
2888         fullscreenLimitCB->setChecked(rc.full_screen_limit);
2889         fullscreenWidthSB->setEnabled(rc.full_screen_limit);
2890         fullscreenWidthLA->setEnabled(rc.full_screen_limit);
2891 }
2892
2893
2894 /////////////////////////////////////////////////////////////////////
2895 //
2896 // PrefShortcuts
2897 //
2898 /////////////////////////////////////////////////////////////////////
2899
2900
2901 GuiShortcutDialog::GuiShortcutDialog(QWidget * parent) : QDialog(parent)
2902 {
2903         Ui::shortcutUi::setupUi(this);
2904         QDialog::setModal(true);
2905         lfunLE->setValidator(new NoNewLineValidator(lfunLE));
2906 }
2907
2908
2909 PrefShortcuts::PrefShortcuts(GuiPreferences * form)
2910         : PrefModule(catEditing, N_("Shortcuts"), form),
2911           editItem_(nullptr), mathItem_(nullptr), bufferItem_(nullptr), layoutItem_(nullptr),
2912           systemItem_(nullptr)
2913 {
2914         setupUi(this);
2915
2916         shortcutsTW->setColumnCount(2);
2917         shortcutsTW->headerItem()->setText(0, qt_("Function"));
2918         shortcutsTW->headerItem()->setText(1, qt_("Shortcut"));
2919         shortcutsTW->setSortingEnabled(true);
2920         // Multi-selection can be annoying.
2921         // shortcutsTW->setSelectionMode(QAbstractItemView::MultiSelection);
2922
2923         connect(bindFilePB, SIGNAL(clicked()),
2924                 this, SLOT(selectBind()));
2925         connect(bindFileED, SIGNAL(textChanged(QString)),
2926                 this, SIGNAL(changed()));
2927
2928         shortcut_ = new GuiShortcutDialog(this);
2929         shortcut_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
2930         shortcut_bc_.setOK(shortcut_->buttonBox->button(QDialogButtonBox::Ok));
2931         shortcut_bc_.setCancel(shortcut_->buttonBox->button(QDialogButtonBox::Cancel));
2932
2933         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2934                 this, SIGNAL(changed()));
2935         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2936                 shortcut_, SLOT(reject()));
2937         connect(shortcut_->clearPB, SIGNAL(clicked()),
2938                 this, SLOT(shortcutClearPressed()));
2939         connect(shortcut_->removePB, SIGNAL(clicked()),
2940                 this, SLOT(shortcutRemovePressed()));
2941         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2942                 this, SLOT(shortcutOkPressed()));
2943         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2944                 this, SLOT(shortcutCancelPressed()));
2945 }
2946
2947
2948 void PrefShortcuts::applyRC(LyXRC & rc) const
2949 {
2950         rc.bind_file = internal_path(fromqstr(bindFileED->text()));
2951         // write user_bind and user_unbind to .lyx/bind/user.bind
2952         FileName bind_dir(addPath(package().user_support().absFileName(), "bind"));
2953         if (!bind_dir.exists() && !bind_dir.createDirectory(0777)) {
2954                 lyxerr << "LyX could not create the user bind directory '"
2955                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2956                 return;
2957         }
2958         if (!bind_dir.isDirWritable()) {
2959                 lyxerr << "LyX could not write to the user bind directory '"
2960                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2961                 return;
2962         }
2963         FileName user_bind_file(bind_dir.absFileName() + "/user.bind");
2964         user_unbind_.write(user_bind_file.toFilesystemEncoding(), false, true);
2965         user_bind_.write(user_bind_file.toFilesystemEncoding(), true, false);
2966         // immediately apply the keybindings. Why this is not done before?
2967         // The good thing is that the menus are updated automatically.
2968         theTopLevelKeymap().clear();
2969         theTopLevelKeymap().read("site");
2970         theTopLevelKeymap().read(rc.bind_file, nullptr, KeyMap::Fallback);
2971         theTopLevelKeymap().read("user", nullptr, KeyMap::MissingOK);
2972 }
2973
2974
2975 void PrefShortcuts::updateRC(LyXRC const & rc)
2976 {
2977         bindFileED->setText(toqstr(external_path(rc.bind_file)));
2978         //
2979         system_bind_.clear();
2980         user_bind_.clear();
2981         user_unbind_.clear();
2982         system_bind_.read("site");
2983         system_bind_.read(rc.bind_file);
2984         // \unbind in user.bind is added to user_unbind_
2985         user_bind_.read("user", &user_unbind_, KeyMap::MissingOK);
2986         updateShortcutsTW();
2987 }
2988
2989
2990 void PrefShortcuts::updateShortcutsTW()
2991 {
2992         shortcutsTW->clear();
2993
2994         editItem_ = new QTreeWidgetItem(shortcutsTW);
2995         editItem_->setText(0, qt_("Cursor, Mouse and Editing Functions"));
2996         editItem_->setFlags(editItem_->flags() & ~Qt::ItemIsSelectable);
2997
2998         mathItem_ = new QTreeWidgetItem(shortcutsTW);
2999         mathItem_->setText(0, qt_("Mathematical Symbols"));
3000         mathItem_->setFlags(mathItem_->flags() & ~Qt::ItemIsSelectable);
3001
3002         bufferItem_ = new QTreeWidgetItem(shortcutsTW);
3003         bufferItem_->setText(0, qt_("Document and Window"));
3004         bufferItem_->setFlags(bufferItem_->flags() & ~Qt::ItemIsSelectable);
3005
3006         layoutItem_ = new QTreeWidgetItem(shortcutsTW);
3007         layoutItem_->setText(0, qt_("Font, Layouts and Textclasses"));
3008         layoutItem_->setFlags(layoutItem_->flags() & ~Qt::ItemIsSelectable);
3009
3010         systemItem_ = new QTreeWidgetItem(shortcutsTW);
3011         systemItem_->setText(0, qt_("System and Miscellaneous"));
3012         systemItem_->setFlags(systemItem_->flags() & ~Qt::ItemIsSelectable);
3013
3014         // listBindings(unbound=true) lists all bound and unbound lfuns
3015         // Items in this list is tagged by its source.
3016         KeyMap::BindingList bindinglist = system_bind_.listBindings(true,
3017                 KeyMap::System);
3018         KeyMap::BindingList user_bindinglist = user_bind_.listBindings(false,
3019                 KeyMap::UserBind);
3020         KeyMap::BindingList user_unbindinglist = user_unbind_.listBindings(false,
3021                 KeyMap::UserUnbind);
3022         bindinglist.insert(bindinglist.end(), user_bindinglist.begin(),
3023                         user_bindinglist.end());
3024         bindinglist.insert(bindinglist.end(), user_unbindinglist.begin(),
3025                         user_unbindinglist.end());
3026
3027         KeyMap::BindingList::const_iterator it = bindinglist.begin();
3028         KeyMap::BindingList::const_iterator it_end = bindinglist.end();
3029         for (; it != it_end; ++it)
3030                 insertShortcutItem(it->request, it->sequence, it->tag);
3031
3032         shortcutsTW->sortItems(0, Qt::AscendingOrder);
3033         on_shortcutsTW_itemSelectionChanged();
3034         on_searchLE_textEdited();
3035         shortcutsTW->resizeColumnToContents(0);
3036 }
3037
3038
3039 //static
3040 KeyMap::ItemType PrefShortcuts::itemType(QTreeWidgetItem & item)
3041 {
3042         return static_cast<KeyMap::ItemType>(item.data(0, Qt::UserRole).toInt());
3043 }
3044
3045
3046 //static
3047 bool PrefShortcuts::isAlwaysHidden(QTreeWidgetItem & item)
3048 {
3049         // Hide rebound system settings that are empty
3050         return itemType(item) == KeyMap::UserUnbind && item.text(1).isEmpty();
3051 }
3052
3053
3054 void PrefShortcuts::setItemType(QTreeWidgetItem * item, KeyMap::ItemType tag)
3055 {
3056         item->setData(0, Qt::UserRole, QVariant(tag));
3057         QFont font;
3058
3059         switch (tag) {
3060         case KeyMap::System:
3061                 break;
3062         case KeyMap::UserBind:
3063                 font.setBold(true);
3064                 break;
3065         case KeyMap::UserUnbind:
3066                 font.setStrikeOut(true);
3067                 break;
3068         // this item is not displayed now.
3069         case KeyMap::UserExtraUnbind:
3070                 font.setStrikeOut(true);
3071                 break;
3072         }
3073         item->setHidden(isAlwaysHidden(*item));
3074         item->setFont(1, font);
3075 }
3076
3077
3078 QTreeWidgetItem * PrefShortcuts::insertShortcutItem(FuncRequest const & lfun,
3079                 KeySequence const & seq, KeyMap::ItemType tag)
3080 {
3081         FuncCode const action = lfun.action();
3082         string const action_name = lyxaction.getActionName(action);
3083         QString const lfun_name = toqstr(from_utf8(action_name)
3084                         + ' ' + lfun.argument());
3085         QString const shortcut = toqstr(seq.print(KeySequence::ForGui));
3086
3087         QTreeWidgetItem * newItem = nullptr;
3088         // for unbind items, try to find an existing item in the system bind list
3089         if (tag == KeyMap::UserUnbind) {
3090                 QList<QTreeWidgetItem*> const items = shortcutsTW->findItems(shortcut,
3091                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 1);
3092                 for (auto const & item : items) {
3093                         if (item->text(0) == lfun_name || lfun == FuncRequest::unknown) {
3094                                 newItem = item;
3095                                 break;
3096                         }
3097                 }
3098                 // if not found, this unbind item is KeyMap::UserExtraUnbind
3099                 // Such an item is not displayed to avoid confusion (what is
3100                 // unmatched removed?).
3101                 if (!newItem) {
3102                         return nullptr;
3103                 }
3104         }
3105         if (!newItem) {
3106                 switch(lyxaction.getActionType(action)) {
3107                 case LyXAction::Hidden:
3108                         return nullptr;
3109                 case LyXAction::Edit:
3110                         newItem = new QTreeWidgetItem(editItem_);
3111                         break;
3112                 case LyXAction::Math:
3113                         newItem = new QTreeWidgetItem(mathItem_);
3114                         break;
3115                 case LyXAction::Buffer:
3116                         newItem = new QTreeWidgetItem(bufferItem_);
3117                         break;
3118                 case LyXAction::Layout:
3119                         newItem = new QTreeWidgetItem(layoutItem_);
3120                         break;
3121                 case LyXAction::System:
3122                         newItem = new QTreeWidgetItem(systemItem_);
3123                         break;
3124                 default:
3125                         // this should not happen
3126                         newItem = new QTreeWidgetItem(shortcutsTW);
3127                 }
3128                 newItem->setText(0, lfun_name);
3129                 newItem->setText(1, shortcut);
3130         }
3131
3132         // record BindFile representation to recover KeySequence when needed.
3133         newItem->setData(1, Qt::UserRole, toqstr(seq.print(KeySequence::BindFile)));
3134         setItemType(newItem, tag);
3135         return newItem;
3136 }
3137
3138
3139 void PrefShortcuts::on_shortcutsTW_itemSelectionChanged()
3140 {
3141         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3142         removePB->setEnabled(!items.isEmpty() && !items[0]->text(1).isEmpty());
3143         modifyPB->setEnabled(!items.isEmpty());
3144         if (items.isEmpty())
3145                 return;
3146
3147         if (itemType(*items[0]) == KeyMap::UserUnbind)
3148                 removePB->setText(qt_("Res&tore"));
3149         else
3150                 removePB->setText(qt_("Remo&ve"));
3151 }
3152
3153
3154 void PrefShortcuts::on_shortcutsTW_itemDoubleClicked()
3155 {
3156         modifyShortcut();
3157 }
3158
3159
3160 void PrefShortcuts::modifyShortcut()
3161 {
3162         QTreeWidgetItem * item = shortcutsTW->currentItem();
3163         if (item->flags() & Qt::ItemIsSelectable) {
3164                 shortcut_->lfunLE->setText(item->text(0));
3165                 save_lfun_ = item->text(0).trimmed();
3166                 shortcut_->shortcutWG->setText(item->text(1));
3167                 KeySequence seq;
3168                 seq.parse(fromqstr(item->data(1, Qt::UserRole).toString()));
3169                 shortcut_->shortcutWG->setKeySequence(seq);
3170                 shortcut_->shortcutWG->setFocus();
3171                 shortcut_->exec();
3172         }
3173 }
3174
3175
3176 void PrefShortcuts::unhideEmpty(QString const & lfun, bool select)
3177 {
3178         // list of items that match lfun
3179         QList<QTreeWidgetItem*> items = shortcutsTW->findItems(lfun,
3180              Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
3181         for (auto const & item : items) {
3182                 if (isAlwaysHidden(*item)) {
3183                         setItemType(item, KeyMap::System);
3184                         if (select)
3185                                 shortcutsTW->setCurrentItem(item);
3186                         return;
3187                 }
3188         }
3189 }
3190
3191
3192 void PrefShortcuts::removeShortcut()
3193 {
3194         // it seems that only one item can be selected, but I am
3195         // removing all selected items anyway.
3196         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3197         for (auto & item : items) {
3198                 string shortcut = fromqstr(item->data(1, Qt::UserRole).toString());
3199                 string lfun = fromqstr(item->text(0));
3200                 FuncRequest const func = lyxaction.lookupFunc(lfun);
3201
3202                 switch (itemType(*item)) {
3203                 case KeyMap::System: {
3204                         // for system bind, we do not touch the item
3205                         // but add an user unbind item
3206                         user_unbind_.bind(shortcut, func);
3207                         setItemType(item, KeyMap::UserUnbind);
3208                         removePB->setText(qt_("Res&tore"));
3209                         break;
3210                 }
3211                 case KeyMap::UserBind: {
3212                         // for user_bind, we remove this bind
3213                         QTreeWidgetItem * parent = item->parent();
3214                         int itemIdx = parent->indexOfChild(item);
3215                         parent->takeChild(itemIdx);
3216                         if (itemIdx > 0)
3217                                 shortcutsTW->scrollToItem(parent->child(itemIdx - 1));
3218                         else
3219                                 shortcutsTW->scrollToItem(parent);
3220                         user_bind_.unbind(shortcut, func);
3221                         // If this user binding hid an empty system binding, unhide the
3222                         // latter and select it.
3223                         unhideEmpty(item->text(0), true);
3224                         break;
3225                 }
3226                 case KeyMap::UserUnbind: {
3227                         // for user_unbind, we remove the unbind, and the item
3228                         // become KeyMap::System again.
3229                         KeySequence seq;
3230                         seq.parse(shortcut);
3231                         // Ask the user to replace current binding
3232                         if (!validateNewShortcut(func, seq, QString()))
3233                                 break;
3234                         user_unbind_.unbind(shortcut, func);
3235                         setItemType(item, KeyMap::System);
3236                         removePB->setText(qt_("Remo&ve"));
3237                         break;
3238                 }
3239                 case KeyMap::UserExtraUnbind: {
3240                         // for user unbind that is not in system bind file,
3241                         // remove this unbind file
3242                         QTreeWidgetItem * parent = item->parent();
3243                         parent->takeChild(parent->indexOfChild(item));
3244                         user_unbind_.unbind(shortcut, func);
3245                 }
3246                 }
3247         }
3248 }
3249
3250
3251 void PrefShortcuts::deactivateShortcuts(QList<QTreeWidgetItem*> const & items)
3252 {
3253         for (auto item : items) {
3254                 string shortcut = fromqstr(item->data(1, Qt::UserRole).toString());
3255                 string lfun = fromqstr(item->text(0));
3256                 FuncRequest const func = lyxaction.lookupFunc(lfun);
3257
3258                 switch (itemType(*item)) {
3259                 case KeyMap::System:
3260                         // for system bind, we do not touch the item
3261                         // but add an user unbind item
3262                         user_unbind_.bind(shortcut, func);
3263                         setItemType(item, KeyMap::UserUnbind);
3264                         break;
3265
3266                 case KeyMap::UserBind: {
3267                         // for user_bind, we remove this bind
3268                         QTreeWidgetItem * parent = item->parent();
3269                         int itemIdx = parent->indexOfChild(item);
3270                         parent->takeChild(itemIdx);
3271                         user_bind_.unbind(shortcut, func);
3272                         unhideEmpty(item->text(0), false);
3273                         break;
3274                 }
3275                 default:
3276                         break;
3277                 }
3278         }
3279 }
3280
3281
3282 void PrefShortcuts::selectBind()
3283 {
3284         QString file = form_->browsebind(internalPath(bindFileED->text()));
3285         if (!file.isEmpty()) {
3286                 bindFileED->setText(file);
3287                 system_bind_ = KeyMap();
3288                 system_bind_.read(fromqstr(file));
3289                 updateShortcutsTW();
3290         }
3291 }
3292
3293
3294 void PrefShortcuts::on_modifyPB_pressed()
3295 {
3296         modifyShortcut();
3297 }
3298
3299
3300 void PrefShortcuts::on_newPB_pressed()
3301 {
3302         shortcut_->lfunLE->clear();
3303         shortcut_->shortcutWG->reset();
3304         save_lfun_ = QString();
3305         shortcut_->exec();
3306 }
3307
3308
3309 void PrefShortcuts::on_removePB_pressed()
3310 {
3311         changed();
3312         removeShortcut();
3313 }
3314
3315
3316 void PrefShortcuts::on_searchLE_textEdited()
3317 {
3318         if (searchLE->text().isEmpty()) {
3319                 // show all hidden items
3320                 QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Hidden);
3321                 for (; *it; ++it)
3322                         (*it)->setHidden(isAlwaysHidden(**it));
3323                 // close all categories
3324                 for (int i = 0; i < shortcutsTW->topLevelItemCount(); ++i)
3325                         shortcutsTW->collapseItem(shortcutsTW->topLevelItem(i));
3326                 return;
3327         }
3328         // search both columns
3329         QList<QTreeWidgetItem *> matched = shortcutsTW->findItems(searchLE->text(),
3330                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 0);
3331         matched += shortcutsTW->findItems(searchLE->text(),
3332                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 1);
3333
3334         // hide everyone (to avoid searching in matched QList repeatedly
3335         QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Selectable);
3336         while (*it)
3337                 (*it++)->setHidden(true);
3338         // show matched items
3339         for (auto & item : matched)
3340                 if (!isAlwaysHidden(*item)) {
3341                         item->setHidden(false);
3342                         if (item->parent())
3343                                 item->parent()->setExpanded(true);
3344                 }
3345 }
3346
3347
3348 docstring makeCmdString(FuncRequest const & f)
3349 {
3350         docstring actionStr = from_ascii(lyxaction.getActionName(f.action()));
3351         if (!f.argument().empty())
3352                 actionStr += " " + f.argument();
3353         return actionStr;
3354 }
3355
3356
3357 FuncRequest PrefShortcuts::currentBinding(KeySequence const & k)
3358 {
3359         FuncRequest res = user_bind_.getBinding(k);
3360         if (res.action() != LFUN_UNKNOWN_ACTION)
3361                 return res;
3362         res = system_bind_.getBinding(k);
3363         // Check if it is unbound. Note: user_unbind_ can only unbind one
3364         // FuncRequest per key sequence.
3365         if (user_unbind_.getBinding(k) == res)
3366                 return FuncRequest::unknown;
3367         return res;
3368 }
3369
3370
3371 bool PrefShortcuts::validateNewShortcut(FuncRequest const & func,
3372                                         KeySequence const & k,
3373                                         QString const & lfun_to_modify)
3374 {
3375         if (func.action() == LFUN_UNKNOWN_ACTION) {
3376                 Alert::error(_("Failed to create shortcut"),
3377                         _("Unknown or invalid LyX function"));
3378                 return false;
3379         }
3380
3381         // It is not currently possible to bind Hidden lfuns such as self-insert. In
3382         // the future, to remove this limitation, see GuiPrefs::insertShortcutItem
3383         // and how it is used in GuiPrefs::shortcutOkPressed.
3384         if (lyxaction.getActionType(func.action()) == LyXAction::Hidden) {
3385                 Alert::error(_("Failed to create shortcut"),
3386                         _("This LyX function is hidden and cannot be bound."));
3387                 return false;
3388         }
3389
3390         if (k.length() == 0) {
3391                 Alert::error(_("Failed to create shortcut"),
3392                         _("Invalid or empty key sequence"));
3393                 return false;
3394         }
3395
3396         FuncRequest oldBinding = currentBinding(k);
3397         if (oldBinding == func)
3398                 // nothing to change
3399                 return false;
3400
3401         // make sure this key isn't already bound---and, if so, prompt user
3402         // (exclude the lfun the user already wants to modify)
3403         docstring const action_string = makeCmdString(oldBinding);
3404         if (oldBinding.action() != LFUN_UNKNOWN_ACTION
3405             && lfun_to_modify != toqstr(action_string)) {
3406                 docstring const new_action_string = makeCmdString(func);
3407                 docstring const text = bformat(_("Shortcut `%1$s' is already bound to "
3408                                                  "%2$s.\n"
3409                                                  "Are you sure you want to unbind the "
3410                                                  "current shortcut and bind it to %3$s?"),
3411                                                k.print(KeySequence::ForGui), action_string,
3412                                                new_action_string);
3413                 int ret = Alert::prompt(_("Redefine shortcut?"),
3414                                         text, 0, 1, _("&Redefine"), _("&Cancel"));
3415                 if (ret != 0)
3416                         return false;
3417                 QString const sequence_text = toqstr(k.print(KeySequence::ForGui));
3418                 QList<QTreeWidgetItem*> items = shortcutsTW->findItems(sequence_text,
3419                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 1);
3420                 deactivateShortcuts(items);
3421         }
3422         return true;
3423 }
3424
3425
3426 void PrefShortcuts::shortcutOkPressed()
3427 {
3428         QString const new_lfun = shortcut_->lfunLE->text();
3429         FuncRequest const func = lyxaction.lookupFunc(fromqstr(new_lfun));
3430         KeySequence k = shortcut_->shortcutWG->getKeySequence();
3431
3432         // save_lfun_ contains the text of the lfun to modify, if the user clicked
3433         // "modify", or is empty if they clicked "new" (which I do not really like)
3434         if (!validateNewShortcut(func, k, save_lfun_))
3435                 return;
3436
3437         if (!save_lfun_.isEmpty()) {
3438                 // real modification of the lfun's shortcut,
3439                 // so remove the previous one
3440                 QList<QTreeWidgetItem*> to_modify = shortcutsTW->selectedItems();
3441                 deactivateShortcuts(to_modify);
3442         }
3443
3444         shortcut_->accept();
3445
3446         QTreeWidgetItem * item = insertShortcutItem(func, k, KeyMap::UserBind);
3447         if (item) {
3448                 user_bind_.bind(&k, func);
3449                 shortcutsTW->sortItems(0, Qt::AscendingOrder);
3450                 if (item->parent())
3451                         item->parent()->setExpanded(true);
3452                 shortcutsTW->setCurrentItem(item);
3453                 shortcutsTW->scrollToItem(item);
3454         } else {
3455                 Alert::error(_("Failed to create shortcut"),
3456                         _("Can not insert shortcut to the list"));
3457                 return;
3458         }
3459 }
3460
3461
3462 void PrefShortcuts::shortcutCancelPressed()
3463 {
3464         shortcut_->shortcutWG->reset();
3465 }
3466
3467
3468 void PrefShortcuts::shortcutClearPressed()
3469 {
3470         shortcut_->shortcutWG->reset();
3471 }
3472
3473
3474 void PrefShortcuts::shortcutRemovePressed()
3475 {
3476         shortcut_->shortcutWG->removeFromSequence();
3477 }
3478
3479
3480 /////////////////////////////////////////////////////////////////////
3481 //
3482 // PrefIdentity
3483 //
3484 /////////////////////////////////////////////////////////////////////
3485
3486 PrefIdentity::PrefIdentity(GuiPreferences * form)
3487         : PrefModule(QString(), N_("Identity"), form)
3488 {
3489         setupUi(this);
3490
3491         connect(nameED, SIGNAL(textChanged(QString)),
3492                 this, SIGNAL(changed()));
3493         connect(emailED, SIGNAL(textChanged(QString)),
3494                 this, SIGNAL(changed()));
3495         connect(initialsED, SIGNAL(textChanged(QString)),
3496                 this, SIGNAL(changed()));
3497
3498         nameED->setValidator(new NoNewLineValidator(nameED));
3499         emailED->setValidator(new NoNewLineValidator(emailED));
3500         initialsED->setValidator(new NoNewLineValidator(initialsED));
3501 }
3502
3503
3504 void PrefIdentity::applyRC(LyXRC & rc) const
3505 {
3506         rc.user_name = fromqstr(nameED->text());
3507         rc.user_email = fromqstr(emailED->text());
3508         rc.user_initials = fromqstr(initialsED->text());
3509 }
3510
3511
3512 void PrefIdentity::updateRC(LyXRC const & rc)
3513 {
3514         nameED->setText(toqstr(rc.user_name));
3515         emailED->setText(toqstr(rc.user_email));
3516         initialsED->setText(toqstr(rc.user_initials));
3517 }
3518
3519
3520
3521 /////////////////////////////////////////////////////////////////////
3522 //
3523 // GuiPreferences
3524 //
3525 /////////////////////////////////////////////////////////////////////
3526
3527 GuiPreferences::GuiPreferences(GuiView & lv)
3528         : GuiDialog(lv, "prefs", qt_("Preferences"))
3529 {
3530         setupUi(this);
3531
3532         QDialog::setModal(false);
3533
3534         connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
3535                 this, SLOT(slotButtonBox(QAbstractButton *)));
3536
3537         addModule(new PrefUserInterface(this));
3538         addModule(new PrefDocHandling(this));
3539         addModule(new PrefEdit(this));
3540         addModule(new PrefShortcuts(this));
3541         PrefScreenFonts * screenfonts = new PrefScreenFonts(this);
3542         connect(this, SIGNAL(prefsApplied(LyXRC const &)),
3543                         screenfonts, SLOT(updateScreenFontSizes(LyXRC const &)));
3544         addModule(screenfonts);
3545         addModule(new PrefColors(this));
3546         addModule(new PrefDisplay(this));
3547         addModule(new PrefInput(this));
3548         addModule(new PrefCompletion(this));
3549
3550         addModule(new PrefPaths(this));
3551
3552         addModule(new PrefIdentity(this));
3553
3554         addModule(new PrefLanguage(this));
3555         addModule(new PrefSpellchecker(this));
3556
3557         PrefOutput * output = new PrefOutput(this);
3558         addModule(output);
3559         addModule(new PrefLatex(this));
3560
3561         PrefConverters * converters = new PrefConverters(this);
3562         PrefFileformats * formats = new PrefFileformats(this);
3563         connect(formats, SIGNAL(formatsChanged()),
3564                         converters, SLOT(updateGui()));
3565         addModule(converters);
3566         addModule(formats);
3567
3568         prefsPS->setCurrentPanel("User Interface");
3569 // FIXME: hack to work around resizing bug in Qt >= 4.2
3570 // bug verified with Qt 4.2.{0-3} (JSpitzm)
3571 #if QT_VERSION >= 0x040200
3572         prefsPS->updateGeometry();
3573 #endif
3574
3575         bc().setPolicy(ButtonPolicy::PreferencesPolicy);
3576         bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
3577         bc().setApply(buttonBox->button(QDialogButtonBox::Apply));
3578         bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
3579         bc().setRestore(buttonBox->button(QDialogButtonBox::Reset));
3580
3581         guilyxfiles_ = new GuiLyXFiles(lv);
3582         connect(guilyxfiles_, SIGNAL(fileSelected(QString)),
3583                         this, SLOT(slotFileSelected(QString)));
3584 }
3585
3586
3587 void GuiPreferences::addModule(PrefModule * module)
3588 {
3589         LASSERT(module, return);
3590         if (module->category().isEmpty())
3591                 prefsPS->addPanel(module, module->title());
3592         else
3593                 prefsPS->addPanel(module, module->title(), module->category());
3594         connect(module, SIGNAL(changed()), this, SLOT(change_adaptor()));
3595         modules_.push_back(module);
3596 }
3597
3598
3599 void GuiPreferences::change_adaptor()
3600 {
3601         changed();
3602 }
3603
3604
3605 void GuiPreferences::applyRC(LyXRC & rc) const
3606 {
3607         size_t end = modules_.size();
3608         for (size_t i = 0; i != end; ++i)
3609                 modules_[i]->applyRC(rc);
3610 }
3611
3612
3613 void GuiPreferences::updateRC(LyXRC const & rc)
3614 {
3615         size_t const end = modules_.size();
3616         for (size_t i = 0; i != end; ++i)
3617                 modules_[i]->updateRC(rc);
3618 }
3619
3620
3621 void GuiPreferences::applyView()
3622 {
3623         applyRC(rc());
3624 }
3625
3626
3627 bool GuiPreferences::initialiseParams(string const &)
3628 {
3629         rc_ = lyxrc;
3630         formats_ = theFormats();
3631         converters_ = theConverters();
3632         converters_.update(formats_);
3633         movers_ = theMovers();
3634         colors_.clear();
3635
3636         updateRC(rc_);
3637         // Make sure that the bc is in the INITIAL state
3638         if (bc().policy().buttonStatus(ButtonPolicy::RESTORE))
3639                 bc().restore();
3640
3641         return true;
3642 }
3643
3644
3645 void GuiPreferences::dispatchParams()
3646 {
3647         ostringstream ss;
3648         rc_.write(ss, true);
3649         dispatch(FuncRequest(LFUN_LYXRC_APPLY, ss.str()));
3650         // issue prefsApplied signal. This will update the
3651         // localized screen font sizes.
3652         prefsApplied(rc_);
3653         // FIXME: these need lfuns
3654         // FIXME UNICODE
3655         Author const & author =
3656                 Author(from_utf8(rc_.user_name), from_utf8(rc_.user_email),
3657                        from_utf8(rc_.user_initials));
3658         theBufferList().recordCurrentAuthor(author);
3659
3660         theFormats() = formats_;
3661
3662         theConverters() = converters_;
3663         theConverters().update(formats_);
3664         theConverters().buildGraph();
3665         theBufferList().invalidateConverterCache();
3666
3667         theMovers() = movers_;
3668
3669         for (string const & color : colors_)
3670                 dispatch(FuncRequest(LFUN_SET_COLOR, color));
3671         colors_.clear();
3672
3673         // Save permanently
3674         if (!tempSaveCB->isChecked())
3675                 dispatch(FuncRequest(LFUN_PREFERENCES_SAVE));
3676 }
3677
3678
3679 void GuiPreferences::setColor(ColorCode col, QString const & hex)
3680 {
3681         colors_.push_back(lcolor.getLyXName(col) + ' ' + fromqstr(hex));
3682 }
3683
3684
3685 void GuiPreferences::slotFileSelected(QString const file)
3686 {
3687         uifile_ = file;
3688 }
3689
3690
3691 QString GuiPreferences::browseLibFile(QString const & dir,
3692         QString const & name, QString const & ext)
3693 {
3694         uifile_.clear();
3695
3696         guilyxfiles_->passParams(fromqstr(dir));
3697         guilyxfiles_->selectItem(name);
3698         guilyxfiles_->exec();
3699
3700         QString const result = uifile_;
3701
3702         // remove the extension if it is the default one
3703         QString noextresult;
3704         if (getExtension(result) == ext)
3705                 noextresult = removeExtension(result);
3706         else
3707                 noextresult = result;
3708
3709         // remove the directory, if it is the default one
3710         QString const file = onlyFileName(noextresult);
3711         if (toqstr(libFileSearch(dir, file, ext).absFileName()) == result)
3712                 return file;
3713         else
3714                 return noextresult;
3715 }
3716
3717
3718 QString GuiPreferences::browsebind(QString const & file)
3719 {
3720         return browseLibFile("bind", file, "bind");
3721 }
3722
3723
3724 QString GuiPreferences::browseUI(QString const & file)
3725 {
3726         return browseLibFile("ui", file, "ui");
3727 }
3728
3729
3730 QString GuiPreferences::browsekbmap(QString const & file)
3731 {
3732         return browseLibFile("kbd", file, "kmap");
3733 }
3734
3735
3736 QString GuiPreferences::browse(QString const & file,
3737         QString const & title) const
3738 {
3739         return browseFile(file, title, QStringList(), true);
3740 }
3741
3742
3743 } // namespace frontend
3744 } // namespace lyx
3745
3746 #include "moc_GuiPrefs.cpp"