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