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