]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiPrefs.cpp
be9e981ca7a1ea6eeaa2372daff385f65a5af431
[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
1035
1036 void PrefScreenFonts::updateRC(LyXRC const & rc)
1037 {
1038         setComboxFont(screenRomanCO, rc.roman_font_name,
1039                         rc.roman_font_foundry);
1040         setComboxFont(screenSansCO, rc.sans_font_name,
1041                         rc.sans_font_foundry);
1042         setComboxFont(screenTypewriterCO, rc.typewriter_font_name,
1043                         rc.typewriter_font_foundry);
1044
1045         selectRoman(screenRomanCO->currentText());
1046         selectSans(screenSansCO->currentText());
1047         selectTypewriter(screenTypewriterCO->currentText());
1048
1049         screenZoomSB->setValue(rc.defaultZoom);
1050         updateScreenFontSizes(rc);
1051 }
1052
1053
1054 void PrefScreenFonts::updateScreenFontSizes(LyXRC const & rc)
1055 {
1056         doubleToWidget(screenTinyED, rc.font_sizes[FONT_SIZE_TINY]);
1057         doubleToWidget(screenSmallestED, rc.font_sizes[FONT_SIZE_SCRIPT]);
1058         doubleToWidget(screenSmallerED, rc.font_sizes[FONT_SIZE_FOOTNOTE]);
1059         doubleToWidget(screenSmallED, rc.font_sizes[FONT_SIZE_SMALL]);
1060         doubleToWidget(screenNormalED, rc.font_sizes[FONT_SIZE_NORMAL]);
1061         doubleToWidget(screenLargeED, rc.font_sizes[FONT_SIZE_LARGE]);
1062         doubleToWidget(screenLargerED, rc.font_sizes[FONT_SIZE_LARGER]);
1063         doubleToWidget(screenLargestED, rc.font_sizes[FONT_SIZE_LARGEST]);
1064         doubleToWidget(screenHugeED, rc.font_sizes[FONT_SIZE_HUGE]);
1065         doubleToWidget(screenHugerED, rc.font_sizes[FONT_SIZE_HUGER]);
1066 }
1067
1068
1069 void PrefScreenFonts::selectRoman(const QString & name)
1070 {
1071         screenRomanFE->set(QFont(name), name);
1072 }
1073
1074
1075 void PrefScreenFonts::selectSans(const QString & name)
1076 {
1077         screenSansFE->set(QFont(name), name);
1078 }
1079
1080
1081 void PrefScreenFonts::selectTypewriter(const QString & name)
1082 {
1083         screenTypewriterFE->set(QFont(name), name);
1084 }
1085
1086
1087 /////////////////////////////////////////////////////////////////////
1088 //
1089 // PrefColors
1090 //
1091 /////////////////////////////////////////////////////////////////////
1092
1093
1094 PrefColors::PrefColors(GuiPreferences * form)
1095         : PrefModule(catLookAndFeel, N_("Colors"), form)
1096 {
1097         setupUi(this);
1098
1099         // FIXME: all of this initialization should be put into the controller.
1100         // See http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg113301.html
1101         // for some discussion of why that is not trivial.
1102         QPixmap icon(32, 32);
1103         for (int i = 0; i < Color_ignore; ++i) {
1104                 ColorCode lc = static_cast<ColorCode>(i);
1105                 if (lc == Color_none
1106                     || lc == Color_black
1107                     || lc == Color_white
1108                     || lc == Color_blue
1109                     || lc == Color_brown
1110                     || lc == Color_cyan
1111                     || lc == Color_darkgray
1112                     || lc == Color_gray
1113                     || lc == Color_green
1114                     || lc == Color_lightgray
1115                     || lc == Color_lime
1116                     || lc == Color_magenta
1117                     || lc == Color_olive
1118                     || lc == Color_orange
1119                     || lc == Color_pink
1120                     || lc == Color_purple
1121                     || lc == Color_red
1122                     || lc == Color_teal
1123                     || lc == Color_violet
1124                     || lc == Color_yellow
1125                     || lc == Color_inherit
1126                     || lc == Color_ignore)
1127                         continue;
1128                 lcolors_.push_back(lc);
1129         }
1130         qSort(lcolors_.begin(), lcolors_.end(), ColorSorter);
1131         vector<ColorCode>::const_iterator cit = lcolors_.begin();
1132         vector<ColorCode>::const_iterator const end = lcolors_.end();
1133         for (; cit != end; ++cit) {
1134                 (void) new QListWidgetItem(QIcon(icon),
1135                         toqstr(lcolor.getGUIName(*cit)), lyxObjectsLW);
1136         }
1137         curcolors_.resize(lcolors_.size());
1138         newcolors_.resize(lcolors_.size());
1139         // End initialization
1140
1141         connect(colorChangePB, SIGNAL(clicked()),
1142                 this, SLOT(changeColor()));
1143         connect(colorResetPB, SIGNAL(clicked()),
1144                 this, SLOT(resetColor()));
1145         connect(colorResetAllPB, SIGNAL(clicked()),
1146                 this, SLOT(resetAllColor()));
1147         connect(lyxObjectsLW, SIGNAL(itemSelectionChanged()),
1148                 this, SLOT(changeLyxObjectsSelection()));
1149         connect(lyxObjectsLW, SIGNAL(itemActivated(QListWidgetItem*)),
1150                 this, SLOT(changeColor()));
1151         connect(syscolorsCB, SIGNAL(toggled(bool)),
1152                 this, SIGNAL(changed()));
1153         connect(syscolorsCB, SIGNAL(toggled(bool)),
1154                 this, SLOT(changeSysColor()));
1155 }
1156
1157
1158 void PrefColors::applyRC(LyXRC & rc) const
1159 {
1160         LyXRC oldrc = rc;
1161
1162         for (unsigned int i = 0; i < lcolors_.size(); ++i)
1163                 if (curcolors_[i] != newcolors_[i])
1164                         form_->setColor(lcolors_[i], newcolors_[i]);
1165         rc.use_system_colors = syscolorsCB->isChecked();
1166
1167         if (oldrc.use_system_colors != rc.use_system_colors)
1168                 guiApp->colorCache().clear();
1169 }
1170
1171
1172 void PrefColors::updateRC(LyXRC const & rc)
1173 {
1174         for (size_type i = 0; i < lcolors_.size(); ++i) {
1175                 QColor color = QColor(guiApp->colorCache().get(lcolors_[i], false));
1176                 QPixmap coloritem(32, 32);
1177                 coloritem.fill(color);
1178                 lyxObjectsLW->item(int(i))->setIcon(QIcon(coloritem));
1179                 newcolors_[i] = curcolors_[i] = color.name();
1180         }
1181         syscolorsCB->setChecked(rc.use_system_colors);
1182         changeLyxObjectsSelection();
1183
1184         setDisabledResets();
1185 }
1186
1187
1188 void PrefColors::changeColor()
1189 {
1190         int const row = lyxObjectsLW->currentRow();
1191
1192         // just to be sure
1193         if (row < 0)
1194                 return;
1195
1196         QString const color = newcolors_[size_t(row)];
1197         QColor const c = QColorDialog::getColor(QColor(color), qApp->focusWidget());
1198
1199         if (setColor(row, c, color)) {
1200                 setDisabledResets();
1201                 // emit signal
1202                 changed();
1203         }
1204 }
1205
1206
1207 void PrefColors::resetColor()
1208 {
1209         int const row = lyxObjectsLW->currentRow();
1210
1211         // just to be sure
1212         if (row < 0)
1213                 return;
1214
1215         QString const color = newcolors_[size_t(row)];
1216         QColor const c = getDefaultColorByRow(row);
1217
1218         if (setColor(row, c, color)) {
1219                 setDisabledResets();
1220                 // emit signal
1221                 changed();
1222         }
1223 }
1224
1225
1226 void PrefColors::resetAllColor()
1227 {
1228         bool isChanged = false;
1229
1230         colorResetAllPB->setDisabled(true);
1231
1232         for (int irow = 0, count = lyxObjectsLW->count(); irow < count; ++irow) {
1233                 QString const color = newcolors_[size_t(irow)];
1234                 QColor const c = getDefaultColorByRow(irow);
1235
1236                 if (setColor(irow, c, color))
1237                         isChanged = true;
1238         }
1239
1240         if (isChanged) {
1241                 setDisabledResets();
1242                 // emit signal
1243                 changed();
1244         }
1245 }
1246
1247
1248 bool PrefColors::setColor(int const row, QColor const new_color,
1249                           QString const old_color)
1250 {
1251         if (new_color.isValid() && new_color.name() != old_color) {
1252                 newcolors_[size_t(row)] = new_color.name();
1253                 QPixmap coloritem(32, 32);
1254                 coloritem.fill(new_color);
1255                 lyxObjectsLW->item(row)->setIcon(QIcon(coloritem));
1256                 return true;
1257         }
1258         return false;
1259 }
1260
1261
1262 void PrefColors::setDisabledResets()
1263 {
1264         int const row = lyxObjectsLW->currentRow();
1265         // set disable reset buttons ...
1266         if (row >= 0)
1267                 colorResetPB->setDisabled(isDefaultColor(row, newcolors_[size_t(row)]));
1268
1269         colorResetAllPB->setDisabled(true);
1270
1271         // ... in between process qt events to give quicker visual feedback to the user ...
1272         guiApp->processEvents();
1273
1274         // ... set disable Reset All button
1275         for (int irow = 0, count = lyxObjectsLW->count(); irow < count; ++irow) {
1276                 if (!isDefaultColor(irow, newcolors_[size_t(irow)])) {
1277                         colorResetAllPB->setDisabled(false);
1278                         // the break condition might hide performance issues
1279                         // if a non-default color is at the top of the list
1280                         break;
1281                 }
1282         }
1283 }
1284
1285
1286 bool PrefColors::isDefaultColor(int const row, QString const color)
1287 {
1288         return color == getDefaultColorByRow(row).name();
1289 }
1290
1291
1292 QColor PrefColors::getDefaultColorByRow(int const row)
1293 {
1294         ColorSet const defaultcolor;
1295         return defaultcolor.getX11Name(lcolors_[size_t(row)]).c_str();
1296 }
1297
1298
1299 void PrefColors::changeSysColor()
1300 {
1301         for (int row = 0 ; row < lyxObjectsLW->count() ; ++row) {
1302                 // skip colors that are taken from system palette
1303                 bool const disable = syscolorsCB->isChecked()
1304                         && guiApp->colorCache().isSystem(lcolors_[size_t(row)]);
1305
1306                 QListWidgetItem * const item = lyxObjectsLW->item(row);
1307                 Qt::ItemFlags const flags = item->flags();
1308
1309                 if (disable)
1310                         item->setFlags(flags & ~Qt::ItemIsEnabled);
1311                 else
1312                         item->setFlags(flags | Qt::ItemIsEnabled);
1313         }
1314 }
1315
1316
1317 void PrefColors::changeLyxObjectsSelection()
1318 {
1319         int currentRow = lyxObjectsLW->currentRow();
1320         colorChangePB->setDisabled(currentRow < 0);
1321
1322         if (currentRow < 0)
1323                 colorResetPB->setDisabled(true);
1324         else
1325                 colorResetPB->setDisabled(
1326                         isDefaultColor(currentRow, newcolors_[size_t(currentRow)]));
1327 }
1328
1329
1330 /////////////////////////////////////////////////////////////////////
1331 //
1332 // PrefDisplay
1333 //
1334 /////////////////////////////////////////////////////////////////////
1335
1336 PrefDisplay::PrefDisplay(GuiPreferences * form)
1337         : PrefModule(catLookAndFeel, N_("Display"), form)
1338 {
1339         setupUi(this);
1340         connect(displayGraphicsCB, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
1341         connect(instantPreviewCO, SIGNAL(activated(int)), this, SIGNAL(changed()));
1342         connect(previewSizeSB, SIGNAL(valueChanged(double)), this, SIGNAL(changed()));
1343         connect(paragraphMarkerCB, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
1344 }
1345
1346
1347 void PrefDisplay::on_instantPreviewCO_currentIndexChanged(int index)
1348 {
1349         previewSizeSB->setEnabled(index != 0);
1350 }
1351
1352
1353 void PrefDisplay::applyRC(LyXRC & rc) const
1354 {
1355         switch (instantPreviewCO->currentIndex()) {
1356                 case 0:
1357                         rc.preview = LyXRC::PREVIEW_OFF;
1358                         break;
1359                 case 1:
1360                         rc.preview = LyXRC::PREVIEW_NO_MATH;
1361                         break;
1362                 case 2:
1363                         rc.preview = LyXRC::PREVIEW_ON;
1364                         break;
1365         }
1366
1367         rc.display_graphics = displayGraphicsCB->isChecked();
1368         rc.preview_scale_factor = previewSizeSB->value();
1369         rc.paragraph_markers = paragraphMarkerCB->isChecked();
1370
1371         // FIXME!! The graphics cache no longer has a changeDisplay method.
1372 #if 0
1373         if (old_value != rc.display_graphics) {
1374                 graphics::GCache & gc = graphics::GCache::get();
1375                 gc.changeDisplay();
1376         }
1377 #endif
1378 }
1379
1380
1381 void PrefDisplay::updateRC(LyXRC const & rc)
1382 {
1383         switch (rc.preview) {
1384         case LyXRC::PREVIEW_OFF:
1385                 instantPreviewCO->setCurrentIndex(0);
1386                 break;
1387         case LyXRC::PREVIEW_NO_MATH :
1388                 instantPreviewCO->setCurrentIndex(1);
1389                 break;
1390         case LyXRC::PREVIEW_ON :
1391                 instantPreviewCO->setCurrentIndex(2);
1392                 break;
1393         }
1394
1395         displayGraphicsCB->setChecked(rc.display_graphics);
1396         previewSizeSB->setValue(rc.preview_scale_factor);
1397         paragraphMarkerCB->setChecked(rc.paragraph_markers);
1398         previewSizeSB->setEnabled(
1399                 rc.display_graphics
1400                 && rc.preview != LyXRC::PREVIEW_OFF);
1401 }
1402
1403
1404 /////////////////////////////////////////////////////////////////////
1405 //
1406 // PrefPaths
1407 //
1408 /////////////////////////////////////////////////////////////////////
1409
1410 PrefPaths::PrefPaths(GuiPreferences * form)
1411         : PrefModule(QString(), N_("Paths"), form)
1412 {
1413         setupUi(this);
1414
1415         connect(workingDirPB, SIGNAL(clicked()), this, SLOT(selectWorkingdir()));
1416         connect(workingDirED, SIGNAL(textChanged(QString)),
1417                 this, SIGNAL(changed()));
1418
1419         connect(templateDirPB, SIGNAL(clicked()), this, SLOT(selectTemplatedir()));
1420         connect(templateDirED, SIGNAL(textChanged(QString)),
1421                 this, SIGNAL(changed()));
1422
1423         connect(exampleDirPB, SIGNAL(clicked()), this, SLOT(selectExampledir()));
1424         connect(exampleDirED, SIGNAL(textChanged(QString)),
1425                 this, SIGNAL(changed()));
1426
1427         connect(backupDirPB, SIGNAL(clicked()), this, SLOT(selectBackupdir()));
1428         connect(backupDirED, SIGNAL(textChanged(QString)),
1429                 this, SIGNAL(changed()));
1430
1431         connect(lyxserverDirPB, SIGNAL(clicked()), this, SLOT(selectLyxPipe()));
1432         connect(lyxserverDirED, SIGNAL(textChanged(QString)),
1433                 this, SIGNAL(changed()));
1434
1435         connect(thesaurusDirPB, SIGNAL(clicked()), this, SLOT(selectThesaurusdir()));
1436         connect(thesaurusDirED, SIGNAL(textChanged(QString)),
1437                 this, SIGNAL(changed()));
1438
1439         connect(tempDirPB, SIGNAL(clicked()), this, SLOT(selectTempdir()));
1440         connect(tempDirED, SIGNAL(textChanged(QString)),
1441                 this, SIGNAL(changed()));
1442
1443 #if defined(USE_HUNSPELL)
1444         connect(hunspellDirPB, SIGNAL(clicked()), this, SLOT(selectHunspelldir()));
1445         connect(hunspellDirED, SIGNAL(textChanged(QString)),
1446                 this, SIGNAL(changed()));
1447 #else
1448         hunspellDirPB->setEnabled(false);
1449         hunspellDirED->setEnabled(false);
1450 #endif
1451
1452         connect(pathPrefixED, SIGNAL(textChanged(QString)),
1453                 this, SIGNAL(changed()));
1454
1455         connect(texinputsPrefixED, SIGNAL(textChanged(QString)),
1456                 this, SIGNAL(changed()));
1457
1458         pathPrefixED->setValidator(new NoNewLineValidator(pathPrefixED));
1459         texinputsPrefixED->setValidator(new NoNewLineValidator(texinputsPrefixED));
1460 }
1461
1462
1463 void PrefPaths::applyRC(LyXRC & rc) const
1464 {
1465         rc.document_path = internal_path(fromqstr(workingDirED->text()));
1466         rc.example_path = internal_path(fromqstr(exampleDirED->text()));
1467         rc.template_path = internal_path(fromqstr(templateDirED->text()));
1468         rc.backupdir_path = internal_path(fromqstr(backupDirED->text()));
1469         rc.tempdir_path = internal_path(fromqstr(tempDirED->text()));
1470         rc.thesaurusdir_path = internal_path(fromqstr(thesaurusDirED->text()));
1471         rc.hunspelldir_path = internal_path(fromqstr(hunspellDirED->text()));
1472         rc.path_prefix = internal_path_list(fromqstr(pathPrefixED->text()));
1473         rc.texinputs_prefix = internal_path_list(fromqstr(texinputsPrefixED->text()));
1474         // FIXME: should be a checkbox only
1475         rc.lyxpipes = internal_path(fromqstr(lyxserverDirED->text()));
1476 }
1477
1478
1479 void PrefPaths::updateRC(LyXRC const & rc)
1480 {
1481         workingDirED->setText(toqstr(external_path(rc.document_path)));
1482         exampleDirED->setText(toqstr(external_path(rc.example_path)));
1483         templateDirED->setText(toqstr(external_path(rc.template_path)));
1484         backupDirED->setText(toqstr(external_path(rc.backupdir_path)));
1485         tempDirED->setText(toqstr(external_path(rc.tempdir_path)));
1486         thesaurusDirED->setText(toqstr(external_path(rc.thesaurusdir_path)));
1487         hunspellDirED->setText(toqstr(external_path(rc.hunspelldir_path)));
1488         pathPrefixED->setText(toqstr(external_path_list(rc.path_prefix)));
1489         texinputsPrefixED->setText(toqstr(external_path_list(rc.texinputs_prefix)));
1490         // FIXME: should be a checkbox only
1491         lyxserverDirED->setText(toqstr(external_path(rc.lyxpipes)));
1492 }
1493
1494
1495 void PrefPaths::selectExampledir()
1496 {
1497         QString file = browseDir(internalPath(exampleDirED->text()),
1498                 qt_("Select directory for example files"));
1499         if (!file.isEmpty())
1500                 exampleDirED->setText(file);
1501 }
1502
1503
1504 void PrefPaths::selectTemplatedir()
1505 {
1506         QString file = browseDir(internalPath(templateDirED->text()),
1507                 qt_("Select a document templates directory"));
1508         if (!file.isEmpty())
1509                 templateDirED->setText(file);
1510 }
1511
1512
1513 void PrefPaths::selectTempdir()
1514 {
1515         QString file = browseDir(internalPath(tempDirED->text()),
1516                 qt_("Select a temporary directory"));
1517         if (!file.isEmpty())
1518                 tempDirED->setText(file);
1519 }
1520
1521
1522 void PrefPaths::selectBackupdir()
1523 {
1524         QString file = browseDir(internalPath(backupDirED->text()),
1525                 qt_("Select a backups directory"));
1526         if (!file.isEmpty())
1527                 backupDirED->setText(file);
1528 }
1529
1530
1531 void PrefPaths::selectWorkingdir()
1532 {
1533         QString file = browseDir(internalPath(workingDirED->text()),
1534                 qt_("Select a document directory"));
1535         if (!file.isEmpty())
1536                 workingDirED->setText(file);
1537 }
1538
1539
1540 void PrefPaths::selectThesaurusdir()
1541 {
1542         QString file = browseDir(internalPath(thesaurusDirED->text()),
1543                 qt_("Set the path to the thesaurus dictionaries"));
1544         if (!file.isEmpty())
1545                 thesaurusDirED->setText(file);
1546 }
1547
1548
1549 void PrefPaths::selectHunspelldir()
1550 {
1551         QString file = browseDir(internalPath(hunspellDirED->text()),
1552                 qt_("Set the path to the Hunspell dictionaries"));
1553         if (!file.isEmpty())
1554                 hunspellDirED->setText(file);
1555 }
1556
1557
1558 void PrefPaths::selectLyxPipe()
1559 {
1560         QString file = form_->browse(internalPath(lyxserverDirED->text()),
1561                 qt_("Give a filename for the LyX server pipe"));
1562         if (!file.isEmpty())
1563                 lyxserverDirED->setText(file);
1564 }
1565
1566
1567 /////////////////////////////////////////////////////////////////////
1568 //
1569 // PrefSpellchecker
1570 //
1571 /////////////////////////////////////////////////////////////////////
1572
1573 PrefSpellchecker::PrefSpellchecker(GuiPreferences * form)
1574         : PrefModule(catLanguage, N_("Spellchecker"), form)
1575 {
1576         setupUi(this);
1577
1578 // FIXME: this check should test the target platform (darwin)
1579 #if defined(USE_MACOSX_PACKAGING)
1580         spellcheckerCB->addItem(qt_("Native"), QString("native"));
1581 #define CONNECT_APPLESPELL
1582 #else
1583 #undef CONNECT_APPLESPELL
1584 #endif
1585 #if defined(USE_ASPELL)
1586         spellcheckerCB->addItem(qt_("Aspell"), QString("aspell"));
1587 #endif
1588 #if defined(USE_ENCHANT)
1589         spellcheckerCB->addItem(qt_("Enchant"), QString("enchant"));
1590 #endif
1591 #if defined(USE_HUNSPELL)
1592         spellcheckerCB->addItem(qt_("Hunspell"), QString("hunspell"));
1593 #endif
1594
1595         #if defined(CONNECT_APPLESPELL) || defined(USE_ASPELL) || defined(USE_ENCHANT) || defined(USE_HUNSPELL)
1596                 connect(spellcheckerCB, SIGNAL(currentIndexChanged(int)),
1597                         this, SIGNAL(changed()));
1598                 connect(altLanguageED, SIGNAL(textChanged(QString)),
1599                         this, SIGNAL(changed()));
1600                 connect(escapeCharactersED, SIGNAL(textChanged(QString)),
1601                         this, SIGNAL(changed()));
1602                 connect(compoundWordCB, SIGNAL(clicked()),
1603                         this, SIGNAL(changed()));
1604                 connect(spellcheckContinuouslyCB, SIGNAL(clicked()),
1605                         this, SIGNAL(changed()));
1606                 connect(spellcheckNotesCB, SIGNAL(clicked()),
1607                         this, SIGNAL(changed()));
1608
1609                 altLanguageED->setValidator(new NoNewLineValidator(altLanguageED));
1610                 escapeCharactersED->setValidator(new NoNewLineValidator(escapeCharactersED));
1611         #else
1612                 spellcheckerCB->setEnabled(false);
1613                 altLanguageED->setEnabled(false);
1614                 escapeCharactersED->setEnabled(false);
1615                 compoundWordCB->setEnabled(false);
1616                 spellcheckContinuouslyCB->setEnabled(false);
1617                 spellcheckNotesCB->setEnabled(false);
1618         #endif
1619 }
1620
1621
1622 void PrefSpellchecker::applyRC(LyXRC & rc) const
1623 {
1624         string const speller = fromqstr(spellcheckerCB->
1625                 itemData(spellcheckerCB->currentIndex()).toString());
1626         if (!speller.empty())
1627                 rc.spellchecker = speller;
1628         rc.spellchecker_alt_lang = fromqstr(altLanguageED->text());
1629         rc.spellchecker_esc_chars = fromqstr(escapeCharactersED->text());
1630         rc.spellchecker_accept_compound = compoundWordCB->isChecked();
1631         rc.spellcheck_continuously = spellcheckContinuouslyCB->isChecked();
1632         rc.spellcheck_notes = spellcheckNotesCB->isChecked();
1633 }
1634
1635
1636 void PrefSpellchecker::updateRC(LyXRC const & rc)
1637 {
1638         spellcheckerCB->setCurrentIndex(
1639                 spellcheckerCB->findData(toqstr(rc.spellchecker)));
1640         altLanguageED->setText(toqstr(rc.spellchecker_alt_lang));
1641         escapeCharactersED->setText(toqstr(rc.spellchecker_esc_chars));
1642         compoundWordCB->setChecked(rc.spellchecker_accept_compound);
1643         spellcheckContinuouslyCB->setChecked(rc.spellcheck_continuously);
1644         spellcheckNotesCB->setChecked(rc.spellcheck_notes);
1645 }
1646
1647
1648 void PrefSpellchecker::on_spellcheckerCB_currentIndexChanged(int index)
1649 {
1650         QString spellchecker = spellcheckerCB->itemData(index).toString();
1651
1652         compoundWordCB->setEnabled(spellchecker == QString("aspell"));
1653 }
1654
1655
1656
1657 /////////////////////////////////////////////////////////////////////
1658 //
1659 // PrefConverters
1660 //
1661 /////////////////////////////////////////////////////////////////////
1662
1663
1664 PrefConverters::PrefConverters(GuiPreferences * form)
1665         : PrefModule(catFiles, N_("Converters"), form)
1666 {
1667         setupUi(this);
1668
1669         connect(converterNewPB, SIGNAL(clicked()),
1670                 this, SLOT(updateConverter()));
1671         connect(converterRemovePB, SIGNAL(clicked()),
1672                 this, SLOT(removeConverter()));
1673         connect(converterModifyPB, SIGNAL(clicked()),
1674                 this, SLOT(updateConverter()));
1675         connect(convertersLW, SIGNAL(currentRowChanged(int)),
1676                 this, SLOT(switchConverter()));
1677         connect(converterFromCO, SIGNAL(activated(QString)),
1678                 this, SLOT(changeConverter()));
1679         connect(converterToCO, SIGNAL(activated(QString)),
1680                 this, SLOT(changeConverter()));
1681         connect(converterED, SIGNAL(textEdited(QString)),
1682                 this, SLOT(changeConverter()));
1683         connect(converterFlagED, SIGNAL(textEdited(QString)),
1684                 this, SLOT(changeConverter()));
1685         connect(converterNewPB, SIGNAL(clicked()),
1686                 this, SIGNAL(changed()));
1687         connect(converterRemovePB, SIGNAL(clicked()),
1688                 this, SIGNAL(changed()));
1689         connect(converterModifyPB, SIGNAL(clicked()),
1690                 this, SIGNAL(changed()));
1691         connect(maxAgeLE, SIGNAL(textEdited(QString)),
1692                 this, SIGNAL(changed()));
1693         connect(needauthForbiddenCB, SIGNAL(toggled(bool)),
1694                 this, SIGNAL(changed()));
1695
1696         converterED->setValidator(new NoNewLineValidator(converterED));
1697         converterFlagED->setValidator(new NoNewLineValidator(converterFlagED));
1698         maxAgeLE->setValidator(new QDoubleValidator(0, HUGE_VAL, 6, maxAgeLE));
1699         //converterDefGB->setFocusProxy(convertersLW);
1700 }
1701
1702
1703 void PrefConverters::applyRC(LyXRC & rc) const
1704 {
1705         rc.use_converter_cache = cacheCB->isChecked();
1706         rc.use_converter_needauth_forbidden = needauthForbiddenCB->isChecked();
1707         rc.use_converter_needauth = needauthCB->isChecked();
1708         rc.converter_cache_maxage = int(widgetToDouble(maxAgeLE) * 86400.0);
1709 }
1710
1711
1712 static void setCheckboxBlockSignals(QCheckBox *cb, bool checked) {
1713         cb->blockSignals(true);
1714         cb->setChecked(checked);
1715         cb->blockSignals(false);
1716 }
1717
1718
1719 void PrefConverters::updateRC(LyXRC const & rc)
1720 {
1721         cacheCB->setChecked(rc.use_converter_cache);
1722         needauthForbiddenCB->setChecked(rc.use_converter_needauth_forbidden);
1723         setCheckboxBlockSignals(needauthCB, rc.use_converter_needauth);
1724         QString max_age;
1725         doubleToWidget(maxAgeLE, (double(rc.converter_cache_maxage) / 86400.0), 'g', 6);
1726         updateGui();
1727 }
1728
1729
1730 void PrefConverters::updateGui()
1731 {
1732         QString const pattern("%1 -> %2");
1733         form_->formats().sort();
1734         form_->converters().update(form_->formats());
1735         // save current selection
1736         QString current =
1737                 pattern
1738                 .arg(converterFromCO->currentText())
1739                 .arg(converterToCO->currentText());
1740
1741         converterFromCO->clear();
1742         converterToCO->clear();
1743
1744         for (Format const & f : form_->formats()) {
1745                 QString const name = toqstr(translateIfPossible(f.prettyname()));
1746                 converterFromCO->addItem(name);
1747                 converterToCO->addItem(name);
1748         }
1749
1750         // currentRowChanged(int) is also triggered when updating the listwidget
1751         // block signals to avoid unnecessary calls to switchConverter()
1752         convertersLW->blockSignals(true);
1753         convertersLW->clear();
1754
1755         for (Converter const & c : form_->converters()) {
1756                 QString const name =
1757                         pattern
1758                         .arg(toqstr(translateIfPossible(c.From()->prettyname())))
1759                         .arg(toqstr(translateIfPossible(c.To()->prettyname())));
1760                 int type = form_->converters().getNumber(c.From()->name(),
1761                                                          c.To()->name());
1762                 new QListWidgetItem(name, convertersLW, type);
1763         }
1764         convertersLW->sortItems(Qt::AscendingOrder);
1765         convertersLW->blockSignals(false);
1766
1767         // restore selection
1768         if (current != pattern.arg(QString()).arg(QString())) {
1769                 QList<QListWidgetItem *> const item =
1770                         convertersLW->findItems(current, Qt::MatchExactly);
1771                 if (!item.isEmpty())
1772                         convertersLW->setCurrentItem(item.at(0));
1773         }
1774
1775         // select first element if restoring failed
1776         if (convertersLW->currentRow() == -1)
1777                 convertersLW->setCurrentRow(0);
1778
1779         updateButtons();
1780 }
1781
1782
1783 void PrefConverters::switchConverter()
1784 {
1785         int const cnr = convertersLW->currentItem()->type();
1786         Converter const & c(form_->converters().get(cnr));
1787         converterFromCO->setCurrentIndex(form_->formats().getNumber(c.from()));
1788         converterToCO->setCurrentIndex(form_->formats().getNumber(c.to()));
1789         converterED->setText(toqstr(c.command()));
1790         converterFlagED->setText(toqstr(c.flags()));
1791
1792         updateButtons();
1793 }
1794
1795
1796 void PrefConverters::changeConverter()
1797 {
1798         updateButtons();
1799 }
1800
1801
1802 void PrefConverters::updateButtons()
1803 {
1804         if (form_->formats().empty())
1805                 return;
1806         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1807         Format const & to = form_->formats().get(converterToCO->currentIndex());
1808         int const sel = form_->converters().getNumber(from.name(), to.name());
1809         bool const known = sel >= 0;
1810         bool const valid = !(converterED->text().isEmpty()
1811                 || from.name() == to.name());
1812
1813         string old_command;
1814         string old_flag;
1815
1816         if (convertersLW->count() > 0) {
1817                 int const cnr = convertersLW->currentItem()->type();
1818                 Converter const & c = form_->converters().get(cnr);
1819                 old_command = c.command();
1820                 old_flag = c.flags();
1821         }
1822
1823         string const new_command = fromqstr(converterED->text());
1824         string const new_flag = fromqstr(converterFlagED->text());
1825
1826         bool modified = (old_command != new_command || old_flag != new_flag);
1827
1828         converterModifyPB->setEnabled(valid && known && modified);
1829         converterNewPB->setEnabled(valid && !known);
1830         converterRemovePB->setEnabled(known);
1831
1832         maxAgeLE->setEnabled(cacheCB->isChecked());
1833         maxAgeLA->setEnabled(cacheCB->isChecked());
1834 }
1835
1836
1837 // FIXME: user must
1838 // specify unique from/to or it doesn't appear. This is really bad UI
1839 // this is why we can use the same function for both new and modify
1840 void PrefConverters::updateConverter()
1841 {
1842         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1843         Format const & to = form_->formats().get(converterToCO->currentIndex());
1844         string const flags = fromqstr(converterFlagED->text());
1845         string const command = fromqstr(converterED->text());
1846
1847         Converter const * old =
1848                 form_->converters().getConverter(from.name(), to.name());
1849         form_->converters().add(from.name(), to.name(), command, flags);
1850
1851         if (!old)
1852                 form_->converters().updateLast(form_->formats());
1853
1854         updateGui();
1855
1856         // Remove all files created by this converter from the cache, since
1857         // the modified converter might create different files.
1858         ConverterCache::get().remove_all(from.name(), to.name());
1859 }
1860
1861
1862 void PrefConverters::removeConverter()
1863 {
1864         Format const & from = form_->formats().get(converterFromCO->currentIndex());
1865         Format const & to = form_->formats().get(converterToCO->currentIndex());
1866         form_->converters().erase(from.name(), to.name());
1867
1868         updateGui();
1869
1870         // Remove all files created by this converter from the cache, since
1871         // a possible new converter might create different files.
1872         ConverterCache::get().remove_all(from.name(), to.name());
1873 }
1874
1875
1876 void PrefConverters::on_cacheCB_stateChanged(int state)
1877 {
1878         maxAgeLE->setEnabled(state == Qt::Checked);
1879         maxAgeLA->setEnabled(state == Qt::Checked);
1880         changed();
1881 }
1882
1883
1884 void PrefConverters::on_needauthForbiddenCB_toggled(bool checked)
1885 {
1886         needauthCB->setEnabled(!checked);
1887 }
1888
1889
1890 void PrefConverters::on_needauthCB_toggled(bool checked)
1891 {
1892         if (checked) {
1893                 changed();
1894                 return;
1895         }
1896
1897         int ret = frontend::Alert::prompt(
1898                 _("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!"),
1899                 0, 0, _("&No"), _("&Yes"));
1900         if (ret == 1)
1901                 changed();
1902         else
1903                 setCheckboxBlockSignals(needauthCB, true);
1904 }
1905
1906
1907 /////////////////////////////////////////////////////////////////////
1908 //
1909 // FormatValidator
1910 //
1911 /////////////////////////////////////////////////////////////////////
1912
1913 class FormatValidator : public QValidator
1914 {
1915 public:
1916         FormatValidator(QWidget *, Formats const & f);
1917         void fixup(QString & input) const;
1918         QValidator::State validate(QString & input, int & pos) const;
1919 private:
1920         virtual QString toString(Format const & format) const = 0;
1921         int nr() const;
1922         Formats const & formats_;
1923 };
1924
1925
1926 FormatValidator::FormatValidator(QWidget * parent, Formats const & f)
1927         : QValidator(parent), formats_(f)
1928 {
1929 }
1930
1931
1932 void FormatValidator::fixup(QString & input) const
1933 {
1934         Formats::const_iterator cit = formats_.begin();
1935         Formats::const_iterator end = formats_.end();
1936         for (; cit != end; ++cit) {
1937                 QString const name = toString(*cit);
1938                 if (distance(formats_.begin(), cit) == nr()) {
1939                         input = name;
1940                         return;
1941                 }
1942         }
1943 }
1944
1945
1946 QValidator::State FormatValidator::validate(QString & input, int & /*pos*/) const
1947 {
1948         Formats::const_iterator cit = formats_.begin();
1949         Formats::const_iterator end = formats_.end();
1950         bool unknown = true;
1951         for (; unknown && cit != end; ++cit) {
1952                 QString const name = toString(*cit);
1953                 if (distance(formats_.begin(), cit) != nr())
1954                         unknown = name != input;
1955         }
1956
1957         if (unknown && !input.isEmpty())
1958                 return QValidator::Acceptable;
1959         else
1960                 return QValidator::Intermediate;
1961 }
1962
1963
1964 int FormatValidator::nr() const
1965 {
1966         QComboBox * p = qobject_cast<QComboBox *>(parent());
1967         return p->itemData(p->currentIndex()).toInt();
1968 }
1969
1970
1971 /////////////////////////////////////////////////////////////////////
1972 //
1973 // FormatNameValidator
1974 //
1975 /////////////////////////////////////////////////////////////////////
1976
1977 class FormatNameValidator : public FormatValidator
1978 {
1979 public:
1980         FormatNameValidator(QWidget * parent, Formats const & f)
1981                 : FormatValidator(parent, f)
1982         {}
1983 private:
1984         QString toString(Format const & format) const
1985         {
1986                 return toqstr(format.name());
1987         }
1988 };
1989
1990
1991 /////////////////////////////////////////////////////////////////////
1992 //
1993 // FormatPrettynameValidator
1994 //
1995 /////////////////////////////////////////////////////////////////////
1996
1997 class FormatPrettynameValidator : public FormatValidator
1998 {
1999 public:
2000         FormatPrettynameValidator(QWidget * parent, Formats const & f)
2001                 : FormatValidator(parent, f)
2002         {}
2003 private:
2004         QString toString(Format const & format) const
2005         {
2006                 return toqstr(translateIfPossible(format.prettyname()));
2007         }
2008 };
2009
2010
2011 /////////////////////////////////////////////////////////////////////
2012 //
2013 // PrefFileformats
2014 //
2015 /////////////////////////////////////////////////////////////////////
2016
2017 PrefFileformats::PrefFileformats(GuiPreferences * form)
2018         : PrefModule(catFiles, N_("File Formats"), form)
2019 {
2020         setupUi(this);
2021
2022         formatED->setValidator(new FormatNameValidator(formatsCB, form_->formats()));
2023         formatsCB->setValidator(new FormatPrettynameValidator(formatsCB, form_->formats()));
2024         extensionsED->setValidator(new NoNewLineValidator(extensionsED));
2025         shortcutED->setValidator(new NoNewLineValidator(shortcutED));
2026         editorED->setValidator(new NoNewLineValidator(editorED));
2027         viewerED->setValidator(new NoNewLineValidator(viewerED));
2028         copierED->setValidator(new NoNewLineValidator(copierED));
2029
2030         connect(documentCB, SIGNAL(clicked()),
2031                 this, SLOT(setFlags()));
2032         connect(vectorCB, SIGNAL(clicked()),
2033                 this, SLOT(setFlags()));
2034         connect(exportMenuCB, SIGNAL(clicked()),
2035                 this, SLOT(setFlags()));
2036         connect(formatsCB->lineEdit(), SIGNAL(editingFinished()),
2037                 this, SLOT(updatePrettyname()));
2038         connect(formatsCB->lineEdit(), SIGNAL(textEdited(QString)),
2039                 this, SIGNAL(changed()));
2040         connect(defaultFormatCB, SIGNAL(activated(QString)),
2041                 this, SIGNAL(changed()));
2042         connect(defaultOTFFormatCB, SIGNAL(activated(QString)),
2043                 this, SIGNAL(changed()));
2044         connect(defaultPlatexFormatCB, SIGNAL(activated(QString)),
2045                 this, SIGNAL(changed()));
2046         connect(viewerCO, SIGNAL(activated(int)),
2047                 this, SIGNAL(changed()));
2048         connect(editorCO, SIGNAL(activated(int)),
2049                 this, SIGNAL(changed()));
2050 }
2051
2052
2053 namespace {
2054
2055 string const l10n_shortcut(docstring const & prettyname, string const & shortcut)
2056 {
2057         if (shortcut.empty())
2058                 return string();
2059
2060         string l10n_format =
2061                 to_utf8(_(to_utf8(prettyname) + '|' + shortcut));
2062         return split(l10n_format, '|');
2063 }
2064
2065 } // namespace
2066
2067
2068 void PrefFileformats::applyRC(LyXRC & rc) const
2069 {
2070         QString const default_format = defaultFormatCB->itemData(
2071                 defaultFormatCB->currentIndex()).toString();
2072         rc.default_view_format = fromqstr(default_format);
2073         QString const default_otf_format = defaultOTFFormatCB->itemData(
2074                 defaultOTFFormatCB->currentIndex()).toString();
2075         rc.default_otf_view_format = fromqstr(default_otf_format);
2076         QString const default_platex_format = defaultPlatexFormatCB->itemData(
2077                 defaultPlatexFormatCB->currentIndex()).toString();
2078         rc.default_platex_view_format = fromqstr(default_platex_format);
2079 }
2080
2081
2082 void PrefFileformats::updateRC(LyXRC const & rc)
2083 {
2084         viewer_alternatives = rc.viewer_alternatives;
2085         editor_alternatives = rc.editor_alternatives;
2086         bool const init = defaultFormatCB->currentText().isEmpty();
2087         updateView();
2088         if (init) {
2089                 int pos =
2090                         defaultFormatCB->findData(toqstr(rc.default_view_format));
2091                 defaultFormatCB->setCurrentIndex(pos);
2092                 pos = defaultOTFFormatCB->findData(toqstr(rc.default_otf_view_format));
2093                                 defaultOTFFormatCB->setCurrentIndex(pos);
2094                 defaultOTFFormatCB->setCurrentIndex(pos);
2095                 pos = defaultPlatexFormatCB->findData(toqstr(rc.default_platex_view_format));
2096                                 defaultPlatexFormatCB->setCurrentIndex(pos);
2097                 defaultPlatexFormatCB->setCurrentIndex(pos);
2098         }
2099 }
2100
2101
2102 void PrefFileformats::updateView()
2103 {
2104         QString const current = formatsCB->currentText();
2105         QString const current_def = defaultFormatCB->currentText();
2106         QString const current_def_otf = defaultOTFFormatCB->currentText();
2107         QString const current_def_platex = defaultPlatexFormatCB->currentText();
2108
2109         // update comboboxes with formats
2110         formatsCB->blockSignals(true);
2111         defaultFormatCB->blockSignals(true);
2112         defaultOTFFormatCB->blockSignals(true);
2113         defaultPlatexFormatCB->blockSignals(true);
2114         formatsCB->clear();
2115         defaultFormatCB->clear();
2116         defaultOTFFormatCB->clear();
2117         defaultPlatexFormatCB->clear();
2118         form_->formats().sort();
2119         for (Format const & f : form_->formats()) {
2120                 QString const prettyname = toqstr(translateIfPossible(f.prettyname()));
2121                 formatsCB->addItem(prettyname,
2122                                    QVariant(form_->formats().getNumber(f.name())));
2123                 if (f.viewer().empty())
2124                         continue;
2125                 if (form_->converters().isReachable("xhtml", f.name())
2126                     || form_->converters().isReachable("dviluatex", f.name())
2127                     || form_->converters().isReachable("luatex", f.name())
2128                     || form_->converters().isReachable("xetex", f.name())) {
2129                         defaultFormatCB->addItem(prettyname,
2130                                         QVariant(toqstr(f.name())));
2131                         defaultOTFFormatCB->addItem(prettyname,
2132                                         QVariant(toqstr(f.name())));
2133                 } else {
2134                         if (form_->converters().isReachable("latex", f.name())
2135                             || form_->converters().isReachable("pdflatex", f.name()))
2136                                 defaultFormatCB->addItem(prettyname,
2137                                         QVariant(toqstr(f.name())));
2138                         if (form_->converters().isReachable("platex", f.name()))
2139                                         defaultPlatexFormatCB->addItem(prettyname,
2140                                                 QVariant(toqstr(f.name())));
2141                 }
2142         }
2143
2144         // restore selections
2145         int item = formatsCB->findText(current, Qt::MatchExactly);
2146         formatsCB->setCurrentIndex(item < 0 ? 0 : item);
2147         on_formatsCB_currentIndexChanged(item < 0 ? 0 : item);
2148         item = defaultFormatCB->findText(current_def, Qt::MatchExactly);
2149         defaultFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2150         item = defaultOTFFormatCB->findText(current_def_otf, Qt::MatchExactly);
2151         defaultOTFFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2152         item = defaultPlatexFormatCB->findText(current_def_platex, Qt::MatchExactly);
2153         defaultPlatexFormatCB->setCurrentIndex(item < 0 ? 0 : item);
2154         formatsCB->blockSignals(false);
2155         defaultFormatCB->blockSignals(false);
2156         defaultOTFFormatCB->blockSignals(false);
2157         defaultPlatexFormatCB->blockSignals(false);
2158 }
2159
2160
2161 void PrefFileformats::on_formatsCB_currentIndexChanged(int i)
2162 {
2163         if (form_->formats().empty())
2164                 return;
2165         int const nr = formatsCB->itemData(i).toInt();
2166         Format const f = form_->formats().get(nr);
2167
2168         formatED->setText(toqstr(f.name()));
2169         copierED->setText(toqstr(form_->movers().command(f.name())));
2170         extensionsED->setText(toqstr(f.extensions()));
2171         mimeED->setText(toqstr(f.mime()));
2172         shortcutED->setText(
2173                 toqstr(l10n_shortcut(f.prettyname(), f.shortcut())));
2174         documentCB->setChecked((f.documentFormat()));
2175         vectorCB->setChecked((f.vectorFormat()));
2176         exportMenuCB->setChecked((f.inExportMenu()));
2177         exportMenuCB->setEnabled((f.documentFormat()));
2178         updateViewers();
2179         updateEditors();
2180 }
2181
2182
2183 void PrefFileformats::setFlags()
2184 {
2185         int flags = Format::none;
2186         if (documentCB->isChecked())
2187                 flags |= Format::document;
2188         if (vectorCB->isChecked())
2189                 flags |= Format::vector;
2190         if (exportMenuCB->isChecked())
2191                 flags |= Format::export_menu;
2192         currentFormat().setFlags(flags);
2193         exportMenuCB->setEnabled(documentCB->isChecked());
2194         changed();
2195 }
2196
2197
2198 void PrefFileformats::on_copierED_textEdited(const QString & s)
2199 {
2200         string const fmt = fromqstr(formatED->text());
2201         form_->movers().set(fmt, fromqstr(s));
2202         changed();
2203 }
2204
2205
2206 void PrefFileformats::on_extensionsED_textEdited(const QString & s)
2207 {
2208         currentFormat().setExtensions(fromqstr(s));
2209         changed();
2210 }
2211
2212
2213 void PrefFileformats::on_viewerED_textEdited(const QString & s)
2214 {
2215         currentFormat().setViewer(fromqstr(s));
2216         changed();
2217 }
2218
2219
2220 void PrefFileformats::on_editorED_textEdited(const QString & s)
2221 {
2222         currentFormat().setEditor(fromqstr(s));
2223         changed();
2224 }
2225
2226
2227 void PrefFileformats::on_mimeED_textEdited(const QString & s)
2228 {
2229         currentFormat().setMime(fromqstr(s));
2230         changed();
2231 }
2232
2233
2234 void PrefFileformats::on_shortcutED_textEdited(const QString & s)
2235 {
2236         string const new_shortcut = fromqstr(s);
2237         if (new_shortcut == l10n_shortcut(currentFormat().prettyname(),
2238                                           currentFormat().shortcut()))
2239                 return;
2240         currentFormat().setShortcut(new_shortcut);
2241         changed();
2242 }
2243
2244
2245 void PrefFileformats::on_formatED_editingFinished()
2246 {
2247         string const newname = fromqstr(formatED->displayText());
2248         string const oldname = currentFormat().name();
2249         if (newname == oldname)
2250                 return;
2251         if (form_->converters().formatIsUsed(oldname)) {
2252                 Alert::error(_("Format in use"),
2253                              _("You cannot change a format's short name "
2254                                "if the format is used by a converter. "
2255                                "Please remove the converter first."));
2256                 updateView();
2257                 return;
2258         }
2259
2260         currentFormat().setName(newname);
2261         changed();
2262 }
2263
2264
2265 void PrefFileformats::on_formatED_textChanged(const QString &)
2266 {
2267         QString t = formatED->text();
2268         int p = 0;
2269         bool valid = formatED->validator()->validate(t, p) == QValidator::Acceptable;
2270         setValid(formatLA, valid);
2271 }
2272
2273
2274 void PrefFileformats::on_formatsCB_editTextChanged(const QString &)
2275 {
2276         QString t = formatsCB->currentText();
2277         int p = 0;
2278         bool valid = formatsCB->validator()->validate(t, p) == QValidator::Acceptable;
2279         setValid(formatsLA, valid);
2280 }
2281
2282
2283 void PrefFileformats::updatePrettyname()
2284 {
2285         QString const newname = formatsCB->currentText();
2286         if (newname == toqstr(translateIfPossible(currentFormat().prettyname())))
2287                 return;
2288
2289         currentFormat().setPrettyname(qstring_to_ucs4(newname));
2290         formatsChanged();
2291         updateView();
2292         changed();
2293 }
2294
2295
2296 namespace {
2297         void updateComboBox(LyXRC::Alternatives const & alts,
2298                             string const & fmt, QComboBox * combo)
2299         {
2300                 LyXRC::Alternatives::const_iterator it =
2301                                 alts.find(fmt);
2302                 if (it != alts.end()) {
2303                         LyXRC::CommandSet const & cmds = it->second;
2304                         LyXRC::CommandSet::const_iterator sit =
2305                                         cmds.begin();
2306                         LyXRC::CommandSet::const_iterator const sen =
2307                                         cmds.end();
2308                         for (; sit != sen; ++sit) {
2309                                 QString const qcmd = toqstr(*sit);
2310                                 combo->addItem(qcmd, qcmd);
2311                         }
2312                 }
2313         }
2314 } // namespace
2315
2316
2317 void PrefFileformats::updateViewers()
2318 {
2319         Format const f = currentFormat();
2320         viewerCO->blockSignals(true);
2321         viewerCO->clear();
2322         viewerCO->addItem(qt_("None"), QString());
2323         updateComboBox(viewer_alternatives, f.name(), viewerCO);
2324         viewerCO->addItem(qt_("Custom"), QString("custom viewer"));
2325         viewerCO->blockSignals(false);
2326
2327         int pos = viewerCO->findData(toqstr(f.viewer()));
2328         if (pos != -1) {
2329                 viewerED->clear();
2330                 viewerED->setEnabled(false);
2331                 viewerCO->setCurrentIndex(pos);
2332         } else {
2333                 viewerED->setEnabled(true);
2334                 viewerED->setText(toqstr(f.viewer()));
2335                 viewerCO->setCurrentIndex(viewerCO->findData(toqstr("custom viewer")));
2336         }
2337 }
2338
2339
2340 void PrefFileformats::updateEditors()
2341 {
2342         Format const f = currentFormat();
2343         editorCO->blockSignals(true);
2344         editorCO->clear();
2345         editorCO->addItem(qt_("None"), QString());
2346         updateComboBox(editor_alternatives, f.name(), editorCO);
2347         editorCO->addItem(qt_("Custom"), QString("custom editor"));
2348         editorCO->blockSignals(false);
2349
2350         int pos = editorCO->findData(toqstr(f.editor()));
2351         if (pos != -1) {
2352                 editorED->clear();
2353                 editorED->setEnabled(false);
2354                 editorCO->setCurrentIndex(pos);
2355         } else {
2356                 editorED->setEnabled(true);
2357                 editorED->setText(toqstr(f.editor()));
2358                 editorCO->setCurrentIndex(editorCO->findData(toqstr("custom editor")));
2359         }
2360 }
2361
2362
2363 void PrefFileformats::on_viewerCO_currentIndexChanged(int i)
2364 {
2365         bool const custom = viewerCO->itemData(i).toString() == "custom viewer";
2366         viewerED->setEnabled(custom);
2367         if (!custom)
2368                 currentFormat().setViewer(fromqstr(viewerCO->itemData(i).toString()));
2369 }
2370
2371
2372 void PrefFileformats::on_editorCO_currentIndexChanged(int i)
2373 {
2374         bool const custom = editorCO->itemData(i).toString() == "custom editor";
2375         editorED->setEnabled(custom);
2376         if (!custom)
2377                 currentFormat().setEditor(fromqstr(editorCO->itemData(i).toString()));
2378 }
2379
2380
2381 Format & PrefFileformats::currentFormat()
2382 {
2383         int const i = formatsCB->currentIndex();
2384         int const nr = formatsCB->itemData(i).toInt();
2385         return form_->formats().get(nr);
2386 }
2387
2388
2389 void PrefFileformats::on_formatNewPB_clicked()
2390 {
2391         form_->formats().add("", "", docstring(), "", "", "", "", Format::none);
2392         updateView();
2393         formatsCB->setCurrentIndex(0);
2394         formatsCB->setFocus(Qt::OtherFocusReason);
2395 }
2396
2397
2398 void PrefFileformats::on_formatRemovePB_clicked()
2399 {
2400         int const i = formatsCB->currentIndex();
2401         int const nr = formatsCB->itemData(i).toInt();
2402         string const current_text = form_->formats().get(nr).name();
2403         if (form_->converters().formatIsUsed(current_text)) {
2404                 Alert::error(_("Format in use"),
2405                              _("Cannot remove a Format used by a Converter. "
2406                                             "Remove the converter first."));
2407                 return;
2408         }
2409
2410         form_->formats().erase(current_text);
2411         formatsChanged();
2412         updateView();
2413         on_formatsCB_editTextChanged(formatsCB->currentText());
2414         changed();
2415 }
2416
2417
2418 /////////////////////////////////////////////////////////////////////
2419 //
2420 // PrefLanguage
2421 //
2422 /////////////////////////////////////////////////////////////////////
2423
2424 PrefLanguage::PrefLanguage(GuiPreferences * form)
2425         : PrefModule(catLanguage, N_("Language"), form)
2426 {
2427         setupUi(this);
2428
2429         connect(visualCursorRB, SIGNAL(clicked()),
2430                 this, SIGNAL(changed()));
2431         connect(logicalCursorRB, SIGNAL(clicked()),
2432                 this, SIGNAL(changed()));
2433         connect(markForeignCB, SIGNAL(clicked()),
2434                 this, SIGNAL(changed()));
2435         connect(autoBeginCB, SIGNAL(clicked()),
2436                 this, SIGNAL(changed()));
2437         connect(autoEndCB, SIGNAL(clicked()),
2438                 this, SIGNAL(changed()));
2439         connect(languagePackageCO, SIGNAL(activated(int)),
2440                 this, SIGNAL(changed()));
2441         connect(languagePackageED, SIGNAL(textChanged(QString)),
2442                 this, SIGNAL(changed()));
2443         connect(globalCB, SIGNAL(clicked()),
2444                 this, SIGNAL(changed()));
2445         connect(startCommandED, SIGNAL(textChanged(QString)),
2446                 this, SIGNAL(changed()));
2447         connect(endCommandED, SIGNAL(textChanged(QString)),
2448                 this, SIGNAL(changed()));
2449         connect(uiLanguageCO, SIGNAL(activated(int)),
2450                 this, SIGNAL(changed()));
2451         connect(defaultDecimalPointLE, SIGNAL(textChanged(QString)),
2452                 this, SIGNAL(changed()));
2453         connect(defaultLengthUnitCO, SIGNAL(activated(int)),
2454                 this, SIGNAL(changed()));
2455
2456         languagePackageED->setValidator(new NoNewLineValidator(languagePackageED));
2457         startCommandED->setValidator(new NoNewLineValidator(startCommandED));
2458         endCommandED->setValidator(new NoNewLineValidator(endCommandED));
2459
2460         defaultDecimalPointLE->setInputMask("X; ");
2461         defaultDecimalPointLE->setMaxLength(1);
2462
2463         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::CM]), Length::CM);
2464         defaultLengthUnitCO->addItem(lyx::qt_(unit_name_gui[Length::IN]), Length::IN);
2465
2466         QAbstractItemModel * language_model = guiApp->languageModel();
2467         language_model->sort(0);
2468         uiLanguageCO->blockSignals(true);
2469         uiLanguageCO->clear();
2470         uiLanguageCO->addItem(qt_("Default"), toqstr("auto"));
2471         for (int i = 0; i != language_model->rowCount(); ++i) {
2472                 QModelIndex index = language_model->index(i, 0);
2473                 // Filter the list based on the available translation and add
2474                 // each language code only once
2475                 string const name = fromqstr(index.data(Qt::UserRole).toString());
2476                 Language const * lang = languages.getLanguage(name);
2477                 if (!lang)
2478                         continue;
2479                 // never remove the currently selected language
2480                 if (name != form->rc().gui_language
2481                     && name != lyxrc.gui_language
2482                     && (!Messages::available(lang->code())
2483                         || !lang->hasGuiSupport()))
2484                         continue;
2485                 uiLanguageCO->addItem(index.data(Qt::DisplayRole).toString(),
2486                                       index.data(Qt::UserRole).toString());
2487         }
2488         uiLanguageCO->blockSignals(false);
2489 }
2490
2491
2492 void PrefLanguage::on_uiLanguageCO_currentIndexChanged(int)
2493 {
2494          QMessageBox::information(this, qt_("LyX needs to be restarted!"),
2495                  qt_("The change of user interface language will be fully "
2496                  "effective only after a restart."));
2497 }
2498
2499
2500 void PrefLanguage::on_languagePackageCO_currentIndexChanged(int i)
2501 {
2502          languagePackageED->setEnabled(i == 2);
2503 }
2504
2505
2506 void PrefLanguage::applyRC(LyXRC & rc) const
2507 {
2508         rc.visual_cursor = visualCursorRB->isChecked();
2509         rc.mark_foreign_language = markForeignCB->isChecked();
2510         rc.language_auto_begin = autoBeginCB->isChecked();
2511         rc.language_auto_end = autoEndCB->isChecked();
2512         int const p = languagePackageCO->currentIndex();
2513         if (p == 0)
2514                 rc.language_package_selection = LyXRC::LP_AUTO;
2515         else if (p == 1)
2516                 rc.language_package_selection = LyXRC::LP_BABEL;
2517         else if (p == 2)
2518                 rc.language_package_selection = LyXRC::LP_CUSTOM;
2519         else if (p == 3)
2520                 rc.language_package_selection = LyXRC::LP_NONE;
2521         rc.language_custom_package = fromqstr(languagePackageED->text());
2522         rc.language_global_options = globalCB->isChecked();
2523         rc.language_command_begin = fromqstr(startCommandED->text());
2524         rc.language_command_end = fromqstr(endCommandED->text());
2525         rc.gui_language = fromqstr(
2526                 uiLanguageCO->itemData(uiLanguageCO->currentIndex()).toString());
2527         rc.default_decimal_point = fromqstr(defaultDecimalPointLE->text());
2528         rc.default_length_unit = (Length::UNIT) defaultLengthUnitCO->itemData(defaultLengthUnitCO->currentIndex()).toInt();
2529 }
2530
2531
2532 void PrefLanguage::updateRC(LyXRC const & rc)
2533 {
2534         if (rc.visual_cursor)
2535                 visualCursorRB->setChecked(true);
2536         else
2537                 logicalCursorRB->setChecked(true);
2538         markForeignCB->setChecked(rc.mark_foreign_language);
2539         autoBeginCB->setChecked(rc.language_auto_begin);
2540         autoEndCB->setChecked(rc.language_auto_end);
2541         languagePackageCO->setCurrentIndex(rc.language_package_selection);
2542         languagePackageED->setText(toqstr(rc.language_custom_package));
2543         languagePackageED->setEnabled(languagePackageCO->currentIndex() == 2);
2544         globalCB->setChecked(rc.language_global_options);
2545         startCommandED->setText(toqstr(rc.language_command_begin));
2546         endCommandED->setText(toqstr(rc.language_command_end));
2547         defaultDecimalPointLE->setText(toqstr(rc.default_decimal_point));
2548         int pos = defaultLengthUnitCO->findData(int(rc.default_length_unit));
2549         defaultLengthUnitCO->setCurrentIndex(pos);
2550
2551         pos = uiLanguageCO->findData(toqstr(rc.gui_language));
2552         uiLanguageCO->blockSignals(true);
2553         uiLanguageCO->setCurrentIndex(pos);
2554         uiLanguageCO->blockSignals(false);
2555 }
2556
2557
2558 /////////////////////////////////////////////////////////////////////
2559 //
2560 // PrefUserInterface
2561 //
2562 /////////////////////////////////////////////////////////////////////
2563
2564 PrefUserInterface::PrefUserInterface(GuiPreferences * form)
2565         : PrefModule(catLookAndFeel, N_("User Interface"), form)
2566 {
2567         setupUi(this);
2568
2569         connect(uiFilePB, SIGNAL(clicked()),
2570                 this, SLOT(selectUi()));
2571         connect(uiFileED, SIGNAL(textChanged(QString)),
2572                 this, SIGNAL(changed()));
2573         connect(iconSetCO, SIGNAL(activated(int)),
2574                 this, SIGNAL(changed()));
2575         connect(useSystemThemeIconsCB, SIGNAL(clicked()),
2576                 this, SIGNAL(changed()));
2577         connect(lastfilesSB, SIGNAL(valueChanged(int)),
2578                 this, SIGNAL(changed()));
2579         connect(tooltipCB, SIGNAL(toggled(bool)),
2580                 this, SIGNAL(changed()));
2581         lastfilesSB->setMaximum(maxlastfiles);
2582
2583         iconSetCO->addItem(qt_("Default"), QString());
2584         iconSetCO->addItem(qt_("Classic"), "classic");
2585         iconSetCO->addItem(qt_("Oxygen"), "oxygen");
2586
2587 #if (!(defined Q_WS_X11 || defined(QPA_XCB)) || QT_VERSION < 0x040600)
2588         useSystemThemeIconsCB->hide();
2589 #endif
2590 }
2591
2592
2593 void PrefUserInterface::applyRC(LyXRC & rc) const
2594 {
2595         rc.icon_set = fromqstr(iconSetCO->itemData(
2596                 iconSetCO->currentIndex()).toString());
2597
2598         rc.ui_file = internal_path(fromqstr(uiFileED->text()));
2599         rc.use_system_theme_icons = useSystemThemeIconsCB->isChecked();
2600         rc.num_lastfiles = lastfilesSB->value();
2601         rc.use_tooltip = tooltipCB->isChecked();
2602 }
2603
2604
2605 void PrefUserInterface::updateRC(LyXRC const & rc)
2606 {
2607         int iconset = iconSetCO->findData(toqstr(rc.icon_set));
2608         if (iconset < 0)
2609                 iconset = 0;
2610         iconSetCO->setCurrentIndex(iconset);
2611         useSystemThemeIconsCB->setChecked(rc.use_system_theme_icons);
2612         uiFileED->setText(toqstr(external_path(rc.ui_file)));
2613         lastfilesSB->setValue(rc.num_lastfiles);
2614         tooltipCB->setChecked(rc.use_tooltip);
2615 }
2616
2617
2618 void PrefUserInterface::selectUi()
2619 {
2620         QString file = form_->browseUI(internalPath(uiFileED->text()));
2621         if (!file.isEmpty())
2622                 uiFileED->setText(file);
2623 }
2624
2625
2626 /////////////////////////////////////////////////////////////////////
2627 //
2628 // PrefDocumentHandling
2629 //
2630 /////////////////////////////////////////////////////////////////////
2631
2632 PrefDocHandling::PrefDocHandling(GuiPreferences * form)
2633         : PrefModule(catLookAndFeel, N_("Document Handling"), form)
2634 {
2635         setupUi(this);
2636
2637         connect(autoSaveCB, SIGNAL(toggled(bool)),
2638                 autoSaveSB, SLOT(setEnabled(bool)));
2639         connect(autoSaveCB, SIGNAL(toggled(bool)),
2640                 TextLabel1, SLOT(setEnabled(bool)));
2641         connect(openDocumentsInTabsCB, SIGNAL(clicked()),
2642                 this, SIGNAL(changed()));
2643         connect(singleInstanceCB, SIGNAL(clicked()),
2644                 this, SIGNAL(changed()));
2645         connect(singleCloseTabButtonCB, SIGNAL(clicked()),
2646                 this, SIGNAL(changed()));
2647         connect(closeLastViewCO, SIGNAL(activated(int)),
2648                 this, SIGNAL(changed()));
2649         connect(restoreCursorCB, SIGNAL(clicked()),
2650                 this, SIGNAL(changed()));
2651         connect(loadSessionCB, SIGNAL(clicked()),
2652                 this, SIGNAL(changed()));
2653         connect(allowGeometrySessionCB, SIGNAL(clicked()),
2654                 this, SIGNAL(changed()));
2655         connect(autoSaveSB, SIGNAL(valueChanged(int)),
2656                 this, SIGNAL(changed()));
2657         connect(autoSaveCB, SIGNAL(clicked()),
2658                 this, SIGNAL(changed()));
2659         connect(backupCB, SIGNAL(clicked()),
2660                 this, SIGNAL(changed()));
2661         connect(saveCompressedCB, SIGNAL(clicked()),
2662                 this, SIGNAL(changed()));
2663         connect(saveOriginCB, SIGNAL(clicked()),
2664                 this, SIGNAL(changed()));
2665 }
2666
2667
2668 void PrefDocHandling::applyRC(LyXRC & rc) const
2669 {
2670         rc.use_lastfilepos = restoreCursorCB->isChecked();
2671         rc.load_session = loadSessionCB->isChecked();
2672         rc.allow_geometry_session = allowGeometrySessionCB->isChecked();
2673         rc.autosave = autoSaveCB->isChecked() ?  autoSaveSB->value() * 60 : 0;
2674         rc.make_backup = backupCB->isChecked();
2675         rc.save_compressed = saveCompressedCB->isChecked();
2676         rc.save_origin = saveOriginCB->isChecked();
2677         rc.open_buffers_in_tabs = openDocumentsInTabsCB->isChecked();
2678         rc.single_instance = singleInstanceCB->isChecked();
2679         rc.single_close_tab_button = singleCloseTabButtonCB->isChecked();
2680
2681         switch (closeLastViewCO->currentIndex()) {
2682         case 0:
2683                 rc.close_buffer_with_last_view = "yes";
2684                 break;
2685         case 1:
2686                 rc.close_buffer_with_last_view = "no";
2687                 break;
2688         case 2:
2689                 rc.close_buffer_with_last_view = "ask";
2690                 break;
2691         default:
2692                 ;
2693         }
2694 }
2695
2696
2697 void PrefDocHandling::updateRC(LyXRC const & rc)
2698 {
2699         restoreCursorCB->setChecked(rc.use_lastfilepos);
2700         loadSessionCB->setChecked(rc.load_session);
2701         allowGeometrySessionCB->setChecked(rc.allow_geometry_session);
2702         // convert to minutes
2703         bool autosave = rc.autosave > 0;
2704         int mins = rc.autosave / 60;
2705         if (!mins)
2706                 mins = 5;
2707         autoSaveSB->setValue(mins);
2708         autoSaveCB->setChecked(autosave);
2709         autoSaveSB->setEnabled(autosave);
2710         backupCB->setChecked(rc.make_backup);
2711         saveCompressedCB->setChecked(rc.save_compressed);
2712         saveOriginCB->setChecked(rc.save_origin);
2713         openDocumentsInTabsCB->setChecked(rc.open_buffers_in_tabs);
2714         singleInstanceCB->setChecked(rc.single_instance && !rc.lyxpipes.empty());
2715         singleInstanceCB->setEnabled(!rc.lyxpipes.empty());
2716         singleCloseTabButtonCB->setChecked(rc.single_close_tab_button);
2717         if (rc.close_buffer_with_last_view == "yes")
2718                 closeLastViewCO->setCurrentIndex(0);
2719         else if (rc.close_buffer_with_last_view == "no")
2720                 closeLastViewCO->setCurrentIndex(1);
2721         else if (rc.close_buffer_with_last_view == "ask")
2722                 closeLastViewCO->setCurrentIndex(2);
2723 }
2724
2725
2726 void PrefDocHandling::on_clearSessionPB_clicked()
2727 {
2728         guiApp->clearSession();
2729 }
2730
2731
2732
2733 /////////////////////////////////////////////////////////////////////
2734 //
2735 // PrefEdit
2736 //
2737 /////////////////////////////////////////////////////////////////////
2738
2739 PrefEdit::PrefEdit(GuiPreferences * form)
2740         : PrefModule(catEditing, N_("Control"), form)
2741 {
2742         setupUi(this);
2743
2744         connect(cursorFollowsCB, SIGNAL(clicked()),
2745                 this, SIGNAL(changed()));
2746         connect(scrollBelowCB, SIGNAL(clicked()),
2747                 this, SIGNAL(changed()));
2748         connect(macLikeCursorMovementCB, SIGNAL(clicked()),
2749                 this, SIGNAL(changed()));
2750         connect(sortEnvironmentsCB, SIGNAL(clicked()),
2751                 this, SIGNAL(changed()));
2752         connect(groupEnvironmentsCB, SIGNAL(clicked()),
2753                 this, SIGNAL(changed()));
2754         connect(macroEditStyleCO, SIGNAL(activated(int)),
2755                 this, SIGNAL(changed()));
2756         connect(cursorWidthSB, SIGNAL(valueChanged(int)),
2757                 this, SIGNAL(changed()));
2758         connect(fullscreenLimitGB, SIGNAL(clicked()),
2759                 this, SIGNAL(changed()));
2760         connect(fullscreenWidthSB, SIGNAL(valueChanged(int)),
2761                 this, SIGNAL(changed()));
2762         connect(toggleTabbarCB, SIGNAL(toggled(bool)),
2763                 this, SIGNAL(changed()));
2764         connect(toggleMenubarCB, SIGNAL(toggled(bool)),
2765                 this, SIGNAL(changed()));
2766         connect(toggleScrollbarCB, SIGNAL(toggled(bool)),
2767                 this, SIGNAL(changed()));
2768         connect(toggleStatusbarCB, SIGNAL(toggled(bool)),
2769                 this, SIGNAL(changed()));
2770         connect(toggleToolbarsCB, SIGNAL(toggled(bool)),
2771                 this, SIGNAL(changed()));
2772 }
2773
2774
2775 void PrefEdit::applyRC(LyXRC & rc) const
2776 {
2777         rc.cursor_follows_scrollbar = cursorFollowsCB->isChecked();
2778         rc.scroll_below_document = scrollBelowCB->isChecked();
2779         rc.mac_like_cursor_movement = macLikeCursorMovementCB->isChecked();
2780         rc.sort_layouts = sortEnvironmentsCB->isChecked();
2781         rc.group_layouts = groupEnvironmentsCB->isChecked();
2782         switch (macroEditStyleCO->currentIndex()) {
2783                 case 0: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE_BOX; break;
2784                 case 1: rc.macro_edit_style = LyXRC::MACRO_EDIT_INLINE; break;
2785                 case 2: rc.macro_edit_style = LyXRC::MACRO_EDIT_LIST;   break;
2786         }
2787         rc.cursor_width = cursorWidthSB->value();
2788         rc.full_screen_toolbars = toggleToolbarsCB->isChecked();
2789         rc.full_screen_scrollbar = toggleScrollbarCB->isChecked();
2790         rc.full_screen_statusbar = toggleStatusbarCB->isChecked();
2791         rc.full_screen_tabbar = toggleTabbarCB->isChecked();
2792         rc.full_screen_menubar = toggleMenubarCB->isChecked();
2793         rc.full_screen_width = fullscreenWidthSB->value();
2794         rc.full_screen_limit = fullscreenLimitGB->isChecked();
2795 }
2796
2797
2798 void PrefEdit::updateRC(LyXRC const & rc)
2799 {
2800         cursorFollowsCB->setChecked(rc.cursor_follows_scrollbar);
2801         scrollBelowCB->setChecked(rc.scroll_below_document);
2802         macLikeCursorMovementCB->setChecked(rc.mac_like_cursor_movement);
2803         sortEnvironmentsCB->setChecked(rc.sort_layouts);
2804         groupEnvironmentsCB->setChecked(rc.group_layouts);
2805         macroEditStyleCO->setCurrentIndex(rc.macro_edit_style);
2806         cursorWidthSB->setValue(rc.cursor_width);
2807         toggleScrollbarCB->setChecked(rc.full_screen_scrollbar);
2808         toggleStatusbarCB->setChecked(rc.full_screen_statusbar);
2809         toggleToolbarsCB->setChecked(rc.full_screen_toolbars);
2810         toggleTabbarCB->setChecked(rc.full_screen_tabbar);
2811         toggleMenubarCB->setChecked(rc.full_screen_menubar);
2812         fullscreenWidthSB->setValue(rc.full_screen_width);
2813         fullscreenLimitGB->setChecked(rc.full_screen_limit);
2814 }
2815
2816
2817 /////////////////////////////////////////////////////////////////////
2818 //
2819 // PrefShortcuts
2820 //
2821 /////////////////////////////////////////////////////////////////////
2822
2823
2824 GuiShortcutDialog::GuiShortcutDialog(QWidget * parent) : QDialog(parent)
2825 {
2826         Ui::shortcutUi::setupUi(this);
2827         QDialog::setModal(true);
2828 }
2829
2830
2831 PrefShortcuts::PrefShortcuts(GuiPreferences * form)
2832         : PrefModule(catEditing, N_("Shortcuts"), form),
2833           editItem_(0), mathItem_(0), bufferItem_(0), layoutItem_(0),
2834           systemItem_(0)
2835 {
2836         setupUi(this);
2837
2838         shortcutsTW->setColumnCount(2);
2839         shortcutsTW->headerItem()->setText(0, qt_("Function"));
2840         shortcutsTW->headerItem()->setText(1, qt_("Shortcut"));
2841         shortcutsTW->setSortingEnabled(true);
2842         // Multi-selection can be annoying.
2843         // shortcutsTW->setSelectionMode(QAbstractItemView::MultiSelection);
2844
2845         connect(bindFilePB, SIGNAL(clicked()),
2846                 this, SLOT(selectBind()));
2847         connect(bindFileED, SIGNAL(textChanged(QString)),
2848                 this, SIGNAL(changed()));
2849
2850         shortcut_ = new GuiShortcutDialog(this);
2851         shortcut_bc_.setPolicy(ButtonPolicy::OkCancelPolicy);
2852         shortcut_bc_.setOK(shortcut_->buttonBox->button(QDialogButtonBox::Ok));
2853         shortcut_bc_.setCancel(shortcut_->buttonBox->button(QDialogButtonBox::Cancel));
2854
2855         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2856                 this, SIGNAL(changed()));
2857         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2858                 shortcut_, SLOT(reject()));
2859         connect(shortcut_->clearPB, SIGNAL(clicked()),
2860                 this, SLOT(shortcutClearPressed()));
2861         connect(shortcut_->removePB, SIGNAL(clicked()),
2862                 this, SLOT(shortcutRemovePressed()));
2863         connect(shortcut_->buttonBox, SIGNAL(accepted()),
2864                 this, SLOT(shortcutOkPressed()));
2865         connect(shortcut_->buttonBox, SIGNAL(rejected()),
2866                 this, SLOT(shortcutCancelPressed()));
2867 }
2868
2869
2870 void PrefShortcuts::applyRC(LyXRC & rc) const
2871 {
2872         rc.bind_file = internal_path(fromqstr(bindFileED->text()));
2873         // write user_bind and user_unbind to .lyx/bind/user.bind
2874         FileName bind_dir(addPath(package().user_support().absFileName(), "bind"));
2875         if (!bind_dir.exists() && !bind_dir.createDirectory(0777)) {
2876                 lyxerr << "LyX could not create the user bind directory '"
2877                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2878                 return;
2879         }
2880         if (!bind_dir.isDirWritable()) {
2881                 lyxerr << "LyX could not write to the user bind directory '"
2882                        << bind_dir << "'. All user-defined key bindings will be lost." << endl;
2883                 return;
2884         }
2885         FileName user_bind_file(bind_dir.absFileName() + "/user.bind");
2886         user_unbind_.write(user_bind_file.toFilesystemEncoding(), false, true);
2887         user_bind_.write(user_bind_file.toFilesystemEncoding(), true, false);
2888         // immediately apply the keybindings. Why this is not done before?
2889         // The good thing is that the menus are updated automatically.
2890         theTopLevelKeymap().clear();
2891         theTopLevelKeymap().read("site");
2892         theTopLevelKeymap().read(rc.bind_file, 0, KeyMap::Fallback);
2893         theTopLevelKeymap().read("user", 0, KeyMap::MissingOK);
2894 }
2895
2896
2897 void PrefShortcuts::updateRC(LyXRC const & rc)
2898 {
2899         bindFileED->setText(toqstr(external_path(rc.bind_file)));
2900         //
2901         system_bind_.clear();
2902         user_bind_.clear();
2903         user_unbind_.clear();
2904         system_bind_.read("site");
2905         system_bind_.read(rc.bind_file);
2906         // \unbind in user.bind is added to user_unbind_
2907         user_bind_.read("user", &user_unbind_, KeyMap::MissingOK);
2908         updateShortcutsTW();
2909 }
2910
2911
2912 void PrefShortcuts::updateShortcutsTW()
2913 {
2914         shortcutsTW->clear();
2915
2916         editItem_ = new QTreeWidgetItem(shortcutsTW);
2917         editItem_->setText(0, qt_("Cursor, Mouse and Editing Functions"));
2918         editItem_->setFlags(editItem_->flags() & ~Qt::ItemIsSelectable);
2919
2920         mathItem_ = new QTreeWidgetItem(shortcutsTW);
2921         mathItem_->setText(0, qt_("Mathematical Symbols"));
2922         mathItem_->setFlags(mathItem_->flags() & ~Qt::ItemIsSelectable);
2923
2924         bufferItem_ = new QTreeWidgetItem(shortcutsTW);
2925         bufferItem_->setText(0, qt_("Document and Window"));
2926         bufferItem_->setFlags(bufferItem_->flags() & ~Qt::ItemIsSelectable);
2927
2928         layoutItem_ = new QTreeWidgetItem(shortcutsTW);
2929         layoutItem_->setText(0, qt_("Font, Layouts and Textclasses"));
2930         layoutItem_->setFlags(layoutItem_->flags() & ~Qt::ItemIsSelectable);
2931
2932         systemItem_ = new QTreeWidgetItem(shortcutsTW);
2933         systemItem_->setText(0, qt_("System and Miscellaneous"));
2934         systemItem_->setFlags(systemItem_->flags() & ~Qt::ItemIsSelectable);
2935
2936         // listBindings(unbound=true) lists all bound and unbound lfuns
2937         // Items in this list is tagged by its source.
2938         KeyMap::BindingList bindinglist = system_bind_.listBindings(true,
2939                 KeyMap::System);
2940         KeyMap::BindingList user_bindinglist = user_bind_.listBindings(false,
2941                 KeyMap::UserBind);
2942         KeyMap::BindingList user_unbindinglist = user_unbind_.listBindings(false,
2943                 KeyMap::UserUnbind);
2944         bindinglist.insert(bindinglist.end(), user_bindinglist.begin(),
2945                         user_bindinglist.end());
2946         bindinglist.insert(bindinglist.end(), user_unbindinglist.begin(),
2947                         user_unbindinglist.end());
2948
2949         KeyMap::BindingList::const_iterator it = bindinglist.begin();
2950         KeyMap::BindingList::const_iterator it_end = bindinglist.end();
2951         for (; it != it_end; ++it)
2952                 insertShortcutItem(it->request, it->sequence, it->tag);
2953
2954         shortcutsTW->sortItems(0, Qt::AscendingOrder);
2955         on_shortcutsTW_itemSelectionChanged();
2956         on_searchLE_textEdited();
2957         shortcutsTW->resizeColumnToContents(0);
2958 }
2959
2960
2961 //static
2962 KeyMap::ItemType PrefShortcuts::itemType(QTreeWidgetItem & item)
2963 {
2964         return static_cast<KeyMap::ItemType>(item.data(0, Qt::UserRole).toInt());
2965 }
2966
2967
2968 //static
2969 bool PrefShortcuts::isAlwaysHidden(QTreeWidgetItem & item)
2970 {
2971         // Hide rebound system settings that are empty
2972         return itemType(item) == KeyMap::UserUnbind && item.text(1).isEmpty();
2973 }
2974
2975
2976 void PrefShortcuts::setItemType(QTreeWidgetItem * item, KeyMap::ItemType tag)
2977 {
2978         item->setData(0, Qt::UserRole, QVariant(tag));
2979         QFont font;
2980
2981         switch (tag) {
2982         case KeyMap::System:
2983                 break;
2984         case KeyMap::UserBind:
2985                 font.setBold(true);
2986                 break;
2987         case KeyMap::UserUnbind:
2988                 font.setStrikeOut(true);
2989                 break;
2990         // this item is not displayed now.
2991         case KeyMap::UserExtraUnbind:
2992                 font.setStrikeOut(true);
2993                 break;
2994         }
2995         item->setHidden(isAlwaysHidden(*item));
2996         item->setFont(1, font);
2997 }
2998
2999
3000 QTreeWidgetItem * PrefShortcuts::insertShortcutItem(FuncRequest const & lfun,
3001                 KeySequence const & seq, KeyMap::ItemType tag)
3002 {
3003         FuncCode const action = lfun.action();
3004         string const action_name = lyxaction.getActionName(action);
3005         QString const lfun_name = toqstr(from_utf8(action_name)
3006                         + ' ' + lfun.argument());
3007         QString const shortcut = toqstr(seq.print(KeySequence::ForGui));
3008
3009         QTreeWidgetItem * newItem = 0;
3010         // for unbind items, try to find an existing item in the system bind list
3011         if (tag == KeyMap::UserUnbind) {
3012                 QList<QTreeWidgetItem*> const items = shortcutsTW->findItems(lfun_name,
3013                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
3014                 for (int i = 0; i < items.size(); ++i) {
3015                         if (items[i]->text(1) == shortcut) {
3016                                 newItem = items[i];
3017                                 break;
3018                         }
3019                 }
3020                 // if not found, this unbind item is KeyMap::UserExtraUnbind
3021                 // Such an item is not displayed to avoid confusion (what is
3022                 // unmatched removed?).
3023                 if (!newItem) {
3024                         return 0;
3025                 }
3026         }
3027         if (!newItem) {
3028                 switch(lyxaction.getActionType(action)) {
3029                 case LyXAction::Hidden:
3030                         return 0;
3031                 case LyXAction::Edit:
3032                         newItem = new QTreeWidgetItem(editItem_);
3033                         break;
3034                 case LyXAction::Math:
3035                         newItem = new QTreeWidgetItem(mathItem_);
3036                         break;
3037                 case LyXAction::Buffer:
3038                         newItem = new QTreeWidgetItem(bufferItem_);
3039                         break;
3040                 case LyXAction::Layout:
3041                         newItem = new QTreeWidgetItem(layoutItem_);
3042                         break;
3043                 case LyXAction::System:
3044                         newItem = new QTreeWidgetItem(systemItem_);
3045                         break;
3046                 default:
3047                         // this should not happen
3048                         newItem = new QTreeWidgetItem(shortcutsTW);
3049                 }
3050         }
3051
3052         newItem->setText(0, lfun_name);
3053         newItem->setText(1, shortcut);
3054         // record BindFile representation to recover KeySequence when needed.
3055         newItem->setData(1, Qt::UserRole, toqstr(seq.print(KeySequence::BindFile)));
3056         setItemType(newItem, tag);
3057         return newItem;
3058 }
3059
3060
3061 void PrefShortcuts::on_shortcutsTW_itemSelectionChanged()
3062 {
3063         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3064         removePB->setEnabled(!items.isEmpty() && !items[0]->text(1).isEmpty());
3065         modifyPB->setEnabled(!items.isEmpty());
3066         if (items.isEmpty())
3067                 return;
3068
3069         if (itemType(*items[0]) == KeyMap::UserUnbind)
3070                 removePB->setText(qt_("Res&tore"));
3071         else
3072                 removePB->setText(qt_("Remo&ve"));
3073 }
3074
3075
3076 void PrefShortcuts::on_shortcutsTW_itemDoubleClicked()
3077 {
3078         modifyShortcut();
3079 }
3080
3081
3082 void PrefShortcuts::modifyShortcut()
3083 {
3084         QTreeWidgetItem * item = shortcutsTW->currentItem();
3085         if (item->flags() & Qt::ItemIsSelectable) {
3086                 shortcut_->lfunLE->setText(item->text(0));
3087                 save_lfun_ = item->text(0).trimmed();
3088                 shortcut_->shortcutWG->setText(item->text(1));
3089                 KeySequence seq;
3090                 seq.parse(fromqstr(item->data(1, Qt::UserRole).toString()));
3091                 shortcut_->shortcutWG->setKeySequence(seq);
3092                 shortcut_->shortcutWG->setFocus();
3093                 shortcut_->exec();
3094         }
3095 }
3096
3097
3098 void PrefShortcuts::unhideEmpty(QString const & lfun, bool select)
3099 {
3100         // list of items that match lfun
3101         QList<QTreeWidgetItem*> items = shortcutsTW->findItems(lfun,
3102              Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 0);
3103         for (int i = 0; i < items.size(); ++i) {
3104                 QTreeWidgetItem * item = items[i];
3105                 if (isAlwaysHidden(*item)) {
3106                         setItemType(item, KeyMap::System);
3107                         if (select)
3108                                 shortcutsTW->setCurrentItem(item);
3109                         return;
3110                 }
3111         }
3112 }
3113
3114
3115 void PrefShortcuts::removeShortcut()
3116 {
3117         // it seems that only one item can be selected, but I am
3118         // removing all selected items anyway.
3119         QList<QTreeWidgetItem*> items = shortcutsTW->selectedItems();
3120         for (int i = 0; i < items.size(); ++i) {
3121                 string shortcut = fromqstr(items[i]->data(1, Qt::UserRole).toString());
3122                 string lfun = fromqstr(items[i]->text(0));
3123                 FuncRequest func = lyxaction.lookupFunc(lfun);
3124
3125                 switch (itemType(*items[i])) {
3126                 case KeyMap::System: {
3127                         // for system bind, we do not touch the item
3128                         // but add an user unbind item
3129                         user_unbind_.bind(shortcut, func);
3130                         setItemType(items[i], KeyMap::UserUnbind);
3131                         removePB->setText(qt_("Res&tore"));
3132                         break;
3133                 }
3134                 case KeyMap::UserBind: {
3135                         // for user_bind, we remove this bind
3136                         QTreeWidgetItem * parent = items[i]->parent();
3137                         int itemIdx = parent->indexOfChild(items[i]);
3138                         parent->takeChild(itemIdx);
3139                         if (itemIdx > 0)
3140                                 shortcutsTW->scrollToItem(parent->child(itemIdx - 1));
3141                         else
3142                                 shortcutsTW->scrollToItem(parent);
3143                         user_bind_.unbind(shortcut, func);
3144                         // If this user binding hid an empty system binding, unhide the
3145                         // latter and select it.
3146                         unhideEmpty(items[i]->text(0), true);
3147                         break;
3148                 }
3149                 case KeyMap::UserUnbind: {
3150                         // for user_unbind, we remove the unbind, and the item
3151                         // become KeyMap::System again.
3152                         KeySequence seq;
3153                         seq.parse(shortcut);
3154                         // Ask the user to replace current binding
3155                         if (!validateNewShortcut(func, seq, QString()))
3156                                 break;
3157                         user_unbind_.unbind(shortcut, func);
3158                         setItemType(items[i], KeyMap::System);
3159                         removePB->setText(qt_("Remo&ve"));
3160                         break;
3161                 }
3162                 case KeyMap::UserExtraUnbind: {
3163                         // for user unbind that is not in system bind file,
3164                         // remove this unbind file
3165                         QTreeWidgetItem * parent = items[i]->parent();
3166                         parent->takeChild(parent->indexOfChild(items[i]));
3167                         user_unbind_.unbind(shortcut, func);
3168                 }
3169                 }
3170         }
3171 }
3172
3173
3174 void PrefShortcuts::deactivateShortcuts(QList<QTreeWidgetItem*> const & items)
3175 {
3176         for (int i = 0; i < items.size(); ++i) {
3177                 string shortcut = fromqstr(items[i]->data(1, Qt::UserRole).toString());
3178                 string lfun = fromqstr(items[i]->text(0));
3179                 FuncRequest func = lyxaction.lookupFunc(lfun);
3180
3181                 switch (itemType(*items[i])) {
3182                 case KeyMap::System:
3183                         // for system bind, we do not touch the item
3184                         // but add an user unbind item
3185                         user_unbind_.bind(shortcut, func);
3186                         setItemType(items[i], KeyMap::UserUnbind);
3187                         break;
3188
3189                 case KeyMap::UserBind: {
3190                         // for user_bind, we remove this bind
3191                         QTreeWidgetItem * parent = items[i]->parent();
3192                         int itemIdx = parent->indexOfChild(items[i]);
3193                         parent->takeChild(itemIdx);
3194                         user_bind_.unbind(shortcut, func);
3195                         unhideEmpty(items[i]->text(0), false);
3196                         break;
3197                 }
3198                 default:
3199                         break;
3200                 }
3201         }
3202 }
3203
3204
3205 void PrefShortcuts::selectBind()
3206 {
3207         QString file = form_->browsebind(internalPath(bindFileED->text()));
3208         if (!file.isEmpty()) {
3209                 bindFileED->setText(file);
3210                 system_bind_ = KeyMap();
3211                 system_bind_.read(fromqstr(file));
3212                 updateShortcutsTW();
3213         }
3214 }
3215
3216
3217 void PrefShortcuts::on_modifyPB_pressed()
3218 {
3219         modifyShortcut();
3220 }
3221
3222
3223 void PrefShortcuts::on_newPB_pressed()
3224 {
3225         shortcut_->lfunLE->clear();
3226         shortcut_->shortcutWG->reset();
3227         save_lfun_ = QString();
3228         shortcut_->exec();
3229 }
3230
3231
3232 void PrefShortcuts::on_removePB_pressed()
3233 {
3234         changed();
3235         removeShortcut();
3236 }
3237
3238
3239 void PrefShortcuts::on_searchLE_textEdited()
3240 {
3241         if (searchLE->text().isEmpty()) {
3242                 // show all hidden items
3243                 QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Hidden);
3244                 for (; *it; ++it)
3245                         shortcutsTW->setItemHidden(*it, isAlwaysHidden(**it));
3246                 // close all categories
3247                 for (int i = 0; i < shortcutsTW->topLevelItemCount(); ++i)
3248                         shortcutsTW->collapseItem(shortcutsTW->topLevelItem(i));
3249                 return;
3250         }
3251         // search both columns
3252         QList<QTreeWidgetItem *> matched = shortcutsTW->findItems(searchLE->text(),
3253                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 0);
3254         matched += shortcutsTW->findItems(searchLE->text(),
3255                 Qt::MatchFlags(Qt::MatchContains | Qt::MatchRecursive), 1);
3256
3257         // hide everyone (to avoid searching in matched QList repeatedly
3258         QTreeWidgetItemIterator it(shortcutsTW, QTreeWidgetItemIterator::Selectable);
3259         while (*it)
3260                 shortcutsTW->setItemHidden(*it++, true);
3261         // show matched items
3262         for (int i = 0; i < matched.size(); ++i)
3263                 if (!isAlwaysHidden(*matched[i])) {
3264                         shortcutsTW->setItemHidden(matched[i], false);
3265                         shortcutsTW->setItemExpanded(matched[i]->parent(), true);
3266                 }
3267 }
3268
3269
3270 docstring makeCmdString(FuncRequest const & f)
3271 {
3272         docstring actionStr = from_ascii(lyxaction.getActionName(f.action()));
3273         if (!f.argument().empty())
3274                 actionStr += " " + f.argument();
3275         return actionStr;
3276 }
3277
3278
3279 FuncRequest PrefShortcuts::currentBinding(KeySequence const & k)
3280 {
3281         FuncRequest res = user_bind_.getBinding(k);
3282         if (res.action() != LFUN_UNKNOWN_ACTION)
3283                 return res;
3284         res = system_bind_.getBinding(k);
3285         // Check if it is unbound. Note: user_unbind_ can only unbind one
3286         // FuncRequest per key sequence.
3287         if (user_unbind_.getBinding(k) == res)
3288                 return FuncRequest::unknown;
3289         return res;
3290 }
3291
3292
3293 bool PrefShortcuts::validateNewShortcut(FuncRequest const & func,
3294                                         KeySequence const & k,
3295                                         QString const & lfun_to_modify)
3296 {
3297         if (func.action() == LFUN_UNKNOWN_ACTION) {
3298                 Alert::error(_("Failed to create shortcut"),
3299                         _("Unknown or invalid LyX function"));
3300                 return false;
3301         }
3302
3303         // It is not currently possible to bind Hidden lfuns such as self-insert. In
3304         // the future, to remove this limitation, see GuiPrefs::insertShortcutItem
3305         // and how it is used in GuiPrefs::shortcutOkPressed.
3306         if (lyxaction.getActionType(func.action()) == LyXAction::Hidden) {
3307                 Alert::error(_("Failed to create shortcut"),
3308                         _("This LyX function is hidden and cannot be bound."));
3309                 return false;
3310         }
3311
3312         if (k.length() == 0) {
3313                 Alert::error(_("Failed to create shortcut"),
3314                         _("Invalid or empty key sequence"));
3315                 return false;
3316         }
3317
3318         FuncRequest oldBinding = currentBinding(k);
3319         if (oldBinding == func)
3320                 // nothing to change
3321                 return false;
3322
3323         // make sure this key isn't already bound---and, if so, prompt user
3324         // (exclude the lfun the user already wants to modify)
3325         docstring const action_string = makeCmdString(oldBinding);
3326         if (oldBinding.action() != LFUN_UNKNOWN_ACTION
3327             && lfun_to_modify != toqstr(action_string)) {
3328                 docstring const new_action_string = makeCmdString(func);
3329                 docstring const text = bformat(_("Shortcut `%1$s' is already bound to "
3330                                                  "%2$s.\n"
3331                                                  "Are you sure you want to unbind the "
3332                                                  "current shortcut and bind it to %3$s?"),
3333                                                k.print(KeySequence::ForGui), action_string,
3334                                                new_action_string);
3335                 int ret = Alert::prompt(_("Redefine shortcut?"),
3336                                         text, 0, 1, _("&Redefine"), _("&Cancel"));
3337                 if (ret != 0)
3338                         return false;
3339                 QString const sequence_text = toqstr(k.print(KeySequence::ForGui));
3340                 QList<QTreeWidgetItem*> items = shortcutsTW->findItems(sequence_text,
3341                         Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), 1);
3342                 deactivateShortcuts(items);
3343         }
3344         return true;
3345 }
3346
3347
3348 void PrefShortcuts::shortcutOkPressed()
3349 {
3350         QString const new_lfun = shortcut_->lfunLE->text();
3351         FuncRequest func = lyxaction.lookupFunc(fromqstr(new_lfun));
3352         KeySequence k = shortcut_->shortcutWG->getKeySequence();
3353
3354         // save_lfun_ contains the text of the lfun to modify, if the user clicked
3355         // "modify", or is empty if they clicked "new" (which I do not really like)
3356         if (!validateNewShortcut(func, k, save_lfun_))
3357                 return;
3358
3359         if (!save_lfun_.isEmpty()) {
3360                 // real modification of the lfun's shortcut,
3361                 // so remove the previous one
3362                 QList<QTreeWidgetItem*> to_modify = shortcutsTW->selectedItems();
3363                 deactivateShortcuts(to_modify);
3364         }
3365
3366         shortcut_->accept();
3367
3368         QTreeWidgetItem * item = insertShortcutItem(func, k, KeyMap::UserBind);
3369         if (item) {
3370                 user_bind_.bind(&k, func);
3371                 shortcutsTW->sortItems(0, Qt::AscendingOrder);
3372                 shortcutsTW->setItemExpanded(item->parent(), true);
3373                 shortcutsTW->setCurrentItem(item);
3374                 shortcutsTW->scrollToItem(item);
3375         } else {
3376                 Alert::error(_("Failed to create shortcut"),
3377                         _("Can not insert shortcut to the list"));
3378                 return;
3379         }
3380 }
3381
3382
3383 void PrefShortcuts::shortcutCancelPressed()
3384 {
3385         shortcut_->shortcutWG->reset();
3386 }
3387
3388
3389 void PrefShortcuts::shortcutClearPressed()
3390 {
3391         shortcut_->shortcutWG->reset();
3392 }
3393
3394
3395 void PrefShortcuts::shortcutRemovePressed()
3396 {
3397         shortcut_->shortcutWG->removeFromSequence();
3398 }
3399
3400
3401 /////////////////////////////////////////////////////////////////////
3402 //
3403 // PrefIdentity
3404 //
3405 /////////////////////////////////////////////////////////////////////
3406
3407 PrefIdentity::PrefIdentity(GuiPreferences * form)
3408         : PrefModule(QString(), N_("Identity"), form)
3409 {
3410         setupUi(this);
3411
3412         connect(nameED, SIGNAL(textChanged(QString)),
3413                 this, SIGNAL(changed()));
3414         connect(emailED, SIGNAL(textChanged(QString)),
3415                 this, SIGNAL(changed()));
3416
3417         nameED->setValidator(new NoNewLineValidator(nameED));
3418         emailED->setValidator(new NoNewLineValidator(emailED));
3419 }
3420
3421
3422 void PrefIdentity::applyRC(LyXRC & rc) const
3423 {
3424         rc.user_name = fromqstr(nameED->text());
3425         rc.user_email = fromqstr(emailED->text());
3426 }
3427
3428
3429 void PrefIdentity::updateRC(LyXRC const & rc)
3430 {
3431         nameED->setText(toqstr(rc.user_name));
3432         emailED->setText(toqstr(rc.user_email));
3433 }
3434
3435
3436
3437 /////////////////////////////////////////////////////////////////////
3438 //
3439 // GuiPreferences
3440 //
3441 /////////////////////////////////////////////////////////////////////
3442
3443 GuiPreferences::GuiPreferences(GuiView & lv)
3444         : GuiDialog(lv, "prefs", qt_("Preferences"))
3445 {
3446         setupUi(this);
3447
3448         QDialog::setModal(false);
3449
3450         connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
3451                 this, SLOT(slotButtonBox(QAbstractButton *)));
3452
3453         addModule(new PrefUserInterface(this));
3454         addModule(new PrefDocHandling(this));
3455         addModule(new PrefEdit(this));
3456         addModule(new PrefShortcuts(this));
3457         PrefScreenFonts * screenfonts = new PrefScreenFonts(this);
3458         connect(this, SIGNAL(prefsApplied(LyXRC const &)),
3459                         screenfonts, SLOT(updateScreenFontSizes(LyXRC const &)));
3460         addModule(screenfonts);
3461         addModule(new PrefColors(this));
3462         addModule(new PrefDisplay(this));
3463         addModule(new PrefInput(this));
3464         addModule(new PrefCompletion(this));
3465
3466         addModule(new PrefPaths(this));
3467
3468         addModule(new PrefIdentity(this));
3469
3470         addModule(new PrefLanguage(this));
3471         addModule(new PrefSpellchecker(this));
3472
3473         PrefOutput * output = new PrefOutput(this);
3474         addModule(output);
3475         addModule(new PrefLatex(this));
3476
3477         PrefConverters * converters = new PrefConverters(this);
3478         PrefFileformats * formats = new PrefFileformats(this);
3479         connect(formats, SIGNAL(formatsChanged()),
3480                         converters, SLOT(updateGui()));
3481         addModule(converters);
3482         addModule(formats);
3483
3484         prefsPS->setCurrentPanel("User Interface");
3485 // FIXME: hack to work around resizing bug in Qt >= 4.2
3486 // bug verified with Qt 4.2.{0-3} (JSpitzm)
3487 #if QT_VERSION >= 0x040200
3488         prefsPS->updateGeometry();
3489 #endif
3490
3491         bc().setPolicy(ButtonPolicy::PreferencesPolicy);
3492         bc().setOK(buttonBox->button(QDialogButtonBox::Ok));
3493         bc().setApply(buttonBox->button(QDialogButtonBox::Apply));
3494         bc().setCancel(buttonBox->button(QDialogButtonBox::Cancel));
3495         bc().setRestore(buttonBox->button(QDialogButtonBox::Reset));
3496 }
3497
3498
3499 void GuiPreferences::addModule(PrefModule * module)
3500 {
3501         LASSERT(module, return);
3502         if (module->category().isEmpty())
3503                 prefsPS->addPanel(module, module->title());
3504         else
3505                 prefsPS->addPanel(module, module->title(), module->category());
3506         connect(module, SIGNAL(changed()), this, SLOT(change_adaptor()));
3507         modules_.push_back(module);
3508 }
3509
3510
3511 void GuiPreferences::change_adaptor()
3512 {
3513         changed();
3514 }
3515
3516
3517 void GuiPreferences::applyRC(LyXRC & rc) const
3518 {
3519         size_t end = modules_.size();
3520         for (size_t i = 0; i != end; ++i)
3521                 modules_[i]->applyRC(rc);
3522 }
3523
3524
3525 void GuiPreferences::updateRC(LyXRC const & rc)
3526 {
3527         size_t const end = modules_.size();
3528         for (size_t i = 0; i != end; ++i)
3529                 modules_[i]->updateRC(rc);
3530 }
3531
3532
3533 void GuiPreferences::applyView()
3534 {
3535         applyRC(rc());
3536 }
3537
3538
3539 bool GuiPreferences::initialiseParams(string const &)
3540 {
3541         rc_ = lyxrc;
3542         formats_ = theFormats();
3543         converters_ = theConverters();
3544         converters_.update(formats_);
3545         movers_ = theMovers();
3546         colors_.clear();
3547
3548         updateRC(rc_);
3549         // Make sure that the bc is in the INITIAL state
3550         if (bc().policy().buttonStatus(ButtonPolicy::RESTORE))
3551                 bc().restore();
3552
3553         return true;
3554 }
3555
3556
3557 void GuiPreferences::dispatchParams()
3558 {
3559         ostringstream ss;
3560         rc_.write(ss, true);
3561         dispatch(FuncRequest(LFUN_LYXRC_APPLY, ss.str()));
3562         // issue prefsApplied signal. This will update the
3563         // localized screen font sizes.
3564         prefsApplied(rc_);
3565         // FIXME: these need lfuns
3566         // FIXME UNICODE
3567         Author const & author =
3568                 Author(from_utf8(rc_.user_name), from_utf8(rc_.user_email));
3569         theBufferList().recordCurrentAuthor(author);
3570
3571         theFormats() = formats_;
3572
3573         theConverters() = converters_;
3574         theConverters().update(formats_);
3575         theConverters().buildGraph();
3576         theBufferList().invalidateConverterCache();
3577
3578         theMovers() = movers_;
3579
3580         for (string const & color : colors_)
3581                 dispatch(FuncRequest(LFUN_SET_COLOR, color));
3582         colors_.clear();
3583
3584         // Save permanently
3585         if (!tempSaveCB->isChecked())
3586                 dispatch(FuncRequest(LFUN_PREFERENCES_SAVE));
3587 }
3588
3589
3590 void GuiPreferences::setColor(ColorCode col, QString const & hex)
3591 {
3592         colors_.push_back(lcolor.getLyXName(col) + ' ' + fromqstr(hex));
3593 }
3594
3595
3596 QString GuiPreferences::browsebind(QString const & file) const
3597 {
3598         return browseLibFile("bind", file, "bind", qt_("Choose bind file"),
3599                              QStringList(qt_("LyX bind files (*.bind)")));
3600 }
3601
3602
3603 QString GuiPreferences::browseUI(QString const & file) const
3604 {
3605         return browseLibFile("ui", file, "ui", qt_("Choose UI file"),
3606                              QStringList(qt_("LyX UI files (*.ui)")));
3607 }
3608
3609
3610 QString GuiPreferences::browsekbmap(QString const & file) const
3611 {
3612         return browseLibFile("kbd", file, "kmap", qt_("Choose keyboard map"),
3613                              QStringList(qt_("LyX keyboard maps (*.kmap)")));
3614 }
3615
3616
3617 QString GuiPreferences::browse(QString const & file,
3618         QString const & title) const
3619 {
3620         return browseFile(file, title, QStringList(), true);
3621 }
3622
3623
3624 Dialog * createGuiPreferences(GuiView & lv) { return new GuiPreferences(lv); }
3625
3626
3627 } // namespace frontend
3628 } // namespace lyx
3629
3630 #include "moc_GuiPrefs.cpp"