]> git.lyx.org Git - lyx.git/blob - src/frontends/qt/GuiPrefs.cpp
Qt6 QFontDatabase is a pure static class. Call the member functions directly.
[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         connect(ctUseBackingstoreCB, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
1332 }
1333
1334
1335 void PrefDisplay::on_instantPreviewCO_currentIndexChanged(int index)
1336 {
1337         previewSizeSB->setEnabled(index != 0);
1338 }
1339
1340
1341 void PrefDisplay::applyRC(LyXRC & rc) const
1342 {
1343         switch (instantPreviewCO->currentIndex()) {
1344                 case 0:
1345                         rc.preview = LyXRC::PREVIEW_OFF;
1346                         break;
1347                 case 1:
1348                         rc.preview = LyXRC::PREVIEW_NO_MATH;
1349                         break;
1350                 case 2:
1351                         rc.preview = LyXRC::PREVIEW_ON;
1352                         break;
1353         }
1354
1355         rc.display_graphics = displayGraphicsCB->isChecked();
1356         rc.preview_scale_factor = previewSizeSB->value();
1357         rc.paragraph_markers = paragraphMarkerCB->isChecked();
1358         rc.ct_additions_underlined = ctAdditionsUnderlinedCB->isChecked();
1359         rc.draw_strategy = ctUseBackingstoreCB->isChecked()
1360                 ? LyXRC::DS_BACKINGSTORE : LyXRC::DS_PARTIAL;
1361
1362         // FIXME!! The graphics cache no longer has a changeDisplay method.
1363 #if 0
1364         if (old_value != rc.display_graphics) {
1365                 graphics::GCache & gc = graphics::GCache::get();
1366                 gc.changeDisplay();
1367         }
1368 #endif
1369 }
1370
1371
1372 void PrefDisplay::updateRC(LyXRC const & rc)
1373 {
1374         switch (rc.preview) {
1375         case LyXRC::PREVIEW_OFF:
1376                 instantPreviewCO->setCurrentIndex(0);
1377                 break;
1378         case LyXRC::PREVIEW_NO_MATH :
1379                 instantPreviewCO->setCurrentIndex(1);
1380                 break;
1381         case LyXRC::PREVIEW_ON :
1382                 instantPreviewCO->setCurrentIndex(2);
1383                 break;
1384         }
1385
1386         displayGraphicsCB->setChecked(rc.display_graphics);
1387         previewSizeSB->setValue(rc.preview_scale_factor);
1388         paragraphMarkerCB->setChecked(rc.paragraph_markers);
1389         ctAdditionsUnderlinedCB->setChecked(rc.ct_additions_underlined);
1390         ctUseBackingstoreCB->setChecked(rc.draw_strategy == LyXRC::DS_BACKINGSTORE);
1391         ctUseBackingstoreCB->setHidden(guiApp->needsBackingStore());
1392         previewSizeSB->setEnabled(
1393                 rc.display_graphics
1394                 && rc.preview != LyXRC::PREVIEW_OFF);
1395 }
1396
1397
1398 /////////////////////////////////////////////////////////////////////
1399 //
1400 // PrefPaths
1401 //
1402 /////////////////////////////////////////////////////////////////////
1403
1404 PrefPaths::PrefPaths(GuiPreferences * form)
1405         : PrefModule(QString(), N_("Paths"), form)
1406 {
1407         setupUi(this);
1408
1409         connect(workingDirPB, SIGNAL(clicked()), this, SLOT(selectWorkingdir()));
1410         connect(workingDirED, SIGNAL(textChanged(QString)),
1411                 this, SIGNAL(changed()));
1412
1413         connect(templateDirPB, SIGNAL(clicked()), this, SLOT(selectTemplatedir()));
1414         connect(templateDirED, SIGNAL(textChanged(QString)),
1415                 this, SIGNAL(changed()));
1416
1417         connect(exampleDirPB, SIGNAL(clicked()), this, SLOT(selectExampledir()));
1418         connect(exampleDirED, SIGNAL(textChanged(QString)),
1419                 this, SIGNAL(changed()));
1420
1421         connect(backupDirPB, SIGNAL(clicked()), this, SLOT(selectBackupdir()));
1422         connect(backupDirED, SIGNAL(textChanged(QString)),
1423                 this, SIGNAL(changed()));
1424
1425         connect(lyxserverDirPB, SIGNAL(clicked()), this, SLOT(selectLyxPipe()));
1426         connect(lyxserverDirED, SIGNAL(textChanged(QString)),
1427                 this, SIGNAL(changed()));
1428
1429         connect(thesaurusDirPB, SIGNAL(clicked()), this, SLOT(selectThesaurusdir()));
1430         connect(thesaurusDirED, SIGNAL(textChanged(QString)),
1431                 this, SIGNAL(changed()));
1432
1433         connect(tempDirPB, SIGNAL(clicked()), this, SLOT(selectTempdir()));
1434         connect(tempDirED, SIGNAL(textChanged(QString)),
1435                 this, SIGNAL(changed()));
1436
1437 #if defined(USE_HUNSPELL)
1438         connect(hunspellDirPB, SIGNAL(clicked()), this, SLOT(selectHunspelldir()));
1439         connect(hunspellDirED, SIGNAL(textChanged(QString)),
1440                 this, SIGNAL(changed()));
1441 #else
1442         hunspellDirPB->setEnabled(false);
1443         hunspellDirED->setEnabled(false);
1444 #endif
1445
1446         connect(pathPrefixED, SIGNAL(textChanged(QString)),
1447                 this, SIGNAL(changed()));
1448
1449         connect(texinputsPrefixED, SIGNAL(textChanged(QString)),
1450                 this, SIGNAL(changed()));
1451
1452         pathPrefixED->setValidator(new NoNewLineValidator(pathPrefixED));
1453         texinputsPrefixED->setValidator(new NoNewLineValidator(texinputsPrefixED));
1454 }
1455
1456
1457 void PrefPaths::applyRC(LyXRC & rc) const
1458 {
1459         rc.document_path = internal_path(fromqstr(workingDirED->text()));
1460         rc.example_path = internal_path(fromqstr(exampleDirED->text()));
1461         rc.template_path = internal_path(fromqstr(templateDirED->text()));
1462         rc.backupdir_path = internal_path(fromqstr(backupDirED->text()));
1463         rc.tempdir_path = internal_path(fromqstr(tempDirED->text()));
1464         rc.thesaurusdir_path = internal_path(fromqstr(thesaurusDirED->text()));
1465         rc.hunspelldir_path = internal_path(fromqstr(hunspellDirED->text()));
1466         rc.path_prefix = internal_path_list(fromqstr(pathPrefixED->text()));
1467         rc.texinputs_prefix = internal_path_list(fromqstr(texinputsPrefixED->text()));
1468         // FIXME: should be a checkbox only
1469         rc.lyxpipes = internal_path(fromqstr(lyxserverDirED->text()));
1470 }
1471
1472
1473 void PrefPaths::updateRC(LyXRC const & rc)
1474 {
1475         workingDirED->setText(toqstr(external_path(rc.document_path)));
1476         exampleDirED->setText(toqstr(external_path(rc.example_path)));
1477         templateDirED->setText(toqstr(external_path(rc.template_path)));
1478         backupDirED->setText(toqstr(external_path(rc.backupdir_path)));
1479         tempDirED->setText(toqstr(external_path(rc.tempdir_path)));
1480         thesaurusDirED->setText(toqstr(external_path(rc.thesaurusdir_path)));
1481         hunspellDirED->setText(toqstr(external_path(rc.hunspelldir_path)));
1482         pathPrefixED->setText(toqstr(external_path_list(rc.path_prefix)));
1483         texinputsPrefixED->setText(toqstr(external_path_list(rc.texinputs_prefix)));
1484         // FIXME: should be a checkbox only
1485         lyxserverDirED->setText(toqstr(external_path(rc.lyxpipes)));
1486 }
1487
1488
1489 void PrefPaths::selectExampledir()
1490 {
1491         QString file = browseDir(internalPath(exampleDirED->text()),
1492                 qt_("Select directory for example files"));
1493         if (!file.isEmpty())
1494                 exampleDirED->setText(file);
1495 }
1496
1497
1498 void PrefPaths::selectTemplatedir()
1499 {
1500         QString file = browseDir(internalPath(templateDirED->text()),
1501                 qt_("Select a document templates directory"));
1502         if (!file.isEmpty())
1503                 templateDirED->setText(file);
1504 }
1505
1506
1507 void PrefPaths::selectTempdir()
1508 {
1509         QString file = browseDir(internalPath(tempDirED->text()),
1510                 qt_("Select a temporary directory"));
1511         if (!file.isEmpty())
1512                 tempDirED->setText(file);
1513 }
1514
1515
1516 void PrefPaths::selectBackupdir()
1517 {
1518         QString file = browseDir(internalPath(backupDirED->text()),
1519                 qt_("Select a backups directory"));
1520         if (!file.isEmpty())
1521                 backupDirED->setText(file);
1522 }
1523
1524
1525 void PrefPaths::selectWorkingdir()
1526 {
1527         QString file = browseDir(internalPath(workingDirED->text()),
1528                 qt_("Select a document directory"));
1529         if (!file.isEmpty())
1530                 workingDirED->setText(file);
1531 }
1532
1533
1534 void PrefPaths::selectThesaurusdir()
1535 {
1536         QString file = browseDir(internalPath(thesaurusDirED->text()),
1537                 qt_("Set the path to the thesaurus dictionaries"));
1538         if (!file.isEmpty())
1539                 thesaurusDirED->setText(file);
1540 }
1541
1542
1543 void PrefPaths::selectHunspelldir()
1544 {
1545         QString file = browseDir(internalPath(hunspellDirED->text()),
1546                 qt_("Set the path to the Hunspell dictionaries"));
1547         if (!file.isEmpty())
1548                 hunspellDirED->setText(file);
1549 }
1550
1551
1552 void PrefPaths::selectLyxPipe()
1553 {
1554         QString file = form_->browse(internalPath(lyxserverDirED->text()),
1555                 qt_("Give a filename for the LyX server pipe"));
1556         if (!file.isEmpty())
1557                 lyxserverDirED->setText(file);
1558 }
1559
1560
1561 /////////////////////////////////////////////////////////////////////
1562 //
1563 // PrefSpellchecker
1564 //
1565 /////////////////////////////////////////////////////////////////////
1566
1567 PrefSpellchecker::PrefSpellchecker(GuiPreferences * form)
1568         : PrefModule(catLanguage, N_("Spellchecker"), form)
1569 {
1570         setupUi(this);
1571
1572 // FIXME: this check should test the target platform (darwin)
1573 #if defined(USE_MACOSX_PACKAGING)
1574         spellcheckerCB->addItem(qt_("Native"), QString("native"));
1575 #define CONNECT_APPLESPELL
1576 #else
1577 #undef CONNECT_APPLESPELL
1578 #endif
1579 #if defined(USE_ASPELL)
1580         spellcheckerCB->addItem(qt_("Aspell"), QString("aspell"));
1581 #endif
1582 #if defined(USE_ENCHANT)
1583         spellcheckerCB->addItem(qt_("Enchant"), QString("enchant"));
1584 #endif
1585 #if defined(USE_HUNSPELL)
1586         spellcheckerCB->addItem(qt_("Hunspell"), QString("hunspell"));
1587 #endif
1588
1589         #if defined(CONNECT_APPLESPELL) || defined(USE_ASPELL) || defined(USE_ENCHANT) || defined(USE_HUNSPELL)
1590                 connect(spellcheckerCB, SIGNAL(currentIndexChanged(int)),
1591                         this, SIGNAL(changed()));
1592                 connect(altLanguageED, SIGNAL(textChanged(QString)),
1593                         this, SIGNAL(changed()));
1594                 connect(escapeCharactersED, SIGNAL(textChanged(QString)),
1595                         this, SIGNAL(changed()));
1596                 connect(compoundWordCB, SIGNAL(clicked()),
1597                         this, SIGNAL(changed()));
1598                 connect(spellcheckContinuouslyCB, SIGNAL(clicked()),
1599                         this, SIGNAL(changed()));
1600                 connect(spellcheckNotesCB, SIGNAL(clicked()),
1601                         this, SIGNAL(changed()));
1602
1603                 altLanguageED->setValidator(new NoNewLineValidator(altLanguageED));
1604                 escapeCharactersED->setValidator(new NoNewLineValidator(escapeCharactersED));
1605         #else
1606                 spellcheckerCB->setEnabled(false);
1607                 altLanguageED->setEnabled(false);
1608                 escapeCharactersED->setEnabled(false);
1609                 compoundWordCB->setEnabled(false);
1610                 spellcheckContinuouslyCB->setEnabled(false);
1611                 spellcheckNotesCB->setEnabled(false);
1612         #endif
1613 }
1614
1615
1616 void PrefSpellchecker::applyRC(LyXRC & rc) const
1617 {
1618         string const speller = fromqstr(spellcheckerCB->
1619                 itemData(spellcheckerCB->currentIndex()).toString());
1620         if (!speller.empty())
1621                 rc.spellchecker = speller;
1622         rc.spellchecker_alt_lang = fromqstr(altLanguageED->text());
1623         rc.spellchecker_esc_chars = fromqstr(escapeCharactersED->text());
1624         rc.spellchecker_accept_compound = compoundWordCB->isChecked();
1625         rc.spellcheck_continuously = spellcheckContinuouslyCB->isChecked();
1626         rc.spellcheck_notes = spellcheckNotesCB->isChecked();
1627 }
1628
1629
1630 void PrefSpellchecker::updateRC(LyXRC const & rc)
1631 {
1632         spellcheckerCB->setCurrentIndex(
1633                 spellcheckerCB->findData(toqstr(rc.spellchecker)));
1634         altLanguageED->setText(toqstr(rc.spellchecker_alt_lang));
1635         escapeCharactersED->setText(toqstr(rc.spellchecker_esc_chars));
1636         compoundWordCB->setChecked(rc.spellchecker_accept_compound);
1637         spellcheckContinuouslyCB->setChecked(rc.spellcheck_continuously);
1638         spellcheckNotesCB->setChecked(rc.spellcheck_notes);
1639 }
1640
1641
1642 void PrefSpellchecker::on_spellcheckerCB_currentIndexChanged(int index)
1643 {
1644         QString spellchecker = spellcheckerCB->itemData(index).toString();
1645
1646         compoundWordCB->setEnabled(spellchecker == QString("aspell"));
1647 }
1648
1649
1650
1651 /////////////////////////////////////////////////////////////////////
1652 //
1653 // PrefConverters
1654 //
1655 /////////////////////////////////////////////////////////////////////
1656
1657
1658 PrefConverters::PrefConverters(GuiPreferences * form)
1659         : PrefModule(catFiles, N_("Converters"), form)
1660 {
1661         setupUi(this);
1662
1663         connect(converterNewPB, SIGNAL(clicked()),
1664                 this, SLOT(updateConverter()));
1665         connect(converterRemovePB, SIGNAL(clicked()),
1666                 this, SLOT(removeConverter()));
1667         connect(converterModifyPB, SIGNAL(clicked()),
1668                 this, SLOT(updateConverter()));
1669         connect(convertersLW, SIGNAL(currentRowChanged(int)),
1670                 this, SLOT(switchConverter()));
1671 #if QT_VERSION < 0x050e00
1672         connect(converterFromCO, SIGNAL(activated(QString)),
1673                 this, SLOT(changeConverter()));
1674         connect(converterToCO, SIGNAL(activated(QString)),
1675                 this, SLOT(changeConverter()));
1676 #else
1677         connect(converterFromCO, SIGNAL(textActivated(QString)),
1678                 this, SLOT(changeConverter()));
1679         connect(converterToCO, SIGNAL(textActivated(QString)),
1680                 this, SLOT(changeConverter()));
1681 #endif
1682         connect(converterED, SIGNAL(textEdited(QString)),
1683                 this, SLOT(changeConverter()));
1684         connect(converterFlagED, SIGNAL(textEdited(QString)),
1685                 this, SLOT(changeConverter()));
1686         connect(converterNewPB, SIGNAL(clicked()),
1687                 this, SIGNAL(changed()));
1688         connect(converterRemovePB, SIGNAL(clicked()),
1689                 this, SIGNAL(changed()));
1690         connect(converterModifyPB, SIGNAL(clicked()),
1691                 this, SIGNAL(changed()));
1692         connect(maxAgeLE, SIGNAL(textEdited(QString)),
1693                 this, SIGNAL(changed()));
1694         connect(needauthForbiddenCB, SIGNAL(toggled(bool)),
1695                 this, SIGNAL(changed()));
1696
1697         converterED->setValidator(new NoNewLineValidator(converterED));
1698         converterFlagED->setValidator(new NoNewLineValidator(converterFlagED));
1699         maxAgeLE->setValidator(new QDoubleValidator(0, HUGE_VAL, 6, maxAgeLE));
1700         //converterDefGB->setFocusProxy(convertersLW);
1701 }
1702
1703
1704 void PrefConverters::applyRC(LyXRC & rc) const
1705 {
1706         rc.use_converter_cache = cacheCB->isChecked();
1707         rc.use_converter_needauth_forbidden = needauthForbiddenCB->isChecked();
1708         rc.use_converter_needauth = needauthCB->isChecked();
1709         rc.converter_cache_maxage = int(widgetToDouble(maxAgeLE) * 86400.0);
1710 }
1711
1712
1713 static void setCheckboxBlockSignals(QCheckBox *cb, bool checked) {
1714         cb->blockSignals(true);
1715         cb->setChecked(checked);
1716         cb->blockSignals(false);
1717 }
1718
1719
1720 void PrefConverters::updateRC(LyXRC const & rc)
1721 {
1722         cacheCB->setChecked(rc.use_converter_cache);
1723         needauthForbiddenCB->setChecked(rc.use_converter_needauth_forbidden);
1724         setCheckboxBlockSignals(needauthCB, rc.use_converter_needauth);
1725         QString max_age;
1726         doubleToWidget(maxAgeLE, (double(rc.converter_cache_maxage) / 86400.0), 'g', 6);
1727         updateGui();
1728 }
1729
1730
1731 void PrefConverters::updateGui()
1732 {
1733         QString const pattern("%1 -> %2");
1734         form_->formats().sort();
1735         form_->converters().update(form_->formats());
1736         // save current selection
1737         QString current =
1738                 pattern
1739                 .arg(converterFromCO->currentText())
1740                 .arg(converterToCO->currentText());
1741
1742         converterFromCO->clear();
1743         converterToCO->clear();
1744
1745         for (Format const & f : form_->formats()) {
1746                 QString const name = toqstr(translateIfPossible(f.prettyname()));
1747                 converterFromCO->addItem(name);
1748                 converterToCO->addItem(name);
1749         }
1750
1751         // currentRowChanged(int) is also triggered when updating the listwidget
1752         // block signals to avoid unnecessary calls to switchConverter()
1753         convertersLW->blockSignals(true);
1754         convertersLW->clear();
1755
1756         for (Converter const & c : form_->converters()) {
1757                 QString const name =
1758                         pattern
1759                         .arg(toqstr(translateIfPossible(c.From()->prettyname())))
1760                         .arg(toqstr(translateIfPossible(c.To()->prettyname())));
1761                 int type = form_->converters().getNumber(c.From()->name(),
1762                                                          c.To()->name());
1763                 new QListWidgetItem(name, convertersLW, type);
1764         }
1765         convertersLW->sortItems(Qt::AscendingOrder);
1766         convertersLW->blockSignals(false);
1767
1768         // restore selection
1769         if (current != pattern.arg(QString()).arg(QString())) {
1770                 QList<QListWidgetItem *> const item =
1771                         convertersLW->findItems(current, Qt::MatchExactly);
1772                 if (!item.isEmpty())
1773                         convertersLW->setCurrentItem(item.at(0));
1774         }
1775
1776         // select first element if restoring failed
1777         if (convertersLW->currentRow() == -1)
1778                 convertersLW->setCurrentRow(0);
1779
1780         updateButtons();
1781 }
1782
1783
1784 void PrefConverters::switchConverter()
1785 {
1786         int const cnr = convertersLW->currentItem()->type();
1787         Converter const & c(form_->converters().get(cnr));
1788         converterFromCO->setCurrentIndex(form_->formats().getNumber(c.from()));
1789         converterToCO->setCurrentIndex(form_->formats().getNumber(c.to()));
1790         converterED->setText(toqstr(c.command()));
1791         converterFlagED->setText(toqstr(c.flags()));
1792
1793         updateButtons();
1794 }
1795
1796
1797 void PrefConverters::changeConverter()
1798 {
1799         updateButtons();
1800 }
1801
1802
1803 void PrefConverters::updateButtons()
1804 {
1805         if (form_->formats().empty())
1806                 return;
1807         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1808         Format const & to = form_->formats().get(converterToCO->currentIndex());
1809         int const sel = form_->converters().getNumber(from.name(), to.name());
1810         bool const known = sel >= 0;
1811         bool const valid = !(converterED->text().isEmpty()
1812                 || from.name() == to.name());
1813
1814         string old_command;
1815         string old_flag;
1816
1817         if (convertersLW->count() > 0) {
1818                 int const cnr = convertersLW->currentItem()->type();
1819                 Converter const & c = form_->converters().get(cnr);
1820                 old_command = c.command();
1821                 old_flag = c.flags();
1822         }
1823
1824         string const new_command = fromqstr(converterED->text());
1825         string const new_flag = fromqstr(converterFlagED->text());
1826
1827         bool modified = (old_command != new_command || old_flag != new_flag);
1828
1829         converterModifyPB->setEnabled(valid && known && modified);
1830         converterNewPB->setEnabled(valid && !known);
1831         converterRemovePB->setEnabled(known);
1832
1833         maxAgeLE->setEnabled(cacheCB->isChecked());
1834         maxAgeLA->setEnabled(cacheCB->isChecked());
1835 }
1836
1837
1838 // FIXME: user must
1839 // specify unique from/to or it doesn't appear. This is really bad UI
1840 // this is why we can use the same function for both new and modify
1841 void PrefConverters::updateConverter()
1842 {
1843         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1844         Format const & to = form_->formats().get(converterToCO->currentIndex());
1845         string const flags = fromqstr(converterFlagED->text());
1846         string const command = fromqstr(converterED->text());
1847
1848         Converter const * old =
1849                 form_->converters().getConverter(from.name(), to.name());
1850         form_->converters().add(from.name(), to.name(), command, flags);
1851
1852         if (!old)
1853                 form_->converters().updateLast(form_->formats());
1854
1855         updateGui();
1856
1857         // Remove all files created by this converter from the cache, since
1858         // the modified converter might create different files.
1859         ConverterCache::get().remove_all(from.name(), to.name());
1860 }
1861
1862
1863 void PrefConverters::removeConverter()
1864 {
1865         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1866         Format const & to = form_->formats().get(converterToCO->currentIndex());
1867         form_->converters().erase(from.name(), to.name());
1868
1869         updateGui();
1870
1871         // Remove all files created by this converter from the cache, since
1872         // a possible new converter might create different files.
1873         ConverterCache::get().remove_all(from.name(), to.name());
1874 }
1875
1876
1877 void PrefConverters::on_cacheCB_stateChanged(int state)
1878 {
1879         maxAgeLE->setEnabled(state == Qt::Checked);
1880         maxAgeLA->setEnabled(state == Qt::Checked);
1881         changed();
1882 }
1883
1884
1885 void PrefConverters::on_needauthForbiddenCB_toggled(bool checked)
1886 {
1887         needauthCB->setEnabled(!checked);
1888 }
1889
1890
1891 void PrefConverters::on_needauthCB_toggled(bool checked)
1892 {
1893         if (checked) {
1894                 changed();
1895                 return;
1896         }
1897
1898         int ret = frontend::Alert::prompt(
1899                 _("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!"),
1900                 0, 0, _("&No"), _("&Yes"));
1901         if (ret == 1)
1902                 changed();
1903         else
1904                 setCheckboxBlockSignals(needauthCB, true);
1905 }
1906
1907
1908 /////////////////////////////////////////////////////////////////////
1909 //
1910 // FormatValidator
1911 //
1912 /////////////////////////////////////////////////////////////////////
1913
1914 class FormatValidator : public QValidator
1915 {
1916 public:
1917         FormatValidator(QWidget *, Formats const & f);
1918         void fixup(QString & input) const override;
1919         QValidator::State validate(QString & input, int & pos) const override;
1920 private:
1921         virtual QString toString(Format const & format) const = 0;
1922         int nr() const;
1923         Formats const & formats_;
1924 };
1925
1926
1927 FormatValidator::FormatValidator(QWidget * parent, Formats const & f)
1928         : QValidator(parent), formats_(f)
1929 {
1930 }
1931
1932
1933 void FormatValidator::fixup(QString & input) const
1934 {
1935         Formats::const_iterator cit = formats_.begin();
1936         Formats::const_iterator end = formats_.end();
1937         for (; cit != end; ++cit) {
1938                 QString const name = toString(*cit);
1939                 if (distance(formats_.begin(), cit) == nr()) {
1940                         input = name;
1941                         return;
1942                 }
1943         }
1944 }
1945
1946
1947 QValidator::State FormatValidator::validate(QString & input, int & /*pos*/) const
1948 {
1949         Formats::const_iterator cit = formats_.begin();
1950         Formats::const_iterator end = formats_.end();
1951         bool unknown = true;
1952         for (; unknown && cit != end; ++cit) {
1953                 QString const name = toString(*cit);
1954                 if (distance(formats_.begin(), cit) != nr())
1955                         unknown = name != input;
1956         }
1957
1958         if (unknown && !input.isEmpty())
1959                 return QValidator::Acceptable;
1960         else
1961                 return QValidator::Intermediate;
1962 }
1963
1964
1965 int FormatValidator::nr() const
1966 {
1967         QComboBox * p = qobject_cast<QComboBox *>(parent());
1968         return p->itemData(p->currentIndex()).toInt();
1969 }
1970
1971
1972 /////////////////////////////////////////////////////////////////////
1973 //
1974 // FormatNameValidator
1975 //
1976 /////////////////////////////////////////////////////////////////////
1977
1978 class FormatNameValidator : public FormatValidator
1979 {
1980 public:
1981         FormatNameValidator(QWidget * parent, Formats const & f)
1982                 : FormatValidator(parent, f)
1983         {}
1984 private:
1985         QString toString(Format const & format) const override
1986         {
1987                 return toqstr(format.name());
1988         }
1989 };
1990
1991
1992 /////////////////////////////////////////////////////////////////////
1993 //
1994 // FormatPrettynameValidator
1995 //
1996 /////////////////////////////////////////////////////////////////////
1997
1998 class FormatPrettynameValidator : public FormatValidator
1999 {
2000 public:
2001         FormatPrettynameValidator(QWidget * parent, Formats const & f)
2002                 : FormatValidator(parent, f)
2003         {}
2004 private:
2005         QString toString(Format const & format) const override
2006         {
2007                 return toqstr(translateIfPossible(format.prettyname()));
2008         }
2009 };
2010
2011
2012 /////////////////////////////////////////////////////////////////////
2013 //
2014 // PrefFileformats
2015 //
2016 /////////////////////////////////////////////////////////////////////
2017
2018 PrefFileformats::PrefFileformats(GuiPreferences * form)
2019         : PrefModule(catFiles, N_("File Formats"), form)
2020 {
2021         setupUi(this);
2022
2023         formatED->setValidator(new FormatNameValidator(formatsCB, form_->formats()));
2024         formatsCB->setValidator(new FormatPrettynameValidator(formatsCB, form_->formats()));
2025         extensionsED->setValidator(new NoNewLineValidator(extensionsED));
2026         shortcutED->setValidator(new NoNewLineValidator(shortcutED));
2027         editorED->setValidator(new NoNewLineValidator(editorED));
2028         viewerED->setValidator(new NoNewLineValidator(viewerED));
2029         copierED->setValidator(new NoNewLineValidator(copierED));
2030
2031         connect(documentCB, SIGNAL(clicked()),
2032                 this, SLOT(setFlags()));
2033         connect(vectorCB, SIGNAL(clicked()),
2034                 this, SLOT(setFlags()));
2035         connect(exportMenuCB, SIGNAL(clicked()),
2036                 this, SLOT(setFlags()));
2037         connect(formatsCB->lineEdit(), SIGNAL(editingFinished()),
2038                 this, SLOT(updatePrettyname()));
2039         connect(formatsCB->lineEdit(), SIGNAL(textEdited(QString)),
2040                 this, SIGNAL(changed()));
2041 #if QT_VERSION < 0x050e00
2042         connect(defaultFormatCB, SIGNAL(activated(QString)),
2043                 this, SIGNAL(changed()));
2044         connect(defaultOTFFormatCB, SIGNAL(activated(QString)),
2045                 this, SIGNAL(changed()));
2046         connect(defaultPlatexFormatCB, SIGNAL(activated(QString)),
2047                 this, SIGNAL(changed()));
2048 #else
2049         connect(defaultFormatCB, SIGNAL(textActivated(QString)),
2050                 this, SIGNAL(changed()));
2051         connect(defaultOTFFormatCB, SIGNAL(textActivated(QString)),
2052                 this, SIGNAL(changed()));
2053         connect(defaultPlatexFormatCB, SIGNAL(textActivated(QString)),
2054                 this, SIGNAL(changed()));
2055 #endif
2056         connect(viewerCO, SIGNAL(activated(int)),
2057                 this, SIGNAL(changed()));
2058         connect(editorCO, SIGNAL(activated(int)),
2059                 this, SIGNAL(changed()));
2060 }
2061
2062
2063 namespace {
2064
2065 string const l10n_shortcut(docstring const & prettyname, string const & shortcut)
2066 {
2067         if (shortcut.empty())
2068                 return string();
2069
2070         string l10n_format =
2071                 to_utf8(_(to_utf8(prettyname) + '|' + shortcut));
2072         return split(l10n_format, '|');
2073 }
2074
2075 } // namespace
2076
2077
2078 void PrefFileformats::applyRC(LyXRC & rc) const
2079 {
2080         QString const default_format = defaultFormatCB->itemData(
2081                 defaultFormatCB->currentIndex()).toString();
2082         rc.default_view_format = fromqstr(default_format);
2083         QString const default_otf_format = defaultOTFFormatCB->itemData(
2084                 defaultOTFFormatCB->currentIndex()).toString();
2085         rc.default_otf_view_format = fromqstr(default_otf_format);
2086         QString const default_platex_format = defaultPlatexFormatCB->itemData(
2087                 defaultPlatexFormatCB->currentIndex()).toString();
2088         rc.default_platex_view_format = fromqstr(default_platex_format);
2089 }
2090
2091
2092 void PrefFileformats::updateRC(LyXRC const & rc)
2093 {
2094         viewer_alternatives = rc.viewer_alternatives;
2095         editor_alternatives = rc.editor_alternatives;
2096         bool const init = defaultFormatCB->currentText().isEmpty();
2097         updateView();
2098         if (init) {
2099                 int pos =
2100                         defaultFormatCB->findData(toqstr(rc.default_view_format));
2101                 defaultFormatCB->setCurrentIndex(pos);
2102                 pos = defaultOTFFormatCB->findData(toqstr(rc.default_otf_view_format));
2103                                 defaultOTFFormatCB->setCurrentIndex(pos);
2104                 defaultOTFFormatCB->setCurrentIndex(pos);
2105                 pos = defaultPlatexFormatCB->findData(toqstr(rc.default_platex_view_format));
2106                                 defaultPlatexFormatCB->setCurrentIndex(pos);
2107                 defaultPlatexFormatCB->setCurrentIndex(pos);
2108         }
2109 }
2110
2111
2112 void PrefFileformats::updateView()
2113 {
2114         QString const current = formatsCB->currentText();
2115         QString const current_def = defaultFormatCB->currentText();
2116         QString const current_def_otf = defaultOTFFormatCB->currentText();
2117         QString const current_def_platex = defaultPlatexFormatCB->currentText();
2118
2119         // update comboboxes with formats
2120         formatsCB->blockSignals(true);
2121         defaultFormatCB->blockSignals(true);
2122         defaultOTFFormatCB->blockSignals(true);
2123         defaultPlatexFormatCB->blockSignals(true);
2124         formatsCB->clear();
2125         defaultFormatCB->clear();
2126         defaultOTFFormatCB->clear();
2127         defaultPlatexFormatCB->clear();
2128         form_->formats().sort();
2129         for (Format const & f : form_->formats()) {
2130                 QString const prettyname = toqstr(translateIfPossible(f.prettyname()));
2131                 formatsCB->addItem(prettyname,
2132                                    QVariant(form_->formats().getNumber(f.name())));
2133                 if (f.viewer().empty())
2134                         continue;
2135                 if (form_->converters().isReachable("xhtml", f.name())
2136                     || form_->converters().isReachable("dviluatex", f.name())
2137                     || form_->converters().isReachable("luatex", f.name())
2138                     || form_->converters().isReachable("xetex", f.name())) {
2139                         defaultFormatCB->addItem(prettyname,
2140                                         QVariant(toqstr(f.name())));
2141                         defaultOTFFormatCB->addItem(prettyname,
2142                                         QVariant(toqstr(f.name())));
2143                 } else {
2144                         if (form_->converters().isReachable("latex", f.name())
2145                             || form_->converters().isReachable("pdflatex", f.name()))
2146                                 defaultFormatCB->addItem(prettyname,
2147                                         QVariant(toqstr(f.name())));
2148                         if (form_->converters().isReachable("platex", f.name()))
2149                                         defaultPlatexFormatCB->addItem(prettyname,
2150                                                 QVariant(toqstr(f.name())));
2151                 }
2152         }
2153
2154         // restore selections
2155         int item = formatsCB->findText(current, Qt::MatchExactly);
2156         formatsCB->setCurrentIndex(item < 0 ? 0 : item);
2157         on_formatsCB_currentIndexChanged(item < 0 ? 0 : item);
2158         item = defaultFormatCB->findText(current_def, Qt::MatchExactly);
2159         defaultFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2160         item = defaultOTFFormatCB->findText(current_def_otf, Qt::MatchExactly);
2161         defaultOTFFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2162         item = defaultPlatexFormatCB->findText(current_def_platex, Qt::MatchExactly);
2163         defaultPlatexFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2164         formatsCB->blockSignals(false);
2165         defaultFormatCB->blockSignals(false);
2166         defaultOTFFormatCB->blockSignals(false);
2167         defaultPlatexFormatCB->blockSignals(false);
2168 }
2169
2170
2171 void PrefFileformats::on_formatsCB_currentIndexChanged(int i)
2172 {
2173         if (form_->formats().empty())
2174                 return;
2175         int const nr = formatsCB->itemData(i).toInt();
2176         Format const f = form_->formats().get(nr);
2177
2178         formatED->setText(toqstr(f.name()));
2179         copierED->setText(toqstr(form_->movers().command(f.name())));
2180         extensionsED->setText(toqstr(f.extensions()));
2181         mimeED->setText(toqstr(f.mime()));
2182         shortcutED->setText(
2183                 toqstr(l10n_shortcut(f.prettyname(), f.shortcut())));
2184         documentCB->setChecked((f.documentFormat()));
2185         vectorCB->setChecked((f.vectorFormat()));
2186         exportMenuCB->setChecked((f.inExportMenu()));
2187         exportMenuCB->setEnabled((f.documentFormat()));
2188         updateViewers();
2189         updateEditors();
2190 }
2191
2192
2193 void PrefFileformats::setFlags()
2194 {
2195         int flags = Format::none;
2196         if (documentCB->isChecked())
2197                 flags |= Format::document;
2198         if (vectorCB->isChecked())
2199                 flags |= Format::vector;
2200         if (exportMenuCB->isChecked())
2201                 flags |= Format::export_menu;
2202         currentFormat().setFlags(flags);
2203         exportMenuCB->setEnabled(documentCB->isChecked());
2204         changed();
2205 }
2206
2207
2208 void PrefFileformats::on_copierED_textEdited(const QString & s)
2209 {
2210         string const fmt = fromqstr(formatED->text());
2211         form_->movers().set(fmt, fromqstr(s));
2212         changed();
2213 }
2214
2215
2216 void PrefFileformats::on_extensionsED_textEdited(const QString & s)
2217 {
2218         currentFormat().setExtensions(fromqstr(s));
2219         changed();
2220 }
2221
2222
2223 void PrefFileformats::on_viewerED_textEdited(const QString & s)
2224 {
2225         currentFormat().setViewer(fromqstr(s));
2226         changed();
2227 }
2228
2229
2230 void PrefFileformats::on_editorED_textEdited(const QString & s)
2231 {
2232         currentFormat().setEditor(fromqstr(s));
2233         changed();
2234 }
2235
2236
2237 void PrefFileformats::on_mimeED_textEdited(const QString & s)
2238 {
2239         currentFormat().setMime(fromqstr(s));
2240         changed();
2241 }
2242
2243
2244 void PrefFileformats::on_shortcutED_textEdited(const QString & s)
2245 {
2246         string const new_shortcut = fromqstr(s);
2247         if (new_shortcut == l10n_shortcut(currentFormat().prettyname(),
2248                                           currentFormat().shortcut()))
2249                 return;
2250         currentFormat().setShortcut(new_shortcut);
2251         changed();
2252 }
2253
2254
2255 void PrefFileformats::on_formatED_editingFinished()
2256 {
2257         string const newname = fromqstr(formatED->displayText());
2258         string const oldname = currentFormat().name();
2259         if (newname == oldname)
2260                 return;
2261         if (form_->converters().formatIsUsed(oldname)) {
2262                 Alert::error(_("Format in use"),
2263                              _("You cannot change a format's short name "
2264                                "if the format is used by a converter. "
2265                                "Please remove the converter first."));
2266                 updateView();
2267                 return;
2268         }
2269
2270         currentFormat().setName(newname);
2271         changed();
2272 }
2273
2274
2275 void PrefFileformats::on_formatED_textChanged(const QString &)
2276 {
2277         QString t = formatED->text();
2278         int p = 0;
2279         bool valid = formatED->validator()->validate(t, p) == QValidator::Acceptable;
2280         setValid(formatLA, valid);
2281 }
2282
2283
2284 void PrefFileformats::on_formatsCB_editTextChanged(const QString &)
2285 {
2286         QString t = formatsCB->currentText();
2287         int p = 0;
2288         bool valid = formatsCB->validator()->validate(t, p) == QValidator::Acceptable;
2289         setValid(formatsLA, valid);
2290 }
2291
2292
2293 void PrefFileformats::updatePrettyname()
2294 {
2295         QString const newname = formatsCB->currentText();
2296         if (newname == toqstr(translateIfPossible(currentFormat().prettyname())))
2297                 return;
2298
2299         currentFormat().setPrettyname(qstring_to_ucs4(newname));
2300         formatsChanged();
2301         updateView();
2302         changed();
2303 }
2304
2305
2306 namespace {
2307         void updateComboBox(LyXRC::Alternatives const & alts,
2308                             string const & fmt, QComboBox * combo)
2309         {
2310                 LyXRC::Alternatives::const_iterator it =
2311                                 alts.find(fmt);
2312                 if (it != alts.end()) {
2313                         LyXRC::CommandSet const & cmds = it->second;
2314                         LyXRC::CommandSet::const_iterator sit =
2315                                         cmds.begin();
2316                         LyXRC::CommandSet::const_iterator const sen =
2317                                         cmds.end();
2318                         for (; sit != sen; ++sit) {
2319                                 QString const qcmd = toqstr(*sit);
2320                                 combo->addItem(qcmd, qcmd);
2321                         }
2322                 }
2323         }
2324 } // namespace
2325
2326
2327 void PrefFileformats::updateViewers()
2328 {
2329         Format const f = currentFormat();
2330         viewerCO->blockSignals(true);
2331         viewerCO->clear();
2332         viewerCO->addItem(qt_("None"), QString());
2333         updateComboBox(viewer_alternatives, f.name(), viewerCO);
2334         viewerCO->addItem(qt_("Custom"), QString("custom viewer"));
2335         viewerCO->blockSignals(false);
2336
2337         int pos = viewerCO->findData(toqstr(f.viewer()));
2338         if (pos != -1) {
2339                 viewerED->clear();
2340                 viewerED->setEnabled(false);
2341                 viewerCO->setCurrentIndex(pos);
2342         } else {
2343                 viewerED->setEnabled(true);
2344                 viewerED->setText(toqstr(f.viewer()));
2345                 viewerCO->setCurrentIndex(viewerCO->findData(toqstr("custom viewer")));
2346         }
2347 }
2348
2349
2350 void PrefFileformats::updateEditors()
2351 {
2352         Format const f = currentFormat();
2353         editorCO->blockSignals(true);
2354         editorCO->clear();
2355         editorCO->addItem(qt_("None"), QString());
2356         updateComboBox(editor_alternatives, f.name(), editorCO);
2357         editorCO->addItem(qt_("Custom"), QString("custom editor"));
2358         editorCO->blockSignals(false);
2359
2360         int pos = editorCO->findData(toqstr(f.editor()));
2361         if (pos != -1) {
2362                 editorED->clear();
2363                 editorED->setEnabled(false);
2364                 editorCO->setCurrentIndex(pos);
2365         } else {
2366                 editorED->setEnabled(true);
2367                 editorED->setText(toqstr(f.editor()));
2368                 editorCO->setCurrentIndex(editorCO->findData(toqstr("custom editor")));
2369         }
2370 }
2371
2372
2373 void PrefFileformats::on_viewerCO_currentIndexChanged(int i)
2374 {
2375         bool const custom = viewerCO->itemData(i).toString() == "custom viewer";
2376         viewerED->setEnabled(custom);
2377         if (!custom)
2378                 currentFormat().setViewer(fromqstr(viewerCO->itemData(i).toString()));
2379 }
2380
2381
2382 void PrefFileformats::on_editorCO_currentIndexChanged(int i)
2383 {
2384         bool const custom = editorCO->itemData(i).toString() == "custom editor";
2385         editorED->setEnabled(custom);
2386         if (!custom)
2387                 currentFormat().setEditor(fromqstr(editorCO->itemData(i).toString()));
2388 }
2389
2390
2391 Format & PrefFileformats::currentFormat()
2392 {
2393         int const i = formatsCB->currentIndex();
2394         int const nr = formatsCB->itemData(i).toInt();
2395         return form_->formats().get(nr);
2396 }
2397
2398
2399 void PrefFileformats::on_formatNewPB_clicked()
2400 {
2401         form_->formats().add("", "", docstring(), "", "", "", "", Format::none);
2402         updateView();
2403         formatsCB->setCurrentIndex(0);
2404         formatsCB->setFocus(Qt::OtherFocusReason);
2405 }
2406
2407
2408 void PrefFileformats::on_formatRemovePB_clicked()
2409 {
2410         int const i = formatsCB->currentIndex();
2411         int const nr = formatsCB->itemData(i).toInt();
2412         string const current_text = form_->formats().get(nr).name();
2413         if (form_->converters().formatIsUsed(current_text)) {
2414                 Alert::error(_("Format in use"),
2415                              _("Cannot remove a Format used by a Converter. "
2416                                             "Remove the converter first."));
2417                 return;
2418         }
2419
2420         form_->formats().erase(current_text);
2421         formatsChanged();
2422         updateView();
2423         on_formatsCB_editTextChanged(formatsCB->currentText());
2424         changed();
2425 }
2426
2427
2428 /////////////////////////////////////////////////////////////////////
2429 //
2430 // PrefLanguage
2431 //
2432 /////////////////////////////////////////////////////////////////////
2433
2434 PrefLanguage::PrefLanguage(GuiPreferences * form)
2435         : PrefModule(catLanguage, N_("Language"), form)
2436 {
2437         setupUi(this);
2438
2439         connect(visualCursorRB, SIGNAL(clicked()),
2440                 this, SIGNAL(changed()));
2441         connect(logicalCursorRB, SIGNAL(clicked()),
2442                 this, SIGNAL(changed()));
2443         connect(markForeignCB, SIGNAL(clicked()),
2444                 this, SIGNAL(changed()));
2445         connect(respectOSkbdCB, SIGNAL(clicked()),
2446                 this, SIGNAL(changed()));
2447         connect(explicitDocLangBeginCB, SIGNAL(clicked()),
2448                 this, SIGNAL(changed()));
2449         connect(explicitDocLangEndCB, SIGNAL(clicked()),
2450                 this, SIGNAL(changed()));
2451         connect(languagePackageCO, SIGNAL(activated(int)),
2452                 this, SIGNAL(changed()));
2453         connect(languagePackageED, SIGNAL(textChanged(QString)),
2454                 this, SIGNAL(changed()));
2455         connect(globalCB, SIGNAL(clicked()),
2456                 this, SIGNAL(changed()));
2457         connect(startCommandED, SIGNAL(textChanged(QString)),
2458                 this, SIGNAL(changed()));
2459         connect(endCommandED, SIGNAL(textChanged(QString)),
2460                 this, SIGNAL(changed()));
2461         connect(uiLanguageCO, SIGNAL(activated(int)),
2462                 this, SIGNAL(changed()));
2463         connect(defaultDecimalSepED, SIGNAL(textChanged(QString)),
2464                 this, SIGNAL(changed()));
2465         connect(defaultDecimalSepCO, SIGNAL(activated(int)),
2466                 this, SIGNAL(changed()));
2467         connect(defaultLengthUnitCO, SIGNAL(activated(int)),
2468                 this, SIGNAL(changed()));
2469
2470         languagePackageED->setValidator(new NoNewLineValidator(languagePackageED));
2471         startCommandED->setValidator(new NoNewLineValidator(startCommandED));
2472         endCommandED->setValidator(new NoNewLineValidator(endCommandED));
2473
2474 #if QT_VERSION < 0x060000
2475         defaultDecimalSepED->setValidator(new QRegExpValidator(QRegExp("\\S"), this));
2476 #else
2477         defaultDecimalSepED->setValidator(new QRegularExpressionValidator(QRegularExpression("\\S"), this));
2478 #endif
2479         defaultDecimalSepED->setMaxLength(1);
2480
2481         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::CM]), Length::CM);
2482         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::IN]), Length::IN);
2483
2484         QAbstractItemModel * language_model = guiApp->languageModel();
2485         language_model->sort(0);
2486         uiLanguageCO->blockSignals(true);
2487         uiLanguageCO->clear();
2488         uiLanguageCO->addItem(qt_("Default"), toqstr("auto"));
2489         for (int i = 0; i != language_model->rowCount(); ++i) {
2490                 QModelIndex index = language_model->index(i, 0);
2491                 // Filter the list based on the available translation and add
2492                 // each language code only once
2493                 string const name = fromqstr(index.data(Qt::UserRole).toString());
2494                 Language const * lang = languages.getLanguage(name);
2495                 if (!lang)
2496                         continue;
2497                 // never remove the currently selected language
2498                 if (name != form->rc().gui_language
2499                     && name != lyxrc.gui_language
2500                     && (!Messages::available(lang->code())
2501                         || !lang->hasGuiSupport()))
2502                         continue;
2503                 uiLanguageCO->addItem(index.data(Qt::DisplayRole).toString(),
2504                                       index.data(Qt::UserRole).toString());
2505         }
2506         uiLanguageCO->blockSignals(false);
2507
2508         // FIXME: restore this when it works (see discussion in #6450).
2509         respectOSkbdCB->hide();
2510 }
2511
2512
2513 void PrefLanguage::on_uiLanguageCO_currentIndexChanged(int)
2514 {
2515          QMessageBox::information(this, qt_("LyX needs to be restarted!"),
2516                  qt_("The change of user interface language will be fully "
2517                  "effective only after a restart."));
2518 }
2519
2520
2521 void PrefLanguage::on_languagePackageCO_currentIndexChanged(int i)
2522 {
2523         if (i == 2)
2524                 languagePackageED->setText(save_langpack_);
2525         else if (!languagePackageED->text().isEmpty()) {
2526                 save_langpack_ = languagePackageED->text();
2527                 languagePackageED->clear();
2528         }
2529         languagePackageED->setEnabled(i == 2);
2530 }
2531
2532
2533 void PrefLanguage::on_defaultDecimalSepCO_currentIndexChanged(int i)
2534 {
2535         defaultDecimalSepED->setEnabled(i == 1);
2536 }
2537
2538
2539 void PrefLanguage::applyRC(LyXRC & rc) const
2540 {
2541         rc.visual_cursor = visualCursorRB->isChecked();
2542         rc.mark_foreign_language = markForeignCB->isChecked();
2543         rc.respect_os_kbd_language = respectOSkbdCB->isChecked();
2544         rc.language_auto_begin = !explicitDocLangBeginCB->isChecked();
2545         rc.language_auto_end = !explicitDocLangEndCB->isChecked();
2546         int const p = languagePackageCO->currentIndex();
2547         if (p == 0)
2548                 rc.language_package_selection = LyXRC::LP_AUTO;
2549         else if (p == 1)
2550                 rc.language_package_selection = LyXRC::LP_BABEL;
2551         else if (p == 2)
2552                 rc.language_package_selection = LyXRC::LP_CUSTOM;
2553         else if (p == 3)
2554                 rc.language_package_selection = LyXRC::LP_NONE;
2555         rc.language_custom_package = fromqstr(languagePackageED->text());
2556         rc.language_global_options = globalCB->isChecked();
2557         rc.language_command_begin = fromqstr(startCommandED->text());
2558         rc.language_command_end = fromqstr(endCommandED->text());
2559         rc.gui_language = fromqstr(
2560                 uiLanguageCO->itemData(uiLanguageCO->currentIndex()).toString());
2561         if (defaultDecimalSepCO->currentIndex() == 0)
2562                 rc.default_decimal_sep = "locale";
2563         else
2564                 rc.default_decimal_sep = fromqstr(defaultDecimalSepED->text());
2565         rc.default_length_unit = (Length::UNIT) defaultLengthUnitCO->itemData(defaultLengthUnitCO->currentIndex()).toInt();
2566 }
2567
2568
2569 void PrefLanguage::updateRC(LyXRC const & rc)
2570 {
2571         if (rc.visual_cursor)
2572                 visualCursorRB->setChecked(true);
2573         else
2574                 logicalCursorRB->setChecked(true);
2575         markForeignCB->setChecked(rc.mark_foreign_language);
2576         respectOSkbdCB->setChecked(rc.respect_os_kbd_language);
2577         explicitDocLangBeginCB->setChecked(!rc.language_auto_begin);
2578         explicitDocLangEndCB->setChecked(!rc.language_auto_end);
2579         languagePackageCO->setCurrentIndex(rc.language_package_selection);
2580         if (languagePackageCO->currentIndex() == 2) {
2581                 languagePackageED->setText(toqstr(rc.language_custom_package));
2582                 languagePackageED->setEnabled(true);
2583         } else {
2584                 languagePackageED->clear();
2585                 save_langpack_ = toqstr(rc.language_custom_package);
2586                 languagePackageED->setEnabled(false);
2587         }
2588         defaultDecimalSepED->setEnabled(defaultDecimalSepCO->currentIndex() == 1);
2589         globalCB->setChecked(rc.language_global_options);
2590         startCommandED->setText(toqstr(rc.language_command_begin));
2591         endCommandED->setText(toqstr(rc.language_command_end));
2592         if (rc.default_decimal_sep == "locale") {
2593                 defaultDecimalSepCO->setCurrentIndex(0);
2594                 defaultDecimalSepED->clear();
2595         } else {
2596                 defaultDecimalSepCO->setCurrentIndex(1);
2597                 defaultDecimalSepED->setText(toqstr(rc.default_decimal_sep));
2598         }
2599         int pos = defaultLengthUnitCO->findData(int(rc.default_length_unit));
2600         defaultLengthUnitCO->setCurrentIndex(pos);
2601
2602         pos = uiLanguageCO->findData(toqstr(rc.gui_language));
2603         uiLanguageCO->blockSignals(true);
2604         uiLanguageCO->setCurrentIndex(pos);
2605         uiLanguageCO->blockSignals(false);
2606 }
2607
2608
2609 /////////////////////////////////////////////////////////////////////
2610 //
2611 // PrefUserInterface
2612 //
2613 /////////////////////////////////////////////////////////////////////
2614
2615 PrefUserInterface::PrefUserInterface(GuiPreferences * form)
2616         : PrefModule(catLookAndFeel, N_("User Interface"), form)
2617 {
2618         setupUi(this);
2619
2620         connect(uiFilePB, SIGNAL(clicked()),
2621                 this, SLOT(selectUi()));
2622         connect(uiFileED, SIGNAL(textChanged(QString)),
2623                 this, SIGNAL(changed()));
2624         connect(iconSetCO, SIGNAL(activated(int)),
2625                 this, SIGNAL(changed()));
2626         connect(useSystemThemeIconsCB, SIGNAL(clicked()),
2627                 this, SIGNAL(changed()));
2628         connect(lastfilesSB, SIGNAL(valueChanged(int)),
2629                 this, SIGNAL(changed()));
2630         connect(tooltipCB, SIGNAL(toggled(bool)),
2631                 this, SIGNAL(changed()));
2632         lastfilesSB->setMaximum(maxlastfiles);
2633
2634         iconSetCO->addItem(qt_("Default"), QString());
2635         iconSetCO->addItem(qt_("Classic"), "classic");
2636         iconSetCO->addItem(qt_("Oxygen"), "oxygen");
2637
2638 #if QT_VERSION >= 0x040600
2639         if (guiApp->platformName() != "qt4x11"
2640             && guiApp->platformName() != "xcb"
2641             && !guiApp->platformName().contains("wayland"))
2642 #endif
2643                 useSystemThemeIconsCB->hide();
2644 }
2645
2646
2647 void PrefUserInterface::applyRC(LyXRC & rc) const
2648 {
2649         rc.icon_set = fromqstr(iconSetCO->itemData(
2650                 iconSetCO->currentIndex()).toString());
2651
2652         rc.ui_file = internal_path(fromqstr(uiFileED->text()));
2653         rc.use_system_theme_icons = useSystemThemeIconsCB->isChecked();
2654         rc.num_lastfiles = lastfilesSB->value();
2655         rc.use_tooltip = tooltipCB->isChecked();
2656 }
2657
2658
2659 void PrefUserInterface::updateRC(LyXRC const & rc)
2660 {
2661         int iconset = iconSetCO->findData(toqstr(rc.icon_set));
2662         if (iconset < 0)
2663                 iconset = 0;
2664         iconSetCO->setCurrentIndex(iconset);
2665         useSystemThemeIconsCB->setChecked(rc.use_system_theme_icons);
2666         uiFileED->setText(toqstr(external_path(rc.ui_file)));
2667         lastfilesSB->setValue(rc.num_lastfiles);
2668         tooltipCB->setChecked(rc.use_tooltip);
2669 }
2670
2671
2672 void PrefUserInterface::selectUi()
2673 {
2674         QString file = form_->browseUI(internalPath(uiFileED->text()));
2675         if (!file.isEmpty())
2676                 uiFileED->setText(file);
2677 }
2678
2679
2680 /////////////////////////////////////////////////////////////////////
2681 //
2682 // PrefDocumentHandling
2683 //
2684 /////////////////////////////////////////////////////////////////////
2685
2686 PrefDocHandling::PrefDocHandling(GuiPreferences * form)
2687         : PrefModule(catLookAndFeel, N_("Document Handling"), form)
2688 {
2689         setupUi(this);
2690
2691         connect(autoSaveCB, SIGNAL(toggled(bool)),
2692                 autoSaveSB, SLOT(setEnabled(bool)));
2693         connect(autoSaveCB, SIGNAL(toggled(bool)),
2694                 TextLabel1, SLOT(setEnabled(bool)));
2695         connect(openDocumentsInTabsCB, SIGNAL(clicked()),
2696                 this, SIGNAL(changed()));
2697         connect(singleInstanceCB, SIGNAL(clicked()),
2698                 this, SIGNAL(changed()));
2699         connect(singleCloseTabButtonCB, SIGNAL(clicked()),
2700                 this, SIGNAL(changed()));
2701         connect(closeLastViewCO, SIGNAL(activated(int)),
2702                 this, SIGNAL(changed()));
2703         connect(restoreCursorCB, SIGNAL(clicked()),
2704                 this, SIGNAL(changed()));
2705         connect(loadSessionCB, SIGNAL(clicked()),
2706                 this, SIGNAL(changed()));
2707         connect(allowGeometrySessionCB, SIGNAL(clicked()),
2708                 this, SIGNAL(changed()));
2709         connect(autoSaveSB, SIGNAL(valueChanged(int)),
2710                 this, SIGNAL(changed()));
2711         connect(autoSaveCB, SIGNAL(clicked()),
2712                 this, SIGNAL(changed()));
2713         connect(backupCB, SIGNAL(clicked()),
2714                 this, SIGNAL(changed()));
2715         connect(saveCompressedCB, SIGNAL(clicked()),
2716                 this, SIGNAL(changed()));
2717         connect(saveOriginCB, SIGNAL(clicked()),
2718                 this, SIGNAL(changed()));
2719 }
2720
2721
2722 void PrefDocHandling::applyRC(LyXRC & rc) const
2723 {
2724         rc.use_lastfilepos = restoreCursorCB->isChecked();
2725         rc.load_session = loadSessionCB->isChecked();
2726         rc.allow_geometry_session = allowGeometrySessionCB->isChecked();
2727         rc.autosave = autoSaveCB->isChecked() ?  autoSaveSB->value() * 60 : 0;
2728         rc.make_backup = backupCB->isChecked();
2729         rc.save_compressed = saveCompressedCB->isChecked();
2730         rc.save_origin = saveOriginCB->isChecked();
2731         rc.open_buffers_in_tabs = openDocumentsInTabsCB->isChecked();
2732         rc.single_instance = singleInstanceCB->isChecked();
2733         rc.single_close_tab_button = singleCloseTabButtonCB->isChecked();
2734
2735         switch (closeLastViewCO->currentIndex()) {
2736         case 0:
2737                 rc.close_buffer_with_last_view = "yes";
2738                 break;
2739         case 1:
2740                 rc.close_buffer_with_last_view = "no";
2741                 break;
2742         case 2:
2743                 rc.close_buffer_with_last_view = "ask";
2744                 break;
2745         default:
2746                 ;
2747         }
2748 }
2749
2750
2751 void PrefDocHandling::updateRC(LyXRC const & rc)
2752 {
2753         restoreCursorCB->setChecked(rc.use_lastfilepos);
2754         loadSessionCB->setChecked(rc.load_session);
2755         allowGeometrySessionCB->setChecked(rc.allow_geometry_session);
2756         // convert to minutes
2757         bool autosave = rc.autosave > 0;
2758         int mins = rc.autosave / 60;
2759         if (!mins)
2760                 mins = 5;
2761         autoSaveSB->setValue(mins);
2762         autoSaveCB->setChecked(autosave);
2763         autoSaveSB->setEnabled(autosave);
2764         backupCB->setChecked(rc.make_backup);
2765         saveCompressedCB->setChecked(rc.save_compressed);
2766         saveOriginCB->setChecked(rc.save_origin);
2767         openDocumentsInTabsCB->setChecked(rc.open_buffers_in_tabs);
2768         singleInstanceCB->setChecked(rc.single_instance && !rc.lyxpipes.empty());
2769         singleInstanceCB->setEnabled(!rc.lyxpipes.empty());
2770         singleCloseTabButtonCB->setChecked(rc.single_close_tab_button);
2771         if (rc.close_buffer_with_last_view == "yes")
2772                 closeLastViewCO->setCurrentIndex(0);
2773         else if (rc.close_buffer_with_last_view == "no")
2774                 closeLastViewCO->setCurrentIndex(1);
2775         else if (rc.close_buffer_with_last_view == "ask")
2776                 closeLastViewCO->setCurrentIndex(2);
2777 }
2778
2779
2780 void PrefDocHandling::on_clearSessionPB_clicked()
2781 {
2782         guiApp->clearSession();
2783 }
2784
2785
2786
2787 /////////////////////////////////////////////////////////////////////
2788 //
2789 // PrefEdit
2790 //
2791 /////////////////////////////////////////////////////////////////////
2792
2793 PrefEdit::PrefEdit(GuiPreferences * form)
2794         : PrefModule(catEditing, N_("Control"), form)
2795 {
2796         setupUi(this);
2797
2798         connect(cursorFollowsCB, SIGNAL(clicked()),
2799                 this, SIGNAL(changed()));
2800         connect(scrollBelowCB, SIGNAL(clicked()),
2801                 this, SIGNAL(changed()));
2802         connect(macLikeCursorMovementCB, SIGNAL(clicked()),
2803                 this, SIGNAL(changed()));
2804         connect(copyCTMarkupCB, SIGNAL(clicked()),
2805                 this, SIGNAL(changed()));
2806         connect(sortEnvironmentsCB, SIGNAL(clicked()),
2807                 this, SIGNAL(changed()));
2808         connect(groupEnvironmentsCB, SIGNAL(clicked()),
2809                 this, SIGNAL(changed()));
2810         connect(macroEditStyleCO, SIGNAL(activated(int)),
2811                 this, SIGNAL(changed()));
2812         connect(cursorWidthSB, SIGNAL(valueChanged(int)),
2813                 this, SIGNAL(changed()));
2814         connect(citationSearchLE, SIGNAL(textChanged(QString)),
2815                 this, SIGNAL(changed()));
2816         connect(fullscreenWidthSB, SIGNAL(valueChanged(int)),
2817                 this, SIGNAL(changed()));
2818         connect(toggleTabbarCB, SIGNAL(toggled(bool)),
2819                 this, SIGNAL(changed()));
2820         connect(toggleMenubarCB, SIGNAL(toggled(bool)),
2821                 this, SIGNAL(changed()));
2822         connect(toggleScrollbarCB, SIGNAL(toggled(bool)),
2823                 this, SIGNAL(changed()));
2824         connect(toggleStatusbarCB, SIGNAL(toggled(bool)),
2825                 this, SIGNAL(changed()));
2826         connect(toggleToolbarsCB, SIGNAL(toggled(bool)),
2827                 this, SIGNAL(changed()));
2828 }
2829
2830
2831 void PrefEdit::on_fullscreenLimitCB_toggled(bool const state)
2832 {
2833         fullscreenWidthSB->setEnabled(state);
2834         fullscreenWidthLA->setEnabled(state);
2835         changed();
2836 }
2837
2838
2839 void PrefEdit::on_citationSearchCB_toggled(bool const state)
2840 {
2841         citationSearchLE->setEnabled(state);
2842         citationSearchLA->setEnabled(state);
2843         changed();
2844 }
2845
2846
2847 void PrefEdit::applyRC(LyXRC & rc) const
2848 {
2849         rc.cursor_follows_scrollbar = cursorFollowsCB->isChecked();
2850         rc.scroll_below_document = scrollBelowCB->isChecked();
2851         rc.mac_like_cursor_movement = macLikeCursorMovementCB->isChecked();
2852         rc.ct_markup_copied = copyCTMarkupCB->isChecked();
2853         rc.sort_layouts = sortEnvironmentsCB->isChecked();
2854         rc.group_layouts = groupEnvironmentsCB->isChecked();
2855         switch (macroEditStyleCO->currentIndex()) {
2856                 case 0: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE_BOX; break;
2857                 case 1: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE; break;
2858                 case 2: rc.macro_edit_style = LyXRC::MACRO_EDIT_LIST;   break;
2859         }
2860         rc.cursor_width = cursorWidthSB->value();
2861         rc.citation_search = citationSearchCB->isChecked();
2862         rc.citation_search_pattern = fromqstr(citationSearchLE->text());
2863         rc.full_screen_toolbars = toggleToolbarsCB->isChecked();
2864         rc.full_screen_scrollbar = toggleScrollbarCB->isChecked();
2865         rc.full_screen_statusbar = toggleStatusbarCB->isChecked();
2866         rc.full_screen_tabbar = toggleTabbarCB->isChecked();
2867         rc.full_screen_menubar = toggleMenubarCB->isChecked();
2868         rc.full_screen_width = fullscreenWidthSB->value();
2869         rc.full_screen_limit = fullscreenLimitCB->isChecked();
2870 }
2871
2872
2873 void PrefEdit::updateRC(LyXRC const & rc)
2874 {
2875         cursorFollowsCB->setChecked(rc.cursor_follows_scrollbar);
2876         scrollBelowCB->setChecked(rc.scroll_below_document);
2877         macLikeCursorMovementCB->setChecked(rc.mac_like_cursor_movement);
2878         copyCTMarkupCB->setChecked(rc.ct_markup_copied);
2879         sortEnvironmentsCB->setChecked(rc.sort_layouts);
2880         groupEnvironmentsCB->setChecked(rc.group_layouts);
2881         macroEditStyleCO->setCurrentIndex(rc.macro_edit_style);
2882         cursorWidthSB->setValue(rc.cursor_width);
2883         citationSearchCB->setChecked(rc.citation_search);
2884         citationSearchLE->setText(toqstr(rc.citation_search_pattern));
2885         citationSearchLE->setEnabled(rc.citation_search);
2886         citationSearchLA->setEnabled(rc.citation_search);
2887         toggleScrollbarCB->setChecked(rc.full_screen_scrollbar);
2888         toggleStatusbarCB->setChecked(rc.full_screen_statusbar);
2889         toggleToolbarsCB->setChecked(rc.full_screen_toolbars);
2890         toggleTabbarCB->setChecked(rc.full_screen_tabbar);
2891         toggleMenubarCB->setChecked(rc.full_screen_menubar);
2892         fullscreenWidthSB->setValue(rc.full_screen_width);
2893         fullscreenLimitCB->setChecked(rc.full_screen_limit);
2894         fullscreenWidthSB->setEnabled(rc.full_screen_limit);
2895         fullscreenWidthLA->setEnabled(rc.full_screen_limit);
2896 }
2897
2898
2899 /////////////////////////////////////////////////////////////////////
2900 //
2901 // PrefShortcuts
2902 //
2903 /////////////////////////////////////////////////////////////////////
2904
2905
2906 GuiShortcutDialog::GuiShortcutDialog(QWidget * parent) : QDialog(parent)
2907 {
2908         Ui::shortcutUi::setupUi(this);
2909         QDialog::setModal(true);
2910         lfunLE->setValidator(new NoNewLineValidator(lfunLE));
2911 }
2912
2913
2914 PrefShortcuts::PrefShortcuts(GuiPreferences * form)
2915         : PrefModule(catEditing, N_("Shortcuts"), form),
2916           editItem_(nullptr), mathItem_(nullptr), bufferItem_(nullptr), layoutItem_(nullptr),
2917           systemItem_(nullptr)
2918 {
2919         setupUi(this);
2920
2921         shortcutsTW->setColumnCount(2);
2922         shortcutsTW->headerItem()->setText(0, qt_("Function"));
2923         shortcutsTW->headerItem()->setText(1, qt_("Shortcut"));
2924         shortcutsTW->setSortingEnabled(true);
2925         // Multi-selection can be annoying.
2926         // shortcutsTW->setSelectionMode(QAbstractItemView::MultiSelection);
2927
2928         connect(bindFilePB, SIGNAL(clicked()),
2929                 this, SLOT(selectBind()));
2930         connect(bindFileED, SIGNAL(textChanged(QString)),
2931                 this, SIGNAL(changed()));
2932
2933         shortcut_ = new GuiShortcutDialog(this);
2934         shortcut_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
2935         shortcut_bc_.setOK(shortcut_->buttonBox->button(QDialogButtonBox::Ok));
2936         shortcut_bc_.setCancel(shortcut_->buttonBox->button(QDialogButtonBox::Cancel));
2937
2938         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2939                 this, SIGNAL(changed()));
2940         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2941                 shortcut_, SLOT(reject()));
2942         connect(shortcut_->clearPB, SIGNAL(clicked()),
2943                 this, SLOT(shortcutClearPressed()));
2944         connect(shortcut_->removePB, SIGNAL(clicked()),
2945                 this, SLOT(shortcutRemovePressed()));
2946         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2947                 this, SLOT(shortcutOkPressed()));
2948         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2949                 this, SLOT(shortcutCancelPressed()));
2950 }
2951
2952
2953 void PrefShortcuts::applyRC(LyXRC & rc) const
2954 {
2955         rc.bind_file = internal_path(fromqstr(bindFileED->text()));
2956         // write user_bind and user_unbind to .lyx/bind/user.bind
2957         FileName bind_dir(addPath(package().user_support().absFileName(), "bind"));
2958         if (!bind_dir.exists() && !bind_dir.createDirectory(0777)) {
2959                 lyxerr << "LyX could not create the user bind directory '"
2960                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2961                 return;
2962         }
2963         if (!bind_dir.isDirWritable()) {
2964                 lyxerr << "LyX could not write to the user bind directory '"
2965                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2966                 return;
2967         }
2968         FileName user_bind_file(bind_dir.absFileName() + "/user.bind");
2969         user_unbind_.write(user_bind_file.toFilesystemEncoding(), false, true);
2970         user_bind_.write(user_bind_file.toFilesystemEncoding(), true, false);
2971         // immediately apply the keybindings. Why this is not done before?
2972         // The good thing is that the menus are updated automatically.
2973         theTopLevelKeymap().clear();
2974         theTopLevelKeymap().read("site");
2975         theTopLevelKeymap().read(rc.bind_file, nullptr, KeyMap::Fallback);
2976         theTopLevelKeymap().read("user", nullptr, KeyMap::MissingOK);
2977 }
2978
2979
2980 void PrefShortcuts::updateRC(LyXRC const & rc)
2981 {
2982         bindFileED->setText(toqstr(external_path(rc.bind_file)));
2983         //
2984         system_bind_.clear();
2985         user_bind_.clear();
2986         user_unbind_.clear();
2987         system_bind_.read("site");
2988         system_bind_.read(rc.bind_file);
2989         // \unbind in user.bind is added to user_unbind_
2990         user_bind_.read("user", &user_unbind_, KeyMap::MissingOK);
2991         updateShortcutsTW();
2992 }
2993
2994
2995 void PrefShortcuts::updateShortcutsTW()
2996 {
2997         shortcutsTW->clear();
2998
2999         editItem_ = new QTreeWidgetItem(shortcutsTW);
3000         editItem_->setText(0, qt_("Cursor, Mouse and Editing Functions"));
3001         editItem_->setFlags(editItem_->flags() & ~Qt::ItemIsSelectable);
3002
3003         mathItem_ = new QTreeWidgetItem(shortcutsTW);
3004         mathItem_->setText(0, qt_("Mathematical Symbols"));
3005         mathItem_->setFlags(mathItem_->flags() & ~Qt::ItemIsSelectable);
3006
3007         bufferItem_ = new QTreeWidgetItem(shortcutsTW);
3008         bufferItem_->setText(0, qt_("Document and Window"));
3009         bufferItem_->setFlags(bufferItem_->flags() & ~Qt::ItemIsSelectable);
3010
3011         layoutItem_ = new QTreeWidgetItem(shortcutsTW);
3012         layoutItem_->setText(0, qt_("Font, Layouts and Textclasses"));
3013         layoutItem_->setFlags(layoutItem_->flags() & ~Qt::ItemIsSelectable);
3014
3015         systemItem_ = new QTreeWidgetItem(shortcutsTW);
3016         systemItem_->setText(0, qt_("System and Miscellaneous"));
3017         systemItem_->setFlags(systemItem_->flags() & ~Qt::ItemIsSelectable);
3018
3019         // listBindings(unbound=true) lists all bound and unbound lfuns
3020         // Items in this list is tagged by its source.
3021         KeyMap::BindingList bindinglist = system_bind_.listBindings(true,
3022                 KeyMap::System);
3023         KeyMap::BindingList user_bindinglist = user_bind_.listBindings(false,
3024                 KeyMap::UserBind);
3025         KeyMap::BindingList user_unbindinglist = user_unbind_.listBindings(false,
3026                 KeyMap::UserUnbind);
3027         bindinglist.insert(bindinglist.end(), user_bindinglist.begin(),
3028                         user_bindinglist.end());
3029         bindinglist.insert(bindinglist.end(), user_unbindinglist.begin(),
3030                         user_unbindinglist.end());
3031
3032         KeyMap::BindingList::const_iterator it = bindinglist.begin();
3033         KeyMap::BindingList::const_iterator it_end = bindinglist.end();
3034         for (; it != it_end; ++it)
3035                 insertShortcutItem(it->request, it->sequence, it->tag);
3036
3037         shortcutsTW->sortItems(0, Qt::AscendingOrder);
3038         on_shortcutsTW_itemSelectionChanged();
3039         on_searchLE_textEdited();
3040         shortcutsTW->resizeColumnToContents(0);
3041 }
3042
3043
3044 //static
3045 KeyMap::ItemType PrefShortcuts::itemType(QTreeWidgetItem & item)
3046 {
3047         return static_cast<KeyMap::ItemType>(item.data(0, Qt::UserRole).toInt());
3048 }
3049
3050
3051 //static
3052 bool PrefShortcuts::isAlwaysHidden(QTreeWidgetItem & item)
3053 {
3054         // Hide rebound system settings that are empty
3055         return itemType(item) == KeyMap::UserUnbind && item.text(1).isEmpty();
3056 }
3057
3058
3059 void PrefShortcuts::setItemType(QTreeWidgetItem * item, KeyMap::ItemType tag)
3060 {
3061         item->setData(0, Qt::UserRole, QVariant(tag));
3062         QFont font;
3063
3064         switch (tag) {
3065         case KeyMap::System:
3066                 break;
3067         case KeyMap::UserBind:
3068                 font.setBold(true);
3069                 break;
3070         case KeyMap::UserUnbind:
3071                 font.setStrikeOut(true);
3072                 break;
3073         // this item is not displayed now.
3074         case KeyMap::UserExtraUnbind:
3075                 font.setStrikeOut(true);
3076                 break;
3077         }
3078         item->setHidden(isAlwaysHidden(*item));
3079         item->setFont(1, font);
3080 }
3081
3082
3083 QTreeWidgetItem * PrefShortcuts::insertShortcutItem(FuncRequest const & lfun,
3084                 KeySequence const & seq, KeyMap::ItemType tag)
3085 {
3086         FuncCode const action = lfun.action();
3087         string const action_name = lyxaction.getActionName(action);
3088         QString const lfun_name = toqstr(from_utf8(action_name)
3089                         + ' ' + lfun.argument());
3090         QString const shortcut = toqstr(seq.print(KeySequence::ForGui));
3091
3092         QTreeWidgetItem * newItem = nullptr;
3093         // for unbind items, try to find an existing item in the system bind list
3094         if (tag == KeyMap::UserUnbind) {
3095                 QList<QTreeWidgetItem*> const items = shortcutsTW->findItems(lfun_name,
3096                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
3097                 for (auto const & item : items) {
3098                         if (item->text(1) == shortcut) {
3099                                 newItem = item;
3100                                 break;
3101                         }
3102                 }
3103                 // if not found, this unbind item is KeyMap::UserExtraUnbind
3104                 // Such an item is not displayed to avoid confusion (what is
3105                 // unmatched removed?).
3106                 if (!newItem) {
3107                         return nullptr;
3108                 }
3109         }
3110         if (!newItem) {
3111                 switch(lyxaction.getActionType(action)) {
3112                 case LyXAction::Hidden:
3113                         return nullptr;
3114                 case LyXAction::Edit:
3115                         newItem = new QTreeWidgetItem(editItem_);
3116                         break;
3117                 case LyXAction::Math:
3118                         newItem = new QTreeWidgetItem(mathItem_);
3119                         break;
3120                 case LyXAction::Buffer:
3121                         newItem = new QTreeWidgetItem(bufferItem_);
3122                         break;
3123                 case LyXAction::Layout:
3124                         newItem = new QTreeWidgetItem(layoutItem_);
3125                         break;
3126                 case LyXAction::System:
3127                         newItem = new QTreeWidgetItem(systemItem_);
3128                         break;
3129                 default:
3130                         // this should not happen
3131                         newItem = new QTreeWidgetItem(shortcutsTW);
3132                 }
3133         }
3134
3135         newItem->setText(0, lfun_name);
3136         newItem->setText(1, shortcut);
3137         // record BindFile representation to recover KeySequence when needed.
3138         newItem->setData(1, Qt::UserRole, toqstr(seq.print(KeySequence::BindFile)));
3139         setItemType(newItem, tag);
3140         return newItem;
3141 }
3142
3143
3144 void PrefShortcuts::on_shortcutsTW_itemSelectionChanged()
3145 {
3146         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3147         removePB->setEnabled(!items.isEmpty() && !items[0]->text(1).isEmpty());
3148         modifyPB->setEnabled(!items.isEmpty());
3149         if (items.isEmpty())
3150                 return;
3151
3152         if (itemType(*items[0]) == KeyMap::UserUnbind)
3153                 removePB->setText(qt_("Res&tore"));
3154         else
3155                 removePB->setText(qt_("Remo&ve"));
3156 }
3157
3158
3159 void PrefShortcuts::on_shortcutsTW_itemDoubleClicked()
3160 {
3161         modifyShortcut();
3162 }
3163
3164
3165 void PrefShortcuts::modifyShortcut()
3166 {
3167         QTreeWidgetItem * item = shortcutsTW->currentItem();
3168         if (item->flags() & Qt::ItemIsSelectable) {
3169                 shortcut_->lfunLE->setText(item->text(0));
3170                 save_lfun_ = item->text(0).trimmed();
3171                 shortcut_->shortcutWG->setText(item->text(1));
3172                 KeySequence seq;
3173                 seq.parse(fromqstr(item->data(1, Qt::UserRole).toString()));
3174                 shortcut_->shortcutWG->setKeySequence(seq);
3175                 shortcut_->shortcutWG->setFocus();
3176                 shortcut_->exec();
3177         }
3178 }
3179
3180
3181 void PrefShortcuts::unhideEmpty(QString const & lfun, bool select)
3182 {
3183         // list of items that match lfun
3184         QList<QTreeWidgetItem*> items = shortcutsTW->findItems(lfun,
3185              Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
3186         for (auto const & item : items) {
3187                 if (isAlwaysHidden(*item)) {
3188                         setItemType(item, KeyMap::System);
3189                         if (select)
3190                                 shortcutsTW->setCurrentItem(item);
3191                         return;
3192                 }
3193         }
3194 }
3195
3196
3197 void PrefShortcuts::removeShortcut()
3198 {
3199         // it seems that only one item can be selected, but I am
3200         // removing all selected items anyway.
3201         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3202         for (auto & item : items) {
3203                 string shortcut = fromqstr(item->data(1, Qt::UserRole).toString());
3204                 string lfun = fromqstr(item->text(0));
3205                 FuncRequest const func = lyxaction.lookupFunc(lfun);
3206
3207                 switch (itemType(*item)) {
3208                 case KeyMap::System: {
3209                         // for system bind, we do not touch the item
3210                         // but add an user unbind item
3211                         user_unbind_.bind(shortcut, func);
3212                         setItemType(item, KeyMap::UserUnbind);
3213                         removePB->setText(qt_("Res&tore"));
3214                         break;
3215                 }
3216                 case KeyMap::UserBind: {
3217                         // for user_bind, we remove this bind
3218                         QTreeWidgetItem * parent = item->parent();
3219                         int itemIdx = parent->indexOfChild(item);
3220                         parent->takeChild(itemIdx);
3221                         if (itemIdx > 0)
3222                                 shortcutsTW->scrollToItem(parent->child(itemIdx - 1));
3223                         else
3224                                 shortcutsTW->scrollToItem(parent);
3225                         user_bind_.unbind(shortcut, func);
3226                         // If this user binding hid an empty system binding, unhide the
3227                         // latter and select it.
3228                         unhideEmpty(item->text(0), true);
3229                         break;
3230                 }
3231                 case KeyMap::UserUnbind: {
3232                         // for user_unbind, we remove the unbind, and the item
3233                         // become KeyMap::System again.
3234                         KeySequence seq;
3235                         seq.parse(shortcut);
3236                         // Ask the user to replace current binding
3237                         if (!validateNewShortcut(func, seq, QString()))
3238                                 break;
3239                         user_unbind_.unbind(shortcut, func);
3240                         setItemType(item, KeyMap::System);
3241                         removePB->setText(qt_("Remo&ve"));
3242                         break;
3243                 }
3244                 case KeyMap::UserExtraUnbind: {
3245                         // for user unbind that is not in system bind file,
3246                         // remove this unbind file
3247                         QTreeWidgetItem * parent = item->parent();
3248                         parent->takeChild(parent->indexOfChild(item));
3249                         user_unbind_.unbind(shortcut, func);
3250                 }
3251                 }
3252         }
3253 }
3254
3255
3256 void PrefShortcuts::deactivateShortcuts(QList<QTreeWidgetItem*> const & items)
3257 {
3258         for (auto item : items) {
3259                 string shortcut = fromqstr(item->data(1, Qt::UserRole).toString());
3260                 string lfun = fromqstr(item->text(0));
3261                 FuncRequest const func = lyxaction.lookupFunc(lfun);
3262
3263                 switch (itemType(*item)) {
3264                 case KeyMap::System:
3265                         // for system bind, we do not touch the item
3266                         // but add an user unbind item
3267                         user_unbind_.bind(shortcut, func);
3268                         setItemType(item, KeyMap::UserUnbind);
3269                         break;
3270
3271                 case KeyMap::UserBind: {
3272                         // for user_bind, we remove this bind
3273                         QTreeWidgetItem * parent = item->parent();
3274                         int itemIdx = parent->indexOfChild(item);
3275                         parent->takeChild(itemIdx);
3276                         user_bind_.unbind(shortcut, func);
3277                         unhideEmpty(item->text(0), false);
3278                         break;
3279                 }
3280                 default:
3281                         break;
3282                 }
3283         }
3284 }
3285
3286
3287 void PrefShortcuts::selectBind()
3288 {
3289         QString file = form_->browsebind(internalPath(bindFileED->text()));
3290         if (!file.isEmpty()) {
3291                 bindFileED->setText(file);
3292                 system_bind_ = KeyMap();
3293                 system_bind_.read(fromqstr(file));
3294                 updateShortcutsTW();
3295         }
3296 }
3297
3298
3299 void PrefShortcuts::on_modifyPB_pressed()
3300 {
3301         modifyShortcut();
3302 }
3303
3304
3305 void PrefShortcuts::on_newPB_pressed()
3306 {
3307         shortcut_->lfunLE->clear();
3308         shortcut_->shortcutWG->reset();
3309         save_lfun_ = QString();
3310         shortcut_->exec();
3311 }
3312
3313
3314 void PrefShortcuts::on_removePB_pressed()
3315 {
3316         changed();
3317         removeShortcut();
3318 }
3319
3320
3321 void PrefShortcuts::on_searchLE_textEdited()
3322 {
3323         if (searchLE->text().isEmpty()) {
3324                 // show all hidden items
3325                 QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Hidden);
3326                 for (; *it; ++it)
3327                         (*it)->setHidden(isAlwaysHidden(**it));
3328                 // close all categories
3329                 for (int i = 0; i < shortcutsTW->topLevelItemCount(); ++i)
3330                         shortcutsTW->collapseItem(shortcutsTW->topLevelItem(i));
3331                 return;
3332         }
3333         // search both columns
3334         QList<QTreeWidgetItem *> matched = shortcutsTW->findItems(searchLE->text(),
3335                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 0);
3336         matched += shortcutsTW->findItems(searchLE->text(),
3337                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 1);
3338
3339         // hide everyone (to avoid searching in matched QList repeatedly
3340         QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Selectable);
3341         while (*it)
3342                 (*it++)->setHidden(true);
3343         // show matched items
3344         for (auto & item : matched)
3345                 if (!isAlwaysHidden(*item)) {
3346                         item->setHidden(false);
3347                         if (item->parent())
3348                                 item->parent()->setExpanded(true);
3349                 }
3350 }
3351
3352
3353 docstring makeCmdString(FuncRequest const & f)
3354 {
3355         docstring actionStr = from_ascii(lyxaction.getActionName(f.action()));
3356         if (!f.argument().empty())
3357                 actionStr += " " + f.argument();
3358         return actionStr;
3359 }
3360
3361
3362 FuncRequest PrefShortcuts::currentBinding(KeySequence const & k)
3363 {
3364         FuncRequest res = user_bind_.getBinding(k);
3365         if (res.action() != LFUN_UNKNOWN_ACTION)
3366                 return res;
3367         res = system_bind_.getBinding(k);
3368         // Check if it is unbound. Note: user_unbind_ can only unbind one
3369         // FuncRequest per key sequence.
3370         if (user_unbind_.getBinding(k) == res)
3371                 return FuncRequest::unknown;
3372         return res;
3373 }
3374
3375
3376 bool PrefShortcuts::validateNewShortcut(FuncRequest const & func,
3377                                         KeySequence const & k,
3378                                         QString const & lfun_to_modify)
3379 {
3380         if (func.action() == LFUN_UNKNOWN_ACTION) {
3381                 Alert::error(_("Failed to create shortcut"),
3382                         _("Unknown or invalid LyX function"));
3383                 return false;
3384         }
3385
3386         // It is not currently possible to bind Hidden lfuns such as self-insert. In
3387         // the future, to remove this limitation, see GuiPrefs::insertShortcutItem
3388         // and how it is used in GuiPrefs::shortcutOkPressed.
3389         if (lyxaction.getActionType(func.action()) == LyXAction::Hidden) {
3390                 Alert::error(_("Failed to create shortcut"),
3391                         _("This LyX function is hidden and cannot be bound."));
3392                 return false;
3393         }
3394
3395         if (k.length() == 0) {
3396                 Alert::error(_("Failed to create shortcut"),
3397                         _("Invalid or empty key sequence"));
3398                 return false;
3399         }
3400
3401         FuncRequest oldBinding = currentBinding(k);
3402         if (oldBinding == func)
3403                 // nothing to change
3404                 return false;
3405
3406         // make sure this key isn't already bound---and, if so, prompt user
3407         // (exclude the lfun the user already wants to modify)
3408         docstring const action_string = makeCmdString(oldBinding);
3409         if (oldBinding.action() != LFUN_UNKNOWN_ACTION
3410             && lfun_to_modify != toqstr(action_string)) {
3411                 docstring const new_action_string = makeCmdString(func);
3412                 docstring const text = bformat(_("Shortcut `%1$s' is already bound to "
3413                                                  "%2$s.\n"
3414                                                  "Are you sure you want to unbind the "
3415                                                  "current shortcut and bind it to %3$s?"),
3416                                                k.print(KeySequence::ForGui), action_string,
3417                                                new_action_string);
3418                 int ret = Alert::prompt(_("Redefine shortcut?"),
3419                                         text, 0, 1, _("&Redefine"), _("&Cancel"));
3420                 if (ret != 0)
3421                         return false;
3422                 QString const sequence_text = toqstr(k.print(KeySequence::ForGui));
3423                 QList<QTreeWidgetItem*> items = shortcutsTW->findItems(sequence_text,
3424                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 1);
3425                 deactivateShortcuts(items);
3426         }
3427         return true;
3428 }
3429
3430
3431 void PrefShortcuts::shortcutOkPressed()
3432 {
3433         QString const new_lfun = shortcut_->lfunLE->text();
3434         FuncRequest const func = lyxaction.lookupFunc(fromqstr(new_lfun));
3435         KeySequence k = shortcut_->shortcutWG->getKeySequence();
3436
3437         // save_lfun_ contains the text of the lfun to modify, if the user clicked
3438         // "modify", or is empty if they clicked "new" (which I do not really like)
3439         if (!validateNewShortcut(func, k, save_lfun_))
3440                 return;
3441
3442         if (!save_lfun_.isEmpty()) {
3443                 // real modification of the lfun's shortcut,
3444                 // so remove the previous one
3445                 QList<QTreeWidgetItem*> to_modify = shortcutsTW->selectedItems();
3446                 deactivateShortcuts(to_modify);
3447         }
3448
3449         shortcut_->accept();
3450
3451         QTreeWidgetItem * item = insertShortcutItem(func, k, KeyMap::UserBind);
3452         if (item) {
3453                 user_bind_.bind(&k, func);
3454                 shortcutsTW->sortItems(0, Qt::AscendingOrder);
3455                 if (item->parent())
3456                         item->parent()->setExpanded(true);
3457                 shortcutsTW->setCurrentItem(item);
3458                 shortcutsTW->scrollToItem(item);
3459         } else {
3460                 Alert::error(_("Failed to create shortcut"),
3461                         _("Can not insert shortcut to the list"));
3462                 return;
3463         }
3464 }
3465
3466
3467 void PrefShortcuts::shortcutCancelPressed()
3468 {
3469         shortcut_->shortcutWG->reset();
3470 }
3471
3472
3473 void PrefShortcuts::shortcutClearPressed()
3474 {
3475         shortcut_->shortcutWG->reset();
3476 }
3477
3478
3479 void PrefShortcuts::shortcutRemovePressed()
3480 {
3481         shortcut_->shortcutWG->removeFromSequence();
3482 }
3483
3484
3485 /////////////////////////////////////////////////////////////////////
3486 //
3487 // PrefIdentity
3488 //
3489 /////////////////////////////////////////////////////////////////////
3490
3491 PrefIdentity::PrefIdentity(GuiPreferences * form)
3492         : PrefModule(QString(), N_("Identity"), form)
3493 {
3494         setupUi(this);
3495
3496         connect(nameED, SIGNAL(textChanged(QString)),
3497                 this, SIGNAL(changed()));
3498         connect(emailED, SIGNAL(textChanged(QString)),
3499                 this, SIGNAL(changed()));
3500         connect(initialsED, SIGNAL(textChanged(QString)),
3501                 this, SIGNAL(changed()));
3502
3503         nameED->setValidator(new NoNewLineValidator(nameED));
3504         emailED->setValidator(new NoNewLineValidator(emailED));
3505         initialsED->setValidator(new NoNewLineValidator(initialsED));
3506 }
3507
3508
3509 void PrefIdentity::applyRC(LyXRC & rc) const
3510 {
3511         rc.user_name = fromqstr(nameED->text());
3512         rc.user_email = fromqstr(emailED->text());
3513         rc.user_initials = fromqstr(initialsED->text());
3514 }
3515
3516
3517 void PrefIdentity::updateRC(LyXRC const & rc)
3518 {
3519         nameED->setText(toqstr(rc.user_name));
3520         emailED->setText(toqstr(rc.user_email));
3521         initialsED->setText(toqstr(rc.user_initials));
3522 }
3523
3524
3525
3526 /////////////////////////////////////////////////////////////////////
3527 //
3528 // GuiPreferences
3529 //
3530 /////////////////////////////////////////////////////////////////////
3531
3532 GuiPreferences::GuiPreferences(GuiView & lv)
3533         : GuiDialog(lv, "prefs", qt_("Preferences"))
3534 {
3535         setupUi(this);
3536
3537         QDialog::setModal(false);
3538
3539         connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
3540                 this, SLOT(slotButtonBox(QAbstractButton *)));
3541
3542         addModule(new PrefUserInterface(this));
3543         addModule(new PrefDocHandling(this));
3544         addModule(new PrefEdit(this));
3545         addModule(new PrefShortcuts(this));
3546         PrefScreenFonts * screenfonts = new PrefScreenFonts(this);
3547         connect(this, SIGNAL(prefsApplied(LyXRC const &)),
3548                         screenfonts, SLOT(updateScreenFontSizes(LyXRC const &)));
3549         addModule(screenfonts);
3550         addModule(new PrefColors(this));
3551         addModule(new PrefDisplay(this));
3552         addModule(new PrefInput(this));
3553         addModule(new PrefCompletion(this));
3554
3555         addModule(new PrefPaths(this));
3556
3557         addModule(new PrefIdentity(this));
3558
3559         addModule(new PrefLanguage(this));
3560         addModule(new PrefSpellchecker(this));
3561
3562         PrefOutput * output = new PrefOutput(this);
3563         addModule(output);
3564         addModule(new PrefLatex(this));
3565
3566         PrefConverters * converters = new PrefConverters(this);
3567         PrefFileformats * formats = new PrefFileformats(this);
3568         connect(formats, SIGNAL(formatsChanged()),
3569                         converters, SLOT(updateGui()));
3570         addModule(converters);
3571         addModule(formats);
3572
3573         prefsPS->setCurrentPanel("User Interface");
3574 // FIXME: hack to work around resizing bug in Qt >= 4.2
3575 // bug verified with Qt 4.2.{0-3} (JSpitzm)
3576 #if QT_VERSION >= 0x040200
3577         prefsPS->updateGeometry();
3578 #endif
3579
3580         bc().setPolicy(ButtonPolicy::PreferencesPolicy);
3581         bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
3582         bc().setApply(buttonBox->button(QDialogButtonBox::Apply));
3583         bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
3584         bc().setRestore(buttonBox->button(QDialogButtonBox::Reset));
3585
3586         guilyxfiles_ = new GuiLyXFiles(lv);
3587         connect(guilyxfiles_, SIGNAL(fileSelected(QString)),
3588                         this, SLOT(slotFileSelected(QString)));
3589 }
3590
3591
3592 void GuiPreferences::addModule(PrefModule * module)
3593 {
3594         LASSERT(module, return);
3595         if (module->category().isEmpty())
3596                 prefsPS->addPanel(module, module->title());
3597         else
3598                 prefsPS->addPanel(module, module->title(), module->category());
3599         connect(module, SIGNAL(changed()), this, SLOT(change_adaptor()));
3600         modules_.push_back(module);
3601 }
3602
3603
3604 void GuiPreferences::change_adaptor()
3605 {
3606         changed();
3607 }
3608
3609
3610 void GuiPreferences::applyRC(LyXRC & rc) const
3611 {
3612         size_t end = modules_.size();
3613         for (size_t i = 0; i != end; ++i)
3614                 modules_[i]->applyRC(rc);
3615 }
3616
3617
3618 void GuiPreferences::updateRC(LyXRC const & rc)
3619 {
3620         size_t const end = modules_.size();
3621         for (size_t i = 0; i != end; ++i)
3622                 modules_[i]->updateRC(rc);
3623 }
3624
3625
3626 void GuiPreferences::applyView()
3627 {
3628         applyRC(rc());
3629 }
3630
3631
3632 bool GuiPreferences::initialiseParams(string const &)
3633 {
3634         rc_ = lyxrc;
3635         formats_ = theFormats();
3636         converters_ = theConverters();
3637         converters_.update(formats_);
3638         movers_ = theMovers();
3639         colors_.clear();
3640
3641         updateRC(rc_);
3642         // Make sure that the bc is in the INITIAL state
3643         if (bc().policy().buttonStatus(ButtonPolicy::RESTORE))
3644                 bc().restore();
3645
3646         return true;
3647 }
3648
3649
3650 void GuiPreferences::dispatchParams()
3651 {
3652         ostringstream ss;
3653         rc_.write(ss, true);
3654         dispatch(FuncRequest(LFUN_LYXRC_APPLY, ss.str()));
3655         // issue prefsApplied signal. This will update the
3656         // localized screen font sizes.
3657         prefsApplied(rc_);
3658         // FIXME: these need lfuns
3659         // FIXME UNICODE
3660         Author const & author =
3661                 Author(from_utf8(rc_.user_name), from_utf8(rc_.user_email),
3662                        from_utf8(rc_.user_initials));
3663         theBufferList().recordCurrentAuthor(author);
3664
3665         theFormats() = formats_;
3666
3667         theConverters() = converters_;
3668         theConverters().update(formats_);
3669         theConverters().buildGraph();
3670         theBufferList().invalidateConverterCache();
3671
3672         theMovers() = movers_;
3673
3674         for (string const & color : colors_)
3675                 dispatch(FuncRequest(LFUN_SET_COLOR, color));
3676         colors_.clear();
3677
3678         // Save permanently
3679         if (!tempSaveCB->isChecked())
3680                 dispatch(FuncRequest(LFUN_PREFERENCES_SAVE));
3681 }
3682
3683
3684 void GuiPreferences::setColor(ColorCode col, QString const & hex)
3685 {
3686         colors_.push_back(lcolor.getLyXName(col) + ' ' + fromqstr(hex));
3687 }
3688
3689
3690 void GuiPreferences::slotFileSelected(QString const file)
3691 {
3692         uifile_ = file;
3693 }
3694
3695
3696 QString GuiPreferences::browseLibFile(QString const & dir,
3697         QString const & name, QString const & ext)
3698 {
3699         uifile_.clear();
3700
3701         guilyxfiles_->passParams(fromqstr(dir));
3702         guilyxfiles_->selectItem(name);
3703         guilyxfiles_->exec();
3704
3705         QString const result = uifile_;
3706
3707         // remove the extension if it is the default one
3708         QString noextresult;
3709         if (getExtension(result) == ext)
3710                 noextresult = removeExtension(result);
3711         else
3712                 noextresult = result;
3713
3714         // remove the directory, if it is the default one
3715         QString const file = onlyFileName(noextresult);
3716         if (toqstr(libFileSearch(dir, file, ext).absFileName()) == result)
3717                 return file;
3718         else
3719                 return noextresult;
3720 }
3721
3722
3723 QString GuiPreferences::browsebind(QString const & file)
3724 {
3725         return browseLibFile("bind", file, "bind");
3726 }
3727
3728
3729 QString GuiPreferences::browseUI(QString const & file)
3730 {
3731         return browseLibFile("ui", file, "ui");
3732 }
3733
3734
3735 QString GuiPreferences::browsekbmap(QString const & file)
3736 {
3737         return browseLibFile("kbd", file, "kmap");
3738 }
3739
3740
3741 QString GuiPreferences::browse(QString const & file,
3742         QString const & title) const
3743 {
3744         return browseFile(file, title, QStringList(), true);
3745 }
3746
3747
3748 } // namespace frontend
3749 } // namespace lyx
3750
3751 #include "moc_GuiPrefs.cpp"