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