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