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