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