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