]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
40d7f275d068b58e2c9e1675f41844192a954100
[lyx.git] / src / lyxfunc.C
1 /**
2  * \file lyxfunc.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  * \author Allan Rae
13  * \author Dekel Tsur
14  * \author Martin Vermeer
15  * \author Jürgen Vigna
16  *
17  * Full author contact details are available in file CREDITS.
18  */
19
20 #include <config.h>
21
22 #include "lyxfunc.h"
23
24 #include "BranchList.h"
25 #include "buffer.h"
26 #include "buffer_funcs.h"
27 #include "bufferlist.h"
28 #include "bufferparams.h"
29 #include "BufferView.h"
30 #include "cursor.h"
31 #include "CutAndPaste.h"
32 #include "debug.h"
33 #include "dispatchresult.h"
34 #include "encoding.h"
35 #include "errorlist.h"
36 #include "exporter.h"
37 #include "format.h"
38 #include "funcrequest.h"
39 #include "gettext.h"
40 #include "importer.h"
41 #include "insetiterator.h"
42 #include "intl.h"
43 #include "kbmap.h"
44 #include "language.h"
45 #include "LColor.h"
46 #include "session.h"
47 #include "lyx_main.h"
48 #include "lyx_cb.h"
49 #include "LyXAction.h"
50 #include "lyxfind.h"
51 #include "lyxlex.h"
52 #include "lyxrc.h"
53 #include "lyxrow.h"
54 #include "lyxserver.h"
55 #include "lyxtextclasslist.h"
56 #include "lyxvc.h"
57 #include "paragraph.h"
58 #include "pariterator.h"
59 #include "ParagraphParameters.h"
60 #include "undo.h"
61
62 #include "insets/insetbox.h"
63 #include "insets/insetbranch.h"
64 #include "insets/insetcommand.h"
65 #include "insets/insetert.h"
66 #include "insets/insetexternal.h"
67 #include "insets/insetfloat.h"
68 #include "insets/insetgraphics.h"
69 #include "insets/insetinclude.h"
70 #include "insets/insetnote.h"
71 #include "insets/insettabular.h"
72 #include "insets/insetvspace.h"
73 #include "insets/insetwrap.h"
74
75 #include "frontends/Application.h"
76 #include "frontends/Alert.h"
77 #include "frontends/Dialogs.h"
78 #include "frontends/FileDialog.h"
79 #include "frontends/FontLoader.h"
80 #include "frontends/lyx_gui.h"
81 #include "frontends/LyXKeySym.h"
82 #include "frontends/LyXView.h"
83 #include "frontends/Menubar.h"
84 #include "frontends/Toolbars.h"
85
86 #include "support/environment.h"
87 #include "support/filefilterlist.h"
88 #include "support/filetools.h"
89 #include "support/forkedcontr.h"
90 #include "support/fs_extras.h"
91 #include "support/lstrings.h"
92 #include "support/path.h"
93 #include "support/package.h"
94 #include "support/systemcall.h"
95 #include "support/convert.h"
96 #include "support/os.h"
97
98 #include <boost/current_function.hpp>
99 #include <boost/filesystem/operations.hpp>
100
101 #include <sstream>
102
103 using bv_funcs::freefont2string;
104
105 using lyx::docstring;
106
107 using lyx::support::absolutePath;
108 using lyx::support::addName;
109 using lyx::support::addPath;
110 using lyx::support::bformat;
111 using lyx::support::changeExtension;
112 using lyx::support::contains;
113 using lyx::support::FileFilterList;
114 using lyx::support::fileSearch;
115 using lyx::support::ForkedcallsController;
116 using lyx::support::i18nLibFileSearch;
117 using lyx::support::isDirWriteable;
118 using lyx::support::isFileReadable;
119 using lyx::support::isStrInt;
120 using lyx::support::makeAbsPath;
121 using lyx::support::makeDisplayPath;
122 using lyx::support::package;
123 using lyx::support::quoteName;
124 using lyx::support::rtrim;
125 using lyx::support::split;
126 using lyx::support::subst;
127 using lyx::support::Systemcall;
128 using lyx::support::token;
129 using lyx::support::trim;
130 using lyx::support::prefixIs;
131
132 using std::endl;
133 using std::make_pair;
134 using std::pair;
135 using std::string;
136 using std::istringstream;
137 using std::ostringstream;
138
139 namespace biblio = lyx::biblio;
140 namespace fs = boost::filesystem;
141
142
143 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
144
145 // (alkis)
146 extern tex_accent_struct get_accent(kb_action action);
147
148
149 namespace {
150
151 bool getStatus(LCursor cursor,
152                FuncRequest const & cmd, FuncStatus & status)
153 {
154         // Try to fix cursor in case it is broken.
155         cursor.fixIfBroken();
156
157         // This is, of course, a mess. Better create a new doc iterator and use
158         // this in Inset::getStatus. This might require an additional
159         // BufferView * arg, though (which should be avoided)
160         //LCursor safe = *this;
161         bool res = false;
162         for ( ; cursor.depth(); cursor.pop()) {
163                 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164                 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165                 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166                 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
167
168                 // The inset's getStatus() will return 'true' if it made
169                 // a definitive decision on whether it want to handle the
170                 // request or not. The result of this decision is put into
171                 // the 'status' parameter.
172                 if (cursor.inset().getStatus(cursor, cmd, status)) {
173                         res = true;
174                         break;
175                 }
176         }
177         return res;
178 }
179
180
181 /** Return the change status at cursor position, taking in account the
182  * status at each level of the document iterator (a table in a deleted
183  * footnote is deleted).
184  * When \param outer is true, the top slice is not looked at.
185  */
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
187 {
188         size_t const depth = dit.depth() - (outer ? 1 : 0);
189
190         for (size_t i = 0 ; i < depth ; ++i) {
191                 CursorSlice const & slice = dit[i];
192                 if (!slice.inset().inMathed()
193                     && slice.pos() < slice.paragraph().size()) {
194                         Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195                         if (ch != Change::UNCHANGED)
196                                 return ch;
197                 }
198         }
199         return Change::UNCHANGED;
200 }
201
202 }
203
204 LyXFunc::LyXFunc(LyXView * lv)
205         : owner(lv),
206         encoded_last_key(0),
207         keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
208         cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
209         meta_fake_bit(key_modifier::none)
210 {
211 }
212
213
214 void LyXFunc::handleKeyFunc(kb_action action)
215 {
216         char c = encoded_last_key;
217
218         if (keyseq.length()) {
219                 c = 0;
220         }
221
222         owner->view()->getIntl().getTransManager()
223                 .deadkey(c, get_accent(action).accent, view()->getLyXText());
224         // Need to clear, in case the minibuffer calls these
225         // actions
226         keyseq.clear();
227         // copied verbatim from do_accent_char
228         view()->cursor().resetAnchor();
229         view()->update();
230 }
231
232
233 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
234 {
235         lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
236
237         // Do nothing if we have nothing (JMarc)
238         if (!keysym->isOK()) {
239                 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
240                                    << endl;
241                 return;
242         }
243
244         if (keysym->isModifier()) {
245                 lyxerr[Debug::KEY] << "isModifier true" << endl;
246                 return;
247         }
248
249         Encoding const * encoding = view()->cursor().getEncoding();
250
251         //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
252         size_t encoded_last_key = keysym->getUCSEncoded();
253
254         // Do a one-deep top-level lookup for
255         // cancel and meta-fake keys. RVDK_PATCH_5
256         cancel_meta_seq.reset();
257
258         FuncRequest func = cancel_meta_seq.addkey(keysym, state);
259         lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
260                            << " action first set to [" << func.action << ']'
261                            << endl;
262
263         // When not cancel or meta-fake, do the normal lookup.
264         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
265         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
266         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
267                 // remove Caps Lock and Mod2 as a modifiers
268                 func = keyseq.addkey(keysym, (state | meta_fake_bit));
269                 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
270                                    << "action now set to ["
271                                    << func.action << ']' << endl;
272         }
273
274         // Dont remove this unless you know what you are doing.
275         meta_fake_bit = key_modifier::none;
276
277         // Can this happen now ?
278         if (func.action == LFUN_NOACTION) {
279                 func = FuncRequest(LFUN_COMMAND_PREFIX);
280         }
281
282         if (lyxerr.debugging(Debug::KEY)) {
283                 lyxerr << BOOST_CURRENT_FUNCTION
284                        << " Key [action="
285                        << func.action << "]["
286                        << keyseq.print() << ']'
287                        << endl;
288         }
289
290         // already here we know if it any point in going further
291         // why not return already here if action == -1 and
292         // num_bytes == 0? (Lgb)
293
294         if (keyseq.length() > 1) {
295                 owner->message(lyx::from_utf8(keyseq.print()));
296         }
297
298
299         // Maybe user can only reach the key via holding down shift.
300         // Let's see. But only if shift is the only modifier
301         if (func.action == LFUN_UNKNOWN_ACTION &&
302             state == key_modifier::shift) {
303                 lyxerr[Debug::KEY] << "Trying without shift" << endl;
304                 func = keyseq.addkey(keysym, key_modifier::none);
305                 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
306         }
307
308         if (func.action == LFUN_UNKNOWN_ACTION) {
309                 // Hmm, we didn't match any of the keysequences. See
310                 // if it's normal insertable text not already covered
311                 // by a binding
312                 if (keysym->isText() && keyseq.length() == 1) {
313                         lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
314                         func = FuncRequest(LFUN_SELF_INSERT,
315                                            FuncRequest::KEYBOARD);
316                 } else {
317                         lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
318                         owner->message(_("Unknown function."));
319                         return;
320                 }
321         }
322
323         if (func.action == LFUN_SELF_INSERT) {
324                 if (encoded_last_key != 0) {
325                         docstring const arg(1, encoded_last_key);
326                         dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
327                                              FuncRequest::KEYBOARD));
328                         lyxerr[Debug::KEY]
329                                 << "SelfInsert arg[`" << lyx::to_utf8(arg) << "']" << endl;
330                 }
331         } else {
332                 dispatch(func);
333         }
334 }
335
336
337 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
338 {
339         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
340         FuncStatus flag;
341         LCursor & cur = view()->cursor();
342
343         /* In LyX/Mac, when a dialog is open, the menus of the
344            application can still be accessed without giving focus to
345            the main window. In this case, we want to disable the menu
346            entries that are buffer-related.
347
348            Note that this code is not perfect, as bug 1941 attests:
349            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
350         */
351         Buffer * buf;
352         if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
353                 buf = 0;
354         else
355                 buf = owner->buffer();
356
357         if (cmd.action == LFUN_NOACTION) {
358                 flag.message(lyx::from_utf8(N_("Nothing to do")));
359                 flag.enabled(false);
360                 return flag;
361         }
362
363         switch (cmd.action) {
364         case LFUN_UNKNOWN_ACTION:
365 #ifndef HAVE_LIBAIKSAURUS
366         case LFUN_THESAURUS_ENTRY:
367 #endif
368                 flag.unknown(true);
369                 flag.enabled(false);
370                 break;
371
372         case LFUN_TOOLTIPS_TOGGLE:
373                 flag.unknown(true);
374                 break;
375
376         default:
377                 break;
378         }
379
380         if (flag.unknown()) {
381                 flag.message(lyx::from_utf8(N_("Unknown action")));
382                 return flag;
383         }
384
385         if (!flag.enabled()) {
386                 if (flag.message().empty())
387                         flag.message(lyx::from_utf8(N_("Command disabled")));
388                 return flag;
389         }
390
391         // Check whether we need a buffer
392         if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
393                 // no, exit directly
394                 flag.message(lyx::from_utf8(N_("Command not allowed with"
395                                     "out any document open")));
396                 flag.enabled(false);
397                 return flag;
398         }
399
400         // I would really like to avoid having this switch and rather try to
401         // encode this in the function itself.
402         // -- And I'd rather let an inset decide which LFUNs it is willing
403         // to handle (Andre')
404         bool enable = true;
405         switch (cmd.action) {
406         case LFUN_TOOLTIPS_TOGGLE:
407                 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
408                 break;
409
410         case LFUN_BUFFER_TOGGLE_READ_ONLY:
411                 flag.setOnOff(buf->isReadonly());
412                 break;
413
414         case LFUN_BUFFER_SWITCH:
415                 // toggle on the current buffer, but do not toggle off
416                 // the other ones (is that a good idea?)
417                 if (lyx::to_utf8(cmd.argument()) == buf->fileName())
418                         flag.setOnOff(true);
419                 break;
420
421         case LFUN_BUFFER_EXPORT:
422                 enable = cmd.argument() == "custom"
423                         || Exporter::isExportable(*buf, lyx::to_utf8(cmd.argument()));
424                 break;
425
426         case LFUN_BUFFER_CHKTEX:
427                 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
428                 break;
429
430         case LFUN_BUILD_PROGRAM:
431                 enable = Exporter::isExportable(*buf, "program");
432                 break;
433
434         case LFUN_LAYOUT_TABULAR:
435                 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
436                 break;
437
438         case LFUN_LAYOUT:
439         case LFUN_LAYOUT_PARAGRAPH:
440                 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
441                 break;
442
443         case LFUN_VC_REGISTER:
444                 enable = !buf->lyxvc().inUse();
445                 break;
446         case LFUN_VC_CHECK_IN:
447                 enable = buf->lyxvc().inUse() && !buf->isReadonly();
448                 break;
449         case LFUN_VC_CHECK_OUT:
450                 enable = buf->lyxvc().inUse() && buf->isReadonly();
451                 break;
452         case LFUN_VC_REVERT:
453         case LFUN_VC_UNDO_LAST:
454                 enable = buf->lyxvc().inUse();
455                 break;
456         case LFUN_BUFFER_RELOAD:
457                 enable = !buf->isUnnamed() && !buf->isClean();
458                 break;
459
460         case LFUN_INSET_SETTINGS: {
461                 enable = false;
462                 if (!cur)
463                         break;
464                 InsetBase::Code code = cur.inset().lyxCode();
465                 switch (code) {
466                         case InsetBase::TABULAR_CODE:
467                                 enable = cmd.argument() == "tabular";
468                                 break;
469                         case InsetBase::ERT_CODE:
470                                 enable = cmd.argument() == "ert";
471                                 break;
472                         case InsetBase::FLOAT_CODE:
473                                 enable = cmd.argument() == "float";
474                                 break;
475                         case InsetBase::WRAP_CODE:
476                                 enable = cmd.argument() == "wrap";
477                                 break;
478                         case InsetBase::NOTE_CODE:
479                                 enable = cmd.argument() == "note";
480                                 break;
481                         case InsetBase::BRANCH_CODE:
482                                 enable = cmd.argument() == "branch";
483                                 break;
484                         case InsetBase::BOX_CODE:
485                                 enable = cmd.argument() == "box";
486                                 break;
487                         default:
488                                 break;
489                 }
490                 break;
491         }
492
493         case LFUN_INSET_APPLY: {
494                 string const name = cmd.getArg(0);
495                 InsetBase * inset = owner->getDialogs().getOpenInset(name);
496                 if (inset) {
497                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
498                         FuncStatus fs;
499                         bool const success = inset->getStatus(cur, fr, fs);
500                         // Every inset is supposed to handle this
501                         BOOST_ASSERT(success);
502                         flag |= fs;
503                 } else {
504                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
505                         flag |= getStatus(fr);
506                 }
507                 enable = flag.enabled();
508                 break;
509         }
510
511         case LFUN_DIALOG_SHOW: {
512                 string const name = cmd.getArg(0);
513                 if (!buf)
514                         enable = name == "aboutlyx"
515                                 || name == "file"
516                                 || name == "forks"
517                                 || name == "prefs"
518                                 || name == "texinfo";
519                 else if (name == "print")
520                         enable = Exporter::isExportable(*buf, "dvi")
521                                 && lyxrc.print_command != "none";
522                 else if (name == "character" || name == "mathpanel")
523                         enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
524                 else if (name == "latexlog")
525                         enable = isFileReadable(buf->getLogName().second);
526 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
527                 else if (name == "spellchecker")
528                         enable = false;
529 #endif
530                 else if (name == "vclog")
531                         enable = buf->lyxvc().inUse();
532                 else if (name == "view-source")
533                         enable = buf;
534                 break;
535         }
536
537         case LFUN_DIALOG_SHOW_NEW_INSET:
538                 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
539                 break;
540
541         case LFUN_DIALOG_UPDATE: {
542                 string const name = cmd.getArg(0);
543                 if (!buf)
544                         enable = name == "prefs";
545                 break;
546         }
547
548         case LFUN_CITATION_INSERT: {
549                 FuncRequest fr(LFUN_INSET_INSERT, "citation");
550                 enable = getStatus(fr).enabled();
551                 break;
552         }
553
554         case LFUN_BUFFER_WRITE: {
555                 enable = view()->buffer()->isUnnamed()
556                         || !view()->buffer()->isClean();
557                 break;
558         }
559
560         // this one is difficult to get right. As a half-baked
561         // solution, we consider only the first action of the sequence
562         case LFUN_COMMAND_SEQUENCE: {
563                 // argument contains ';'-terminated commands
564                 string const firstcmd = token(lyx::to_utf8(cmd.argument()), ';', 0);
565                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
566                 func.origin = cmd.origin;
567                 flag = getStatus(func);
568         }
569
570         case LFUN_BUFFER_NEW:
571         case LFUN_BUFFER_NEW_TEMPLATE:
572         case LFUN_WORD_FIND_FORWARD:
573         case LFUN_WORD_FIND_BACKWARD:
574         case LFUN_COMMAND_PREFIX:
575         case LFUN_COMMAND_EXECUTE:
576         case LFUN_CANCEL:
577         case LFUN_META_PREFIX:
578         case LFUN_BUFFER_CLOSE:
579         case LFUN_BUFFER_WRITE_AS:
580         case LFUN_BUFFER_UPDATE:
581         case LFUN_BUFFER_VIEW:
582         case LFUN_BUFFER_IMPORT:
583         case LFUN_LYX_QUIT:
584         case LFUN_TOC_VIEW:
585         case LFUN_BUFFER_AUTO_SAVE:
586         case LFUN_RECONFIGURE:
587         case LFUN_HELP_OPEN:
588         case LFUN_FILE_NEW:
589         case LFUN_FILE_OPEN:
590         case LFUN_DROP_LAYOUTS_CHOICE:
591         case LFUN_MENU_OPEN:
592         case LFUN_SERVER_GET_NAME:
593         case LFUN_SERVER_NOTIFY:
594         case LFUN_SERVER_GOTO_FILE_ROW:
595         case LFUN_DIALOG_HIDE:
596         case LFUN_DIALOG_DISCONNECT_INSET:
597         case LFUN_BUFFER_CHILD_OPEN:
598         case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
599         case LFUN_KEYMAP_OFF:
600         case LFUN_KEYMAP_PRIMARY:
601         case LFUN_KEYMAP_SECONDARY:
602         case LFUN_KEYMAP_TOGGLE:
603         case LFUN_REPEAT:
604         case LFUN_BUFFER_EXPORT_CUSTOM:
605         case LFUN_BUFFER_PRINT:
606         case LFUN_PREFERENCES_SAVE:
607         case LFUN_SCREEN_FONT_UPDATE:
608         case LFUN_SET_COLOR:
609         case LFUN_MESSAGE:
610         case LFUN_EXTERNAL_EDIT:
611         case LFUN_GRAPHICS_EDIT:
612         case LFUN_ALL_INSETS_TOGGLE:
613         case LFUN_BUFFER_LANGUAGE:
614         case LFUN_TEXTCLASS_APPLY:
615         case LFUN_TEXTCLASS_LOAD:
616         case LFUN_BUFFER_SAVE_AS_DEFAULT:
617         case LFUN_BUFFER_PARAMS_APPLY:
618         case LFUN_LYXRC_APPLY:
619         case LFUN_BUFFER_NEXT:
620         case LFUN_BUFFER_PREVIOUS:
621                 // these are handled in our dispatch()
622                 break;
623
624         default:
625
626                 if (!::getStatus(cur, cmd, flag))
627                         flag = view()->getStatus(cmd);
628         }
629
630         if (!enable)
631                 flag.enabled(false);
632
633         // Can we use a readonly buffer?
634         if (buf && buf->isReadonly()
635             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
636             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
637                 flag.message(lyx::from_utf8(N_("Document is read-only")));
638                 flag.enabled(false);
639         }
640
641         // Are we in a DELETED change-tracking region?
642         if (buf && lookupChangeType(cur, true) == Change::DELETED
643             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
644             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
645                 flag.message(lyx::from_utf8(N_("This portion of the document is deleted.")));
646                 flag.enabled(false);
647         }
648
649         // the default error message if we disable the command
650         if (!flag.enabled() && flag.message().empty())
651                 flag.message(lyx::from_utf8(N_("Command disabled")));
652
653         return flag;
654 }
655
656
657 bool LyXFunc::ensureBufferClean(BufferView * bv)
658 {
659         Buffer & buf = *bv->buffer();
660         if (buf.isClean())
661                 return true;
662
663         docstring const file = makeDisplayPath(buf.fileName(), 30);
664         docstring text = bformat(_("The document %1$s has unsaved "
665                                              "changes.\n\nDo you want to save "
666                                              "the document?"), file);
667         int const ret = Alert::prompt(_("Save changed document?"),
668                                       text, 0, 1, _("&Save"),
669                                       _("&Cancel"));
670
671         if (ret == 0)
672                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
673
674         return buf.isClean();
675 }
676
677
678 namespace {
679
680 void showPrintError(string const & name)
681 {
682         docstring str = bformat(_("Could not print the document %1$s.\n"
683                                             "Check that your printer is set up correctly."),
684                              makeDisplayPath(name, 50));
685         Alert::error(_("Print document failed"), str);
686 }
687
688
689 void loadTextclass(string const & name)
690 {
691         std::pair<bool, lyx::textclass_type> const tc_pair =
692                 textclasslist.numberOfClass(name);
693
694         if (!tc_pair.first) {
695                 lyxerr << "Document class \"" << name
696                        << "\" does not exist."
697                        << std::endl;
698                 return;
699         }
700
701         lyx::textclass_type const tc = tc_pair.second;
702
703         if (!textclasslist[tc].load()) {
704                 docstring s = bformat(_("The document could not be converted\n"
705                                                   "into the document class %1$s."),
706                                    lyx::from_utf8(textclasslist[tc].name()));
707                 Alert::error(_("Could not change class"), s);
708         }
709 }
710
711
712 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
713
714 } //namespace anon
715
716
717 void LyXFunc::dispatch(FuncRequest const & cmd)
718 {
719         BOOST_ASSERT(view());
720         string const argument = lyx::to_utf8(cmd.argument());
721         kb_action const action = cmd.action;
722
723         lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
724         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
725
726         // we have not done anything wrong yet.
727         errorstat = false;
728         dispatch_buffer.erase();
729
730         // redraw the screen at the end (first of the two drawing steps).
731         //This is done unless explicitely requested otherwise
732         bool update = true;
733         // also do the second redrawing step. Only done if requested.
734         bool updateforce = false;
735
736         FuncStatus const flag = getStatus(cmd);
737         if (!flag.enabled()) {
738                 // We cannot use this function here
739                 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
740                        << lyxaction.getActionName(action)
741                        << " [" << action << "] is disabled at this location"
742                        << endl;
743                 setErrorMessage(flag.message());
744         } else {
745                 switch (action) {
746
747                 case LFUN_WORD_FIND_FORWARD:
748                 case LFUN_WORD_FIND_BACKWARD: {
749                         static string last_search;
750                         string searched_string;
751
752                         if (!argument.empty()) {
753                                 last_search = argument;
754                                 searched_string = argument;
755                         } else {
756                                 searched_string = last_search;
757                         }
758
759                         if (searched_string.empty())
760                                 break;
761
762                         bool const fw = action == LFUN_WORD_FIND_FORWARD;
763                         string const data =
764                                 lyx::find::find2string(searched_string, true, false, fw);
765                         lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
766                         break;
767                 }
768
769                 case LFUN_COMMAND_PREFIX:
770                         owner->message(lyx::from_utf8(keyseq.printOptions()));
771                         break;
772
773                 case LFUN_COMMAND_EXECUTE:
774                         owner->getToolbars().display("minibuffer", true);
775                         owner->focus_command_buffer();
776                         break;
777
778                 case LFUN_CANCEL:
779                         keyseq.reset();
780                         meta_fake_bit = key_modifier::none;
781                         if (view()->buffer())
782                                 // cancel any selection
783                                 dispatch(FuncRequest(LFUN_MARK_OFF));
784                         setMessage(_("Cancel"));
785                         break;
786
787                 case LFUN_META_PREFIX:
788                         meta_fake_bit = key_modifier::alt;
789                         setMessage(lyx::from_utf8(keyseq.print()));
790                         break;
791
792                 case LFUN_BUFFER_TOGGLE_READ_ONLY:
793                         if (owner->buffer()->lyxvc().inUse())
794                                 owner->buffer()->lyxvc().toggleReadOnly();
795                         else
796                                 owner->buffer()->setReadonly(
797                                         !owner->buffer()->isReadonly());
798                         break;
799
800                 // --- Menus -----------------------------------------------
801                 case LFUN_BUFFER_NEW:
802                         menuNew(argument, false);
803                         break;
804
805                 case LFUN_BUFFER_NEW_TEMPLATE:
806                         menuNew(argument, true);
807                         break;
808
809                 case LFUN_BUFFER_CLOSE:
810                         closeBuffer();
811                         break;
812
813                 case LFUN_BUFFER_WRITE:
814                         if (!owner->buffer()->isUnnamed()) {
815                                 docstring const str = bformat(_("Saving document %1$s..."),
816                                          makeDisplayPath(owner->buffer()->fileName()));
817                                 owner->message(str);
818                                 menuWrite(owner->buffer());
819                                 owner->message(str + _(" done."));
820                         } else
821                                 writeAs(owner->buffer());
822                         update = false;
823                         break;
824
825                 case LFUN_BUFFER_WRITE_AS:
826                         writeAs(owner->buffer(), argument);
827                         update = false;
828                         break;
829
830                 case LFUN_BUFFER_RELOAD: {
831                         docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
832                         docstring text = bformat(_("Any changes will be lost. Are you sure "
833                                                              "you want to revert to the saved version of the document %1$s?"), file);
834                         int const ret = Alert::prompt(_("Revert to saved document?"),
835                                 text, 0, 1, _("&Revert"), _("&Cancel"));
836
837                         if (ret == 0)
838                                 view()->reload();
839                         break;
840                 }
841
842                 case LFUN_BUFFER_UPDATE:
843                         Exporter::Export(owner->buffer(), argument, true);
844                         break;
845
846                 case LFUN_BUFFER_VIEW:
847                         Exporter::preview(owner->buffer(), argument);
848                         break;
849
850                 case LFUN_BUILD_PROGRAM:
851                         Exporter::Export(owner->buffer(), "program", true);
852                         break;
853
854                 case LFUN_BUFFER_CHKTEX:
855                         owner->buffer()->runChktex();
856                         break;
857
858                 case LFUN_BUFFER_EXPORT:
859                         if (argument == "custom")
860                                 owner->getDialogs().show("sendto");
861                         else {
862                                 Exporter::Export(owner->buffer(), argument, false);
863                         }
864                         break;
865
866                 case LFUN_BUFFER_EXPORT_CUSTOM: {
867                         string format_name;
868                         string command = split(argument, format_name, ' ');
869                         Format const * format = formats.getFormat(format_name);
870                         if (!format) {
871                                 lyxerr << "Format \"" << format_name
872                                        << "\" not recognized!"
873                                        << std::endl;
874                                 break;
875                         }
876
877                         Buffer * buffer = owner->buffer();
878
879                         // The name of the file created by the conversion process
880                         string filename;
881
882                         // Output to filename
883                         if (format->name() == "lyx") {
884                                 string const latexname =
885                                         buffer->getLatexName(false);
886                                 filename = changeExtension(latexname,
887                                                            format->extension());
888                                 filename = addName(buffer->temppath(), filename);
889
890                                 if (!buffer->writeFile(filename))
891                                         break;
892
893                         } else {
894                                 Exporter::Export(buffer, format_name, true, filename);
895                         }
896
897                         // Substitute $$FName for filename
898                         if (!contains(command, "$$FName"))
899                                 command = "( " + command + " ) < $$FName";
900                         command = subst(command, "$$FName", filename);
901
902                         // Execute the command in the background
903                         Systemcall call;
904                         call.startscript(Systemcall::DontWait, command);
905                         break;
906                 }
907
908                 case LFUN_BUFFER_PRINT: {
909                         string target;
910                         string target_name;
911                         string command = split(split(argument, target, ' '),
912                                                target_name, ' ');
913
914                         if (target.empty()
915                             || target_name.empty()
916                             || command.empty()) {
917                                 lyxerr << "Unable to parse \""
918                                        << argument << '"' << std::endl;
919                                 break;
920                         }
921                         if (target != "printer" && target != "file") {
922                                 lyxerr << "Unrecognized target \""
923                                        << target << '"' << std::endl;
924                                 break;
925                         }
926
927                         Buffer * buffer = owner->buffer();
928
929                         if (!Exporter::Export(buffer, "dvi", true)) {
930                                 showPrintError(buffer->fileName());
931                                 break;
932                         }
933
934                         // Push directory path.
935                         string const path = buffer->temppath();
936                         lyx::support::Path p(path);
937
938                         // there are three cases here:
939                         // 1. we print to a file
940                         // 2. we print directly to a printer
941                         // 3. we print using a spool command (print to file first)
942                         Systemcall one;
943                         int res = 0;
944                         string const dviname =
945                                 changeExtension(buffer->getLatexName(true),
946                                                 "dvi");
947
948                         if (target == "printer") {
949                                 if (!lyxrc.print_spool_command.empty()) {
950                                         // case 3: print using a spool
951                                         string const psname =
952                                                 changeExtension(dviname,".ps");
953                                         command += lyxrc.print_to_file
954                                                 + quoteName(psname)
955                                                 + ' '
956                                                 + quoteName(dviname);
957
958                                         string command2 =
959                                                 lyxrc.print_spool_command +' ';
960                                         if (target_name != "default") {
961                                                 command2 += lyxrc.print_spool_printerprefix
962                                                         + target_name
963                                                         + ' ';
964                                         }
965                                         command2 += quoteName(psname);
966                                         // First run dvips.
967                                         // If successful, then spool command
968                                         res = one.startscript(
969                                                 Systemcall::Wait,
970                                                 command);
971
972                                         if (res == 0)
973                                                 res = one.startscript(
974                                                         Systemcall::DontWait,
975                                                         command2);
976                                 } else {
977                                         // case 2: print directly to a printer
978                                         res = one.startscript(
979                                                 Systemcall::DontWait,
980                                                 command + quoteName(dviname));
981                                 }
982
983                         } else {
984                                 // case 1: print to a file
985                                 command += lyxrc.print_to_file
986                                         + quoteName(makeAbsPath(target_name,
987                                                                 path))
988                                         + ' '
989                                         + quoteName(dviname);
990                                 res = one.startscript(Systemcall::DontWait,
991                                                       command);
992                         }
993
994                         if (res != 0)
995                                 showPrintError(buffer->fileName());
996                         break;
997                 }
998
999                 case LFUN_BUFFER_IMPORT:
1000                         doImport(argument);
1001                         break;
1002
1003                 case LFUN_LYX_QUIT:
1004                         if (view()->buffer()) {
1005                                 // save cursor Position for opened files to .lyx/session
1006                                 LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1007                                         boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1008                                 // save bookmarks to .lyx/session
1009                                 view()->saveSavedPositions();
1010                         }
1011                         quitLyX(argument == "force");
1012                         break;
1013
1014                 case LFUN_TOC_VIEW: {
1015                         InsetCommandParams p("tableofcontents");
1016                         string const data = InsetCommandMailer::params2string("toc", p);
1017                         owner->getDialogs().show("toc", data, 0);
1018                         break;
1019                 }
1020
1021                 case LFUN_BUFFER_AUTO_SAVE:
1022                         autoSave(view());
1023                         break;
1024
1025                 case LFUN_RECONFIGURE:
1026                         reconfigure(view());
1027                         break;
1028
1029                 case LFUN_HELP_OPEN: {
1030                         string const arg = argument;
1031                         if (arg.empty()) {
1032                                 setErrorMessage(_("Missing argument"));
1033                                 break;
1034                         }
1035                         string const fname = i18nLibFileSearch("doc", arg, "lyx");
1036                         if (fname.empty()) {
1037                                 lyxerr << "LyX: unable to find documentation file `"
1038                                                          << arg << "'. Bad installation?" << endl;
1039                                 break;
1040                         }
1041                         owner->message(bformat(_("Opening help file %1$s..."),
1042                                 makeDisplayPath(fname)));
1043                         owner->loadLyXFile(fname, false);
1044                         break;
1045                 }
1046
1047                 // --- version control -------------------------------
1048                 case LFUN_VC_REGISTER:
1049                         if (!ensureBufferClean(view()))
1050                                 break;
1051                         if (!owner->buffer()->lyxvc().inUse()) {
1052                                 owner->buffer()->lyxvc().registrer();
1053                                 view()->reload();
1054                         }
1055                         break;
1056
1057                 case LFUN_VC_CHECK_IN:
1058                         if (!ensureBufferClean(view()))
1059                                 break;
1060                         if (owner->buffer()->lyxvc().inUse()
1061                                         && !owner->buffer()->isReadonly()) {
1062                                 owner->buffer()->lyxvc().checkIn();
1063                                 view()->reload();
1064                         }
1065                         break;
1066
1067                 case LFUN_VC_CHECK_OUT:
1068                         if (!ensureBufferClean(view()))
1069                                 break;
1070                         if (owner->buffer()->lyxvc().inUse()
1071                                         && owner->buffer()->isReadonly()) {
1072                                 owner->buffer()->lyxvc().checkOut();
1073                                 view()->reload();
1074                         }
1075                         break;
1076
1077                 case LFUN_VC_REVERT:
1078                         owner->buffer()->lyxvc().revert();
1079                         view()->reload();
1080                         break;
1081
1082                 case LFUN_VC_UNDO_LAST:
1083                         owner->buffer()->lyxvc().undoLast();
1084                         view()->reload();
1085                         break;
1086
1087                 // --- buffers ----------------------------------------
1088                 case LFUN_BUFFER_SWITCH:
1089                         owner->setBuffer(theApp->bufferList().getBuffer(argument));
1090                         break;
1091
1092                 case LFUN_BUFFER_NEXT:
1093                         owner->setBuffer(theApp->bufferList().next(view()->buffer()));
1094                         break;
1095
1096                 case LFUN_BUFFER_PREVIOUS:
1097                         owner->setBuffer(theApp->bufferList().previous(view()->buffer()));
1098                         break;
1099
1100                 case LFUN_FILE_NEW:
1101                         newFile(view(), argument);
1102                         break;
1103
1104                 case LFUN_FILE_OPEN:
1105                         open(argument);
1106                         break;
1107
1108                 case LFUN_DROP_LAYOUTS_CHOICE:
1109                         owner->getToolbars().openLayoutList();
1110                         break;
1111
1112                 case LFUN_MENU_OPEN:
1113                         owner->getMenubar().openByName(lyx::from_utf8(argument));
1114                         break;
1115
1116                 // --- lyxserver commands ----------------------------
1117                 case LFUN_SERVER_GET_NAME:
1118                         setMessage(lyx::from_utf8(owner->buffer()->fileName()));
1119                         lyxerr[Debug::INFO] << "FNAME["
1120                                                          << owner->buffer()->fileName()
1121                                                          << "] " << endl;
1122                         break;
1123
1124                 case LFUN_SERVER_NOTIFY:
1125                         dispatch_buffer = lyx::from_utf8(keyseq.print());
1126                         theApp->server().notifyClient(lyx::to_utf8(dispatch_buffer));
1127                         break;
1128
1129                 case LFUN_SERVER_GOTO_FILE_ROW: {
1130                         string file_name;
1131                         int row;
1132                         istringstream is(argument);
1133                         is >> file_name >> row;
1134                         if (prefixIs(file_name, package().temp_dir())) {
1135                                 // Needed by inverse dvi search. If it is a file
1136                                 // in tmpdir, call the apropriated function
1137                                 owner->setBuffer(theApp->bufferList().getBufferFromTmp(file_name));
1138                         } else {
1139                                 // Must replace extension of the file to be .lyx
1140                                 // and get full path
1141                                 string const s = changeExtension(file_name, ".lyx");
1142                                 // Either change buffer or load the file
1143                                 if (theApp->bufferList().exists(s)) {
1144                                         owner->setBuffer(theApp->bufferList().getBuffer(s));
1145                                 } else {
1146                                         owner->loadLyXFile(s);
1147                                 }
1148                         }
1149
1150                         view()->setCursorFromRow(row);
1151
1152                         view()->center();
1153                         // see BufferView::center()
1154                         break;
1155                 }
1156
1157                 case LFUN_DIALOG_SHOW: {
1158                         string const name = cmd.getArg(0);
1159                         string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1160
1161                         if (name == "character") {
1162                                 data = freefont2string();
1163                                 if (!data.empty())
1164                                         owner->getDialogs().show("character", data);
1165                         } else if (name == "latexlog") {
1166                                 pair<Buffer::LogType, string> const logfile =
1167                                         owner->buffer()->getLogName();
1168                                 switch (logfile.first) {
1169                                 case Buffer::latexlog:
1170                                         data = "latex ";
1171                                         break;
1172                                 case Buffer::buildlog:
1173                                         data = "literate ";
1174                                         break;
1175                                 }
1176                                 data += LyXLex::quoteString(logfile.second);
1177                                 owner->getDialogs().show("log", data);
1178                         } else if (name == "vclog") {
1179                                 string const data = "vc " +
1180                                         LyXLex::quoteString(owner->buffer()->lyxvc().getLogFile());
1181                                 owner->getDialogs().show("log", data);
1182                         } else
1183                                 owner->getDialogs().show(name, data);
1184                         break;
1185                 }
1186
1187                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1188                         string const name = cmd.getArg(0);
1189                         string data = trim(lyx::to_utf8(cmd.argument()).substr(name.size()));
1190                         if (name == "bibitem" ||
1191                             name == "bibtex" ||
1192                             name == "index" ||
1193                             name == "label" ||
1194                             name == "ref" ||
1195                             name == "toc" ||
1196                             name == "url") {
1197                                 InsetCommandParams p(name);
1198                                 data = InsetCommandMailer::params2string(name, p);
1199                         } else if (name == "include") {
1200                                 InsetCommandParams p(data);
1201                                 data = InsetIncludeMailer::params2string(p);
1202                         } else if (name == "box") {
1203                                 // \c data == "Boxed" || "Frameless" etc
1204                                 InsetBoxParams p(data);
1205                                 data = InsetBoxMailer::params2string(p);
1206                         } else if (name == "branch") {
1207                                 InsetBranchParams p;
1208                                 data = InsetBranchMailer::params2string(p);
1209                         } else if (name == "citation") {
1210                                 InsetCommandParams p("cite");
1211                                 data = InsetCommandMailer::params2string(name, p);
1212                         } else if (name == "ert") {
1213                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1214                         } else if (name == "external") {
1215                                 InsetExternalParams p;
1216                                 Buffer const & buffer = *owner->buffer();
1217                                 data = InsetExternalMailer::params2string(p, buffer);
1218                         } else if (name == "float") {
1219                                 InsetFloatParams p;
1220                                 data = InsetFloatMailer::params2string(p);
1221                         } else if (name == "graphics") {
1222                                 InsetGraphicsParams p;
1223                                 Buffer const & buffer = *owner->buffer();
1224                                 data = InsetGraphicsMailer::params2string(p, buffer);
1225                         } else if (name == "note") {
1226                                 InsetNoteParams p;
1227                                 data = InsetNoteMailer::params2string(p);
1228                         } else if (name == "vspace") {
1229                                 VSpace space;
1230                                 data = InsetVSpaceMailer::params2string(space);
1231                         } else if (name == "wrap") {
1232                                 InsetWrapParams p;
1233                                 data = InsetWrapMailer::params2string(p);
1234                         }
1235                         owner->getDialogs().show(name, data, 0);
1236                         break;
1237                 }
1238
1239                 case LFUN_DIALOG_UPDATE: {
1240                         string const & name = argument;
1241                         // Can only update a dialog connected to an existing inset
1242                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1243                         if (inset) {
1244                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1245                                 inset->dispatch(view()->cursor(), fr);
1246                         } else if (name == "paragraph") {
1247                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1248                         } else if (name == "prefs") {
1249                                 owner->getDialogs().update(name, string());
1250                         }
1251                         break;
1252                 }
1253
1254                 case LFUN_DIALOG_HIDE:
1255                         Dialogs::hide(argument, 0);
1256                         break;
1257
1258                 case LFUN_DIALOG_DISCONNECT_INSET:
1259                         owner->getDialogs().disconnect(argument);
1260                         break;
1261
1262
1263                 case LFUN_CITATION_INSERT: {
1264                         if (!argument.empty()) {
1265                                 // we can have one optional argument, delimited by '|'
1266                                 // citation-insert <key>|<text_before>
1267                                 // this should be enhanced to also support text_after
1268                                 // and citation style
1269                                 string arg = argument;
1270                                 string opt1;
1271                                 if (contains(argument, "|")) {
1272                                         arg = token(argument, '|', 0);
1273                                         opt1 = '[' + token(argument, '|', 1) + ']';
1274                                 }
1275                                 std::ostringstream os;
1276                                 os << "citation LatexCommand\n"
1277                                    << "\\cite" << opt1 << "{" << arg << "}\n"
1278                                    << "\\end_inset";
1279                                 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1280                                 dispatch(fr);
1281                         } else
1282                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1283                         break;
1284                 }
1285
1286                 case LFUN_BUFFER_CHILD_OPEN: {
1287                         string const filename =
1288                                 makeAbsPath(argument, owner->buffer()->filePath());
1289                         // FIXME Should use bformat
1290                         setMessage(_("Opening child document ") +
1291                                          makeDisplayPath(filename) + lyx::from_ascii("..."));
1292                         view()->savePosition(0);
1293                         string const parentfilename = owner->buffer()->fileName();
1294                         if (theApp->bufferList().exists(filename))
1295                                 owner->setBuffer(theApp->bufferList().getBuffer(filename));
1296                         else
1297                                 owner->loadLyXFile(filename);
1298                         // Set the parent name of the child document.
1299                         // This makes insertion of citations and references in the child work,
1300                         // when the target is in the parent or another child document.
1301                         owner->buffer()->setParentName(parentfilename);
1302                         break;
1303                 }
1304
1305                 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1306                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1307                         break;
1308
1309                 case LFUN_KEYMAP_OFF:
1310                         owner->view()->getIntl().keyMapOn(false);
1311                         break;
1312
1313                 case LFUN_KEYMAP_PRIMARY:
1314                         owner->view()->getIntl().keyMapPrim();
1315                         break;
1316
1317                 case LFUN_KEYMAP_SECONDARY:
1318                         owner->view()->getIntl().keyMapSec();
1319                         break;
1320
1321                 case LFUN_KEYMAP_TOGGLE:
1322                         owner->view()->getIntl().toggleKeyMap();
1323                         break;
1324
1325                 case LFUN_REPEAT: {
1326                         // repeat command
1327                         string countstr;
1328                         string rest = split(argument, countstr, ' ');
1329                         istringstream is(countstr);
1330                         int count = 0;
1331                         is >> count;
1332                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1333                         for (int i = 0; i < count; ++i)
1334                                 dispatch(lyxaction.lookupFunc(rest));
1335                         break;
1336                 }
1337
1338                 case LFUN_COMMAND_SEQUENCE: {
1339                         // argument contains ';'-terminated commands
1340                         string arg = argument;
1341                         while (!arg.empty()) {
1342                                 string first;
1343                                 arg = split(arg, first, ';');
1344                                 FuncRequest func(lyxaction.lookupFunc(first));
1345                                 func.origin = cmd.origin;
1346                                 dispatch(func);
1347                         }
1348                         break;
1349                 }
1350
1351                 case LFUN_PREFERENCES_SAVE: {
1352                         lyx::support::Path p(package().user_support());
1353                         lyxrc.write("preferences", false);
1354                         break;
1355                 }
1356
1357                 case LFUN_SCREEN_FONT_UPDATE:
1358                         // handle the screen font changes.
1359                         lyxrc.set_font_norm_type();
1360                         theApp->fontLoader().update();
1361                         // All visible buffers will need resize
1362                         view()->resize();
1363                         break;
1364
1365                 case LFUN_SET_COLOR: {
1366                         string lyx_name;
1367                         string const x11_name = split(argument, lyx_name, ' ');
1368                         if (lyx_name.empty() || x11_name.empty()) {
1369                                 setErrorMessage(_("Syntax: set-color <lyx_name>"
1370                                                         " <x11_name>"));
1371                                 break;
1372                         }
1373
1374                         bool const graphicsbg_changed =
1375                                 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1376                                  x11_name != lcolor.getX11Name(LColor::graphicsbg));
1377
1378                         if (!lcolor.setColor(lyx_name, x11_name)) {
1379                                 setErrorMessage(
1380                                                 bformat(_("Set-color \"%1$s\" failed "
1381                                                                        "- color is undefined or "
1382                                                                        "may not be redefined"),
1383                                                                            lyx::from_utf8(lyx_name)));
1384                                 break;
1385                         }
1386
1387                         lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1388
1389                         if (graphicsbg_changed) {
1390 #ifdef WITH_WARNINGS
1391 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1392 #endif
1393 #if 0
1394                                 lyx::graphics::GCache::get().changeDisplay(true);
1395 #endif
1396                         }
1397                         break;
1398                 }
1399
1400                 case LFUN_MESSAGE:
1401                         owner->message(lyx::from_utf8(argument));
1402                         break;
1403
1404                 case LFUN_TOOLTIPS_TOGGLE:
1405                         owner->getDialogs().toggleTooltips();
1406                         break;
1407
1408                 case LFUN_EXTERNAL_EDIT: {
1409                         FuncRequest fr(action, argument);
1410                         InsetExternal().dispatch(view()->cursor(), fr);
1411                         break;
1412                 }
1413
1414                 case LFUN_GRAPHICS_EDIT: {
1415                         FuncRequest fr(action, argument);
1416                         InsetGraphics().dispatch(view()->cursor(), fr);
1417                         break;
1418                 }
1419
1420                 case LFUN_INSET_APPLY: {
1421                         string const name = cmd.getArg(0);
1422                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1423                         if (inset) {
1424                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1425                                 inset->dispatch(view()->cursor(), fr);
1426                         } else {
1427                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1428                                 dispatch(fr);
1429                         }
1430                         // ideally, the update flag should be set by the insets,
1431                         // but this is not possible currently
1432                         updateforce = true;
1433                         break;
1434                 }
1435
1436                 case LFUN_ALL_INSETS_TOGGLE: {
1437                         string action;
1438                         string const name = split(argument, action, ' ');
1439                         InsetBase::Code const inset_code =
1440                                 InsetBase::translate(name);
1441
1442                         LCursor & cur = view()->cursor();
1443                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1444
1445                         InsetBase & inset = owner->buffer()->inset();
1446                         InsetIterator it  = inset_iterator_begin(inset);
1447                         InsetIterator const end = inset_iterator_end(inset);
1448                         for (; it != end; ++it) {
1449                                 if (inset_code == InsetBase::NO_CODE
1450                                     || inset_code == it->lyxCode()) {
1451                                         LCursor tmpcur = cur;
1452                                         tmpcur.pushLeft(*it);
1453                                         it->dispatch(tmpcur, fr);
1454                                 }
1455                         }
1456                         updateforce = true;
1457                         break;
1458                 }
1459
1460                 case LFUN_BUFFER_LANGUAGE: {
1461                         Buffer & buffer = *owner->buffer();
1462                         Language const * oldL = buffer.params().language;
1463                         Language const * newL = languages.getLanguage(argument);
1464                         if (!newL || oldL == newL)
1465                                 break;
1466
1467                         if (oldL->rightToLeft() == newL->rightToLeft()
1468                             && !buffer.isMultiLingual())
1469                                 buffer.changeLanguage(oldL, newL);
1470                         else
1471                                 buffer.updateDocLang(newL);
1472                         break;
1473                 }
1474
1475                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1476                         string const fname =
1477                                 addName(addPath(package().user_support(), "templates/"),
1478                                         "defaults.lyx");
1479                         Buffer defaults(fname);
1480
1481                         istringstream ss(argument);
1482                         LyXLex lex(0,0);
1483                         lex.setStream(ss);
1484                         int const unknown_tokens = defaults.readHeader(lex);
1485
1486                         if (unknown_tokens != 0) {
1487                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1488                                        << unknown_tokens << " unknown token"
1489                                        << (unknown_tokens == 1 ? "" : "s")
1490                                        << endl;
1491                         }
1492
1493                         if (defaults.writeFile(defaults.fileName()))
1494                                 // FIXME Should use bformat
1495                                 setMessage(_("Document defaults saved in ")
1496                                            + makeDisplayPath(fname));
1497                         else
1498                                 setErrorMessage(_("Unable to save document defaults"));
1499                         break;
1500                 }
1501
1502                 case LFUN_BUFFER_PARAMS_APPLY: {
1503                         biblio::CiteEngine const engine =
1504                                 owner->buffer()->params().cite_engine;
1505
1506                         istringstream ss(argument);
1507                         LyXLex lex(0,0);
1508                         lex.setStream(ss);
1509                         int const unknown_tokens =
1510                                 owner->buffer()->readHeader(lex);
1511
1512                         if (unknown_tokens != 0) {
1513                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1514                                        << unknown_tokens << " unknown token"
1515                                        << (unknown_tokens == 1 ? "" : "s")
1516                                        << endl;
1517                         }
1518                         if (engine == owner->buffer()->params().cite_engine)
1519                                 break;
1520
1521                         LCursor & cur = view()->cursor();
1522                         FuncRequest fr(LFUN_INSET_REFRESH);
1523
1524                         InsetBase & inset = owner->buffer()->inset();
1525                         InsetIterator it  = inset_iterator_begin(inset);
1526                         InsetIterator const end = inset_iterator_end(inset);
1527                         for (; it != end; ++it)
1528                                 if (it->lyxCode() == InsetBase::CITE_CODE)
1529                                         it->dispatch(cur, fr);
1530                         break;
1531                 }
1532
1533                 case LFUN_TEXTCLASS_APPLY: {
1534                         Buffer * buffer = owner->buffer();
1535
1536                         lyx::textclass_type const old_class =
1537                                 buffer->params().textclass;
1538
1539                         loadTextclass(argument);
1540
1541                         std::pair<bool, lyx::textclass_type> const tc_pair =
1542                                 textclasslist.numberOfClass(argument);
1543
1544                         if (!tc_pair.first)
1545                                 break;
1546
1547                         lyx::textclass_type const new_class = tc_pair.second;
1548                         if (old_class == new_class)
1549                                 // nothing to do
1550                                 break;
1551
1552                         owner->message(_("Converting document to new document class..."));
1553                         recordUndoFullDocument(view());
1554                         buffer->params().textclass = new_class;
1555                         StableDocIterator backcur(view()->cursor());
1556                         ErrorList & el = buffer->errorList("Class Switch");
1557                         lyx::cap::switchBetweenClasses(
1558                                 old_class, new_class,
1559                                 static_cast<InsetText &>(buffer->inset()), el);
1560
1561                         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1562
1563                         buffer->errors("Class Switch");
1564                         updateLabels(*buffer);
1565                         updateforce = true;
1566                         break;
1567                 }
1568
1569                 case LFUN_TEXTCLASS_LOAD:
1570                         loadTextclass(argument);
1571                         break;
1572
1573                 case LFUN_LYXRC_APPLY: {
1574                         LyXRC const lyxrc_orig = lyxrc;
1575
1576                         istringstream ss(argument);
1577                         bool const success = lyxrc.read(ss) == 0;
1578
1579                         if (!success) {
1580                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1581                                        << "Unable to read lyxrc data"
1582                                        << endl;
1583                                 break;
1584                         }
1585
1586                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1587                         break;
1588                 }
1589
1590                 default: {
1591                         view()->cursor().dispatch(cmd);
1592                         updateforce |= view()->cursor().result().update();
1593                         if (!view()->cursor().result().dispatched())
1594                                 updateforce |= view()->dispatch(cmd);
1595                         break;
1596                 }
1597                 }
1598
1599                 if (view()->buffer()) {
1600                         // Redraw screen unless explicitly told otherwise.
1601                         // This also initializes the position cache for all insets
1602                         // in (at least partially) visible top-level paragraphs.
1603                         if (updateforce)
1604                                 view()->update(Update::FitCursor | Update::Force);
1605                         else if (update)
1606                                 view()->update(Update::FitCursor);
1607
1608                         owner->redrawWorkArea();
1609
1610                         // if we executed a mutating lfun, mark the buffer as dirty
1611                         if (flag.enabled()
1612                             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1613                             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1614                                 view()->buffer()->markDirty();
1615
1616                         if (view()->cursor().inTexted()) {
1617                                 owner->updateLayoutChoice();
1618                         }
1619                 }
1620         }
1621         if (!quitting)
1622                 // FIXME UNICODE: _() does not support anything but ascii.
1623                 // Do we need a lyx::to_ascii() method?
1624                 sendDispatchMessage(getMessage(), cmd);
1625 }
1626
1627
1628 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1629 {
1630         /* When an action did not originate from the UI/kbd, it makes
1631          * sense to avoid updating the GUI. It turns out that this
1632          * fixes bug 1941, for reasons that are described here:
1633          * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1634          */
1635         if (cmd.origin != FuncRequest::INTERNAL) {
1636                 owner->updateMenubar();
1637                 owner->updateToolbars();
1638         }
1639
1640         const bool verbose = (cmd.origin == FuncRequest::UI
1641                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1642
1643         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1644                 lyxerr[Debug::ACTION] << "dispatch msg is " << lyx::to_utf8(msg) << endl;
1645                 if (!msg.empty())
1646                         owner->message(msg);
1647                 return;
1648         }
1649
1650         docstring dispatch_msg = msg;
1651         if (!dispatch_msg.empty())
1652                 dispatch_msg += ' ';
1653
1654         string comname = lyxaction.getActionName(cmd.action);
1655
1656         bool argsadded = false;
1657
1658         if (!cmd.argument().empty()) {
1659                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1660                         comname += ' ' + lyx::to_utf8(cmd.argument());
1661                         argsadded = true;
1662                 }
1663         }
1664
1665         string const shortcuts = toplevel_keymap->printbindings(cmd);
1666
1667         if (!shortcuts.empty())
1668                 comname += ": " + shortcuts;
1669         else if (!argsadded && !cmd.argument().empty())
1670                 comname += ' ' + lyx::to_utf8(cmd.argument());
1671
1672         if (!comname.empty()) {
1673                 comname = rtrim(comname);
1674                 dispatch_msg += lyx::from_utf8('(' + rtrim(comname) + ')');
1675         }
1676
1677         lyxerr[Debug::ACTION] << "verbose dispatch msg "
1678                 << lyx::to_utf8(dispatch_msg) << endl;
1679         if (!dispatch_msg.empty())
1680                 owner->message(dispatch_msg);
1681 }
1682
1683
1684 void LyXFunc::setupLocalKeymap()
1685 {
1686         keyseq.stdmap = toplevel_keymap.get();
1687         keyseq.curmap = toplevel_keymap.get();
1688         cancel_meta_seq.stdmap = toplevel_keymap.get();
1689         cancel_meta_seq.curmap = toplevel_keymap.get();
1690 }
1691
1692
1693 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1694 {
1695         string initpath = lyxrc.document_path;
1696         string filename(name);
1697
1698         if (view()->buffer()) {
1699                 string const trypath = owner->buffer()->filePath();
1700                 // If directory is writeable, use this as default.
1701                 if (isDirWriteable(trypath))
1702                         initpath = trypath;
1703         }
1704
1705         static int newfile_number;
1706
1707         if (filename.empty()) {
1708                 filename = addName(lyxrc.document_path,
1709                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1710                 while (theApp->bufferList().exists(filename) || fs::is_readable(filename)) {
1711                         ++newfile_number;
1712                         filename = addName(lyxrc.document_path,
1713                                            "newfile" +  convert<string>(newfile_number) +
1714                                     ".lyx");
1715                 }
1716         }
1717
1718         // The template stuff
1719         string templname;
1720         if (fromTemplate) {
1721                 FileDialog fileDlg(lyx::to_utf8(_("Select template file")),
1722                         LFUN_SELECT_FILE_SYNC,
1723                         make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1724                                   string(lyxrc.document_path)),
1725                         make_pair(string(lyx::to_utf8(_("Templates|#T#t"))),
1726                                   string(lyxrc.template_path)));
1727
1728                 FileDialog::Result result =
1729                         fileDlg.open(lyxrc.template_path,
1730                                      FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1731                                      string());
1732
1733                 if (result.first == FileDialog::Later)
1734                         return;
1735                 if (result.second.empty())
1736                         return;
1737                 templname = result.second;
1738         }
1739
1740         Buffer * const b = newFile(filename, templname, !name.empty());
1741         if (b)
1742                 owner->setBuffer(b);
1743 }
1744
1745
1746 void LyXFunc::open(string const & fname)
1747 {
1748         string initpath = lyxrc.document_path;
1749
1750         if (view()->buffer()) {
1751                 string const trypath = owner->buffer()->filePath();
1752                 // If directory is writeable, use this as default.
1753                 if (isDirWriteable(trypath))
1754                         initpath = trypath;
1755         }
1756
1757         string filename;
1758
1759         if (fname.empty()) {
1760                 FileDialog fileDlg(lyx::to_utf8(_("Select document to open")),
1761                         LFUN_FILE_OPEN,
1762                         make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1763                                   string(lyxrc.document_path)),
1764                         make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1765                                   string(addPath(package().system_support(), "examples"))));
1766
1767                 FileDialog::Result result =
1768                         fileDlg.open(initpath,
1769                                      FileFilterList(lyx::to_utf8(_("LyX Documents (*.lyx)"))),
1770                                      string());
1771
1772                 if (result.first == FileDialog::Later)
1773                         return;
1774
1775                 filename = result.second;
1776
1777                 // check selected filename
1778                 if (filename.empty()) {
1779                         owner->message(_("Canceled."));
1780                         return;
1781                 }
1782         } else
1783                 filename = fname;
1784
1785         // get absolute path of file and add ".lyx" to the filename if
1786         // necessary
1787         string const fullpath = fileSearch(string(), filename, "lyx");
1788         if (!fullpath.empty()) {
1789                 filename = fullpath;
1790         }
1791
1792         docstring const disp_fn = makeDisplayPath(filename);
1793
1794         // if the file doesn't exist, let the user create one
1795         if (!fs::exists(filename)) {
1796                 // the user specifically chose this name. Believe him.
1797                 Buffer * const b = newFile(filename, string(), true);
1798                 if (b)
1799                         owner->setBuffer(b);
1800                 return;
1801         }
1802
1803         owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1804
1805         docstring str2;
1806         if (owner->loadLyXFile(filename)) {
1807                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1808         } else {
1809                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1810         }
1811         owner->message(str2);
1812 }
1813
1814
1815 void LyXFunc::doImport(string const & argument)
1816 {
1817         string format;
1818         string filename = split(argument, format, ' ');
1819
1820         lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1821                             << " file: " << filename << endl;
1822
1823         // need user interaction
1824         if (filename.empty()) {
1825                 string initpath = lyxrc.document_path;
1826
1827                 if (view()->buffer()) {
1828                         string const trypath = owner->buffer()->filePath();
1829                         // If directory is writeable, use this as default.
1830                         if (isDirWriteable(trypath))
1831                                 initpath = trypath;
1832                 }
1833
1834                 docstring const text = bformat(_("Select %1$s file to import"),
1835                         formats.prettyName(format));
1836
1837                 FileDialog fileDlg(lyx::to_utf8(text),
1838                         LFUN_BUFFER_IMPORT,
1839                         make_pair(string(lyx::to_utf8(_("Documents|#o#O"))),
1840                                   string(lyxrc.document_path)),
1841                         make_pair(string(lyx::to_utf8(_("Examples|#E#e"))),
1842                                   string(addPath(package().system_support(), "examples"))));
1843
1844                 string const filter = lyx::to_utf8(formats.prettyName(format))
1845                         + " (*." + formats.extension(format) + ')';
1846
1847                 FileDialog::Result result =
1848                         fileDlg.open(initpath,
1849                                      FileFilterList(filter),
1850                                      string());
1851
1852                 if (result.first == FileDialog::Later)
1853                         return;
1854
1855                 filename = result.second;
1856
1857                 // check selected filename
1858                 if (filename.empty())
1859                         owner->message(_("Canceled."));
1860         }
1861
1862         if (filename.empty())
1863                 return;
1864
1865         // get absolute path of file
1866         filename = makeAbsPath(filename);
1867
1868         string const lyxfile = changeExtension(filename, ".lyx");
1869
1870         // Check if the document already is open
1871         if (lyx_gui::use_gui && theApp->bufferList().exists(lyxfile)) {
1872                 if (!theApp->bufferList().close(theApp->bufferList().getBuffer(lyxfile), true)) {
1873                         owner->message(_("Canceled."));
1874                         return;
1875                 }
1876         }
1877
1878         // if the file exists already, and we didn't do
1879         // -i lyx thefile.lyx, warn
1880         if (fs::exists(lyxfile) && filename != lyxfile) {
1881                 docstring const file = makeDisplayPath(lyxfile, 30);
1882
1883                 docstring text = bformat(_("The document %1$s already exists.\n\n"
1884                                                      "Do you want to over-write that document?"), file);
1885                 int const ret = Alert::prompt(_("Over-write document?"),
1886                         text, 0, 1, _("&Over-write"), _("&Cancel"));
1887
1888                 if (ret == 1) {
1889                         owner->message(_("Canceled."));
1890                         return;
1891                 }
1892         }
1893
1894         ErrorList errorList;
1895         Importer::Import(owner, filename, format, errorList);
1896         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1897 }
1898
1899
1900 void LyXFunc::closeBuffer()
1901 {
1902         // save current cursor position
1903         LyX::ref().session().saveFilePosition(owner->buffer()->fileName(),
1904                 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1905         if (theApp->bufferList().close(owner->buffer(), true) && !quitting) {
1906                 if (theApp->bufferList().empty()) {
1907                         // need this otherwise SEGV may occur while
1908                         // trying to set variables that don't exist
1909                         // since there's no current buffer
1910                         owner->getDialogs().hideBufferDependent();
1911                 } else {
1912                         owner->setBuffer(theApp->bufferList().first());
1913                 }
1914         }
1915 }
1916
1917
1918 // Each "owner" should have it's own message method. lyxview and
1919 // the minibuffer would use the minibuffer, but lyxserver would
1920 // send an ERROR signal to its client.  Alejandro 970603
1921 // This function is bit problematic when it comes to NLS, to make the
1922 // lyx servers client be language indepenent we must not translate
1923 // strings sent to this func.
1924 void LyXFunc::setErrorMessage(docstring const & m) const
1925 {
1926         dispatch_buffer = m;
1927         errorstat = true;
1928 }
1929
1930
1931 void LyXFunc::setMessage(docstring const & m) const
1932 {
1933         dispatch_buffer = m;
1934 }
1935
1936
1937 string const LyXFunc::viewStatusMessage()
1938 {
1939         // When meta-fake key is pressed, show the key sequence so far + "M-".
1940         if (wasMetaKey())
1941                 return keyseq.print() + "M-";
1942
1943         // Else, when a non-complete key sequence is pressed,
1944         // show the available options.
1945         if (keyseq.length() > 0 && !keyseq.deleted())
1946                 return keyseq.printOptions();
1947
1948         if (!view()->buffer())
1949                 return lyx::to_utf8(_("Welcome to LyX!"));
1950
1951         return view()->cursor().currentState();
1952 }
1953
1954
1955 BufferView * LyXFunc::view() const
1956 {
1957         BOOST_ASSERT(owner);
1958         return owner->view();
1959 }
1960
1961
1962 bool LyXFunc::wasMetaKey() const
1963 {
1964         return (meta_fake_bit != key_modifier::none);
1965 }
1966
1967
1968 namespace {
1969
1970 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1971 {
1972         // Why the switch you might ask. It is a trick to ensure that all
1973         // the elements in the LyXRCTags enum is handled. As you can see
1974         // there are no breaks at all. So it is just a huge fall-through.
1975         // The nice thing is that we will get a warning from the compiler
1976         // if we forget an element.
1977         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1978         switch (tag) {
1979         case LyXRC::RC_ACCEPT_COMPOUND:
1980         case LyXRC::RC_ALT_LANG:
1981         case LyXRC::RC_ASCIIROFF_COMMAND:
1982         case LyXRC::RC_ASCII_LINELEN:
1983         case LyXRC::RC_AUTOREGIONDELETE:
1984         case LyXRC::RC_AUTORESET_OPTIONS:
1985         case LyXRC::RC_AUTOSAVE:
1986         case LyXRC::RC_AUTO_NUMBER:
1987         case LyXRC::RC_BACKUPDIR_PATH:
1988         case LyXRC::RC_BIBTEX_COMMAND:
1989         case LyXRC::RC_BINDFILE:
1990         case LyXRC::RC_CHECKLASTFILES:
1991         case LyXRC::RC_USELASTFILEPOS:
1992         case LyXRC::RC_LOADSESSION:
1993         case LyXRC::RC_CHKTEX_COMMAND:
1994         case LyXRC::RC_CONVERTER:
1995         case LyXRC::RC_COPIER:
1996         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1997         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1998         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1999         case LyXRC::RC_DATE_INSERT_FORMAT:
2000         case LyXRC::RC_DEFAULT_LANGUAGE:
2001         case LyXRC::RC_DEFAULT_PAPERSIZE:
2002         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2003         case LyXRC::RC_DISPLAY_GRAPHICS:
2004         case LyXRC::RC_DOCUMENTPATH:
2005                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2006                         if (fs::exists(lyxrc_new.document_path) &&
2007                             fs::is_directory(lyxrc_new.document_path)) {
2008                                 using lyx::support::package;
2009                                 package().document_dir() = lyxrc.document_path;
2010                         }
2011                 }
2012         case LyXRC::RC_ESC_CHARS:
2013         case LyXRC::RC_FONT_ENCODING:
2014         case LyXRC::RC_FORMAT:
2015         case LyXRC::RC_INDEX_COMMAND:
2016         case LyXRC::RC_INPUT:
2017         case LyXRC::RC_KBMAP:
2018         case LyXRC::RC_KBMAP_PRIMARY:
2019         case LyXRC::RC_KBMAP_SECONDARY:
2020         case LyXRC::RC_LABEL_INIT_LENGTH:
2021         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2022         case LyXRC::RC_LANGUAGE_AUTO_END:
2023         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2024         case LyXRC::RC_LANGUAGE_COMMAND_END:
2025         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2026         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2027         case LyXRC::RC_LANGUAGE_PACKAGE:
2028         case LyXRC::RC_LANGUAGE_USE_BABEL:
2029         case LyXRC::RC_MAKE_BACKUP:
2030         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2031         case LyXRC::RC_NUMLASTFILES:
2032         case LyXRC::RC_PATH_PREFIX:
2033                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2034                         using lyx::support::prependEnvPath;
2035                         prependEnvPath("PATH", lyxrc.path_prefix);
2036                 }
2037         case LyXRC::RC_PERS_DICT:
2038         case LyXRC::RC_POPUP_BOLD_FONT:
2039         case LyXRC::RC_POPUP_FONT_ENCODING:
2040         case LyXRC::RC_POPUP_NORMAL_FONT:
2041         case LyXRC::RC_PREVIEW:
2042         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2043         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2044         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2045         case LyXRC::RC_PRINTCOPIESFLAG:
2046         case LyXRC::RC_PRINTER:
2047         case LyXRC::RC_PRINTEVENPAGEFLAG:
2048         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2049         case LyXRC::RC_PRINTFILEEXTENSION:
2050         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2051         case LyXRC::RC_PRINTODDPAGEFLAG:
2052         case LyXRC::RC_PRINTPAGERANGEFLAG:
2053         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2054         case LyXRC::RC_PRINTPAPERFLAG:
2055         case LyXRC::RC_PRINTREVERSEFLAG:
2056         case LyXRC::RC_PRINTSPOOL_COMMAND:
2057         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2058         case LyXRC::RC_PRINTTOFILE:
2059         case LyXRC::RC_PRINTTOPRINTER:
2060         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2061         case LyXRC::RC_PRINT_COMMAND:
2062         case LyXRC::RC_RTL_SUPPORT:
2063         case LyXRC::RC_SCREEN_DPI:
2064         case LyXRC::RC_SCREEN_FONT_ENCODING:
2065         case LyXRC::RC_SCREEN_FONT_ROMAN:
2066         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2067         case LyXRC::RC_SCREEN_FONT_SANS:
2068         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2069         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2070         case LyXRC::RC_SCREEN_FONT_SIZES:
2071         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2072         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2073         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2074         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2075         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2076         case LyXRC::RC_SCREEN_ZOOM:
2077         case LyXRC::RC_SERVERPIPE:
2078         case LyXRC::RC_SET_COLOR:
2079         case LyXRC::RC_SHOW_BANNER:
2080         case LyXRC::RC_SPELL_COMMAND:
2081         case LyXRC::RC_TEMPDIRPATH:
2082         case LyXRC::RC_TEMPLATEPATH:
2083         case LyXRC::RC_TEX_ALLOWS_SPACES:
2084         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2085                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2086                         namespace os = lyx::support::os;
2087                         os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2088                 }
2089         case LyXRC::RC_UIFILE:
2090         case LyXRC::RC_USER_EMAIL:
2091         case LyXRC::RC_USER_NAME:
2092         case LyXRC::RC_USETEMPDIR:
2093         case LyXRC::RC_USE_ALT_LANG:
2094         case LyXRC::RC_USE_ESC_CHARS:
2095         case LyXRC::RC_USE_INP_ENC:
2096         case LyXRC::RC_USE_PERS_DICT:
2097         case LyXRC::RC_USE_SPELL_LIB:
2098         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2099         case LyXRC::RC_VIEWER:
2100         case LyXRC::RC_LAST:
2101                 break;
2102         }
2103 }
2104
2105 } // namespace anon