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