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