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