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