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