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