]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiPrefs.cpp
prefs/needauth: added warning if user tries to disable authorization for needauth...
[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.zoom = 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.zoom != oldrc.zoom) {
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.zoom);
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 void PrefConverters::updateRC(LyXRC const & rc)
1705 {
1706         cacheCB->setChecked(rc.use_converter_cache);
1707         needauthForbiddenCB->setChecked(rc.use_converter_needauth_forbidden);
1708         needauthCB->setChecked(rc.use_converter_needauth);
1709         QString max_age;
1710         doubleToWidget(maxAgeLE, (double(rc.converter_cache_maxage) / 86400.0), 'g', 6);
1711         updateGui();
1712 }
1713
1714
1715 void PrefConverters::updateGui()
1716 {
1717         QString const pattern("%1 -> %2");
1718         form_->formats().sort();
1719         form_->converters().update(form_->formats());
1720         // save current selection
1721         QString current =
1722                 pattern
1723                 .arg(converterFromCO->currentText())
1724                 .arg(converterToCO->currentText());
1725
1726         converterFromCO->clear();
1727         converterToCO->clear();
1728
1729         for (Format const & f : form_->formats()) {
1730                 QString const name = toqstr(translateIfPossible(f.prettyname()));
1731                 converterFromCO->addItem(name);
1732                 converterToCO->addItem(name);
1733         }
1734
1735         // currentRowChanged(int) is also triggered when updating the listwidget
1736         // block signals to avoid unnecessary calls to switchConverter()
1737         convertersLW->blockSignals(true);
1738         convertersLW->clear();
1739
1740         for (Converter const & c : form_->converters()) {
1741                 QString const name =
1742                         pattern
1743                         .arg(toqstr(translateIfPossible(c.From()->prettyname())))
1744                         .arg(toqstr(translateIfPossible(c.To()->prettyname())));
1745                 int type = form_->converters().getNumber(c.From()->name(),
1746                                                          c.To()->name());
1747                 new QListWidgetItem(name, convertersLW, type);
1748         }
1749         convertersLW->sortItems(Qt::AscendingOrder);
1750         convertersLW->blockSignals(false);
1751
1752         // restore selection
1753         if (current != pattern.arg(QString()).arg(QString())) {
1754                 QList<QListWidgetItem *> const item =
1755                         convertersLW->findItems(current, Qt::MatchExactly);
1756                 if (!item.isEmpty())
1757                         convertersLW->setCurrentItem(item.at(0));
1758         }
1759
1760         // select first element if restoring failed
1761         if (convertersLW->currentRow() == -1)
1762                 convertersLW->setCurrentRow(0);
1763
1764         updateButtons();
1765 }
1766
1767
1768 void PrefConverters::switchConverter()
1769 {
1770         int const cnr = convertersLW->currentItem()->type();
1771         Converter const & c(form_->converters().get(cnr));
1772         converterFromCO->setCurrentIndex(form_->formats().getNumber(c.from()));
1773         converterToCO->setCurrentIndex(form_->formats().getNumber(c.to()));
1774         converterED->setText(toqstr(c.command()));
1775         converterFlagED->setText(toqstr(c.flags()));
1776
1777         updateButtons();
1778 }
1779
1780
1781 void PrefConverters::changeConverter()
1782 {
1783         updateButtons();
1784 }
1785
1786
1787 void PrefConverters::updateButtons()
1788 {
1789         if (form_->formats().empty())
1790                 return;
1791         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1792         Format const & to = form_->formats().get(converterToCO->currentIndex());
1793         int const sel = form_->converters().getNumber(from.name(), to.name());
1794         bool const known = sel >= 0;
1795         bool const valid = !(converterED->text().isEmpty()
1796                 || from.name() == to.name());
1797
1798         string old_command;
1799         string old_flag;
1800
1801         if (convertersLW->count() > 0) {
1802                 int const cnr = convertersLW->currentItem()->type();
1803                 Converter const & c = form_->converters().get(cnr);
1804                 old_command = c.command();
1805                 old_flag = c.flags();
1806         }
1807
1808         string const new_command = fromqstr(converterED->text());
1809         string const new_flag = fromqstr(converterFlagED->text());
1810
1811         bool modified = (old_command != new_command || old_flag != new_flag);
1812
1813         converterModifyPB->setEnabled(valid && known && modified);
1814         converterNewPB->setEnabled(valid && !known);
1815         converterRemovePB->setEnabled(known);
1816
1817         maxAgeLE->setEnabled(cacheCB->isChecked());
1818         maxAgeLA->setEnabled(cacheCB->isChecked());
1819 }
1820
1821
1822 // FIXME: user must
1823 // specify unique from/to or it doesn't appear. This is really bad UI
1824 // this is why we can use the same function for both new and modify
1825 void PrefConverters::updateConverter()
1826 {
1827         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1828         Format const & to = form_->formats().get(converterToCO->currentIndex());
1829         string const flags = fromqstr(converterFlagED->text());
1830         string const command = fromqstr(converterED->text());
1831
1832         Converter const * old =
1833                 form_->converters().getConverter(from.name(), to.name());
1834         form_->converters().add(from.name(), to.name(), command, flags);
1835
1836         if (!old)
1837                 form_->converters().updateLast(form_->formats());
1838
1839         updateGui();
1840
1841         // Remove all files created by this converter from the cache, since
1842         // the modified converter might create different files.
1843         ConverterCache::get().remove_all(from.name(), to.name());
1844 }
1845
1846
1847 void PrefConverters::removeConverter()
1848 {
1849         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1850         Format const & to = form_->formats().get(converterToCO->currentIndex());
1851         form_->converters().erase(from.name(), to.name());
1852
1853         updateGui();
1854
1855         // Remove all files created by this converter from the cache, since
1856         // a possible new converter might create different files.
1857         ConverterCache::get().remove_all(from.name(), to.name());
1858 }
1859
1860
1861 void PrefConverters::on_cacheCB_stateChanged(int state)
1862 {
1863         maxAgeLE->setEnabled(state == Qt::Checked);
1864         maxAgeLA->setEnabled(state == Qt::Checked);
1865         changed();
1866 }
1867
1868
1869 void PrefConverters::on_needauthForbiddenCB_toggled(bool checked)
1870 {
1871         needauthCB->setEnabled(!checked);
1872 }
1873
1874
1875 void PrefConverters::on_needauthCB_toggled(bool checked)
1876 {
1877         if (checked)
1878                 return;
1879
1880         int ret = frontend::Alert::prompt(
1881                 _("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!"),
1882                 0, 0, _("&No"), _("&Yes"));
1883         if (ret == 1)
1884                 changed();
1885         else
1886                 needauthCB->setChecked(true);
1887 }
1888
1889
1890 /////////////////////////////////////////////////////////////////////
1891 //
1892 // FormatValidator
1893 //
1894 /////////////////////////////////////////////////////////////////////
1895
1896 class FormatValidator : public QValidator
1897 {
1898 public:
1899         FormatValidator(QWidget *, Formats const & f);
1900         void fixup(QString & input) const;
1901         QValidator::State validate(QString & input, int & pos) const;
1902 private:
1903         virtual QString toString(Format const & format) const = 0;
1904         int nr() const;
1905         Formats const & formats_;
1906 };
1907
1908
1909 FormatValidator::FormatValidator(QWidget * parent, Formats const & f)
1910         : QValidator(parent), formats_(f)
1911 {
1912 }
1913
1914
1915 void FormatValidator::fixup(QString & input) const
1916 {
1917         Formats::const_iterator cit = formats_.begin();
1918         Formats::const_iterator end = formats_.end();
1919         for (; cit != end; ++cit) {
1920                 QString const name = toString(*cit);
1921                 if (distance(formats_.begin(), cit) == nr()) {
1922                         input = name;
1923                         return;
1924                 }
1925         }
1926 }
1927
1928
1929 QValidator::State FormatValidator::validate(QString & input, int & /*pos*/) const
1930 {
1931         Formats::const_iterator cit = formats_.begin();
1932         Formats::const_iterator end = formats_.end();
1933         bool unknown = true;
1934         for (; unknown && cit != end; ++cit) {
1935                 QString const name = toString(*cit);
1936                 if (distance(formats_.begin(), cit) != nr())
1937                         unknown = name != input;
1938         }
1939
1940         if (unknown && !input.isEmpty())
1941                 return QValidator::Acceptable;
1942         else
1943                 return QValidator::Intermediate;
1944 }
1945
1946
1947 int FormatValidator::nr() const
1948 {
1949         QComboBox * p = qobject_cast<QComboBox *>(parent());
1950         return p->itemData(p->currentIndex()).toInt();
1951 }
1952
1953
1954 /////////////////////////////////////////////////////////////////////
1955 //
1956 // FormatNameValidator
1957 //
1958 /////////////////////////////////////////////////////////////////////
1959
1960 class FormatNameValidator : public FormatValidator
1961 {
1962 public:
1963         FormatNameValidator(QWidget * parent, Formats const & f)
1964                 : FormatValidator(parent, f)
1965         {}
1966 private:
1967         QString toString(Format const & format) const
1968         {
1969                 return toqstr(format.name());
1970         }
1971 };
1972
1973
1974 /////////////////////////////////////////////////////////////////////
1975 //
1976 // FormatPrettynameValidator
1977 //
1978 /////////////////////////////////////////////////////////////////////
1979
1980 class FormatPrettynameValidator : public FormatValidator
1981 {
1982 public:
1983         FormatPrettynameValidator(QWidget * parent, Formats const & f)
1984                 : FormatValidator(parent, f)
1985         {}
1986 private:
1987         QString toString(Format const & format) const
1988         {
1989                 return toqstr(translateIfPossible(format.prettyname()));
1990         }
1991 };
1992
1993
1994 /////////////////////////////////////////////////////////////////////
1995 //
1996 // PrefFileformats
1997 //
1998 /////////////////////////////////////////////////////////////////////
1999
2000 PrefFileformats::PrefFileformats(GuiPreferences * form)
2001         : PrefModule(catFiles, N_("File Formats"), form)
2002 {
2003         setupUi(this);
2004
2005         formatED->setValidator(new FormatNameValidator(formatsCB, form_->formats()));
2006         formatsCB->setValidator(new FormatPrettynameValidator(formatsCB, form_->formats()));
2007         extensionsED->setValidator(new NoNewLineValidator(extensionsED));
2008         shortcutED->setValidator(new NoNewLineValidator(shortcutED));
2009         editorED->setValidator(new NoNewLineValidator(editorED));
2010         viewerED->setValidator(new NoNewLineValidator(viewerED));
2011         copierED->setValidator(new NoNewLineValidator(copierED));
2012
2013         connect(documentCB, SIGNAL(clicked()),
2014                 this, SLOT(setFlags()));
2015         connect(vectorCB, SIGNAL(clicked()),
2016                 this, SLOT(setFlags()));
2017         connect(exportMenuCB, SIGNAL(clicked()),
2018                 this, SLOT(setFlags()));
2019         connect(formatsCB->lineEdit(), SIGNAL(editingFinished()),
2020                 this, SLOT(updatePrettyname()));
2021         connect(formatsCB->lineEdit(), SIGNAL(textEdited(QString)),
2022                 this, SIGNAL(changed()));
2023         connect(defaultFormatCB, SIGNAL(activated(QString)),
2024                 this, SIGNAL(changed()));
2025         connect(defaultOTFFormatCB, SIGNAL(activated(QString)),
2026                 this, SIGNAL(changed()));
2027         connect(viewerCO, SIGNAL(activated(int)),
2028                 this, SIGNAL(changed()));
2029         connect(editorCO, SIGNAL(activated(int)),
2030                 this, SIGNAL(changed()));
2031 }
2032
2033
2034 namespace {
2035
2036 string const l10n_shortcut(docstring const & prettyname, string const & shortcut)
2037 {
2038         if (shortcut.empty())
2039                 return string();
2040
2041         string l10n_format =
2042                 to_utf8(_(to_utf8(prettyname) + '|' + shortcut));
2043         return split(l10n_format, '|');
2044 }
2045
2046 } // namespace
2047
2048
2049 void PrefFileformats::applyRC(LyXRC & rc) const
2050 {
2051         QString const default_format = defaultFormatCB->itemData(
2052                 defaultFormatCB->currentIndex()).toString();
2053         rc.default_view_format = fromqstr(default_format);
2054         QString const default_otf_format = defaultOTFFormatCB->itemData(
2055                 defaultOTFFormatCB->currentIndex()).toString();
2056         rc.default_otf_view_format = fromqstr(default_otf_format);
2057 }
2058
2059
2060 void PrefFileformats::updateRC(LyXRC const & rc)
2061 {
2062         viewer_alternatives = rc.viewer_alternatives;
2063         editor_alternatives = rc.editor_alternatives;
2064         bool const init = defaultFormatCB->currentText().isEmpty();
2065         updateView();
2066         if (init) {
2067                 int pos =
2068                         defaultFormatCB->findData(toqstr(rc.default_view_format));
2069                 defaultFormatCB->setCurrentIndex(pos);
2070                 pos = defaultOTFFormatCB->findData(toqstr(rc.default_otf_view_format));
2071                                 defaultOTFFormatCB->setCurrentIndex(pos);
2072                 defaultOTFFormatCB->setCurrentIndex(pos);
2073         }
2074 }
2075
2076
2077 void PrefFileformats::updateView()
2078 {
2079         QString const current = formatsCB->currentText();
2080         QString const current_def = defaultFormatCB->currentText();
2081         QString const current_def_otf = defaultOTFFormatCB->currentText();
2082
2083         // update comboboxes with formats
2084         formatsCB->blockSignals(true);
2085         defaultFormatCB->blockSignals(true);
2086         defaultOTFFormatCB->blockSignals(true);
2087         formatsCB->clear();
2088         defaultFormatCB->clear();
2089         defaultOTFFormatCB->clear();
2090         form_->formats().sort();
2091         for (Format const & f : form_->formats()) {
2092                 QString const prettyname = toqstr(translateIfPossible(f.prettyname()));
2093                 formatsCB->addItem(prettyname,
2094                                    QVariant(form_->formats().getNumber(f.name())));
2095                 if (f.viewer().empty())
2096                         continue;
2097                 if (form_->converters().isReachable("xhtml", f.name())
2098                     || form_->converters().isReachable("dviluatex", f.name())
2099                     || form_->converters().isReachable("luatex", f.name())
2100                     || form_->converters().isReachable("xetex", f.name())) {
2101                         defaultFormatCB->addItem(prettyname,
2102                                         QVariant(toqstr(f.name())));
2103                         defaultOTFFormatCB->addItem(prettyname,
2104                                         QVariant(toqstr(f.name())));
2105                 } else if (form_->converters().isReachable("latex", f.name())
2106                            || form_->converters().isReachable("pdflatex", f.name()))
2107                         defaultFormatCB->addItem(prettyname,
2108                                         QVariant(toqstr(f.name())));
2109         }
2110
2111         // restore selections
2112         int item = formatsCB->findText(current, Qt::MatchExactly);
2113         formatsCB->setCurrentIndex(item < 0 ? 0 : item);
2114         on_formatsCB_currentIndexChanged(item < 0 ? 0 : item);
2115         item = defaultFormatCB->findText(current_def, Qt::MatchExactly);
2116         defaultFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2117         item = defaultOTFFormatCB->findText(current_def_otf, Qt::MatchExactly);
2118         defaultOTFFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2119         formatsCB->blockSignals(false);
2120         defaultFormatCB->blockSignals(false);
2121         defaultOTFFormatCB->blockSignals(false);
2122 }
2123
2124
2125 void PrefFileformats::on_formatsCB_currentIndexChanged(int i)
2126 {
2127         if (form_->formats().empty())
2128                 return;
2129         int const nr = formatsCB->itemData(i).toInt();
2130         Format const f = form_->formats().get(nr);
2131
2132         formatED->setText(toqstr(f.name()));
2133         copierED->setText(toqstr(form_->movers().command(f.name())));
2134         extensionsED->setText(toqstr(f.extensions()));
2135         mimeED->setText(toqstr(f.mime()));
2136         shortcutED->setText(
2137                 toqstr(l10n_shortcut(f.prettyname(), f.shortcut())));
2138         documentCB->setChecked((f.documentFormat()));
2139         vectorCB->setChecked((f.vectorFormat()));
2140         exportMenuCB->setChecked((f.inExportMenu()));
2141         exportMenuCB->setEnabled((f.documentFormat()));
2142         updateViewers();
2143         updateEditors();
2144 }
2145
2146
2147 void PrefFileformats::setFlags()
2148 {
2149         int flags = Format::none;
2150         if (documentCB->isChecked())
2151                 flags |= Format::document;
2152         if (vectorCB->isChecked())
2153                 flags |= Format::vector;
2154         if (exportMenuCB->isChecked())
2155                 flags |= Format::export_menu;
2156         currentFormat().setFlags(flags);
2157         exportMenuCB->setEnabled(documentCB->isChecked());
2158         changed();
2159 }
2160
2161
2162 void PrefFileformats::on_copierED_textEdited(const QString & s)
2163 {
2164         string const fmt = fromqstr(formatED->text());
2165         form_->movers().set(fmt, fromqstr(s));
2166         changed();
2167 }
2168
2169
2170 void PrefFileformats::on_extensionsED_textEdited(const QString & s)
2171 {
2172         currentFormat().setExtensions(fromqstr(s));
2173         changed();
2174 }
2175
2176
2177 void PrefFileformats::on_viewerED_textEdited(const QString & s)
2178 {
2179         currentFormat().setViewer(fromqstr(s));
2180         changed();
2181 }
2182
2183
2184 void PrefFileformats::on_editorED_textEdited(const QString & s)
2185 {
2186         currentFormat().setEditor(fromqstr(s));
2187         changed();
2188 }
2189
2190
2191 void PrefFileformats::on_mimeED_textEdited(const QString & s)
2192 {
2193         currentFormat().setMime(fromqstr(s));
2194         changed();
2195 }
2196
2197
2198 void PrefFileformats::on_shortcutED_textEdited(const QString & s)
2199 {
2200         string const new_shortcut = fromqstr(s);
2201         if (new_shortcut == l10n_shortcut(currentFormat().prettyname(),
2202                                           currentFormat().shortcut()))
2203                 return;
2204         currentFormat().setShortcut(new_shortcut);
2205         changed();
2206 }
2207
2208
2209 void PrefFileformats::on_formatED_editingFinished()
2210 {
2211         string const newname = fromqstr(formatED->displayText());
2212         string const oldname = currentFormat().name();
2213         if (newname == oldname)
2214                 return;
2215         if (form_->converters().formatIsUsed(oldname)) {
2216                 Alert::error(_("Format in use"),
2217                              _("You cannot change a format's short name "
2218                                "if the format is used by a converter. "
2219                                "Please remove the converter first."));
2220                 updateView();
2221                 return;
2222         }
2223
2224         currentFormat().setName(newname);
2225         changed();
2226 }
2227
2228
2229 void PrefFileformats::on_formatED_textChanged(const QString &)
2230 {
2231         QString t = formatED->text();
2232         int p = 0;
2233         bool valid = formatED->validator()->validate(t, p) == QValidator::Acceptable;
2234         setValid(formatLA, valid);
2235 }
2236
2237
2238 void PrefFileformats::on_formatsCB_editTextChanged(const QString &)
2239 {
2240         QString t = formatsCB->currentText();
2241         int p = 0;
2242         bool valid = formatsCB->validator()->validate(t, p) == QValidator::Acceptable;
2243         setValid(formatsLA, valid);
2244 }
2245
2246
2247 void PrefFileformats::updatePrettyname()
2248 {
2249         QString const newname = formatsCB->currentText();
2250         if (newname == toqstr(translateIfPossible(currentFormat().prettyname())))
2251                 return;
2252
2253         currentFormat().setPrettyname(qstring_to_ucs4(newname));
2254         formatsChanged();
2255         updateView();
2256         changed();
2257 }
2258
2259
2260 namespace {
2261         void updateComboBox(LyXRC::Alternatives const & alts,
2262                             string const & fmt, QComboBox * combo)
2263         {
2264                 LyXRC::Alternatives::const_iterator it =
2265                                 alts.find(fmt);
2266                 if (it != alts.end()) {
2267                         LyXRC::CommandSet const & cmds = it->second;
2268                         LyXRC::CommandSet::const_iterator sit =
2269                                         cmds.begin();
2270                         LyXRC::CommandSet::const_iterator const sen =
2271                                         cmds.end();
2272                         for (; sit != sen; ++sit) {
2273                                 QString const qcmd = toqstr(*sit);
2274                                 combo->addItem(qcmd, qcmd);
2275                         }
2276                 }
2277         }
2278 } // namespace
2279
2280
2281 void PrefFileformats::updateViewers()
2282 {
2283         Format const f = currentFormat();
2284         viewerCO->blockSignals(true);
2285         viewerCO->clear();
2286         viewerCO->addItem(qt_("None"), QString());
2287         updateComboBox(viewer_alternatives, f.name(), viewerCO);
2288         viewerCO->addItem(qt_("Custom"), QString("custom viewer"));
2289         viewerCO->blockSignals(false);
2290
2291         int pos = viewerCO->findData(toqstr(f.viewer()));
2292         if (pos != -1) {
2293                 viewerED->clear();
2294                 viewerED->setEnabled(false);
2295                 viewerCO->setCurrentIndex(pos);
2296         } else {
2297                 viewerED->setEnabled(true);
2298                 viewerED->setText(toqstr(f.viewer()));
2299                 viewerCO->setCurrentIndex(viewerCO->findData(toqstr("custom viewer")));
2300         }
2301 }
2302
2303
2304 void PrefFileformats::updateEditors()
2305 {
2306         Format const f = currentFormat();
2307         editorCO->blockSignals(true);
2308         editorCO->clear();
2309         editorCO->addItem(qt_("None"), QString());
2310         updateComboBox(editor_alternatives, f.name(), editorCO);
2311         editorCO->addItem(qt_("Custom"), QString("custom editor"));
2312         editorCO->blockSignals(false);
2313
2314         int pos = editorCO->findData(toqstr(f.editor()));
2315         if (pos != -1) {
2316                 editorED->clear();
2317                 editorED->setEnabled(false);
2318                 editorCO->setCurrentIndex(pos);
2319         } else {
2320                 editorED->setEnabled(true);
2321                 editorED->setText(toqstr(f.editor()));
2322                 editorCO->setCurrentIndex(editorCO->findData(toqstr("custom editor")));
2323         }
2324 }
2325
2326
2327 void PrefFileformats::on_viewerCO_currentIndexChanged(int i)
2328 {
2329         bool const custom = viewerCO->itemData(i).toString() == "custom viewer";
2330         viewerED->setEnabled(custom);
2331         if (!custom)
2332                 currentFormat().setViewer(fromqstr(viewerCO->itemData(i).toString()));
2333 }
2334
2335
2336 void PrefFileformats::on_editorCO_currentIndexChanged(int i)
2337 {
2338         bool const custom = editorCO->itemData(i).toString() == "custom editor";
2339         editorED->setEnabled(custom);
2340         if (!custom)
2341                 currentFormat().setEditor(fromqstr(editorCO->itemData(i).toString()));
2342 }
2343
2344
2345 Format & PrefFileformats::currentFormat()
2346 {
2347         int const i = formatsCB->currentIndex();
2348         int const nr = formatsCB->itemData(i).toInt();
2349         return form_->formats().get(nr);
2350 }
2351
2352
2353 void PrefFileformats::on_formatNewPB_clicked()
2354 {
2355         form_->formats().add("", "", docstring(), "", "", "", "", Format::none);
2356         updateView();
2357         formatsCB->setCurrentIndex(0);
2358         formatsCB->setFocus(Qt::OtherFocusReason);
2359 }
2360
2361
2362 void PrefFileformats::on_formatRemovePB_clicked()
2363 {
2364         int const i = formatsCB->currentIndex();
2365         int const nr = formatsCB->itemData(i).toInt();
2366         string const current_text = form_->formats().get(nr).name();
2367         if (form_->converters().formatIsUsed(current_text)) {
2368                 Alert::error(_("Format in use"),
2369                              _("Cannot remove a Format used by a Converter. "
2370                                             "Remove the converter first."));
2371                 return;
2372         }
2373
2374         form_->formats().erase(current_text);
2375         formatsChanged();
2376         updateView();
2377         on_formatsCB_editTextChanged(formatsCB->currentText());
2378         changed();
2379 }
2380
2381
2382 /////////////////////////////////////////////////////////////////////
2383 //
2384 // PrefLanguage
2385 //
2386 /////////////////////////////////////////////////////////////////////
2387
2388 PrefLanguage::PrefLanguage(GuiPreferences * form)
2389         : PrefModule(catLanguage, N_("Language"), form)
2390 {
2391         setupUi(this);
2392
2393         connect(visualCursorRB, SIGNAL(clicked()),
2394                 this, SIGNAL(changed()));
2395         connect(logicalCursorRB, SIGNAL(clicked()),
2396                 this, SIGNAL(changed()));
2397         connect(markForeignCB, SIGNAL(clicked()),
2398                 this, SIGNAL(changed()));
2399         connect(autoBeginCB, SIGNAL(clicked()),
2400                 this, SIGNAL(changed()));
2401         connect(autoEndCB, SIGNAL(clicked()),
2402                 this, SIGNAL(changed()));
2403         connect(languagePackageCO, SIGNAL(activated(int)),
2404                 this, SIGNAL(changed()));
2405         connect(languagePackageED, SIGNAL(textChanged(QString)),
2406                 this, SIGNAL(changed()));
2407         connect(globalCB, SIGNAL(clicked()),
2408                 this, SIGNAL(changed()));
2409         connect(startCommandED, SIGNAL(textChanged(QString)),
2410                 this, SIGNAL(changed()));
2411         connect(endCommandED, SIGNAL(textChanged(QString)),
2412                 this, SIGNAL(changed()));
2413         connect(uiLanguageCO, SIGNAL(activated(int)),
2414                 this, SIGNAL(changed()));
2415         connect(defaultDecimalPointLE, SIGNAL(textChanged(QString)),
2416                 this, SIGNAL(changed()));
2417         connect(defaultLengthUnitCO, SIGNAL(activated(int)),
2418                 this, SIGNAL(changed()));
2419
2420         languagePackageED->setValidator(new NoNewLineValidator(languagePackageED));
2421         startCommandED->setValidator(new NoNewLineValidator(startCommandED));
2422         endCommandED->setValidator(new NoNewLineValidator(endCommandED));
2423
2424         defaultDecimalPointLE->setInputMask("X; ");
2425         defaultDecimalPointLE->setMaxLength(1);
2426
2427         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::CM]), Length::CM);
2428         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::IN]), Length::IN);
2429
2430         QAbstractItemModel * language_model = guiApp->languageModel();
2431         language_model->sort(0);
2432         uiLanguageCO->blockSignals(true);
2433         uiLanguageCO->clear();
2434         uiLanguageCO->addItem(qt_("Default"), toqstr("auto"));
2435         for (int i = 0; i != language_model->rowCount(); ++i) {
2436                 QModelIndex index = language_model->index(i, 0);
2437                 // Filter the list based on the available translation and add
2438                 // each language code only once
2439                 string const name = fromqstr(index.data(Qt::UserRole).toString());
2440                 Language const * lang = languages.getLanguage(name);
2441                 if (!lang)
2442                         continue;
2443                 // never remove the currently selected language
2444                 if (name != form->rc().gui_language
2445                     && name != lyxrc.gui_language
2446                     && (!Messages::available(lang->code())
2447                         || !lang->hasGuiSupport()))
2448                         continue;
2449                 uiLanguageCO->addItem(index.data(Qt::DisplayRole).toString(),
2450                                       index.data(Qt::UserRole).toString());
2451         }
2452         uiLanguageCO->blockSignals(false);
2453 }
2454
2455
2456 void PrefLanguage::on_uiLanguageCO_currentIndexChanged(int)
2457 {
2458          QMessageBox::information(this, qt_("LyX needs to be restarted!"),
2459                  qt_("The change of user interface language will be fully "
2460                  "effective only after a restart."));
2461 }
2462
2463
2464 void PrefLanguage::on_languagePackageCO_currentIndexChanged(int i)
2465 {
2466          languagePackageED->setEnabled(i == 2);
2467 }
2468
2469
2470 void PrefLanguage::applyRC(LyXRC & rc) const
2471 {
2472         rc.visual_cursor = visualCursorRB->isChecked();
2473         rc.mark_foreign_language = markForeignCB->isChecked();
2474         rc.language_auto_begin = autoBeginCB->isChecked();
2475         rc.language_auto_end = autoEndCB->isChecked();
2476         int const p = languagePackageCO->currentIndex();
2477         if (p == 0)
2478                 rc.language_package_selection = LyXRC::LP_AUTO;
2479         else if (p == 1)
2480                 rc.language_package_selection = LyXRC::LP_BABEL;
2481         else if (p == 2)
2482                 rc.language_package_selection = LyXRC::LP_CUSTOM;
2483         else if (p == 3)
2484                 rc.language_package_selection = LyXRC::LP_NONE;
2485         rc.language_custom_package = fromqstr(languagePackageED->text());
2486         rc.language_global_options = globalCB->isChecked();
2487         rc.language_command_begin = fromqstr(startCommandED->text());
2488         rc.language_command_end = fromqstr(endCommandED->text());
2489         rc.gui_language = fromqstr(
2490                 uiLanguageCO->itemData(uiLanguageCO->currentIndex()).toString());
2491         rc.default_decimal_point = fromqstr(defaultDecimalPointLE->text());
2492         rc.default_length_unit = (Length::UNIT) defaultLengthUnitCO->itemData(defaultLengthUnitCO->currentIndex()).toInt();
2493 }
2494
2495
2496 void PrefLanguage::updateRC(LyXRC const & rc)
2497 {
2498         if (rc.visual_cursor)
2499                 visualCursorRB->setChecked(true);
2500         else
2501                 logicalCursorRB->setChecked(true);
2502         markForeignCB->setChecked(rc.mark_foreign_language);
2503         autoBeginCB->setChecked(rc.language_auto_begin);
2504         autoEndCB->setChecked(rc.language_auto_end);
2505         languagePackageCO->setCurrentIndex(rc.language_package_selection);
2506         languagePackageED->setText(toqstr(rc.language_custom_package));
2507         languagePackageED->setEnabled(languagePackageCO->currentIndex() == 2);
2508         globalCB->setChecked(rc.language_global_options);
2509         startCommandED->setText(toqstr(rc.language_command_begin));
2510         endCommandED->setText(toqstr(rc.language_command_end));
2511         defaultDecimalPointLE->setText(toqstr(rc.default_decimal_point));
2512         int pos = defaultLengthUnitCO->findData(int(rc.default_length_unit));
2513         defaultLengthUnitCO->setCurrentIndex(pos);
2514
2515         pos = uiLanguageCO->findData(toqstr(rc.gui_language));
2516         uiLanguageCO->blockSignals(true);
2517         uiLanguageCO->setCurrentIndex(pos);
2518         uiLanguageCO->blockSignals(false);
2519 }
2520
2521
2522 /////////////////////////////////////////////////////////////////////
2523 //
2524 // PrefUserInterface
2525 //
2526 /////////////////////////////////////////////////////////////////////
2527
2528 PrefUserInterface::PrefUserInterface(GuiPreferences * form)
2529         : PrefModule(catLookAndFeel, N_("User Interface"), form)
2530 {
2531         setupUi(this);
2532
2533         connect(uiFilePB, SIGNAL(clicked()),
2534                 this, SLOT(selectUi()));
2535         connect(uiFileED, SIGNAL(textChanged(QString)),
2536                 this, SIGNAL(changed()));
2537         connect(iconSetCO, SIGNAL(activated(int)),
2538                 this, SIGNAL(changed()));
2539         connect(useSystemThemeIconsCB, SIGNAL(clicked()),
2540                 this, SIGNAL(changed()));
2541         connect(lastfilesSB, SIGNAL(valueChanged(int)),
2542                 this, SIGNAL(changed()));
2543         connect(tooltipCB, SIGNAL(toggled(bool)),
2544                 this, SIGNAL(changed()));
2545         lastfilesSB->setMaximum(maxlastfiles);
2546
2547         iconSetCO->addItem(qt_("Default"), QString());
2548         iconSetCO->addItem(qt_("Classic"), "classic");
2549         iconSetCO->addItem(qt_("Oxygen"), "oxygen");
2550
2551 #if (!(defined Q_WS_X11 || defined(QPA_XCB)) || QT_VERSION < 0x040600)
2552         useSystemThemeIconsCB->hide();
2553 #endif
2554 }
2555
2556
2557 void PrefUserInterface::applyRC(LyXRC & rc) const
2558 {
2559         rc.icon_set = fromqstr(iconSetCO->itemData(
2560                 iconSetCO->currentIndex()).toString());
2561
2562         rc.ui_file = internal_path(fromqstr(uiFileED->text()));
2563         rc.use_system_theme_icons = useSystemThemeIconsCB->isChecked();
2564         rc.num_lastfiles = lastfilesSB->value();
2565         rc.use_tooltip = tooltipCB->isChecked();
2566 }
2567
2568
2569 void PrefUserInterface::updateRC(LyXRC const & rc)
2570 {
2571         int iconset = iconSetCO->findData(toqstr(rc.icon_set));
2572         if (iconset < 0)
2573                 iconset = 0;
2574         iconSetCO->setCurrentIndex(iconset);
2575         useSystemThemeIconsCB->setChecked(rc.use_system_theme_icons);
2576         uiFileED->setText(toqstr(external_path(rc.ui_file)));
2577         lastfilesSB->setValue(rc.num_lastfiles);
2578         tooltipCB->setChecked(rc.use_tooltip);
2579 }
2580
2581
2582 void PrefUserInterface::selectUi()
2583 {
2584         QString file = form_->browseUI(internalPath(uiFileED->text()));
2585         if (!file.isEmpty())
2586                 uiFileED->setText(file);
2587 }
2588
2589
2590 /////////////////////////////////////////////////////////////////////
2591 //
2592 // PrefDocumentHandling
2593 //
2594 /////////////////////////////////////////////////////////////////////
2595
2596 PrefDocHandling::PrefDocHandling(GuiPreferences * form)
2597         : PrefModule(catLookAndFeel, N_("Document Handling"), form)
2598 {
2599         setupUi(this);
2600
2601         connect(autoSaveCB, SIGNAL(toggled(bool)),
2602                 autoSaveSB, SLOT(setEnabled(bool)));
2603         connect(autoSaveCB, SIGNAL(toggled(bool)),
2604                 TextLabel1, SLOT(setEnabled(bool)));
2605         connect(openDocumentsInTabsCB, SIGNAL(clicked()),
2606                 this, SIGNAL(changed()));
2607         connect(singleInstanceCB, SIGNAL(clicked()),
2608                 this, SIGNAL(changed()));
2609         connect(singleCloseTabButtonCB, SIGNAL(clicked()),
2610                 this, SIGNAL(changed()));
2611         connect(closeLastViewCO, SIGNAL(activated(int)),
2612                 this, SIGNAL(changed()));
2613         connect(restoreCursorCB, SIGNAL(clicked()),
2614                 this, SIGNAL(changed()));
2615         connect(loadSessionCB, SIGNAL(clicked()),
2616                 this, SIGNAL(changed()));
2617         connect(allowGeometrySessionCB, SIGNAL(clicked()),
2618                 this, SIGNAL(changed()));
2619         connect(autoSaveSB, SIGNAL(valueChanged(int)),
2620                 this, SIGNAL(changed()));
2621         connect(autoSaveCB, SIGNAL(clicked()),
2622                 this, SIGNAL(changed()));
2623         connect(backupCB, SIGNAL(clicked()),
2624                 this, SIGNAL(changed()));
2625         connect(saveCompressedCB, SIGNAL(clicked()),
2626                 this, SIGNAL(changed()));
2627         connect(saveOriginCB, SIGNAL(clicked()),
2628                 this, SIGNAL(changed()));
2629 }
2630
2631
2632 void PrefDocHandling::applyRC(LyXRC & rc) const
2633 {
2634         rc.use_lastfilepos = restoreCursorCB->isChecked();
2635         rc.load_session = loadSessionCB->isChecked();
2636         rc.allow_geometry_session = allowGeometrySessionCB->isChecked();
2637         rc.autosave = autoSaveCB->isChecked() ?  autoSaveSB->value() * 60 : 0;
2638         rc.make_backup = backupCB->isChecked();
2639         rc.save_compressed = saveCompressedCB->isChecked();
2640         rc.save_origin = saveOriginCB->isChecked();
2641         rc.open_buffers_in_tabs = openDocumentsInTabsCB->isChecked();
2642         rc.single_instance = singleInstanceCB->isChecked();
2643         rc.single_close_tab_button = singleCloseTabButtonCB->isChecked();
2644
2645         switch (closeLastViewCO->currentIndex()) {
2646         case 0:
2647                 rc.close_buffer_with_last_view = "yes";
2648                 break;
2649         case 1:
2650                 rc.close_buffer_with_last_view = "no";
2651                 break;
2652         case 2:
2653                 rc.close_buffer_with_last_view = "ask";
2654                 break;
2655         default:
2656                 ;
2657         }
2658 }
2659
2660
2661 void PrefDocHandling::updateRC(LyXRC const & rc)
2662 {
2663         restoreCursorCB->setChecked(rc.use_lastfilepos);
2664         loadSessionCB->setChecked(rc.load_session);
2665         allowGeometrySessionCB->setChecked(rc.allow_geometry_session);
2666         // convert to minutes
2667         bool autosave = rc.autosave > 0;
2668         int mins = rc.autosave / 60;
2669         if (!mins)
2670                 mins = 5;
2671         autoSaveSB->setValue(mins);
2672         autoSaveCB->setChecked(autosave);
2673         autoSaveSB->setEnabled(autosave);
2674         backupCB->setChecked(rc.make_backup);
2675         saveCompressedCB->setChecked(rc.save_compressed);
2676         saveOriginCB->setChecked(rc.save_origin);
2677         openDocumentsInTabsCB->setChecked(rc.open_buffers_in_tabs);
2678         singleInstanceCB->setChecked(rc.single_instance && !rc.lyxpipes.empty());
2679         singleInstanceCB->setEnabled(!rc.lyxpipes.empty());
2680         singleCloseTabButtonCB->setChecked(rc.single_close_tab_button);
2681         if (rc.close_buffer_with_last_view == "yes")
2682                 closeLastViewCO->setCurrentIndex(0);
2683         else if (rc.close_buffer_with_last_view == "no")
2684                 closeLastViewCO->setCurrentIndex(1);
2685         else if (rc.close_buffer_with_last_view == "ask")
2686                 closeLastViewCO->setCurrentIndex(2);
2687 }
2688
2689
2690 void PrefDocHandling::on_clearSessionPB_clicked()
2691 {
2692         guiApp->clearSession();
2693 }
2694
2695
2696
2697 /////////////////////////////////////////////////////////////////////
2698 //
2699 // PrefEdit
2700 //
2701 /////////////////////////////////////////////////////////////////////
2702
2703 PrefEdit::PrefEdit(GuiPreferences * form)
2704         : PrefModule(catEditing, N_("Control"), form)
2705 {
2706         setupUi(this);
2707
2708         connect(cursorFollowsCB, SIGNAL(clicked()),
2709                 this, SIGNAL(changed()));
2710         connect(scrollBelowCB, SIGNAL(clicked()),
2711                 this, SIGNAL(changed()));
2712         connect(macLikeCursorMovementCB, SIGNAL(clicked()),
2713                 this, SIGNAL(changed()));
2714         connect(sortEnvironmentsCB, SIGNAL(clicked()),
2715                 this, SIGNAL(changed()));
2716         connect(groupEnvironmentsCB, SIGNAL(clicked()),
2717                 this, SIGNAL(changed()));
2718         connect(macroEditStyleCO, SIGNAL(activated(int)),
2719                 this, SIGNAL(changed()));
2720         connect(cursorWidthSB, SIGNAL(valueChanged(int)),
2721                 this, SIGNAL(changed()));
2722         connect(fullscreenLimitGB, SIGNAL(clicked()),
2723                 this, SIGNAL(changed()));
2724         connect(fullscreenWidthSB, SIGNAL(valueChanged(int)),
2725                 this, SIGNAL(changed()));
2726         connect(toggleTabbarCB, SIGNAL(toggled(bool)),
2727                 this, SIGNAL(changed()));
2728         connect(toggleMenubarCB, SIGNAL(toggled(bool)),
2729                 this, SIGNAL(changed()));
2730         connect(toggleScrollbarCB, SIGNAL(toggled(bool)),
2731                 this, SIGNAL(changed()));
2732         connect(toggleStatusbarCB, SIGNAL(toggled(bool)),
2733                 this, SIGNAL(changed()));
2734         connect(toggleToolbarsCB, SIGNAL(toggled(bool)),
2735                 this, SIGNAL(changed()));
2736 }
2737
2738
2739 void PrefEdit::applyRC(LyXRC & rc) const
2740 {
2741         rc.cursor_follows_scrollbar = cursorFollowsCB->isChecked();
2742         rc.scroll_below_document = scrollBelowCB->isChecked();
2743         rc.mac_like_cursor_movement = macLikeCursorMovementCB->isChecked();
2744         rc.sort_layouts = sortEnvironmentsCB->isChecked();
2745         rc.group_layouts = groupEnvironmentsCB->isChecked();
2746         switch (macroEditStyleCO->currentIndex()) {
2747                 case 0: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE_BOX; break;
2748                 case 1: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE; break;
2749                 case 2: rc.macro_edit_style = LyXRC::MACRO_EDIT_LIST;   break;
2750         }
2751         rc.cursor_width = cursorWidthSB->value();
2752         rc.full_screen_toolbars = toggleToolbarsCB->isChecked();
2753         rc.full_screen_scrollbar = toggleScrollbarCB->isChecked();
2754         rc.full_screen_statusbar = toggleStatusbarCB->isChecked();
2755         rc.full_screen_tabbar = toggleTabbarCB->isChecked();
2756         rc.full_screen_menubar = toggleMenubarCB->isChecked();
2757         rc.full_screen_width = fullscreenWidthSB->value();
2758         rc.full_screen_limit = fullscreenLimitGB->isChecked();
2759 }
2760
2761
2762 void PrefEdit::updateRC(LyXRC const & rc)
2763 {
2764         cursorFollowsCB->setChecked(rc.cursor_follows_scrollbar);
2765         scrollBelowCB->setChecked(rc.scroll_below_document);
2766         macLikeCursorMovementCB->setChecked(rc.mac_like_cursor_movement);
2767         sortEnvironmentsCB->setChecked(rc.sort_layouts);
2768         groupEnvironmentsCB->setChecked(rc.group_layouts);
2769         macroEditStyleCO->setCurrentIndex(rc.macro_edit_style);
2770         cursorWidthSB->setValue(rc.cursor_width);
2771         toggleScrollbarCB->setChecked(rc.full_screen_scrollbar);
2772         toggleStatusbarCB->setChecked(rc.full_screen_statusbar);
2773         toggleToolbarsCB->setChecked(rc.full_screen_toolbars);
2774         toggleTabbarCB->setChecked(rc.full_screen_tabbar);
2775         toggleMenubarCB->setChecked(rc.full_screen_menubar);
2776         fullscreenWidthSB->setValue(rc.full_screen_width);
2777         fullscreenLimitGB->setChecked(rc.full_screen_limit);
2778 }
2779
2780
2781 /////////////////////////////////////////////////////////////////////
2782 //
2783 // PrefShortcuts
2784 //
2785 /////////////////////////////////////////////////////////////////////
2786
2787
2788 GuiShortcutDialog::GuiShortcutDialog(QWidget * parent) : QDialog(parent)
2789 {
2790         Ui::shortcutUi::setupUi(this);
2791         QDialog::setModal(true);
2792 }
2793
2794
2795 PrefShortcuts::PrefShortcuts(GuiPreferences * form)
2796         : PrefModule(catEditing, N_("Shortcuts"), form),
2797           editItem_(0), mathItem_(0), bufferItem_(0), layoutItem_(0),
2798           systemItem_(0)
2799 {
2800         setupUi(this);
2801
2802         shortcutsTW->setColumnCount(2);
2803         shortcutsTW->headerItem()->setText(0, qt_("Function"));
2804         shortcutsTW->headerItem()->setText(1, qt_("Shortcut"));
2805         shortcutsTW->setSortingEnabled(true);
2806         // Multi-selection can be annoying.
2807         // shortcutsTW->setSelectionMode(QAbstractItemView::MultiSelection);
2808
2809         connect(bindFilePB, SIGNAL(clicked()),
2810                 this, SLOT(selectBind()));
2811         connect(bindFileED, SIGNAL(textChanged(QString)),
2812                 this, SIGNAL(changed()));
2813
2814         shortcut_ = new GuiShortcutDialog(this);
2815         shortcut_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
2816         shortcut_bc_.setOK(shortcut_->okPB);
2817         shortcut_bc_.setCancel(shortcut_->cancelPB);
2818
2819         connect(shortcut_->okPB, SIGNAL(clicked()),
2820                 this, SIGNAL(changed()));
2821         connect(shortcut_->cancelPB, SIGNAL(clicked()),
2822                 shortcut_, SLOT(reject()));
2823         connect(shortcut_->clearPB, SIGNAL(clicked()),
2824                 this, SLOT(shortcutClearPressed()));
2825         connect(shortcut_->removePB, SIGNAL(clicked()),
2826                 this, SLOT(shortcutRemovePressed()));
2827         connect(shortcut_->okPB, SIGNAL(clicked()),
2828                 this, SLOT(shortcutOkPressed()));
2829         connect(shortcut_->cancelPB, SIGNAL(clicked()),
2830                 this, SLOT(shortcutCancelPressed()));
2831 }
2832
2833
2834 void PrefShortcuts::applyRC(LyXRC & rc) const
2835 {
2836         rc.bind_file = internal_path(fromqstr(bindFileED->text()));
2837         // write user_bind and user_unbind to .lyx/bind/user.bind
2838         FileName bind_dir(addPath(package().user_support().absFileName(), "bind"));
2839         if (!bind_dir.exists() && !bind_dir.createDirectory(0777)) {
2840                 lyxerr << "LyX could not create the user bind directory '"
2841                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2842                 return;
2843         }
2844         if (!bind_dir.isDirWritable()) {
2845                 lyxerr << "LyX could not write to the user bind directory '"
2846                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2847                 return;
2848         }
2849         FileName user_bind_file(bind_dir.absFileName() + "/user.bind");
2850         user_unbind_.write(user_bind_file.toFilesystemEncoding(), false, true);
2851         user_bind_.write(user_bind_file.toFilesystemEncoding(), true, false);
2852         // immediately apply the keybindings. Why this is not done before?
2853         // The good thing is that the menus are updated automatically.
2854         theTopLevelKeymap().clear();
2855         theTopLevelKeymap().read("site");
2856         theTopLevelKeymap().read(rc.bind_file, 0, KeyMap::Fallback);
2857         theTopLevelKeymap().read("user", 0, KeyMap::MissingOK);
2858 }
2859
2860
2861 void PrefShortcuts::updateRC(LyXRC const & rc)
2862 {
2863         bindFileED->setText(toqstr(external_path(rc.bind_file)));
2864         //
2865         system_bind_.clear();
2866         user_bind_.clear();
2867         user_unbind_.clear();
2868         system_bind_.read("site");
2869         system_bind_.read(rc.bind_file);
2870         // \unbind in user.bind is added to user_unbind_
2871         user_bind_.read("user", &user_unbind_, KeyMap::MissingOK);
2872         updateShortcutsTW();
2873 }
2874
2875
2876 void PrefShortcuts::updateShortcutsTW()
2877 {
2878         shortcutsTW->clear();
2879
2880         editItem_ = new QTreeWidgetItem(shortcutsTW);
2881         editItem_->setText(0, qt_("Cursor, Mouse and Editing Functions"));
2882         editItem_->setFlags(editItem_->flags() & ~Qt::ItemIsSelectable);
2883
2884         mathItem_ = new QTreeWidgetItem(shortcutsTW);
2885         mathItem_->setText(0, qt_("Mathematical Symbols"));
2886         mathItem_->setFlags(mathItem_->flags() & ~Qt::ItemIsSelectable);
2887
2888         bufferItem_ = new QTreeWidgetItem(shortcutsTW);
2889         bufferItem_->setText(0, qt_("Document and Window"));
2890         bufferItem_->setFlags(bufferItem_->flags() & ~Qt::ItemIsSelectable);
2891
2892         layoutItem_ = new QTreeWidgetItem(shortcutsTW);
2893         layoutItem_->setText(0, qt_("Font, Layouts and Textclasses"));
2894         layoutItem_->setFlags(layoutItem_->flags() & ~Qt::ItemIsSelectable);
2895
2896         systemItem_ = new QTreeWidgetItem(shortcutsTW);
2897         systemItem_->setText(0, qt_("System and Miscellaneous"));
2898         systemItem_->setFlags(systemItem_->flags() & ~Qt::ItemIsSelectable);
2899
2900         // listBindings(unbound=true) lists all bound and unbound lfuns
2901         // Items in this list is tagged by its source.
2902         KeyMap::BindingList bindinglist = system_bind_.listBindings(true,
2903                 KeyMap::System);
2904         KeyMap::BindingList user_bindinglist = user_bind_.listBindings(false,
2905                 KeyMap::UserBind);
2906         KeyMap::BindingList user_unbindinglist = user_unbind_.listBindings(false,
2907                 KeyMap::UserUnbind);
2908         bindinglist.insert(bindinglist.end(), user_bindinglist.begin(),
2909                         user_bindinglist.end());
2910         bindinglist.insert(bindinglist.end(), user_unbindinglist.begin(),
2911                         user_unbindinglist.end());
2912
2913         KeyMap::BindingList::const_iterator it = bindinglist.begin();
2914         KeyMap::BindingList::const_iterator it_end = bindinglist.end();
2915         for (; it != it_end; ++it)
2916                 insertShortcutItem(it->request, it->sequence, it->tag);
2917
2918         shortcutsTW->sortItems(0, Qt::AscendingOrder);
2919         on_shortcutsTW_itemSelectionChanged();
2920         on_searchLE_textEdited();
2921         shortcutsTW->resizeColumnToContents(0);
2922 }
2923
2924
2925 //static
2926 KeyMap::ItemType PrefShortcuts::itemType(QTreeWidgetItem & item)
2927 {
2928         return static_cast<KeyMap::ItemType>(item.data(0, Qt::UserRole).toInt());
2929 }
2930
2931
2932 //static
2933 bool PrefShortcuts::isAlwaysHidden(QTreeWidgetItem & item)
2934 {
2935         // Hide rebound system settings that are empty
2936         return itemType(item) == KeyMap::UserUnbind && item.text(1).isEmpty();
2937 }
2938
2939
2940 void PrefShortcuts::setItemType(QTreeWidgetItem * item, KeyMap::ItemType tag)
2941 {
2942         item->setData(0, Qt::UserRole, QVariant(tag));
2943         QFont font;
2944
2945         switch (tag) {
2946         case KeyMap::System:
2947                 break;
2948         case KeyMap::UserBind:
2949                 font.setBold(true);
2950                 break;
2951         case KeyMap::UserUnbind:
2952                 font.setStrikeOut(true);
2953                 break;
2954         // this item is not displayed now.
2955         case KeyMap::UserExtraUnbind:
2956                 font.setStrikeOut(true);
2957                 break;
2958         }
2959         item->setHidden(isAlwaysHidden(*item));
2960         item->setFont(1, font);
2961 }
2962
2963
2964 QTreeWidgetItem * PrefShortcuts::insertShortcutItem(FuncRequest const & lfun,
2965                 KeySequence const & seq, KeyMap::ItemType tag)
2966 {
2967         FuncCode const action = lfun.action();
2968         string const action_name = lyxaction.getActionName(action);
2969         QString const lfun_name = toqstr(from_utf8(action_name)
2970                         + ' ' + lfun.argument());
2971         QString const shortcut = toqstr(seq.print(KeySequence::ForGui));
2972
2973         QTreeWidgetItem * newItem = 0;
2974         // for unbind items, try to find an existing item in the system bind list
2975         if (tag == KeyMap::UserUnbind) {
2976                 QList<QTreeWidgetItem*> const items = shortcutsTW->findItems(lfun_name,
2977                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
2978                 for (int i = 0; i < items.size(); ++i) {
2979                         if (items[i]->text(1) == shortcut) {
2980                                 newItem = items[i];
2981                                 break;
2982                         }
2983                 }
2984                 // if not found, this unbind item is KeyMap::UserExtraUnbind
2985                 // Such an item is not displayed to avoid confusion (what is
2986                 // unmatched removed?).
2987                 if (!newItem) {
2988                         return 0;
2989                 }
2990         }
2991         if (!newItem) {
2992                 switch(lyxaction.getActionType(action)) {
2993                 case LyXAction::Hidden:
2994                         return 0;
2995                 case LyXAction::Edit:
2996                         newItem = new QTreeWidgetItem(editItem_);
2997                         break;
2998                 case LyXAction::Math:
2999                         newItem = new QTreeWidgetItem(mathItem_);
3000                         break;
3001                 case LyXAction::Buffer:
3002                         newItem = new QTreeWidgetItem(bufferItem_);
3003                         break;
3004                 case LyXAction::Layout:
3005                         newItem = new QTreeWidgetItem(layoutItem_);
3006                         break;
3007                 case LyXAction::System:
3008                         newItem = new QTreeWidgetItem(systemItem_);
3009                         break;
3010                 default:
3011                         // this should not happen
3012                         newItem = new QTreeWidgetItem(shortcutsTW);
3013                 }
3014         }
3015
3016         newItem->setText(0, lfun_name);
3017         newItem->setText(1, shortcut);
3018         // record BindFile representation to recover KeySequence when needed.
3019         newItem->setData(1, Qt::UserRole, toqstr(seq.print(KeySequence::BindFile)));
3020         setItemType(newItem, tag);
3021         return newItem;
3022 }
3023
3024
3025 void PrefShortcuts::on_shortcutsTW_itemSelectionChanged()
3026 {
3027         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3028         removePB->setEnabled(!items.isEmpty() && !items[0]->text(1).isEmpty());
3029         modifyPB->setEnabled(!items.isEmpty());
3030         if (items.isEmpty())
3031                 return;
3032
3033         if (itemType(*items[0]) == KeyMap::UserUnbind)
3034                 removePB->setText(qt_("Res&tore"));
3035         else
3036                 removePB->setText(qt_("Remo&ve"));
3037 }
3038
3039
3040 void PrefShortcuts::on_shortcutsTW_itemDoubleClicked()
3041 {
3042         modifyShortcut();
3043 }
3044
3045
3046 void PrefShortcuts::modifyShortcut()
3047 {
3048         QTreeWidgetItem * item = shortcutsTW->currentItem();
3049         if (item->flags() & Qt::ItemIsSelectable) {
3050                 shortcut_->lfunLE->setText(item->text(0));
3051                 save_lfun_ = item->text(0).trimmed();
3052                 shortcut_->shortcutWG->setText(item->text(1));
3053                 KeySequence seq;
3054                 seq.parse(fromqstr(item->data(1, Qt::UserRole).toString()));
3055                 shortcut_->shortcutWG->setKeySequence(seq);
3056                 shortcut_->shortcutWG->setFocus();
3057                 shortcut_->exec();
3058         }
3059 }
3060
3061
3062 void PrefShortcuts::unhideEmpty(QString const & lfun, bool select)
3063 {
3064         // list of items that match lfun
3065         QList<QTreeWidgetItem*> items = shortcutsTW->findItems(lfun,
3066              Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
3067         for (int i = 0; i < items.size(); ++i) {
3068                 QTreeWidgetItem * item = items[i];
3069                 if (isAlwaysHidden(*item)) {
3070                         setItemType(item, KeyMap::System);
3071                         if (select)
3072                                 shortcutsTW->setCurrentItem(item);
3073                         return;
3074                 }
3075         }
3076 }
3077
3078
3079 void PrefShortcuts::removeShortcut()
3080 {
3081         // it seems that only one item can be selected, but I am
3082         // removing all selected items anyway.
3083         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3084         for (int i = 0; i < items.size(); ++i) {
3085                 string shortcut = fromqstr(items[i]->data(1, Qt::UserRole).toString());
3086                 string lfun = fromqstr(items[i]->text(0));
3087                 FuncRequest func = lyxaction.lookupFunc(lfun);
3088
3089                 switch (itemType(*items[i])) {
3090                 case KeyMap::System: {
3091                         // for system bind, we do not touch the item
3092                         // but add an user unbind item
3093                         user_unbind_.bind(shortcut, func);
3094                         setItemType(items[i], KeyMap::UserUnbind);
3095                         removePB->setText(qt_("Res&tore"));
3096                         break;
3097                 }
3098                 case KeyMap::UserBind: {
3099                         // for user_bind, we remove this bind
3100                         QTreeWidgetItem * parent = items[i]->parent();
3101                         int itemIdx = parent->indexOfChild(items[i]);
3102                         parent->takeChild(itemIdx);
3103                         if (itemIdx > 0)
3104                                 shortcutsTW->scrollToItem(parent->child(itemIdx - 1));
3105                         else
3106                                 shortcutsTW->scrollToItem(parent);
3107                         user_bind_.unbind(shortcut, func);
3108                         // If this user binding hid an empty system binding, unhide the
3109                         // latter and select it.
3110                         unhideEmpty(items[i]->text(0), true);
3111                         break;
3112                 }
3113                 case KeyMap::UserUnbind: {
3114                         // for user_unbind, we remove the unbind, and the item
3115                         // become KeyMap::System again.
3116                         KeySequence seq;
3117                         seq.parse(shortcut);
3118                         // Ask the user to replace current binding
3119                         if (!validateNewShortcut(func, seq, QString()))
3120                                 break;
3121                         user_unbind_.unbind(shortcut, func);
3122                         setItemType(items[i], KeyMap::System);
3123                         removePB->setText(qt_("Remo&ve"));
3124                         break;
3125                 }
3126                 case KeyMap::UserExtraUnbind: {
3127                         // for user unbind that is not in system bind file,
3128                         // remove this unbind file
3129                         QTreeWidgetItem * parent = items[i]->parent();
3130                         parent->takeChild(parent->indexOfChild(items[i]));
3131                         user_unbind_.unbind(shortcut, func);
3132                 }
3133                 }
3134         }
3135 }
3136
3137
3138 void PrefShortcuts::deactivateShortcuts(QList<QTreeWidgetItem*> const & items)
3139 {
3140         for (int i = 0; i < items.size(); ++i) {
3141                 string shortcut = fromqstr(items[i]->data(1, Qt::UserRole).toString());
3142                 string lfun = fromqstr(items[i]->text(0));
3143                 FuncRequest func = lyxaction.lookupFunc(lfun);
3144
3145                 switch (itemType(*items[i])) {
3146                 case KeyMap::System:
3147                         // for system bind, we do not touch the item
3148                         // but add an user unbind item
3149                         user_unbind_.bind(shortcut, func);
3150                         setItemType(items[i], KeyMap::UserUnbind);
3151                         break;
3152
3153                 case KeyMap::UserBind: {
3154                         // for user_bind, we remove this bind
3155                         QTreeWidgetItem * parent = items[i]->parent();
3156                         int itemIdx = parent->indexOfChild(items[i]);
3157                         parent->takeChild(itemIdx);
3158                         user_bind_.unbind(shortcut, func);
3159                         unhideEmpty(items[i]->text(0), false);
3160                         break;
3161                 }
3162                 default:
3163                         break;
3164                 }
3165         }
3166 }
3167
3168
3169 void PrefShortcuts::selectBind()
3170 {
3171         QString file = form_->browsebind(internalPath(bindFileED->text()));
3172         if (!file.isEmpty()) {
3173                 bindFileED->setText(file);
3174                 system_bind_ = KeyMap();
3175                 system_bind_.read(fromqstr(file));
3176                 updateShortcutsTW();
3177         }
3178 }
3179
3180
3181 void PrefShortcuts::on_modifyPB_pressed()
3182 {
3183         modifyShortcut();
3184 }
3185
3186
3187 void PrefShortcuts::on_newPB_pressed()
3188 {
3189         shortcut_->lfunLE->clear();
3190         shortcut_->shortcutWG->reset();
3191         save_lfun_ = QString();
3192         shortcut_->exec();
3193 }
3194
3195
3196 void PrefShortcuts::on_removePB_pressed()
3197 {
3198         changed();
3199         removeShortcut();
3200 }
3201
3202
3203 void PrefShortcuts::on_searchLE_textEdited()
3204 {
3205         if (searchLE->text().isEmpty()) {
3206                 // show all hidden items
3207                 QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Hidden);
3208                 for (; *it; ++it)
3209                         shortcutsTW->setItemHidden(*it, isAlwaysHidden(**it));
3210                 // close all categories
3211                 for (int i = 0; i < shortcutsTW->topLevelItemCount(); ++i)
3212                         shortcutsTW->collapseItem(shortcutsTW->topLevelItem(i));
3213                 return;
3214         }
3215         // search both columns
3216         QList<QTreeWidgetItem *> matched = shortcutsTW->findItems(searchLE->text(),
3217                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 0);
3218         matched += shortcutsTW->findItems(searchLE->text(),
3219                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 1);
3220
3221         // hide everyone (to avoid searching in matched QList repeatedly
3222         QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Selectable);
3223         while (*it)
3224                 shortcutsTW->setItemHidden(*it++, true);
3225         // show matched items
3226         for (int i = 0; i < matched.size(); ++i)
3227                 if (!isAlwaysHidden(*matched[i])) {
3228                         shortcutsTW->setItemHidden(matched[i], false);
3229                         shortcutsTW->setItemExpanded(matched[i]->parent(), true);
3230                 }
3231 }
3232
3233
3234 docstring makeCmdString(FuncRequest const & f)
3235 {
3236         docstring actionStr = from_ascii(lyxaction.getActionName(f.action()));
3237         if (!f.argument().empty())
3238                 actionStr += " " + f.argument();
3239         return actionStr;
3240 }
3241
3242
3243 FuncRequest PrefShortcuts::currentBinding(KeySequence const & k)
3244 {
3245         FuncRequest res = user_bind_.getBinding(k);
3246         if (res.action() != LFUN_UNKNOWN_ACTION)
3247                 return res;
3248         res = system_bind_.getBinding(k);
3249         // Check if it is unbound. Note: user_unbind_ can only unbind one
3250         // FuncRequest per key sequence.
3251         if (user_unbind_.getBinding(k) == res)
3252                 return FuncRequest::unknown;
3253         return res;
3254 }
3255
3256
3257 bool PrefShortcuts::validateNewShortcut(FuncRequest const & func,
3258                                         KeySequence const & k,
3259                                         QString const & lfun_to_modify)
3260 {
3261         if (func.action() == LFUN_UNKNOWN_ACTION) {
3262                 Alert::error(_("Failed to create shortcut"),
3263                         _("Unknown or invalid LyX function"));
3264                 return false;
3265         }
3266
3267         // It is not currently possible to bind Hidden lfuns such as self-insert. In
3268         // the future, to remove this limitation, see GuiPrefs::insertShortcutItem
3269         // and how it is used in GuiPrefs::shortcutOkPressed.
3270         if (lyxaction.getActionType(func.action()) == LyXAction::Hidden) {
3271                 Alert::error(_("Failed to create shortcut"),
3272                         _("This LyX function is hidden and cannot be bound."));
3273                 return false;
3274         }
3275
3276         if (k.length() == 0) {
3277                 Alert::error(_("Failed to create shortcut"),
3278                         _("Invalid or empty key sequence"));
3279                 return false;
3280         }
3281
3282         FuncRequest oldBinding = currentBinding(k);
3283         if (oldBinding == func)
3284                 // nothing to change
3285                 return false;
3286
3287         // make sure this key isn't already bound---and, if so, prompt user
3288         // (exclude the lfun the user already wants to modify)
3289         docstring const action_string = makeCmdString(oldBinding);
3290         if (oldBinding.action() != LFUN_UNKNOWN_ACTION
3291             && lfun_to_modify != toqstr(action_string)) {
3292                 docstring const new_action_string = makeCmdString(func);
3293                 docstring const text = bformat(_("Shortcut `%1$s' is already bound to "
3294                                                  "%2$s.\n"
3295                                                  "Are you sure you want to unbind the "
3296                                                  "current shortcut and bind it to %3$s?"),
3297                                                k.print(KeySequence::ForGui), action_string,
3298                                                new_action_string);
3299                 int ret = Alert::prompt(_("Redefine shortcut?"),
3300                                         text, 0, 1, _("&Redefine"), _("&Cancel"));
3301                 if (ret != 0)
3302                         return false;
3303                 QString const sequence_text = toqstr(k.print(KeySequence::ForGui));
3304                 QList<QTreeWidgetItem*> items = shortcutsTW->findItems(sequence_text,
3305                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 1);
3306                 deactivateShortcuts(items);
3307         }
3308         return true;
3309 }
3310
3311
3312 void PrefShortcuts::shortcutOkPressed()
3313 {
3314         QString const new_lfun = shortcut_->lfunLE->text();
3315         FuncRequest func = lyxaction.lookupFunc(fromqstr(new_lfun));
3316         KeySequence k = shortcut_->shortcutWG->getKeySequence();
3317
3318         // save_lfun_ contains the text of the lfun to modify, if the user clicked
3319         // "modify", or is empty if they clicked "new" (which I do not really like)
3320         if (!validateNewShortcut(func, k, save_lfun_))
3321                 return;
3322
3323         if (!save_lfun_.isEmpty()) {
3324                 // real modification of the lfun's shortcut,
3325                 // so remove the previous one
3326                 QList<QTreeWidgetItem*> to_modify = shortcutsTW->selectedItems();
3327                 deactivateShortcuts(to_modify);
3328         }
3329
3330         shortcut_->accept();
3331
3332         QTreeWidgetItem * item = insertShortcutItem(func, k, KeyMap::UserBind);
3333         if (item) {
3334                 user_bind_.bind(&k, func);
3335                 shortcutsTW->sortItems(0, Qt::AscendingOrder);
3336                 shortcutsTW->setItemExpanded(item->parent(), true);
3337                 shortcutsTW->setCurrentItem(item);
3338                 shortcutsTW->scrollToItem(item);
3339         } else {
3340                 Alert::error(_("Failed to create shortcut"),
3341                         _("Can not insert shortcut to the list"));
3342                 return;
3343         }
3344 }
3345
3346
3347 void PrefShortcuts::shortcutCancelPressed()
3348 {
3349         shortcut_->shortcutWG->reset();
3350 }
3351
3352
3353 void PrefShortcuts::shortcutClearPressed()
3354 {
3355         shortcut_->shortcutWG->reset();
3356 }
3357
3358
3359 void PrefShortcuts::shortcutRemovePressed()
3360 {
3361         shortcut_->shortcutWG->removeFromSequence();
3362 }
3363
3364
3365 /////////////////////////////////////////////////////////////////////
3366 //
3367 // PrefIdentity
3368 //
3369 /////////////////////////////////////////////////////////////////////
3370
3371 PrefIdentity::PrefIdentity(GuiPreferences * form)
3372         : PrefModule(QString(), N_("Identity"), form)
3373 {
3374         setupUi(this);
3375
3376         connect(nameED, SIGNAL(textChanged(QString)),
3377                 this, SIGNAL(changed()));
3378         connect(emailED, SIGNAL(textChanged(QString)),
3379                 this, SIGNAL(changed()));
3380
3381         nameED->setValidator(new NoNewLineValidator(nameED));
3382         emailED->setValidator(new NoNewLineValidator(emailED));
3383 }
3384
3385
3386 void PrefIdentity::applyRC(LyXRC & rc) const
3387 {
3388         rc.user_name = fromqstr(nameED->text());
3389         rc.user_email = fromqstr(emailED->text());
3390 }
3391
3392
3393 void PrefIdentity::updateRC(LyXRC const & rc)
3394 {
3395         nameED->setText(toqstr(rc.user_name));
3396         emailED->setText(toqstr(rc.user_email));
3397 }
3398
3399
3400
3401 /////////////////////////////////////////////////////////////////////
3402 //
3403 // GuiPreferences
3404 //
3405 /////////////////////////////////////////////////////////////////////
3406
3407 GuiPreferences::GuiPreferences(GuiView & lv)
3408         : GuiDialog(lv, "prefs", qt_("Preferences")), update_screen_font_(false),
3409           update_previews_(false)
3410 {
3411         setupUi(this);
3412
3413         QDialog::setModal(false);
3414
3415         connect(savePB, SIGNAL(clicked()), this, SLOT(slotOK()));
3416         connect(applyPB, SIGNAL(clicked()), this, SLOT(slotApply()));
3417         connect(closePB, SIGNAL(clicked()), this, SLOT(slotClose()));
3418         connect(restorePB, SIGNAL(clicked()), this, SLOT(slotRestore()));
3419
3420         addModule(new PrefUserInterface(this));
3421         addModule(new PrefDocHandling(this));
3422         addModule(new PrefEdit(this));
3423         addModule(new PrefShortcuts(this));
3424         PrefScreenFonts * screenfonts = new PrefScreenFonts(this);
3425         connect(this, SIGNAL(prefsApplied(LyXRC const &)),
3426                         screenfonts, SLOT(updateScreenFontSizes(LyXRC const &)));
3427         addModule(screenfonts);
3428         addModule(new PrefColors(this));
3429         addModule(new PrefDisplay(this));
3430         addModule(new PrefInput(this));
3431         addModule(new PrefCompletion(this));
3432
3433         addModule(new PrefPaths(this));
3434
3435         addModule(new PrefIdentity(this));
3436
3437         addModule(new PrefLanguage(this));
3438         addModule(new PrefSpellchecker(this));
3439
3440         //for strftime validator
3441         PrefOutput * output = new PrefOutput(this);
3442         addModule(output);
3443         addModule(new PrefLatex(this));
3444
3445         PrefConverters * converters = new PrefConverters(this);
3446         PrefFileformats * formats = new PrefFileformats(this);
3447         connect(formats, SIGNAL(formatsChanged()),
3448                         converters, SLOT(updateGui()));
3449         addModule(converters);
3450         addModule(formats);
3451
3452         prefsPS->setCurrentPanel("User Interface");
3453 // FIXME: hack to work around resizing bug in Qt >= 4.2
3454 // bug verified with Qt 4.2.{0-3} (JSpitzm)
3455 #if QT_VERSION >= 0x040200
3456         prefsPS->updateGeometry();
3457 #endif
3458
3459         bc().setPolicy(ButtonPolicy::PreferencesPolicy);
3460         bc().setOK(savePB);
3461         bc().setApply(applyPB);
3462         bc().setCancel(closePB);
3463         bc().setRestore(restorePB);
3464
3465         // initialize the strftime validator
3466         bc().addCheckedLineEdit(output->DateED);
3467 }
3468
3469
3470 void GuiPreferences::addModule(PrefModule * module)
3471 {
3472         LASSERT(module, return);
3473         if (module->category().isEmpty())
3474                 prefsPS->addPanel(module, module->title());
3475         else
3476                 prefsPS->addPanel(module, module->title(), module->category());
3477         connect(module, SIGNAL(changed()), this, SLOT(change_adaptor()));
3478         modules_.push_back(module);
3479 }
3480
3481
3482 void GuiPreferences::change_adaptor()
3483 {
3484         changed();
3485 }
3486
3487
3488 void GuiPreferences::applyRC(LyXRC & rc) const
3489 {
3490         size_t end = modules_.size();
3491         for (size_t i = 0; i != end; ++i)
3492                 modules_[i]->applyRC(rc);
3493 }
3494
3495
3496 void GuiPreferences::updateRC(LyXRC const & rc)
3497 {
3498         size_t const end = modules_.size();
3499         for (size_t i = 0; i != end; ++i)
3500                 modules_[i]->updateRC(rc);
3501 }
3502
3503
3504 void GuiPreferences::applyView()
3505 {
3506         applyRC(rc());
3507 }
3508
3509
3510 bool GuiPreferences::initialiseParams(string const &)
3511 {
3512         rc_ = lyxrc;
3513         formats_ = theFormats();
3514         converters_ = theConverters();
3515         converters_.update(formats_);
3516         movers_ = theMovers();
3517         colors_.clear();
3518         update_screen_font_ = false;
3519         update_previews_ = false;
3520
3521         updateRC(rc_);
3522         // Make sure that the bc is in the INITIAL state
3523         if (bc().policy().buttonStatus(ButtonPolicy::RESTORE))
3524                 bc().restore();
3525
3526         return true;
3527 }
3528
3529
3530 void GuiPreferences::dispatchParams()
3531 {
3532         ostringstream ss;
3533         rc_.write(ss, true);
3534         dispatch(FuncRequest(LFUN_LYXRC_APPLY, ss.str()));
3535         // issue prefsApplied signal. This will update the
3536         // localized screen font sizes.
3537         prefsApplied(rc_);
3538         // FIXME: these need lfuns
3539         // FIXME UNICODE
3540         Author const & author =
3541                 Author(from_utf8(rc_.user_name), from_utf8(rc_.user_email));
3542         theBufferList().recordCurrentAuthor(author);
3543
3544         theFormats() = formats_;
3545
3546         theConverters() = converters_;
3547         theConverters().update(formats_);
3548         theConverters().buildGraph();
3549         theBufferList().invalidateConverterCache();
3550
3551         theMovers() = movers_;
3552
3553         vector<string>::const_iterator it = colors_.begin();
3554         vector<string>::const_iterator const end = colors_.end();
3555         for (; it != end; ++it)
3556                 dispatch(FuncRequest(LFUN_SET_COLOR, *it));
3557         colors_.clear();
3558
3559         if (update_screen_font_) {
3560                 dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
3561                 // resets flag in case second apply in same dialog
3562                 update_screen_font_ = false;
3563         }
3564
3565         if (update_previews_) {
3566                 // resets flag in case second apply in same dialog
3567                 theBufferList().updatePreviews();
3568                 update_previews_ = false;
3569         }
3570
3571         // The Save button has been pressed
3572         if (isClosing())
3573                 dispatch(FuncRequest(LFUN_PREFERENCES_SAVE));
3574 }
3575
3576
3577 void GuiPreferences::setColor(ColorCode col, QString const & hex)
3578 {
3579         colors_.push_back(lcolor.getLyXName(col) + ' ' + fromqstr(hex));
3580 }
3581
3582
3583 void GuiPreferences::updateScreenFonts()
3584 {
3585         update_screen_font_ = true;
3586 }
3587
3588
3589 void GuiPreferences::updatePreviews()
3590 {
3591         update_previews_ = true;
3592 }
3593
3594
3595 QString GuiPreferences::browsebind(QString const & file) const
3596 {
3597         return browseLibFile("bind", file, "bind", qt_("Choose bind file"),
3598                              QStringList(qt_("LyX bind files (*.bind)")));
3599 }
3600
3601
3602 QString GuiPreferences::browseUI(QString const & file) const
3603 {
3604         return browseLibFile("ui", file, "ui", qt_("Choose UI file"),
3605                              QStringList(qt_("LyX UI files (*.ui)")));
3606 }
3607
3608
3609 QString GuiPreferences::browsekbmap(QString const & file) const
3610 {
3611         return browseLibFile("kbd", file, "kmap", qt_("Choose keyboard map"),
3612                              QStringList(qt_("LyX keyboard maps (*.kmap)")));
3613 }
3614
3615
3616 QString GuiPreferences::browse(QString const & file,
3617         QString const & title) const
3618 {
3619         return browseFile(file, title, QStringList(), true);
3620 }
3621
3622
3623 Dialog * createGuiPreferences(GuiView & lv) { return new GuiPreferences(lv); }
3624
3625
3626 } // namespace frontend
3627 } // namespace lyx
3628
3629 #include "moc_GuiPrefs.cpp"