]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
* Move LFUN_BUFFER_CHILD_OPEN to GuiView.
[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_KEYMAP_OFF:
568         case LFUN_KEYMAP_PRIMARY:
569         case LFUN_KEYMAP_SECONDARY:
570         case LFUN_KEYMAP_TOGGLE:
571         case LFUN_REPEAT:
572         case LFUN_PREFERENCES_SAVE:
573         case LFUN_INSET_EDIT:
574         case LFUN_TEXTCLASS_APPLY:
575         case LFUN_TEXTCLASS_LOAD:
576         case LFUN_BUFFER_SAVE_AS_DEFAULT:
577         case LFUN_BUFFER_PARAMS_APPLY:
578         case LFUN_LAYOUT_MODULES_CLEAR:
579         case LFUN_LAYOUT_MODULE_ADD:
580         case LFUN_LAYOUT_RELOAD:
581         case LFUN_LYXRC_APPLY:
582                 // these are handled in our dispatch()
583                 break;
584
585         default:
586                 if (!theApp()) {
587                         enable = false;
588                         break;
589                 }
590                 if (theApp()->getStatus(cmd, flag))
591                         break;
592
593                 // Does the view know something?
594                 if (!lv) {
595                         enable = false;
596                         break;
597                 }
598                 if (lv->getStatus(cmd, flag))
599                         break;
600
601                 BufferView * bv = lv->currentBufferView();
602                 BufferView * doc_bv = lv->documentBufferView();
603                 // If we do not have a BufferView, then other functions are disabled
604                 if (!bv) {
605                         enable = false;
606                         break;
607                 }
608                 // Is this a function that acts on inset at point?
609                 Inset * inset = bv->cursor().nextInset();
610                 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
611                     && inset && inset->getStatus(bv->cursor(), cmd, flag))
612                         break;
613
614                 bool decided = getLocalStatus(bv->cursor(), cmd, flag);
615                 if (!decided)
616                         // try the BufferView
617                         decided = bv->getStatus(cmd, flag);
618                 if (!decided)
619                         // try the Buffer
620                         decided = bv->buffer().getStatus(cmd, flag);
621                 if (!decided && doc_bv)
622                         // try the Document Buffer
623                         decided = doc_bv->buffer().getStatus(cmd, flag);
624         }
625
626         if (!enable)
627                 flag.setEnabled(false);
628
629         // Can we use a readonly buffer?
630         if (buf && buf->isReadonly()
631             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
632             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
633                 flag.message(from_utf8(N_("Document is read-only")));
634                 flag.setEnabled(false);
635         }
636
637         // Are we in a DELETED change-tracking region?
638         if (lyx_view_ && lyx_view_->documentBufferView()
639                 && (lookupChangeType(lyx_view_->documentBufferView()->cursor(), true)
640                     == Change::DELETED)
641             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
642             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
643                 flag.message(from_utf8(N_("This portion of the document is deleted.")));
644                 flag.setEnabled(false);
645         }
646
647         // the default error message if we disable the command
648         if (!flag.enabled() && flag.message().empty())
649                 flag.message(from_utf8(N_("Command disabled")));
650
651         return flag;
652 }
653
654
655 namespace {
656
657 bool loadLayoutFile(string const & name, string const & buf_path)
658 {
659         if (!LayoutFileList::get().haveClass(name)) {
660                 lyxerr << "Document class \"" << name
661                        << "\" does not exist."
662                        << endl;
663                 return false;
664         }
665
666         LayoutFile & tc = LayoutFileList::get()[name];
667         if (!tc.load(buf_path)) {
668                 docstring s = bformat(_("The document class %1$s "
669                                    "could not be loaded."), from_utf8(name));
670                 Alert::error(_("Could not load class"), s);
671                 return false;
672         }
673         return true;
674 }
675
676
677 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
678
679 } //namespace anon
680
681
682 void LyXFunc::dispatch(FuncRequest const & cmd)
683 {
684         string const argument = to_utf8(cmd.argument());
685         FuncCode const action = cmd.action;
686
687         LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
688         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
689
690         // we have not done anything wrong yet.
691         errorstat = false;
692         dispatch_buffer.erase();
693
694         // redraw the screen at the end (first of the two drawing steps).
695         //This is done unless explicitely requested otherwise
696         Update::flags updateFlags = Update::FitCursor;
697
698         FuncStatus const flag = getStatus(cmd);
699         if (!flag.enabled()) {
700                 // We cannot use this function here
701                 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
702                        << lyxaction.getActionName(action)
703                        << " [" << action << "] is disabled at this location");
704                 setErrorMessage(flag.message());
705                 if (lyx_view_)
706                         lyx_view_->restartCursor();
707         } else {
708                 Buffer * buffer = 0;
709                 if (lyx_view_ && lyx_view_->currentBufferView())
710                         buffer = &lyx_view_->currentBufferView()->buffer();
711                 switch (action) {
712
713                 case LFUN_COMMAND_PREFIX:
714                         LASSERT(lyx_view_, /**/);
715                         lyx_view_->message(keyseq.printOptions(true));
716                         break;
717
718                 case LFUN_CANCEL:
719                         LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
720                         keyseq.reset();
721                         meta_fake_bit = NoModifier;
722                         if (buffer)
723                                 // cancel any selection
724                                 dispatch(FuncRequest(LFUN_MARK_OFF));
725                         setMessage(from_ascii(N_("Cancel")));
726                         break;
727
728                 case LFUN_META_PREFIX:
729                         meta_fake_bit = AltModifier;
730                         setMessage(keyseq.print(KeySequence::ForGui));
731                         break;
732
733                 // --- Menus -----------------------------------------------
734                 case LFUN_RECONFIGURE:
735                         // argument is any additional parameter to the configure.py command
736                         reconfigure(lyx_view_, argument);
737                         break;
738
739                 case LFUN_HELP_OPEN: {
740                         if (lyx_view_ == 0)
741                                 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
742                         string const arg = argument;
743                         if (arg.empty()) {
744                                 setErrorMessage(from_utf8(N_("Missing argument")));
745                                 break;
746                         }
747                         FileName fname = i18nLibFileSearch("doc", arg, "lyx");
748                         if (fname.empty()) 
749                                 fname = i18nLibFileSearch("examples", arg, "lyx");
750
751                         if (fname.empty()) {
752                                 lyxerr << "LyX: unable to find documentation file `"
753                                                          << arg << "'. Bad installation?" << endl;
754                                 break;
755                         }
756                         lyx_view_->message(bformat(_("Opening help file %1$s..."),
757                                 makeDisplayPath(fname.absFilename())));
758                         Buffer * buf = lyx_view_->loadDocument(fname, false);
759                         if (buf) {
760                                 buf->updateLabels();
761                                 lyx_view_->setBuffer(buf);
762                                 buf->errors("Parse");
763                         }
764                         updateFlags = Update::None;
765                         break;
766                 }
767
768                 // --- lyxserver commands ----------------------------
769                 case LFUN_SERVER_GET_FILENAME:
770                         LASSERT(lyx_view_ && buffer, /**/);
771                         setMessage(from_utf8(buffer->absFileName()));
772                         LYXERR(Debug::INFO, "FNAME["
773                                 << buffer->absFileName() << ']');
774                         break;
775
776                 case LFUN_SERVER_NOTIFY:
777                         dispatch_buffer = keyseq.print(KeySequence::Portable);
778                         theServer().notifyClient(to_utf8(dispatch_buffer));
779                         break;
780
781                 case LFUN_SERVER_GOTO_FILE_ROW: {
782                         LASSERT(lyx_view_, /**/);
783                         string file_name;
784                         int row;
785                         istringstream is(argument);
786                         is >> file_name >> row;
787                         file_name = os::internal_path(file_name);
788                         Buffer * buf = 0;
789                         bool loaded = false;
790                         string const abstmp = package().temp_dir().absFilename();
791                         string const realtmp = package().temp_dir().realPath();
792                         // We have to use os::path_prefix_is() here, instead of
793                         // simply prefixIs(), because the file name comes from
794                         // an external application and may need case adjustment.
795                         if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
796                             || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
797                                 // Needed by inverse dvi search. If it is a file
798                                 // in tmpdir, call the apropriated function.
799                                 // If tmpdir is a symlink, we may have the real
800                                 // path passed back, so we correct for that.
801                                 if (!prefixIs(file_name, abstmp))
802                                         file_name = subst(file_name, realtmp, abstmp);
803                                 buf = theBufferList().getBufferFromTmp(file_name);
804                         } else {
805                                 // Must replace extension of the file to be .lyx
806                                 // and get full path
807                                 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
808                                 // Either change buffer or load the file
809                                 if (theBufferList().exists(s))
810                                         buf = theBufferList().getBuffer(s);
811                                 else if (s.exists()) {
812                                         buf = lyx_view_->loadDocument(s);
813                                         loaded = true;
814                                 } else
815                                         lyx_view_->message(bformat(
816                                                 _("File does not exist: %1$s"),
817                                                 makeDisplayPath(file_name)));
818                         }
819
820                         if (!buf) {
821                                 updateFlags = Update::None;
822                                 break;
823                         }
824
825                         buf->updateLabels();
826                         lyx_view_->setBuffer(buf);
827                         lyx_view_->documentBufferView()->setCursorFromRow(row);
828                         if (loaded)
829                                 buf->errors("Parse");
830                         updateFlags = Update::FitCursor;
831                         break;
832                 }
833
834
835                 case LFUN_DIALOG_SHOW_NEW_INSET: {
836                         LASSERT(lyx_view_, /**/);
837                         string const name = cmd.getArg(0);
838                         InsetCode code = insetCode(name);
839                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
840                         bool insetCodeOK = true;
841                         switch (code) {
842                         case BIBITEM_CODE:
843                         case BIBTEX_CODE:
844                         case INDEX_CODE:
845                         case LABEL_CODE:
846                         case NOMENCL_CODE:
847                         case NOMENCL_PRINT_CODE:
848                         case REF_CODE:
849                         case TOC_CODE:
850                         case HYPERLINK_CODE: {
851                                 InsetCommandParams p(code);
852                                 data = InsetCommand::params2string(name, p);
853                                 break;
854                         }
855                         case INCLUDE_CODE: {
856                                 // data is the include type: one of "include",
857                                 // "input", "verbatiminput" or "verbatiminput*"
858                                 if (data.empty())
859                                         // default type is requested
860                                         data = "include";
861                                 InsetCommandParams p(INCLUDE_CODE, data);
862                                 data = InsetCommand::params2string("include", p);
863                                 break;
864                         }
865                         case BOX_CODE: {
866                                 // \c data == "Boxed" || "Frameless" etc
867                                 InsetBoxParams p(data);
868                                 data = InsetBox::params2string(p);
869                                 break;
870                         }
871                         case BRANCH_CODE: {
872                                 InsetBranchParams p;
873                                 data = InsetBranch::params2string(p);
874                                 break;
875                         }
876                         case CITE_CODE: {
877                                 InsetCommandParams p(CITE_CODE);
878                                 data = InsetCommand::params2string(name, p);
879                                 break;
880                         }
881                         case ERT_CODE: {
882                                 data = InsetERT::params2string(InsetCollapsable::Open);
883                                 break;
884                         }
885                         case EXTERNAL_CODE: {
886                                 InsetExternalParams p;
887                                 data = InsetExternal::params2string(p, *buffer);
888                                 break;
889                         }
890                         case FLOAT_CODE:  {
891                                 InsetFloatParams p;
892                                 data = InsetFloat::params2string(p);
893                                 break;
894                         }
895                         case LISTINGS_CODE: {
896                                 InsetListingsParams p;
897                                 data = InsetListings::params2string(p);
898                                 break;
899                         }
900                         case GRAPHICS_CODE: {
901                                 InsetGraphicsParams p;
902                                 data = InsetGraphics::params2string(p, *buffer);
903                                 break;
904                         }
905                         case NOTE_CODE: {
906                                 InsetNoteParams p;
907                                 data = InsetNote::params2string(p);
908                                 break;
909                         }
910                         case PHANTOM_CODE: {
911                                 InsetPhantomParams p;
912                                 data = InsetPhantom::params2string(p);
913                                 break;
914                         }
915                         case SPACE_CODE: {
916                                 InsetSpaceParams p;
917                                 data = InsetSpace::params2string(p);
918                                 break;
919                         }
920                         case VSPACE_CODE: {
921                                 VSpace space;
922                                 data = InsetVSpace::params2string(space);
923                                 break;
924                         }
925                         case WRAP_CODE: {
926                                 InsetWrapParams p;
927                                 data = InsetWrap::params2string(p);
928                                 break;
929                         }
930                         default:
931                                 lyxerr << "Inset type '" << name << 
932                                         "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" <<  endl;
933                                 insetCodeOK = false;
934                                 break;
935                         } // end switch(code)
936                         if (insetCodeOK)
937                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
938                         break;
939                 }
940
941                 case LFUN_CITATION_INSERT: {
942                         LASSERT(lyx_view_, /**/);
943                         if (!argument.empty()) {
944                                 // we can have one optional argument, delimited by '|'
945                                 // citation-insert <key>|<text_before>
946                                 // this should be enhanced to also support text_after
947                                 // and citation style
948                                 string arg = argument;
949                                 string opt1;
950                                 if (contains(argument, "|")) {
951                                         arg = token(argument, '|', 0);
952                                         opt1 = token(argument, '|', 1);
953                                 }
954                                 InsetCommandParams icp(CITE_CODE);
955                                 icp["key"] = from_utf8(arg);
956                                 if (!opt1.empty())
957                                         icp["before"] = from_utf8(opt1);
958                                 string icstr = InsetCommand::params2string("citation", icp);
959                                 FuncRequest fr(LFUN_INSET_INSERT, icstr);
960                                 dispatch(fr);
961                         } else
962                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
963                         break;
964                 }
965
966                 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
967                         LASSERT(lyx_view_, /**/);
968                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
969                         break;
970
971                 case LFUN_KEYMAP_OFF:
972                         LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
973                         lyx_view_->currentBufferView()->getIntl().keyMapOn(false);
974                         break;
975
976                 case LFUN_KEYMAP_PRIMARY:
977                         LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
978                         lyx_view_->currentBufferView()->getIntl().keyMapPrim();
979                         break;
980
981                 case LFUN_KEYMAP_SECONDARY:
982                         LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
983                         lyx_view_->currentBufferView()->getIntl().keyMapSec();
984                         break;
985
986                 case LFUN_KEYMAP_TOGGLE:
987                         LASSERT(lyx_view_ && lyx_view_->currentBufferView(), /**/);
988                         lyx_view_->currentBufferView()->getIntl().toggleKeyMap();
989                         break;
990
991                 case LFUN_REPEAT: {
992                         // repeat command
993                         string countstr;
994                         string rest = split(argument, countstr, ' ');
995                         istringstream is(countstr);
996                         int count = 0;
997                         is >> count;
998                         //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
999                         for (int i = 0; i < count; ++i)
1000                                 dispatch(lyxaction.lookupFunc(rest));
1001                         break;
1002                 }
1003
1004                 case LFUN_COMMAND_SEQUENCE: {
1005                         // argument contains ';'-terminated commands
1006                         string arg = argument;
1007                         if (theBufferList().isLoaded(buffer))
1008                                 buffer->undo().beginUndoGroup();
1009                         while (!arg.empty()) {
1010                                 string first;
1011                                 arg = split(arg, first, ';');
1012                                 FuncRequest func(lyxaction.lookupFunc(first));
1013                                 func.origin = cmd.origin;
1014                                 dispatch(func);
1015                         }
1016                         if (theBufferList().isLoaded(buffer))
1017                                 buffer->undo().endUndoGroup();
1018                         break;
1019                 }
1020
1021                 case LFUN_COMMAND_ALTERNATIVES: {
1022                         // argument contains ';'-terminated commands
1023                         string arg = argument;
1024                         while (!arg.empty()) {
1025                                 string first;
1026                                 arg = split(arg, first, ';');
1027                                 FuncRequest func(lyxaction.lookupFunc(first));
1028                                 func.origin = cmd.origin;
1029                                 FuncStatus stat = getStatus(func);
1030                                 if (stat.enabled()) {
1031                                         dispatch(func);
1032                                         break;
1033                                 }
1034                         }
1035                         break;
1036                 }
1037
1038                 case LFUN_CALL: {
1039                         FuncRequest func;
1040                         if (theTopLevelCmdDef().lock(argument, func)) {
1041                                 func.origin = cmd.origin;
1042                                 dispatch(func);
1043                                 theTopLevelCmdDef().release(argument);
1044                         } else {
1045                                 if (func.action == LFUN_UNKNOWN_ACTION) {
1046                                         // unknown command definition
1047                                         lyxerr << "Warning: unknown command definition `"
1048                                                    << argument << "'"
1049                                                    << endl;
1050                                 } else {
1051                                         // recursion detected
1052                                         lyxerr << "Warning: Recursion in the command definition `"
1053                                                    << argument << "' detected"
1054                                                    << endl;
1055                                 }
1056                         }
1057                         break;
1058                 }
1059
1060                 case LFUN_PREFERENCES_SAVE: {
1061                         lyxrc.write(makeAbsPath("preferences",
1062                                                 package().user_support().absFilename()),
1063                                     false);
1064                         break;
1065                 }
1066
1067                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1068                         string const fname =
1069                                 addName(addPath(package().user_support().absFilename(), "templates/"),
1070                                         "defaults.lyx");
1071                         Buffer defaults(fname);
1072
1073                         istringstream ss(argument);
1074                         Lexer lex;
1075                         lex.setStream(ss);
1076                         int const unknown_tokens = defaults.readHeader(lex);
1077
1078                         if (unknown_tokens != 0) {
1079                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1080                                        << unknown_tokens << " unknown token"
1081                                        << (unknown_tokens == 1 ? "" : "s")
1082                                        << endl;
1083                         }
1084
1085                         if (defaults.writeFile(FileName(defaults.absFileName())))
1086                                 setMessage(bformat(_("Document defaults saved in %1$s"),
1087                                                    makeDisplayPath(fname)));
1088                         else
1089                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1090                         break;
1091                 }
1092
1093                 case LFUN_BUFFER_PARAMS_APPLY: {
1094                         LASSERT(lyx_view_, /**/);
1095                         
1096                         DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1097                         Cursor & cur = lyx_view_->documentBufferView()->cursor();
1098                         cur.recordUndoFullDocument();
1099                         
1100                         istringstream ss(argument);
1101                         Lexer lex;
1102                         lex.setStream(ss);
1103                         int const unknown_tokens = buffer->readHeader(lex);
1104
1105                         if (unknown_tokens != 0) {
1106                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1107                                                 << unknown_tokens << " unknown token"
1108                                                 << (unknown_tokens == 1 ? "" : "s")
1109                                                 << endl;
1110                         }
1111                         
1112                         updateLayout(oldClass, buffer);
1113                         
1114                         updateFlags = Update::Force | Update::FitCursor;
1115                         // We are most certainly here because of a change in the document
1116                         // It is then better to make sure that all dialogs are in sync with
1117                         // current document settings. LyXView::restartCursor() achieve this.
1118                         lyx_view_->restartCursor();
1119                         break;
1120                 }
1121                 
1122                 case LFUN_LAYOUT_MODULES_CLEAR: {
1123                         LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1124                         DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1125                         lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1126                         buffer->params().clearLayoutModules();
1127                         buffer->params().makeDocumentClass();
1128                         updateLayout(oldClass, buffer);
1129                         updateFlags = Update::Force | Update::FitCursor;
1130                         break;
1131                 }
1132                 
1133                 case LFUN_LAYOUT_MODULE_ADD: {
1134                         LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1135                         BufferParams const & params = buffer->params();
1136                         if (!params.moduleCanBeAdded(argument)) {
1137                                 LYXERR0("Module `" << argument << 
1138                                                 "' cannot be added due to failed requirements or "
1139                                                 "conflicts with installed modules.");
1140                                 break;
1141                         }
1142                         DocumentClass const * const oldClass = params.documentClassPtr();
1143                         lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1144                         buffer->params().addLayoutModule(argument);
1145                         buffer->params().makeDocumentClass();
1146                         updateLayout(oldClass, buffer);
1147                         updateFlags = Update::Force | Update::FitCursor;
1148                         break;
1149                 }
1150
1151                 case LFUN_TEXTCLASS_APPLY: {
1152                         LASSERT(lyx_view_ && lyx_view_->documentBufferView(), /**/);
1153
1154                         if (!loadLayoutFile(argument, buffer->temppath()) &&
1155                                 !loadLayoutFile(argument, buffer->filePath()))
1156                                 break;
1157
1158                         LayoutFile const * old_layout = buffer->params().baseClass();
1159                         LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1160
1161                         if (old_layout == new_layout)
1162                                 // nothing to do
1163                                 break;
1164
1165                         //Save the old, possibly modular, layout for use in conversion.
1166                         DocumentClass const * const oldDocClass =
1167                                 buffer->params().documentClassPtr();
1168                         lyx_view_->documentBufferView()->cursor().recordUndoFullDocument();
1169                         buffer->params().setBaseClass(argument);
1170                         buffer->params().makeDocumentClass();
1171                         updateLayout(oldDocClass, buffer);
1172                         updateFlags = Update::Force | Update::FitCursor;
1173                         break;
1174                 }
1175                 
1176                 case LFUN_LAYOUT_RELOAD: {
1177                         LASSERT(lyx_view_, /**/);
1178                         DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1179                         LayoutFileIndex bc = buffer->params().baseClassID();
1180                         LayoutFileList::get().reset(bc);
1181                         buffer->params().setBaseClass(bc);
1182                         buffer->params().makeDocumentClass();
1183                         updateLayout(oldClass, buffer);
1184                         updateFlags = Update::Force | Update::FitCursor;
1185                         break;
1186                 }
1187
1188                 case LFUN_TEXTCLASS_LOAD:
1189                         loadLayoutFile(argument, buffer->temppath()) ||
1190                         loadLayoutFile(argument, buffer->filePath());
1191                         break;
1192
1193                 case LFUN_LYXRC_APPLY: {
1194                         // reset active key sequences, since the bindings
1195                         // are updated (bug 6064)
1196                         keyseq.reset();
1197                         LyXRC const lyxrc_orig = lyxrc;
1198
1199                         istringstream ss(argument);
1200                         bool const success = lyxrc.read(ss) == 0;
1201
1202                         if (!success) {
1203                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1204                                        << "Unable to read lyxrc data"
1205                                        << endl;
1206                                 break;
1207                         }
1208
1209                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1210
1211                         setSpellChecker();
1212
1213                         theApp()->resetGui();
1214
1215                         /// We force the redraw in any case because there might be
1216                         /// some screen font changes.
1217                         /// FIXME: only the current view will be updated. the Gui
1218                         /// class is able to furnish the list of views.
1219                         updateFlags = Update::Force;
1220                         break;
1221                 }
1222
1223                 case LFUN_BOOKMARK_GOTO:
1224                         // go to bookmark, open unopened file and switch to buffer if necessary
1225                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1226                         updateFlags = Update::FitCursor;
1227                         break;
1228
1229                 case LFUN_BOOKMARK_CLEAR:
1230                         theSession().bookmarks().clear();
1231                         break;
1232
1233                 default:
1234                         LASSERT(theApp(), /**/);
1235                         // Let the frontend dispatch its own actions.
1236                         if (theApp()->dispatch(cmd))
1237                                 // Nothing more to do.
1238                                 return;
1239
1240                         // Everything below is only for active lyx_view_
1241                         if (lyx_view_ == 0)
1242                                 break;
1243
1244                         // Start an undo group. This may be needed for
1245                         // some stuff like inset-apply on labels.
1246                         if (theBufferList().isLoaded(buffer))
1247                                 buffer->undo().beginUndoGroup();
1248                                 
1249                         // Let the current LyXView dispatch its own actions.
1250                         if (lyx_view_->dispatch(cmd)) {
1251                                 if (lyx_view_->currentBufferView()) {
1252                                         updateFlags = lyx_view_->currentBufferView()->cursor().result().update();
1253                                         if (theBufferList().isLoaded(buffer))
1254                                                 buffer->undo().endUndoGroup();
1255                                 }
1256                                 break;
1257                         }
1258
1259                         LASSERT(lyx_view_->currentBufferView(), /**/);
1260
1261                         // Let the current BufferView dispatch its own actions.
1262                         if (lyx_view_->currentBufferView()->dispatch(cmd)) {
1263                                 // The BufferView took care of its own updates if needed.
1264                                 updateFlags = Update::None;
1265                                 if (theBufferList().isLoaded(buffer))
1266                                         buffer->undo().endUndoGroup();
1267                                 break;
1268                         }
1269
1270                         // OK, so try the Buffer itself
1271                         DispatchResult dr;
1272                         BufferView * bv = lyx_view_->currentBufferView();
1273                         bv->buffer().dispatch(cmd, dr);
1274                         if (dr.dispatched()) {
1275                                 updateFlags = dr.update();
1276                                 break;
1277                         }
1278                         // OK, so try with the document Buffer.
1279                         BufferView * doc_bv = lyx_view_->documentBufferView();
1280                         if (doc_bv) {
1281                                 buffer = &(doc_bv->buffer());
1282                                 buffer->dispatch(cmd, dr);
1283                                 if (dr.dispatched()) {
1284                                         updateFlags = dr.update();
1285                                         break;
1286                                 }
1287                         }
1288
1289                         // Is this a function that acts on inset at point?
1290                         Inset * inset = bv->cursor().nextInset();
1291                         if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1292                             && inset) {
1293                                 bv->cursor().result().dispatched(true);
1294                                 bv->cursor().result().update(Update::FitCursor | Update::Force);
1295                                 FuncRequest tmpcmd = cmd;
1296                                 inset->dispatch(bv->cursor(), tmpcmd);
1297                                 if (bv->cursor().result().dispatched()) {
1298                                         updateFlags = bv->cursor().result().update();
1299                                         break;
1300                                 }
1301                         }
1302
1303                         // Let the current Cursor dispatch its own actions.
1304                         Cursor old = bv->cursor();
1305                         bv->cursor().getPos(cursorPosBeforeDispatchX_,
1306                                                 cursorPosBeforeDispatchY_);
1307                         bv->cursor().dispatch(cmd);
1308
1309                         // notify insets we just left
1310                         if (bv->cursor() != old) {
1311                                 old.fixIfBroken();
1312                                 bool badcursor = notifyCursorLeavesOrEnters(old, bv->cursor());
1313                                 if (badcursor)
1314                                         bv->cursor().fixIfBroken();
1315                         }
1316
1317                         if (theBufferList().isLoaded(buffer))
1318                                 buffer->undo().endUndoGroup();
1319
1320                         // update completion. We do it here and not in
1321                         // processKeySym to avoid another redraw just for a
1322                         // changed inline completion
1323                         if (cmd.origin == FuncRequest::KEYBOARD) {
1324                                 if (cmd.action == LFUN_SELF_INSERT
1325                                     || (cmd.action == LFUN_ERT_INSERT && bv->cursor().inMathed()))
1326                                         lyx_view_->updateCompletion(bv->cursor(), true, true);
1327                                 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1328                                         lyx_view_->updateCompletion(bv->cursor(), false, true);
1329                                 else
1330                                         lyx_view_->updateCompletion(bv->cursor(), false, false);
1331                         }
1332
1333                         updateFlags = bv->cursor().result().update();
1334                 }
1335
1336                 // if we executed a mutating lfun, mark the buffer as dirty
1337                 if (theBufferList().isLoaded(buffer) && flag.enabled()
1338                     && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1339                     && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1340                         buffer->markDirty();                    
1341
1342                 if (lyx_view_ && lyx_view_->currentBufferView()) {
1343                         // BufferView::update() updates the ViewMetricsInfo and
1344                         // also initializes the position cache for all insets in
1345                         // (at least partially) visible top-level paragraphs.
1346                         // We will redraw the screen only if needed.
1347                         lyx_view_->currentBufferView()->processUpdateFlags(updateFlags);
1348
1349                         // Do we have a selection?
1350                         theSelection().haveSelection(
1351                                 lyx_view_->currentBufferView()->cursor().selection());
1352                         
1353                         // update gui
1354                         lyx_view_->restartCursor();
1355                 }
1356         }
1357         if (lyx_view_) {
1358                 // Some messages may already be translated, so we cannot use _()
1359                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1360         }
1361 }
1362
1363
1364 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1365 {
1366         const bool verbose = (cmd.origin == FuncRequest::MENU
1367                               || cmd.origin == FuncRequest::TOOLBAR
1368                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1369
1370         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1371                 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1372                 if (!msg.empty())
1373                         lyx_view_->message(msg);
1374                 return;
1375         }
1376
1377         docstring dispatch_msg = msg;
1378         if (!dispatch_msg.empty())
1379                 dispatch_msg += ' ';
1380
1381         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1382
1383         bool argsadded = false;
1384
1385         if (!cmd.argument().empty()) {
1386                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1387                         comname += ' ' + cmd.argument();
1388                         argsadded = true;
1389                 }
1390         }
1391
1392         docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1393
1394         if (!shortcuts.empty())
1395                 comname += ": " + shortcuts;
1396         else if (!argsadded && !cmd.argument().empty())
1397                 comname += ' ' + cmd.argument();
1398
1399         if (!comname.empty()) {
1400                 comname = rtrim(comname);
1401                 dispatch_msg += '(' + rtrim(comname) + ')';
1402         }
1403
1404         LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1405         if (!dispatch_msg.empty())
1406                 lyx_view_->message(dispatch_msg);
1407 }
1408
1409
1410 // Each "lyx_view_" should have it's own message method. lyxview and
1411 // the minibuffer would use the minibuffer, but lyxserver would
1412 // send an ERROR signal to its client.  Alejandro 970603
1413 // This function is bit problematic when it comes to NLS, to make the
1414 // lyx servers client be language indepenent we must not translate
1415 // strings sent to this func.
1416 void LyXFunc::setErrorMessage(docstring const & m) const
1417 {
1418         dispatch_buffer = m;
1419         errorstat = true;
1420 }
1421
1422
1423 void LyXFunc::setMessage(docstring const & m) const
1424 {
1425         dispatch_buffer = m;
1426 }
1427
1428
1429 docstring LyXFunc::viewStatusMessage()
1430 {
1431         // When meta-fake key is pressed, show the key sequence so far + "M-".
1432         if (wasMetaKey())
1433                 return keyseq.print(KeySequence::ForGui) + "M-";
1434
1435         // Else, when a non-complete key sequence is pressed,
1436         // show the available options.
1437         if (keyseq.length() > 0 && !keyseq.deleted())
1438                 return keyseq.printOptions(true);
1439
1440         LASSERT(lyx_view_, /**/);
1441         if (!lyx_view_->currentBufferView())
1442                 return _("Welcome to LyX!");
1443
1444         return lyx_view_->currentBufferView()->cursor().currentState();
1445 }
1446
1447
1448 bool LyXFunc::wasMetaKey() const
1449 {
1450         return (meta_fake_bit != NoModifier);
1451 }
1452
1453
1454 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1455 {
1456         lyx_view_->message(_("Converting document to new document class..."));
1457         
1458         StableDocIterator backcur(lyx_view_->currentBufferView()->cursor());
1459         ErrorList & el = buf->errorList("Class Switch");
1460         cap::switchBetweenClasses(
1461                         oldlayout, buf->params().documentClassPtr(),
1462                         static_cast<InsetText &>(buf->inset()), el);
1463
1464         lyx_view_->currentBufferView()->setCursor(backcur.asDocIterator(buf));
1465
1466         buf->errors("Class Switch");
1467         buf->updateLabels();
1468 }
1469
1470
1471 namespace {
1472
1473 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1474 {
1475         // Why the switch you might ask. It is a trick to ensure that all
1476         // the elements in the LyXRCTags enum is handled. As you can see
1477         // there are no breaks at all. So it is just a huge fall-through.
1478         // The nice thing is that we will get a warning from the compiler
1479         // if we forget an element.
1480         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1481         switch (tag) {
1482         case LyXRC::RC_ACCEPT_COMPOUND:
1483         case LyXRC::RC_ALT_LANG:
1484         case LyXRC::RC_PLAINTEXT_LINELEN:
1485         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1486         case LyXRC::RC_AUTOCORRECTION_MATH:
1487         case LyXRC::RC_AUTOREGIONDELETE:
1488         case LyXRC::RC_AUTORESET_OPTIONS:
1489         case LyXRC::RC_AUTOSAVE:
1490         case LyXRC::RC_AUTO_NUMBER:
1491         case LyXRC::RC_BACKUPDIR_PATH:
1492         case LyXRC::RC_BIBTEX_ALTERNATIVES:
1493         case LyXRC::RC_BIBTEX_COMMAND:
1494         case LyXRC::RC_BINDFILE:
1495         case LyXRC::RC_CHECKLASTFILES:
1496         case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1497         case LyXRC::RC_COMPLETION_INLINE_DELAY:
1498         case LyXRC::RC_COMPLETION_INLINE_DOTS:
1499         case LyXRC::RC_COMPLETION_INLINE_MATH:
1500         case LyXRC::RC_COMPLETION_INLINE_TEXT:
1501         case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1502         case LyXRC::RC_COMPLETION_POPUP_DELAY:
1503         case LyXRC::RC_COMPLETION_POPUP_MATH:
1504         case LyXRC::RC_COMPLETION_POPUP_TEXT:
1505         case LyXRC::RC_USELASTFILEPOS:
1506         case LyXRC::RC_LOADSESSION:
1507         case LyXRC::RC_CHKTEX_COMMAND:
1508         case LyXRC::RC_CONVERTER:
1509         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1510         case LyXRC::RC_COPIER:
1511         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1512         case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1513         case LyXRC::RC_DATE_INSERT_FORMAT:
1514         case LyXRC::RC_DEFAULT_LANGUAGE:
1515         case LyXRC::RC_GUI_LANGUAGE:
1516         case LyXRC::RC_DEFAULT_PAPERSIZE:
1517         case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1518         case LyXRC::RC_DEFFILE:
1519         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1520         case LyXRC::RC_DISPLAY_GRAPHICS:
1521         case LyXRC::RC_DOCUMENTPATH:
1522                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1523                         FileName path(lyxrc_new.document_path);
1524                         if (path.exists() && path.isDirectory())
1525                                 package().document_dir() = FileName(lyxrc.document_path);
1526                 }
1527         case LyXRC::RC_EDITOR_ALTERNATIVES:
1528         case LyXRC::RC_ESC_CHARS:
1529         case LyXRC::RC_EXAMPLEPATH:
1530         case LyXRC::RC_FONT_ENCODING:
1531         case LyXRC::RC_FORMAT:
1532         case LyXRC::RC_GROUP_LAYOUTS:
1533         case LyXRC::RC_HUNSPELLDIR_PATH:
1534         case LyXRC::RC_INDEX_ALTERNATIVES:
1535         case LyXRC::RC_INDEX_COMMAND:
1536         case LyXRC::RC_JBIBTEX_COMMAND:
1537         case LyXRC::RC_JINDEX_COMMAND:
1538         case LyXRC::RC_NOMENCL_COMMAND:
1539         case LyXRC::RC_INPUT:
1540         case LyXRC::RC_KBMAP:
1541         case LyXRC::RC_KBMAP_PRIMARY:
1542         case LyXRC::RC_KBMAP_SECONDARY:
1543         case LyXRC::RC_LABEL_INIT_LENGTH:
1544         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1545         case LyXRC::RC_LANGUAGE_AUTO_END:
1546         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1547         case LyXRC::RC_LANGUAGE_COMMAND_END:
1548         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1549         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1550         case LyXRC::RC_LANGUAGE_PACKAGE:
1551         case LyXRC::RC_LANGUAGE_USE_BABEL:
1552         case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1553         case LyXRC::RC_MACRO_EDIT_STYLE:
1554         case LyXRC::RC_MAKE_BACKUP:
1555         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1556         case LyXRC::RC_MOUSE_WHEEL_SPEED:
1557         case LyXRC::RC_NUMLASTFILES:
1558         case LyXRC::RC_PARAGRAPH_MARKERS:
1559         case LyXRC::RC_PATH_PREFIX:
1560                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1561                         prependEnvPath("PATH", lyxrc.path_prefix);
1562                 }
1563         case LyXRC::RC_PERS_DICT:
1564         case LyXRC::RC_PREVIEW:
1565         case LyXRC::RC_PREVIEW_HASHED_LABELS:
1566         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1567         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1568         case LyXRC::RC_PRINTCOPIESFLAG:
1569         case LyXRC::RC_PRINTER:
1570         case LyXRC::RC_PRINTEVENPAGEFLAG:
1571         case LyXRC::RC_PRINTEXSTRAOPTIONS:
1572         case LyXRC::RC_PRINTFILEEXTENSION:
1573         case LyXRC::RC_PRINTLANDSCAPEFLAG:
1574         case LyXRC::RC_PRINTODDPAGEFLAG:
1575         case LyXRC::RC_PRINTPAGERANGEFLAG:
1576         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
1577         case LyXRC::RC_PRINTPAPERFLAG:
1578         case LyXRC::RC_PRINTREVERSEFLAG:
1579         case LyXRC::RC_PRINTSPOOL_COMMAND:
1580         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
1581         case LyXRC::RC_PRINTTOFILE:
1582         case LyXRC::RC_PRINTTOPRINTER:
1583         case LyXRC::RC_PRINT_ADAPTOUTPUT:
1584         case LyXRC::RC_PRINT_COMMAND:
1585         case LyXRC::RC_RTL_SUPPORT:
1586         case LyXRC::RC_SCREEN_DPI:
1587         case LyXRC::RC_SCREEN_FONT_ROMAN:
1588         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
1589         case LyXRC::RC_SCREEN_FONT_SANS:
1590         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
1591         case LyXRC::RC_SCREEN_FONT_SCALABLE:
1592         case LyXRC::RC_SCREEN_FONT_SIZES:
1593         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
1594         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
1595         case LyXRC::RC_GEOMETRY_SESSION:
1596         case LyXRC::RC_SCREEN_ZOOM:
1597         case LyXRC::RC_SERVERPIPE:
1598         case LyXRC::RC_SET_COLOR:
1599         case LyXRC::RC_SHOW_BANNER:
1600         case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
1601         case LyXRC::RC_SPELL_COMMAND:
1602         case LyXRC::RC_SPELLCHECKER:
1603         case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
1604         case LyXRC::RC_SPLITINDEX_COMMAND:
1605         case LyXRC::RC_TEMPDIRPATH:
1606         case LyXRC::RC_TEMPLATEPATH:
1607         case LyXRC::RC_TEX_ALLOWS_SPACES:
1608         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
1609                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
1610                         os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
1611                 }
1612         case LyXRC::RC_THESAURUSDIRPATH:
1613         case LyXRC::RC_UIFILE:
1614         case LyXRC::RC_USER_EMAIL:
1615         case LyXRC::RC_USER_NAME:
1616         case LyXRC::RC_USETEMPDIR:
1617         case LyXRC::RC_USE_ALT_LANG:
1618         case LyXRC::RC_USE_CONVERTER_CACHE:
1619         case LyXRC::RC_USE_ESC_CHARS:
1620         case LyXRC::RC_USE_INP_ENC:
1621         case LyXRC::RC_USE_PERS_DICT:
1622         case LyXRC::RC_USE_TOOLTIP:
1623         case LyXRC::RC_USE_PIXMAP_CACHE:
1624         case LyXRC::RC_USE_SPELL_LIB:
1625         case LyXRC::RC_VIEWDVI_PAPEROPTION:
1626         case LyXRC::RC_SORT_LAYOUTS:
1627         case LyXRC::RC_FULL_SCREEN_LIMIT:
1628         case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
1629         case LyXRC::RC_FULL_SCREEN_MENUBAR:
1630         case LyXRC::RC_FULL_SCREEN_TABBAR:
1631         case LyXRC::RC_FULL_SCREEN_TOOLBARS:
1632         case LyXRC::RC_FULL_SCREEN_WIDTH:
1633         case LyXRC::RC_VISUAL_CURSOR:
1634         case LyXRC::RC_VIEWER:
1635         case LyXRC::RC_VIEWER_ALTERNATIVES:
1636         case LyXRC::RC_LAST:
1637                 break;
1638         }
1639 }
1640
1641 } // namespace anon
1642 } // namespace lyx