]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
Move LFUN_KEYMAP_* to BufferView.
[lyx.git] / src / LyXFunc.cpp
1 /**
2  * \file LyXFunc.cpp
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 "LayoutFile.h"
25 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "Buffer.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
31 #include "CmdDef.h"
32 #include "Color.h"
33 #include "Converter.h"
34 #include "Cursor.h"
35 #include "CutAndPaste.h"
36 #include "DispatchResult.h"
37 #include "Encoding.h"
38 #include "ErrorList.h"
39 #include "Format.h"
40 #include "FuncRequest.h"
41 #include "FuncStatus.h"
42 #include "InsetIterator.h"
43 #include "Intl.h"
44 #include "KeyMap.h"
45 #include "Language.h"
46 #include "Lexer.h"
47 #include "LyXAction.h"
48 #include "lyxfind.h"
49 #include "LyX.h"
50 #include "LyXRC.h"
51 #include "LyXVC.h"
52 #include "Paragraph.h"
53 #include "ParagraphParameters.h"
54 #include "ParIterator.h"
55 #include "Row.h"
56 #include "Server.h"
57 #include "Session.h"
58 #include "SpellChecker.h"
59
60 #include "insets/InsetBox.h"
61 #include "insets/InsetBranch.h"
62 #include "insets/InsetCommand.h"
63 #include "insets/InsetERT.h"
64 #include "insets/InsetExternal.h"
65 #include "insets/InsetFloat.h"
66 #include "insets/InsetGraphics.h"
67 #include "insets/InsetInclude.h"
68 #include "insets/InsetListings.h"
69 #include "insets/InsetNote.h"
70 #include "insets/InsetPhantom.h"
71 #include "insets/InsetSpace.h"
72 #include "insets/InsetTabular.h"
73 #include "insets/InsetVSpace.h"
74 #include "insets/InsetWrap.h"
75
76 #include "frontends/alert.h"
77 #include "frontends/Application.h"
78 #include "frontends/KeySymbol.h"
79 #include "frontends/LyXView.h"
80 #include "frontends/Selection.h"
81
82 #include "support/debug.h"
83 #include "support/environment.h"
84 #include "support/FileName.h"
85 #include "support/filetools.h"
86 #include "support/gettext.h"
87 #include "support/lstrings.h"
88 #include "support/Path.h"
89 #include "support/Package.h"
90 #include "support/Systemcall.h"
91 #include "support/convert.h"
92 #include "support/os.h"
93
94 #include <sstream>
95 #include <vector>
96
97 using namespace std;
98 using namespace lyx::support;
99
100 namespace lyx {
101
102 using frontend::LyXView;
103
104 namespace Alert = frontend::Alert;
105
106 namespace {
107
108
109 // This function runs "configure" and then rereads lyx.defaults to
110 // reconfigure the automatic settings.
111 void reconfigure(LyXView * lv, string const & option)
112 {
113         // emit message signal.
114         if (lv)
115                 lv->message(_("Running configure..."));
116
117         // Run configure in user lyx directory
118         PathChanger p(package().user_support());
119         string configure_command = package().configure_command();
120         configure_command += option;
121         Systemcall one;
122         int ret = one.startscript(Systemcall::Wait, configure_command);
123         p.pop();
124         // emit message signal.
125         if (lv)
126                 lv->message(_("Reloading configuration..."));
127         lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
128         // Re-read packages.lst
129         LaTeXFeatures::getAvailable();
130
131         if (ret)
132                 Alert::information(_("System reconfiguration failed"),
133                            _("The system reconfiguration has failed.\n"
134                                   "Default textclass is used but LyX may "
135                                   "not be able to work properly.\n"
136                                   "Please reconfigure again if needed."));
137         else
138
139                 Alert::information(_("System reconfigured"),
140                            _("The system has been reconfigured.\n"
141                              "You need to restart LyX to make use of any\n"
142                              "updated document class specifications."));
143 }
144
145
146 bool getLocalStatus(Cursor cursor, FuncRequest const & cmd, FuncStatus & status)
147 {
148         // Try to fix cursor in case it is broken.
149         cursor.fixIfBroken();
150
151         // This is, of course, a mess. Better create a new doc iterator and use
152         // this in Inset::getStatus. This might require an additional
153         // BufferView * arg, though (which should be avoided)
154         //Cursor safe = *this;
155         bool res = false;
156         for ( ; cursor.depth(); cursor.pop()) {
157                 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
158                 LASSERT(cursor.idx() <= cursor.lastidx(), /**/);
159                 LASSERT(cursor.pit() <= cursor.lastpit(), /**/);
160                 LASSERT(cursor.pos() <= cursor.lastpos(), /**/);
161
162                 // The inset's getStatus() will return 'true' if it made
163                 // a definitive decision on whether it want to handle the
164                 // request or not. The result of this decision is put into
165                 // the 'status' parameter.
166                 if (cursor.inset().getStatus(cursor, cmd, status)) {
167                         res = true;
168                         break;
169                 }
170         }
171         return res;
172 }
173
174
175 /** Return the change status at cursor position, taking in account the
176  * status at each level of the document iterator (a table in a deleted
177  * footnote is deleted).
178  * When \param outer is true, the top slice is not looked at.
179  */
180 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
181 {
182         size_t const depth = dit.depth() - (outer ? 1 : 0);
183
184         for (size_t i = 0 ; i < depth ; ++i) {
185                 CursorSlice const & slice = dit[i];
186                 if (!slice.inset().inMathed()
187                     && slice.pos() < slice.paragraph().size()) {
188                         Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
189                         if (ch != Change::UNCHANGED)
190                                 return ch;
191                 }
192         }
193         return Change::UNCHANGED;
194 }
195
196 }
197
198
199 LyXFunc::LyXFunc()
200         : lyx_view_(0), encoded_last_key(0), meta_fake_bit(NoModifier)
201 {
202 }
203
204
205 void LyXFunc::initKeySequences(KeyMap * kb)
206 {
207         keyseq = KeySequence(kb, kb);
208         cancel_meta_seq = KeySequence(kb, kb);
209 }
210
211
212 void LyXFunc::setLyXView(LyXView * lv)
213 {
214         if (lyx_view_ && lyx_view_->currentBufferView() && lyx_view_ != lv)
215                 // save current selection to the selection buffer to allow
216                 // middle-button paste in another window
217                 cap::saveSelection(lyx_view_->currentBufferView()->cursor());
218         lyx_view_ = lv;
219 }
220
221
222 void LyXFunc::handleKeyFunc(FuncCode action)
223 {
224         char_type c = encoded_last_key;
225
226         if (keyseq.length())
227                 c = 0;
228
229         LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
230         BufferView * bv = lyx_view_->currentBufferView();
231         bv->getIntl().getTransManager().deadkey(
232                 c, get_accent(action).accent, bv->cursor().innerText(),
233                 bv->cursor());
234         // Need to clear, in case the minibuffer calls these
235         // actions
236         keyseq.clear();
237         // copied verbatim from do_accent_char
238         bv->cursor().resetAnchor();
239         bv->processUpdateFlags(Update::FitCursor);
240 }
241
242 //FIXME: bookmark handling is a frontend issue. This code should be transferred
243 // to GuiView and be GuiView and be window dependent.
244 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
245 {
246         LASSERT(lyx_view_, /**/);
247         if (!theSession().bookmarks().isValid(idx))
248                 return;
249         BookmarksSection::Bookmark const & bm = theSession().bookmarks().bookmark(idx);
250         LASSERT(!bm.filename.empty(), /**/);
251         string const file = bm.filename.absFilename();
252         // if the file is not opened, open it.
253         if (!theBufferList().exists(bm.filename)) {
254                 if (openFile)
255                         dispatch(FuncRequest(LFUN_FILE_OPEN, file));
256                 else
257                         return;
258         }
259         // open may fail, so we need to test it again
260         if (!theBufferList().exists(bm.filename))
261                 return;
262
263         // bm can be changed when saving
264         BookmarksSection::Bookmark tmp = bm;
265
266         // Special case idx == 0 used for back-from-back jump navigation
267         if (idx == 0)
268                 dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
269
270         // if the current buffer is not that one, switch to it.
271         if (!lyx_view_->documentBufferView()
272                 || lyx_view_->documentBufferView()->buffer().fileName() != tmp.filename) {
273                 if (!switchToBuffer)
274                         return;
275                 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
276         }
277
278         // moveToPosition try paragraph id first and then paragraph (pit, pos).
279         if (!lyx_view_->documentBufferView()->moveToPosition(
280                 tmp.bottom_pit, tmp.bottom_pos, tmp.top_id, tmp.top_pos))
281                 return;
282
283         // bm changed
284         if (idx == 0)
285                 return;
286
287         // Cursor jump succeeded!
288         Cursor const & cur = lyx_view_->documentBufferView()->cursor();
289         pit_type new_pit = cur.pit();
290         pos_type new_pos = cur.pos();
291         int new_id = cur.paragraph().id();
292
293         // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
294         // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
295         if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos 
296                 || bm.top_id != new_id) {
297                 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
298                         new_pit, new_pos, new_id);
299         }
300 }
301
302
303 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
304 {
305         LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
306
307         // Do nothing if we have nothing (JMarc)
308         if (!keysym.isOK()) {
309                 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
310                 lyx_view_->restartCursor();
311                 return;
312         }
313
314         if (keysym.isModifier()) {
315                 LYXERR(Debug::KEY, "isModifier true");
316                 if (lyx_view_)
317                         lyx_view_->restartCursor();
318                 return;
319         }
320
321         //Encoding const * encoding = lyx_view_->documentBufferView()->cursor().getEncoding();
322         //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
323         // FIXME: encoded_last_key shadows the member variable of the same
324         // name. Is that intended?
325         char_type encoded_last_key = keysym.getUCSEncoded();
326
327         // Do a one-deep top-level lookup for
328         // cancel and meta-fake keys. RVDK_PATCH_5
329         cancel_meta_seq.reset();
330
331         FuncRequest func = cancel_meta_seq.addkey(keysym, state);
332         LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
333
334         // When not cancel or meta-fake, do the normal lookup.
335         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
336         // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
337         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
338                 // remove Caps Lock and Mod2 as a modifiers
339                 func = keyseq.addkey(keysym, (state | meta_fake_bit));
340                 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
341         }
342
343         // Dont remove this unless you know what you are doing.
344         meta_fake_bit = NoModifier;
345
346         // Can this happen now ?
347         if (func.action == LFUN_NOACTION)
348                 func = FuncRequest(LFUN_COMMAND_PREFIX);
349
350         LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
351                 << keyseq.print(KeySequence::Portable) << ']');
352
353         // already here we know if it any point in going further
354         // why not return already here if action == -1 and
355         // num_bytes == 0? (Lgb)
356
357         if (keyseq.length() > 1)
358                 lyx_view_->message(keyseq.print(KeySequence::ForGui));
359
360
361         // Maybe user can only reach the key via holding down shift.
362         // Let's see. But only if shift is the only modifier
363         if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
364                 LYXERR(Debug::KEY, "Trying without shift");
365                 func = keyseq.addkey(keysym, NoModifier);
366                 LYXERR(Debug::KEY, "Action now " << func.action);
367         }
368
369         if (func.action == LFUN_UNKNOWN_ACTION) {
370                 // Hmm, we didn't match any of the keysequences. See
371                 // if it's normal insertable text not already covered
372                 // by a binding
373                 if (keysym.isText() && keyseq.length() == 1) {
374                         LYXERR(Debug::KEY, "isText() is true, inserting.");
375                         func = FuncRequest(LFUN_SELF_INSERT,
376                                            FuncRequest::KEYBOARD);
377                 } else {
378                         LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
379                         lyx_view_->message(_("Unknown function."));
380                         lyx_view_->restartCursor();
381                         return;
382                 }
383         }
384
385         if (func.action == LFUN_SELF_INSERT) {
386                 if (encoded_last_key != 0) {
387                         docstring const arg(1, encoded_last_key);
388                         dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
389                                              FuncRequest::KEYBOARD));
390                         LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
391                 }
392         } else {
393                 dispatch(func);
394                 if (!lyx_view_)
395                         return;
396         }
397 }
398
399
400 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
401 {
402         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
403         FuncStatus flag;
404
405         /* In LyX/Mac, when a dialog is open, the menus of the
406            application can still be accessed without giving focus to
407            the main window. In this case, we want to disable the menu
408            entries that are buffer or view-related.
409
410            If this code is moved somewhere else (like in
411            GuiView::getStatus), then several functions will not be
412            handled correctly.
413         */
414         frontend::LyXView * lv = 0;
415         Buffer * buf = 0;
416         if (lyx_view_ 
417             && (cmd.origin != FuncRequest::MENU || lyx_view_->hasFocus())) {
418                 lv = lyx_view_;
419                 if (lyx_view_->documentBufferView())
420                         buf = &lyx_view_->documentBufferView()->buffer();
421         }
422
423         if (cmd.action == LFUN_NOACTION) {
424                 flag.message(from_utf8(N_("Nothing to do")));
425                 flag.setEnabled(false);
426                 return flag;
427         }
428
429         switch (cmd.action) {
430         case LFUN_UNKNOWN_ACTION:
431                 flag.unknown(true);
432                 flag.setEnabled(false);
433                 break;
434
435         default:
436                 break;
437         }
438
439         if (flag.unknown()) {
440                 flag.message(from_utf8(N_("Unknown action")));
441                 return flag;
442         }
443
444         if (!flag.enabled()) {
445                 if (flag.message().empty())
446                         flag.message(from_utf8(N_("Command disabled")));
447                 return flag;
448         }
449
450         // Check whether we need a buffer
451         if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
452                 // no, exit directly
453                 flag.message(from_utf8(N_("Command not allowed with"
454                                     "out any document open")));
455                 flag.setEnabled(false);
456                 return flag;
457         }
458
459         // I would really like to avoid having this switch and rather try to
460         // encode this in the function itself.
461         // -- And I'd rather let an inset decide which LFUNs it is willing
462         // to handle (Andre')
463         bool enable = true;
464         switch (cmd.action) {
465
466         case LFUN_CITATION_INSERT: {
467                 FuncRequest fr(LFUN_INSET_INSERT, "citation");
468                 enable = getStatus(fr).enabled();
469                 break;
470         }
471         
472         // This could be used for the no-GUI version. The GUI version is handled in
473         // LyXView::getStatus(). See above.
474         /*
475         case LFUN_BUFFER_WRITE:
476         case LFUN_BUFFER_WRITE_AS: {
477                 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
478                 enable = b && (b->isUnnamed() || !b->isClean());
479                 break;
480         }
481         */
482
483         case LFUN_BUFFER_WRITE_ALL: {
484                 // We enable the command only if there are some modified buffers
485                 Buffer * first = theBufferList().first();
486                 enable = false;
487                 if (!first)
488                         break;
489                 Buffer * b = first;
490                 // We cannot use a for loop as the buffer list is a cycle.
491                 do {
492                         if (!b->isClean()) {
493                                 enable = true;
494                                 break;
495                         }
496                         b = theBufferList().next(b);
497                 } while (b != first); 
498                 break;
499         }
500
501         case LFUN_BOOKMARK_GOTO: {
502                 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
503                 enable = theSession().bookmarks().isValid(num);
504                 break;
505         }
506
507         case LFUN_BOOKMARK_CLEAR:
508                 enable = theSession().bookmarks().hasValid();
509                 break;
510
511         // this one is difficult to get right. As a half-baked
512         // solution, we consider only the first action of the sequence
513         case LFUN_COMMAND_SEQUENCE: {
514                 // argument contains ';'-terminated commands
515                 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
516                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
517                 func.origin = cmd.origin;
518                 flag = getStatus(func);
519                 break;
520         }
521
522         // we want to check if at least one of these is enabled
523         case LFUN_COMMAND_ALTERNATIVES: {
524                 // argument contains ';'-terminated commands
525                 string arg = to_utf8(cmd.argument());
526                 while (!arg.empty()) {
527                         string first;
528                         arg = split(arg, first, ';');
529                         FuncRequest func(lyxaction.lookupFunc(first));
530                         func.origin = cmd.origin;
531                         flag = getStatus(func);
532                         // if this one is enabled, the whole thing is
533                         if (flag.enabled())
534                                 break;
535                 }
536                 break;
537         }
538
539         case LFUN_CALL: {
540                 FuncRequest func;
541                 string name = to_utf8(cmd.argument());
542                 if (theTopLevelCmdDef().lock(name, func)) {
543                         func.origin = cmd.origin;
544                         flag = getStatus(func);
545                         theTopLevelCmdDef().release(name);
546                 } else {
547                         // catch recursion or unknown command
548                         // definition. all operations until the
549                         // recursion or unknown command definition
550                         // occurs are performed, so set the state to
551                         // enabled
552                         enable = true;
553                 }
554                 break;
555         }
556
557         case LFUN_COMMAND_PREFIX:
558         case LFUN_CANCEL:
559         case LFUN_META_PREFIX:
560         case LFUN_RECONFIGURE:
561         case LFUN_HELP_OPEN:
562         case LFUN_DROP_LAYOUTS_CHOICE:
563         case LFUN_SERVER_GET_FILENAME:
564         case LFUN_SERVER_NOTIFY:
565         case LFUN_SERVER_GOTO_FILE_ROW:
566         case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
567         case LFUN_REPEAT:
568         case LFUN_PREFERENCES_SAVE:
569         case LFUN_INSET_EDIT:
570         case LFUN_TEXTCLASS_APPLY:
571         case LFUN_TEXTCLASS_LOAD:
572         case LFUN_BUFFER_SAVE_AS_DEFAULT:
573         case LFUN_BUFFER_PARAMS_APPLY:
574         case LFUN_LAYOUT_MODULES_CLEAR:
575         case LFUN_LAYOUT_MODULE_ADD:
576         case LFUN_LAYOUT_RELOAD:
577         case LFUN_LYXRC_APPLY:
578                 // these are handled in our dispatch()
579                 break;
580
581         default:
582                 if (!theApp()) {
583                         enable = false;
584                         break;
585                 }
586                 if (theApp()->getStatus(cmd, flag))
587                         break;
588
589                 // Does the view know something?
590                 if (!lv) {
591                         enable = false;
592                         break;
593                 }
594                 if (lv->getStatus(cmd, flag))
595                         break;
596
597                 BufferView * bv = lv->currentBufferView();
598                 BufferView * doc_bv = lv->documentBufferView();
599                 // If we do not have a BufferView, then other functions are disabled
600                 if (!bv) {
601                         enable = false;
602                         break;
603                 }
604                 // Is this a function that acts on inset at point?
605                 Inset * inset = bv->cursor().nextInset();
606                 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
607                     && inset && inset->getStatus(bv->cursor(), cmd, flag))
608                         break;
609
610                 bool decided = getLocalStatus(bv->cursor(), cmd, flag);
611                 if (!decided)
612                         // try the BufferView
613                         decided = bv->getStatus(cmd, flag);
614                 if (!decided)
615                         // try the Buffer
616                         decided = bv->buffer().getStatus(cmd, flag);
617                 if (!decided && doc_bv)
618                         // try the Document Buffer
619                         decided = doc_bv->buffer().getStatus(cmd, flag);
620         }
621
622         if (!enable)
623                 flag.setEnabled(false);
624
625         // Can we use a readonly buffer?
626         if (buf && buf->isReadonly()
627             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
628             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
629                 flag.message(from_utf8(N_("Document is read-only")));
630                 flag.setEnabled(false);
631         }
632
633         // Are we in a DELETED change-tracking region?
634         if (lyx_view_ && lyx_view_->documentBufferView()
635                 && (lookupChangeType(lyx_view_->documentBufferView()->cursor(), true)
636                     == Change::DELETED)
637             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
638             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
639                 flag.message(from_utf8(N_("This portion of the document is deleted.")));
640                 flag.setEnabled(false);
641         }
642
643         // the default error message if we disable the command
644         if (!flag.enabled() && flag.message().empty())
645                 flag.message(from_utf8(N_("Command disabled")));
646
647         return flag;
648 }
649
650
651 namespace {
652
653 bool loadLayoutFile(string const & name, string const & buf_path)
654 {
655         if (!LayoutFileList::get().haveClass(name)) {
656                 lyxerr << "Document class \"" << name
657                        << "\" does not exist."
658                        << endl;
659                 return false;
660         }
661
662         LayoutFile & tc = LayoutFileList::get()[name];
663         if (!tc.load(buf_path)) {
664                 docstring s = bformat(_("The document class %1$s "
665                                    "could not be loaded."), from_utf8(name));
666                 Alert::error(_("Could not load class"), s);
667                 return false;
668         }
669         return true;
670 }
671
672
673 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
674
675 } //namespace anon
676
677
678 void LyXFunc::dispatch(FuncRequest const & cmd)
679 {
680         string const argument = to_utf8(cmd.argument());
681         FuncCode const action = cmd.action;
682
683         LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
684         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
685
686         // we have not done anything wrong yet.
687         errorstat = false;
688         dispatch_buffer.erase();
689
690         // redraw the screen at the end (first of the two drawing steps).
691         //This is done unless explicitely requested otherwise
692         Update::flags updateFlags = Update::FitCursor;
693
694         FuncStatus const flag = getStatus(cmd);
695         if (!flag.enabled()) {
696                 // We cannot use this function here
697                 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
698                        << lyxaction.getActionName(action)
699                        << " [" << action << "] is disabled at this location");
700                 setErrorMessage(flag.message());
701                 if (lyx_view_)
702                         lyx_view_->restartCursor();
703         } else {
704                 Buffer * buffer = 0;
705                 if (lyx_view_ && lyx_view_->currentBufferView())
706                         buffer = &lyx_view_->currentBufferView()->buffer();
707                 switch (action) {
708
709                 case LFUN_COMMAND_PREFIX:
710                         LASSERT(lyx_view_, /**/);
711                         lyx_view_->message(keyseq.printOptions(true));
712                         break;
713
714                 case LFUN_CANCEL:
715                         LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
716                         keyseq.reset();
717                         meta_fake_bit = NoModifier;
718                         if (buffer)
719                                 // cancel any selection
720                                 dispatch(FuncRequest(LFUN_MARK_OFF));
721                         setMessage(from_ascii(N_("Cancel")));
722                         break;
723
724                 case LFUN_META_PREFIX:
725                         meta_fake_bit = AltModifier;
726                         setMessage(keyseq.print(KeySequence::ForGui));
727                         break;
728
729                 // --- Menus -----------------------------------------------
730                 case LFUN_RECONFIGURE:
731                         // argument is any additional parameter to the configure.py command
732                         reconfigure(lyx_view_, argument);
733                         break;
734
735                 case LFUN_HELP_OPEN: {
736                         if (lyx_view_ == 0)
737                                 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
738                         string const arg = argument;
739                         if (arg.empty()) {
740                                 setErrorMessage(from_utf8(N_("Missing argument")));
741                                 break;
742                         }
743                         FileName fname = i18nLibFileSearch("doc", arg, "lyx");
744                         if (fname.empty()) 
745                                 fname = i18nLibFileSearch("examples", arg, "lyx");
746
747                         if (fname.empty()) {
748                                 lyxerr << "LyX: unable to find documentation file `"
749                                                          << arg << "'. Bad installation?" << endl;
750                                 break;
751                         }
752                         lyx_view_->message(bformat(_("Opening help file %1$s..."),
753                                 makeDisplayPath(fname.absFilename())));
754                         Buffer * buf = lyx_view_->loadDocument(fname, false);
755                         if (buf) {
756                                 buf->updateLabels();
757                                 lyx_view_->setBuffer(buf);
758                                 buf->errors("Parse");
759                         }
760                         updateFlags = Update::None;
761                         break;
762                 }
763
764                 // --- lyxserver commands ----------------------------
765                 case LFUN_SERVER_GET_FILENAME:
766                         LASSERT(lyx_view_ && buffer, /**/);
767                         setMessage(from_utf8(buffer->absFileName()));
768                         LYXERR(Debug::INFO, "FNAME["
769                                 << buffer->absFileName() << ']');
770                         break;
771
772                 case LFUN_SERVER_NOTIFY:
773                         dispatch_buffer = keyseq.print(KeySequence::Portable);
774                         theServer().notifyClient(to_utf8(dispatch_buffer));
775                         break;
776
777                 case LFUN_SERVER_GOTO_FILE_ROW: {
778                         LASSERT(lyx_view_, /**/);
779                         string file_name;
780                         int row;
781                         istringstream is(argument);
782                         is >> file_name >> row;
783                         file_name = os::internal_path(file_name);
784                         Buffer * buf = 0;
785                         bool loaded = false;
786                         string const abstmp = package().temp_dir().absFilename();
787                         string const realtmp = package().temp_dir().realPath();
788                         // We have to use os::path_prefix_is() here, instead of
789                         // simply prefixIs(), because the file name comes from
790                         // an external application and may need case adjustment.
791                         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
792                             || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
793                                 // Needed by inverse dvi search. If it is a file
794                                 // in tmpdir, call the apropriated function.
795                                 // If tmpdir is a symlink, we may have the real
796                                 // path passed back, so we correct for that.
797                                 if (!prefixIs(file_name, abstmp))
798                                         file_name = subst(file_name, realtmp, abstmp);
799                                 buf = theBufferList().getBufferFromTmp(file_name);
800                         } else {
801                                 // Must replace extension of the file to be .lyx
802                                 // and get full path
803                                 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
804                                 // Either change buffer or load the file
805                                 if (theBufferList().exists(s))
806                                         buf = theBufferList().getBuffer(s);
807                                 else if (s.exists()) {
808                                         buf = lyx_view_->loadDocument(s);
809                                         loaded = true;
810                                 } else
811                                         lyx_view_->message(bformat(
812                                                 _("File does not exist: %1$s"),
813                                                 makeDisplayPath(file_name)));
814                         }
815
816                         if (!buf) {
817                                 updateFlags = Update::None;
818                                 break;
819                         }
820
821                         buf->updateLabels();
822                         lyx_view_->setBuffer(buf);
823                         lyx_view_->documentBufferView()->setCursorFromRow(row);
824                         if (loaded)
825                                 buf->errors("Parse");
826                         updateFlags = Update::FitCursor;
827                         break;
828                 }
829
830
831                 case LFUN_DIALOG_SHOW_NEW_INSET: {
832                         LASSERT(lyx_view_, /**/);
833                         string const name = cmd.getArg(0);
834                         InsetCode code = insetCode(name);
835                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
836                         bool insetCodeOK = true;
837                         switch (code) {
838                         case BIBITEM_CODE:
839                         case BIBTEX_CODE:
840                         case INDEX_CODE:
841                         case LABEL_CODE:
842                         case NOMENCL_CODE:
843                         case NOMENCL_PRINT_CODE:
844                         case REF_CODE:
845                         case TOC_CODE:
846                         case HYPERLINK_CODE: {
847                                 InsetCommandParams p(code);
848                                 data = InsetCommand::params2string(name, p);
849                                 break;
850                         }
851                         case INCLUDE_CODE: {
852                                 // data is the include type: one of "include",
853                                 // "input", "verbatiminput" or "verbatiminput*"
854                                 if (data.empty())
855                                         // default type is requested
856                                         data = "include";
857                                 InsetCommandParams p(INCLUDE_CODE, data);
858                                 data = InsetCommand::params2string("include", p);
859                                 break;
860                         }
861                         case BOX_CODE: {
862                                 // \c data == "Boxed" || "Frameless" etc
863                                 InsetBoxParams p(data);
864                                 data = InsetBox::params2string(p);
865                                 break;
866                         }
867                         case BRANCH_CODE: {
868                                 InsetBranchParams p;
869                                 data = InsetBranch::params2string(p);
870                                 break;
871                         }
872                         case CITE_CODE: {
873                                 InsetCommandParams p(CITE_CODE);
874                                 data = InsetCommand::params2string(name, p);
875                                 break;
876                         }
877                         case ERT_CODE: {
878                                 data = InsetERT::params2string(InsetCollapsable::Open);
879                                 break;
880                         }
881                         case EXTERNAL_CODE: {
882                                 InsetExternalParams p;
883                                 data = InsetExternal::params2string(p, *buffer);
884                                 break;
885                         }
886                         case FLOAT_CODE:  {
887                                 InsetFloatParams p;
888                                 data = InsetFloat::params2string(p);
889                                 break;
890                         }
891                         case LISTINGS_CODE: {
892                                 InsetListingsParams p;
893                                 data = InsetListings::params2string(p);
894                                 break;
895                         }
896                         case GRAPHICS_CODE: {
897                                 InsetGraphicsParams p;
898                                 data = InsetGraphics::params2string(p, *buffer);
899                                 break;
900                         }
901                         case NOTE_CODE: {
902                                 InsetNoteParams p;
903                                 data = InsetNote::params2string(p);
904                                 break;
905                         }
906                         case PHANTOM_CODE: {
907                                 InsetPhantomParams p;
908                                 data = InsetPhantom::params2string(p);
909                                 break;
910                         }
911                         case SPACE_CODE: {
912                                 InsetSpaceParams p;
913                                 data = InsetSpace::params2string(p);
914                                 break;
915                         }
916                         case VSPACE_CODE: {
917                                 VSpace space;
918                                 data = InsetVSpace::params2string(space);
919                                 break;
920                         }
921                         case WRAP_CODE: {
922                                 InsetWrapParams p;
923                                 data = InsetWrap::params2string(p);
924                                 break;
925                         }
926                         default:
927                                 lyxerr << "Inset type '" << name << 
928                                         "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" <<  endl;
929                                 insetCodeOK = false;
930                                 break;
931                         } // end switch(code)
932                         if (insetCodeOK)
933                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
934                         break;
935                 }
936
937                 case LFUN_CITATION_INSERT: {
938                         LASSERT(lyx_view_, /**/);
939                         if (!argument.empty()) {
940                                 // we can have one optional argument, delimited by '|'
941                                 // citation-insert <key>|<text_before>
942                                 // this should be enhanced to also support text_after
943                                 // and citation style
944                                 string arg = argument;
945                                 string opt1;
946                                 if (contains(argument, "|")) {
947                                         arg = token(argument, '|', 0);
948                                         opt1 = token(argument, '|', 1);
949                                 }
950                                 InsetCommandParams icp(CITE_CODE);
951                                 icp["key"] = from_utf8(arg);
952                                 if (!opt1.empty())
953                                         icp["before"] = from_utf8(opt1);
954                                 string icstr = InsetCommand::params2string("citation", icp);
955                                 FuncRequest fr(LFUN_INSET_INSERT, icstr);
956                                 dispatch(fr);
957                         } else
958                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
959                         break;
960                 }
961
962                 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
963                         LASSERT(lyx_view_, /**/);
964                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
965                         break;
966
967                 case LFUN_REPEAT: {
968                         // repeat command
969                         string countstr;
970                         string rest = split(argument, countstr, ' ');
971                         istringstream is(countstr);
972                         int count = 0;
973                         is >> count;
974                         //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
975                         for (int i = 0; i < count; ++i)
976                                 dispatch(lyxaction.lookupFunc(rest));
977                         break;
978                 }
979
980                 case LFUN_COMMAND_SEQUENCE: {
981                         // argument contains ';'-terminated commands
982                         string arg = argument;
983                         if (theBufferList().isLoaded(buffer))
984                                 buffer->undo().beginUndoGroup();
985                         while (!arg.empty()) {
986                                 string first;
987                                 arg = split(arg, first, ';');
988                                 FuncRequest func(lyxaction.lookupFunc(first));
989                                 func.origin = cmd.origin;
990                                 dispatch(func);
991                         }
992                         if (theBufferList().isLoaded(buffer))
993                                 buffer->undo().endUndoGroup();
994                         break;
995                 }
996
997                 case LFUN_COMMAND_ALTERNATIVES: {
998                         // argument contains ';'-terminated commands
999                         string arg = argument;
1000                         while (!arg.empty()) {
1001                                 string first;
1002                                 arg = split(arg, first, ';');
1003                                 FuncRequest func(lyxaction.lookupFunc(first));
1004                                 func.origin = cmd.origin;
1005                                 FuncStatus stat = getStatus(func);
1006                                 if (stat.enabled()) {
1007                                         dispatch(func);
1008                                         break;
1009                                 }
1010                         }
1011                         break;
1012                 }
1013
1014                 case LFUN_CALL: {
1015                         FuncRequest func;
1016                         if (theTopLevelCmdDef().lock(argument, func)) {
1017                                 func.origin = cmd.origin;
1018                                 dispatch(func);
1019                                 theTopLevelCmdDef().release(argument);
1020                         } else {
1021                                 if (func.action == LFUN_UNKNOWN_ACTION) {
1022                                         // unknown command definition
1023                                         lyxerr << "Warning: unknown command definition `"
1024                                                    << argument << "'"
1025                                                    << endl;
1026                                 } else {
1027                                         // recursion detected
1028                                         lyxerr << "Warning: Recursion in the command definition `"
1029                                                    << argument << "' detected"
1030                                                    << endl;
1031                                 }
1032                         }
1033                         break;
1034                 }
1035
1036                 case LFUN_PREFERENCES_SAVE: {
1037                         lyxrc.write(makeAbsPath("preferences",
1038                                                 package().user_support().absFilename()),
1039                                     false);
1040                         break;
1041                 }
1042
1043                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1044                         string const fname =
1045                                 addName(addPath(package().user_support().absFilename(), "templates/"),
1046                                         "defaults.lyx");
1047                         Buffer defaults(fname);
1048
1049                         istringstream ss(argument);
1050                         Lexer lex;
1051                         lex.setStream(ss);
1052                         int const unknown_tokens = defaults.readHeader(lex);
1053
1054                         if (unknown_tokens != 0) {
1055                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1056                                        << unknown_tokens << " unknown token"
1057                                        << (unknown_tokens == 1 ? "" : "s")
1058                                        << endl;
1059                         }
1060
1061                         if (defaults.writeFile(FileName(defaults.absFileName())))
1062                                 setMessage(bformat(_("Document defaults saved in %1$s"),
1063                                                    makeDisplayPath(fname)));
1064                         else
1065                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1066                         break;
1067                 }
1068
1069                 case LFUN_BUFFER_PARAMS_APPLY: {
1070                         LASSERT(lyx_view_, /**/);
1071                         
1072                         DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1073                         Cursor & cur = lyx_view_->documentBufferView()->cursor();
1074                         cur.recordUndoFullDocument();
1075                         
1076                         istringstream ss(argument);
1077                         Lexer lex;
1078                         lex.setStream(ss);
1079                         int const unknown_tokens = buffer->readHeader(lex);
1080
1081                         if (unknown_tokens != 0) {
1082                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1083                                                 << unknown_tokens << " unknown token"
1084                                                 << (unknown_tokens == 1 ? "" : "s")
1085                                                 << endl;
1086                         }
1087                         
1088                         updateLayout(oldClass, buffer);
1089                         
1090                         updateFlags = Update::Force | Update::FitCursor;
1091                         // We are most certainly here because of a change in the document
1092                         // It is then better to make sure that all dialogs are in sync with
1093                         // current document settings. LyXView::restartCursor() achieve this.
1094                         lyx_view_->restartCursor();
1095                         break;
1096                 }
1097                 
1098                 case LFUN_LAYOUT_MODULES_CLEAR: {
1099                         LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1100                         DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1101                         lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1102                         buffer->params().clearLayoutModules();
1103                         buffer->params().makeDocumentClass();
1104                         updateLayout(oldClass, buffer);
1105                         updateFlags = Update::Force | Update::FitCursor;
1106                         break;
1107                 }
1108                 
1109                 case LFUN_LAYOUT_MODULE_ADD: {
1110                         LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1111                         BufferParams const & params = buffer->params();
1112                         if (!params.moduleCanBeAdded(argument)) {
1113                                 LYXERR0("Module `" << argument << 
1114                                                 "' cannot be added due to failed requirements or "
1115                                                 "conflicts with installed modules.");
1116                                 break;
1117                         }
1118                         DocumentClass const * const oldClass = params.documentClassPtr();
1119                         lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1120                         buffer->params().addLayoutModule(argument);
1121                         buffer->params().makeDocumentClass();
1122                         updateLayout(oldClass, buffer);
1123                         updateFlags = Update::Force | Update::FitCursor;
1124                         break;
1125                 }
1126
1127                 case LFUN_TEXTCLASS_APPLY: {
1128                         LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1129
1130                         if (!loadLayoutFile(argument, buffer->temppath()) &&
1131                                 !loadLayoutFile(argument, buffer->filePath()))
1132                                 break;
1133
1134                         LayoutFile const * old_layout = buffer->params().baseClass();
1135                         LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1136
1137                         if (old_layout == new_layout)
1138                                 // nothing to do
1139                                 break;
1140
1141                         //Save the old, possibly modular, layout for use in conversion.
1142                         DocumentClass const * const oldDocClass =
1143                                 buffer->params().documentClassPtr();
1144                         lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1145                         buffer->params().setBaseClass(argument);
1146                         buffer->params().makeDocumentClass();
1147                         updateLayout(oldDocClass, buffer);
1148                         updateFlags = Update::Force | Update::FitCursor;
1149                         break;
1150                 }
1151                 
1152                 case LFUN_LAYOUT_RELOAD: {
1153                         LASSERT(lyx_view_, /**/);
1154                         DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1155                         LayoutFileIndex bc = buffer->params().baseClassID();
1156                         LayoutFileList::get().reset(bc);
1157                         buffer->params().setBaseClass(bc);
1158                         buffer->params().makeDocumentClass();
1159                         updateLayout(oldClass, buffer);
1160                         updateFlags = Update::Force | Update::FitCursor;
1161                         break;
1162                 }
1163
1164                 case LFUN_TEXTCLASS_LOAD:
1165                         loadLayoutFile(argument, buffer->temppath()) ||
1166                         loadLayoutFile(argument, buffer->filePath());
1167                         break;
1168
1169                 case LFUN_LYXRC_APPLY: {
1170                         // reset active key sequences, since the bindings
1171                         // are updated (bug 6064)
1172                         keyseq.reset();
1173                         LyXRC const lyxrc_orig = lyxrc;
1174
1175                         istringstream ss(argument);
1176                         bool const success = lyxrc.read(ss) == 0;
1177
1178                         if (!success) {
1179                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1180                                        << "Unable to read lyxrc data"
1181                                        << endl;
1182                                 break;
1183                         }
1184
1185                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1186
1187                         setSpellChecker();
1188
1189                         theApp()->resetGui();
1190
1191                         /// We force the redraw in any case because there might be
1192                         /// some screen font changes.
1193                         /// FIXME: only the current view will be updated. the Gui
1194                         /// class is able to furnish the list of views.
1195                         updateFlags = Update::Force;
1196                         break;
1197                 }
1198
1199                 case LFUN_BOOKMARK_GOTO:
1200                         // go to bookmark, open unopened file and switch to buffer if necessary
1201                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1202                         updateFlags = Update::FitCursor;
1203                         break;
1204
1205                 case LFUN_BOOKMARK_CLEAR:
1206                         theSession().bookmarks().clear();
1207                         break;
1208
1209                 default:
1210                         LASSERT(theApp(), /**/);
1211                         // Let the frontend dispatch its own actions.
1212                         if (theApp()->dispatch(cmd))
1213                                 // Nothing more to do.
1214                                 return;
1215
1216                         // Everything below is only for active lyx_view_
1217                         if (lyx_view_ == 0)
1218                                 break;
1219
1220                         // Start an undo group. This may be needed for
1221                         // some stuff like inset-apply on labels.
1222                         if (theBufferList().isLoaded(buffer))
1223                                 buffer->undo().beginUndoGroup();
1224                                 
1225                         // Let the current LyXView dispatch its own actions.
1226                         if (lyx_view_->dispatch(cmd)) {
1227                                 if (lyx_view_->currentBufferView()) {
1228                                         updateFlags = lyx_view_->currentBufferView()->cursor().result().update();
1229                                         if (theBufferList().isLoaded(buffer))
1230                                                 buffer->undo().endUndoGroup();
1231                                 }
1232                                 break;
1233                         }
1234
1235                         LASSERT(lyx_view_->currentBufferView(), /**/);
1236
1237                         // Let the current BufferView dispatch its own actions.
1238                         if (lyx_view_->currentBufferView()->dispatch(cmd)) {
1239                                 // The BufferView took care of its own updates if needed.
1240                                 updateFlags = Update::None;
1241                                 if (theBufferList().isLoaded(buffer))
1242                                         buffer->undo().endUndoGroup();
1243                                 break;
1244                         }
1245
1246                         // OK, so try the Buffer itself
1247                         DispatchResult dr;
1248                         BufferView * bv = lyx_view_->currentBufferView();
1249                         bv->buffer().dispatch(cmd, dr);
1250                         if (dr.dispatched()) {
1251                                 updateFlags = dr.update();
1252                                 break;
1253                         }
1254                         // OK, so try with the document Buffer.
1255                         BufferView * doc_bv = lyx_view_->documentBufferView();
1256                         if (doc_bv) {
1257                                 buffer = &(doc_bv->buffer());
1258                                 buffer->dispatch(cmd, dr);
1259                                 if (dr.dispatched()) {
1260                                         updateFlags = dr.update();
1261                                         break;
1262                                 }
1263                         }
1264
1265                         // Is this a function that acts on inset at point?
1266                         Inset * inset = bv->cursor().nextInset();
1267                         if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1268                             && inset) {
1269                                 bv->cursor().result().dispatched(true);
1270                                 bv->cursor().result().update(Update::FitCursor | Update::Force);
1271                                 FuncRequest tmpcmd = cmd;
1272                                 inset->dispatch(bv->cursor(), tmpcmd);
1273                                 if (bv->cursor().result().dispatched()) {
1274                                         updateFlags = bv->cursor().result().update();
1275                                         break;
1276                                 }
1277                         }
1278
1279                         // Let the current Cursor dispatch its own actions.
1280                         Cursor old = bv->cursor();
1281                         bv->cursor().getPos(cursorPosBeforeDispatchX_,
1282                                                 cursorPosBeforeDispatchY_);
1283                         bv->cursor().dispatch(cmd);
1284
1285                         // notify insets we just left
1286                         if (bv->cursor() != old) {
1287                                 old.fixIfBroken();
1288                                 bool badcursor = notifyCursorLeavesOrEnters(old, bv->cursor());
1289                                 if (badcursor)
1290                                         bv->cursor().fixIfBroken();
1291                         }
1292
1293                         if (theBufferList().isLoaded(buffer))
1294                                 buffer->undo().endUndoGroup();
1295
1296                         // update completion. We do it here and not in
1297                         // processKeySym to avoid another redraw just for a
1298                         // changed inline completion
1299                         if (cmd.origin == FuncRequest::KEYBOARD) {
1300                                 if (cmd.action == LFUN_SELF_INSERT
1301                                     || (cmd.action == LFUN_ERT_INSERT && bv->cursor().inMathed()))
1302                                         lyx_view_->updateCompletion(bv->cursor(), true, true);
1303                                 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1304                                         lyx_view_->updateCompletion(bv->cursor(), false, true);
1305                                 else
1306                                         lyx_view_->updateCompletion(bv->cursor(), false, false);
1307                         }
1308
1309                         updateFlags = bv->cursor().result().update();
1310                 }
1311
1312                 // if we executed a mutating lfun, mark the buffer as dirty
1313                 if (theBufferList().isLoaded(buffer) && flag.enabled()
1314                     && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1315                     && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1316                         buffer->markDirty();                    
1317
1318                 if (lyx_view_ && lyx_view_->currentBufferView()) {
1319                         // BufferView::update() updates the ViewMetricsInfo and
1320                         // also initializes the position cache for all insets in
1321                         // (at least partially) visible top-level paragraphs.
1322                         // We will redraw the screen only if needed.
1323                         lyx_view_->currentBufferView()->processUpdateFlags(updateFlags);
1324
1325                         // Do we have a selection?
1326                         theSelection().haveSelection(
1327                                 lyx_view_->currentBufferView()->cursor().selection());
1328                         
1329                         // update gui
1330                         lyx_view_->restartCursor();
1331                 }
1332         }
1333         if (lyx_view_) {
1334                 // Some messages may already be translated, so we cannot use _()
1335                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1336         }
1337 }
1338
1339
1340 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1341 {
1342         const bool verbose = (cmd.origin == FuncRequest::MENU
1343                               || cmd.origin == FuncRequest::TOOLBAR
1344                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1345
1346         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1347                 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1348                 if (!msg.empty())
1349                         lyx_view_->message(msg);
1350                 return;
1351         }
1352
1353         docstring dispatch_msg = msg;
1354         if (!dispatch_msg.empty())
1355                 dispatch_msg += ' ';
1356
1357         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1358
1359         bool argsadded = false;
1360
1361         if (!cmd.argument().empty()) {
1362                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1363                         comname += ' ' + cmd.argument();
1364                         argsadded = true;
1365                 }
1366         }
1367
1368         docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1369
1370         if (!shortcuts.empty())
1371                 comname += ": " + shortcuts;
1372         else if (!argsadded && !cmd.argument().empty())
1373                 comname += ' ' + cmd.argument();
1374
1375         if (!comname.empty()) {
1376                 comname = rtrim(comname);
1377                 dispatch_msg += '(' + rtrim(comname) + ')';
1378         }
1379
1380         LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1381         if (!dispatch_msg.empty())
1382                 lyx_view_->message(dispatch_msg);
1383 }
1384
1385
1386 // Each "lyx_view_" should have it's own message method. lyxview and
1387 // the minibuffer would use the minibuffer, but lyxserver would
1388 // send an ERROR signal to its client.  Alejandro 970603
1389 // This function is bit problematic when it comes to NLS, to make the
1390 // lyx servers client be language indepenent we must not translate
1391 // strings sent to this func.
1392 void LyXFunc::setErrorMessage(docstring const & m) const
1393 {
1394         dispatch_buffer = m;
1395         errorstat = true;
1396 }
1397
1398
1399 void LyXFunc::setMessage(docstring const & m) const
1400 {
1401         dispatch_buffer = m;
1402 }
1403
1404
1405 docstring LyXFunc::viewStatusMessage()
1406 {
1407         // When meta-fake key is pressed, show the key sequence so far + "M-".
1408         if (wasMetaKey())
1409                 return keyseq.print(KeySequence::ForGui) + "M-";
1410
1411         // Else, when a non-complete key sequence is pressed,
1412         // show the available options.
1413         if (keyseq.length() > 0 && !keyseq.deleted())
1414                 return keyseq.printOptions(true);
1415
1416         LASSERT(lyx_view_, /**/);
1417         if (!lyx_view_->currentBufferView())
1418                 return _("Welcome to LyX!");
1419
1420         return lyx_view_->currentBufferView()->cursor().currentState();
1421 }
1422
1423
1424 bool LyXFunc::wasMetaKey() const
1425 {
1426         return (meta_fake_bit != NoModifier);
1427 }
1428
1429
1430 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1431 {
1432         lyx_view_->message(_("Converting document to new document class..."));
1433         
1434         StableDocIterator backcur(lyx_view_->currentBufferView()->cursor());
1435         ErrorList & el = buf->errorList("Class Switch");
1436         cap::switchBetweenClasses(
1437                         oldlayout, buf->params().documentClassPtr(),
1438                         static_cast<InsetText &>(buf->inset()), el);
1439
1440         lyx_view_->currentBufferView()->setCursor(backcur.asDocIterator(buf));
1441
1442         buf->errors("Class Switch");
1443         buf->updateLabels();
1444 }
1445
1446
1447 namespace {
1448
1449 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1450 {
1451         // Why the switch you might ask. It is a trick to ensure that all
1452         // the elements in the LyXRCTags enum is handled. As you can see
1453         // there are no breaks at all. So it is just a huge fall-through.
1454         // The nice thing is that we will get a warning from the compiler
1455         // if we forget an element.
1456         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1457         switch (tag) {
1458         case LyXRC::RC_ACCEPT_COMPOUND:
1459         case LyXRC::RC_ALT_LANG:
1460         case LyXRC::RC_PLAINTEXT_LINELEN:
1461         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1462         case LyXRC::RC_AUTOCORRECTION_MATH:
1463         case LyXRC::RC_AUTOREGIONDELETE:
1464         case LyXRC::RC_AUTORESET_OPTIONS:
1465         case LyXRC::RC_AUTOSAVE:
1466         case LyXRC::RC_AUTO_NUMBER:
1467         case LyXRC::RC_BACKUPDIR_PATH:
1468         case LyXRC::RC_BIBTEX_ALTERNATIVES:
1469         case LyXRC::RC_BIBTEX_COMMAND:
1470         case LyXRC::RC_BINDFILE:
1471         case LyXRC::RC_CHECKLASTFILES:
1472         case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1473         case LyXRC::RC_COMPLETION_INLINE_DELAY:
1474         case LyXRC::RC_COMPLETION_INLINE_DOTS:
1475         case LyXRC::RC_COMPLETION_INLINE_MATH:
1476         case LyXRC::RC_COMPLETION_INLINE_TEXT:
1477         case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1478         case LyXRC::RC_COMPLETION_POPUP_DELAY:
1479         case LyXRC::RC_COMPLETION_POPUP_MATH:
1480         case LyXRC::RC_COMPLETION_POPUP_TEXT:
1481         case LyXRC::RC_USELASTFILEPOS:
1482         case LyXRC::RC_LOADSESSION:
1483         case LyXRC::RC_CHKTEX_COMMAND:
1484         case LyXRC::RC_CONVERTER:
1485         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1486         case LyXRC::RC_COPIER:
1487         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1488         case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1489         case LyXRC::RC_DATE_INSERT_FORMAT:
1490         case LyXRC::RC_DEFAULT_LANGUAGE:
1491         case LyXRC::RC_GUI_LANGUAGE:
1492         case LyXRC::RC_DEFAULT_PAPERSIZE:
1493         case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1494         case LyXRC::RC_DEFFILE:
1495         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1496         case LyXRC::RC_DISPLAY_GRAPHICS:
1497         case LyXRC::RC_DOCUMENTPATH:
1498                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1499                         FileName path(lyxrc_new.document_path);
1500                         if (path.exists() && path.isDirectory())
1501                                 package().document_dir() = FileName(lyxrc.document_path);
1502                 }
1503         case LyXRC::RC_EDITOR_ALTERNATIVES:
1504         case LyXRC::RC_ESC_CHARS:
1505         case LyXRC::RC_EXAMPLEPATH:
1506         case LyXRC::RC_FONT_ENCODING:
1507         case LyXRC::RC_FORMAT:
1508         case LyXRC::RC_GROUP_LAYOUTS:
1509         case LyXRC::RC_HUNSPELLDIR_PATH:
1510         case LyXRC::RC_INDEX_ALTERNATIVES:
1511         case LyXRC::RC_INDEX_COMMAND:
1512         case LyXRC::RC_JBIBTEX_COMMAND:
1513         case LyXRC::RC_JINDEX_COMMAND:
1514         case LyXRC::RC_NOMENCL_COMMAND:
1515         case LyXRC::RC_INPUT:
1516         case LyXRC::RC_KBMAP:
1517         case LyXRC::RC_KBMAP_PRIMARY:
1518         case LyXRC::RC_KBMAP_SECONDARY:
1519         case LyXRC::RC_LABEL_INIT_LENGTH:
1520         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1521         case LyXRC::RC_LANGUAGE_AUTO_END:
1522         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1523         case LyXRC::RC_LANGUAGE_COMMAND_END:
1524         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1525         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1526         case LyXRC::RC_LANGUAGE_PACKAGE:
1527         case LyXRC::RC_LANGUAGE_USE_BABEL:
1528         case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1529         case LyXRC::RC_MACRO_EDIT_STYLE:
1530         case LyXRC::RC_MAKE_BACKUP:
1531         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1532         case LyXRC::RC_MOUSE_WHEEL_SPEED:
1533         case LyXRC::RC_NUMLASTFILES:
1534         case LyXRC::RC_PARAGRAPH_MARKERS:
1535         case LyXRC::RC_PATH_PREFIX:
1536                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1537                         prependEnvPath("PATH", lyxrc.path_prefix);
1538                 }
1539         case LyXRC::RC_PERS_DICT:
1540         case LyXRC::RC_PREVIEW:
1541         case LyXRC::RC_PREVIEW_HASHED_LABELS:
1542         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1543         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1544         case LyXRC::RC_PRINTCOPIESFLAG:
1545         case LyXRC::RC_PRINTER:
1546         case LyXRC::RC_PRINTEVENPAGEFLAG:
1547         case LyXRC::RC_PRINTEXSTRAOPTIONS:
1548         case LyXRC::RC_PRINTFILEEXTENSION:
1549         case LyXRC::RC_PRINTLANDSCAPEFLAG:
1550         case LyXRC::RC_PRINTODDPAGEFLAG:
1551         case LyXRC::RC_PRINTPAGERANGEFLAG:
1552         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
1553         case LyXRC::RC_PRINTPAPERFLAG:
1554         case LyXRC::RC_PRINTREVERSEFLAG:
1555         case LyXRC::RC_PRINTSPOOL_COMMAND:
1556         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
1557         case LyXRC::RC_PRINTTOFILE:
1558         case LyXRC::RC_PRINTTOPRINTER:
1559         case LyXRC::RC_PRINT_ADAPTOUTPUT:
1560         case LyXRC::RC_PRINT_COMMAND:
1561         case LyXRC::RC_RTL_SUPPORT:
1562         case LyXRC::RC_SCREEN_DPI:
1563         case LyXRC::RC_SCREEN_FONT_ROMAN:
1564         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
1565         case LyXRC::RC_SCREEN_FONT_SANS:
1566         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
1567         case LyXRC::RC_SCREEN_FONT_SCALABLE:
1568         case LyXRC::RC_SCREEN_FONT_SIZES:
1569         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
1570         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
1571         case LyXRC::RC_GEOMETRY_SESSION:
1572         case LyXRC::RC_SCREEN_ZOOM:
1573         case LyXRC::RC_SERVERPIPE:
1574         case LyXRC::RC_SET_COLOR:
1575         case LyXRC::RC_SHOW_BANNER:
1576         case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
1577         case LyXRC::RC_SPELL_COMMAND:
1578         case LyXRC::RC_SPELLCHECKER:
1579         case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
1580         case LyXRC::RC_SPLITINDEX_COMMAND:
1581         case LyXRC::RC_TEMPDIRPATH:
1582         case LyXRC::RC_TEMPLATEPATH:
1583         case LyXRC::RC_TEX_ALLOWS_SPACES:
1584         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
1585                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
1586                         os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
1587                 }
1588         case LyXRC::RC_THESAURUSDIRPATH:
1589         case LyXRC::RC_UIFILE:
1590         case LyXRC::RC_USER_EMAIL:
1591         case LyXRC::RC_USER_NAME:
1592         case LyXRC::RC_USETEMPDIR:
1593         case LyXRC::RC_USE_ALT_LANG:
1594         case LyXRC::RC_USE_CONVERTER_CACHE:
1595         case LyXRC::RC_USE_ESC_CHARS:
1596         case LyXRC::RC_USE_INP_ENC:
1597         case LyXRC::RC_USE_PERS_DICT:
1598         case LyXRC::RC_USE_TOOLTIP:
1599         case LyXRC::RC_USE_PIXMAP_CACHE:
1600         case LyXRC::RC_USE_SPELL_LIB:
1601         case LyXRC::RC_VIEWDVI_PAPEROPTION:
1602         case LyXRC::RC_SORT_LAYOUTS:
1603         case LyXRC::RC_FULL_SCREEN_LIMIT:
1604         case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
1605         case LyXRC::RC_FULL_SCREEN_MENUBAR:
1606         case LyXRC::RC_FULL_SCREEN_TABBAR:
1607         case LyXRC::RC_FULL_SCREEN_TOOLBARS:
1608         case LyXRC::RC_FULL_SCREEN_WIDTH:
1609         case LyXRC::RC_VISUAL_CURSOR:
1610         case LyXRC::RC_VIEWER:
1611         case LyXRC::RC_VIEWER_ALTERNATIVES:
1612         case LyXRC::RC_LAST:
1613                 break;
1614         }
1615 }
1616
1617 } // namespace anon
1618 } // namespace lyx