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