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