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