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