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