]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
Enable File -> Revert when the file is externally modified, rename this menu item...
[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 #include <vector>
22
23 #include "LyXFunc.h"
24
25 #include "BranchList.h"
26 #include "Buffer.h"
27 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
31 #include "bufferview_funcs.h"
32 #include "Cursor.h"
33 #include "CutAndPaste.h"
34 #include "debug.h"
35 #include "DispatchResult.h"
36 #include "Encoding.h"
37 #include "ErrorList.h"
38 #include "Exporter.h"
39 #include "Format.h"
40 #include "FuncRequest.h"
41 #include "FuncStatus.h"
42 #include "gettext.h"
43 #include "Importer.h"
44 #include "InsetIterator.h"
45 #include "Intl.h"
46 #include "KeyMap.h"
47 #include "Language.h"
48 #include "Color.h"
49 #include "Session.h"
50 #include "LyX.h"
51 #include "callback.h"
52 #include "LyXAction.h"
53 #include "lyxfind.h"
54 #include "Lexer.h"
55 #include "LyXRC.h"
56 #include "Row.h"
57 #include "Server.h"
58 #include "TextClassList.h"
59 #include "LyXVC.h"
60 #include "Paragraph.h"
61 #include "ParIterator.h"
62 #include "ParagraphParameters.h"
63 #include "Undo.h"
64
65 #include "insets/InsetBox.h"
66 #include "insets/InsetBranch.h"
67 #include "insets/InsetCommand.h"
68 #include "insets/InsetERT.h"
69 #include "insets/InsetExternal.h"
70 #include "insets/InsetFloat.h"
71 #include "insets/InsetListings.h"
72 #include "insets/InsetGraphics.h"
73 #include "insets/InsetInclude.h"
74 #include "insets/InsetNote.h"
75 #include "insets/InsetTabular.h"
76 #include "insets/InsetVSpace.h"
77 #include "insets/InsetWrap.h"
78
79 #include "frontends/Application.h"
80 #include "frontends/alert.h"
81 #include "frontends/Dialogs.h"
82 #include "frontends/FileDialog.h"
83 #include "frontends/FontLoader.h"
84 #include "frontends/Gui.h"
85 #include "frontends/KeySymbol.h"
86 #include "frontends/LyXView.h"
87 #include "frontends/Menubar.h"
88 #include "frontends/Toolbars.h"
89 #include "frontends/Selection.h"
90
91 #include "support/environment.h"
92 #include "support/FileFilterList.h"
93 #include "support/filetools.h"
94 #include "support/ForkedcallsController.h"
95 #include "support/fs_extras.h"
96 #include "support/lstrings.h"
97 #include "support/Path.h"
98 #include "support/Package.h"
99 #include "support/Systemcall.h"
100 #include "support/convert.h"
101 #include "support/os.h"
102
103 #include <boost/current_function.hpp>
104 #include <boost/filesystem/operations.hpp>
105
106 #include <sstream>
107
108
109 namespace lyx {
110
111 using bv_funcs::freefont2string;
112
113 using support::absolutePath;
114 using support::addName;
115 using support::addPath;
116 using support::bformat;
117 using support::changeExtension;
118 using support::contains;
119 using support::FileFilterList;
120 using support::FileName;
121 using support::fileSearch;
122 using support::ForkedcallsController;
123 using support::i18nLibFileSearch;
124 using support::isDirWriteable;
125 using support::isFileReadable;
126 using support::isStrInt;
127 using support::makeAbsPath;
128 using support::makeDisplayPath;
129 using support::package;
130 using support::quoteName;
131 using support::rtrim;
132 using support::split;
133 using support::subst;
134 using support::Systemcall;
135 using support::token;
136 using support::trim;
137 using support::prefixIs;
138
139 using std::endl;
140 using std::make_pair;
141 using std::pair;
142 using std::string;
143 using std::istringstream;
144 using std::ostringstream;
145
146 namespace Alert = frontend::Alert;
147 namespace fs = boost::filesystem;
148
149
150 namespace {
151
152 bool getLocalStatus(Cursor cursor,
153                FuncRequest const & cmd, FuncStatus & status)
154 {
155         // Try to fix cursor in case it is broken.
156         cursor.fixIfBroken();
157
158         // This is, of course, a mess. Better create a new doc iterator and use
159         // this in Inset::getStatus. This might require an additional
160         // BufferView * arg, though (which should be avoided)
161         //Cursor safe = *this;
162         bool res = false;
163         for ( ; cursor.depth(); cursor.pop()) {
164                 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
165                 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
166                 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
167                 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168
169                 // The inset's getStatus() will return 'true' if it made
170                 // a definitive decision on whether it want to handle the
171                 // request or not. The result of this decision is put into
172                 // the 'status' parameter.
173                 if (cursor.inset().getStatus(cursor, cmd, status)) {
174                         res = true;
175                         break;
176                 }
177         }
178         return res;
179 }
180
181
182 /** Return the change status at cursor position, taking in account the
183  * status at each level of the document iterator (a table in a deleted
184  * footnote is deleted).
185  * When \param outer is true, the top slice is not looked at.
186  */
187 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 {
189         size_t const depth = dit.depth() - (outer ? 1 : 0);
190
191         for (size_t i = 0 ; i < depth ; ++i) {
192                 CursorSlice const & slice = dit[i];
193                 if (!slice.inset().inMathed()
194                     && slice.pos() < slice.paragraph().size()) {
195                         Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
196                         if (ch != Change::UNCHANGED)
197                                 return ch;
198                 }
199         }
200         return Change::UNCHANGED;
201 }
202
203 }
204
205 LyXFunc::LyXFunc()
206         : lyx_view_(0),
207         encoded_last_key(0),
208         meta_fake_bit(key_modifier::none)
209 {
210 }
211
212
213 void LyXFunc::initKeySequences(KeyMap * kb)
214 {
215         keyseq.reset(new KeySequence(kb, kb));
216         cancel_meta_seq.reset(new KeySequence(kb, kb));
217 }
218
219
220 void LyXFunc::setLyXView(LyXView * lv)
221 {
222         if (!quitting && lyx_view_ && lyx_view_ != lv)
223                 // save current selection to the selection buffer to allow
224                 // middle-button paste in another window
225                 cap::saveSelection(lyx_view_->view()->cursor());
226         lyx_view_ = lv;
227 }
228
229
230 void LyXFunc::handleKeyFunc(kb_action action)
231 {
232         char_type c = encoded_last_key;
233
234         if (keyseq->length())
235                 c = 0;
236
237         lyx_view_->view()->getIntl().getTransManager().deadkey(
238                 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
239         // Need to clear, in case the minibuffer calls these
240         // actions
241         keyseq->clear();
242         // copied verbatim from do_accent_char
243         view()->cursor().resetAnchor();
244         view()->update();
245 }
246
247
248 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
249 {
250         BOOST_ASSERT(lyx_view_);
251         if (!LyX::ref().session().bookmarks().isValid(idx))
252                 return;
253         BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
254         BOOST_ASSERT(!bm.filename.empty());
255         string const file = bm.filename.absFilename();
256         // if the file is not opened, open it.
257         if (!theBufferList().exists(file)) {
258                 if (openFile)
259                         dispatch(FuncRequest(LFUN_FILE_OPEN, file));
260                 else
261                         return;
262         }
263         // open may fail, so we need to test it again
264         if (theBufferList().exists(file)) {
265                 // if the current buffer is not that one, switch to it.
266                 if (lyx_view_->buffer()->fileName() != file) {
267                         if (switchToBuffer)
268                                 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
269                         else
270                                 return;
271                 }
272                 // moveToPosition use par_id, and par_pit and return new par_id.
273                 pit_type new_pit;
274                 pos_type new_pos;
275                 int new_id;
276                 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
277                 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
278                 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
279                 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
280                         const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
281         }
282 }
283
284
285 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
286 {
287         LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
288
289         // Do nothing if we have nothing (JMarc)
290         if (!keysym->isOK()) {
291                 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
292                                    << endl;
293                 return;
294         }
295
296         if (keysym->isModifier()) {
297                 LYXERR(Debug::KEY) << "isModifier true" << endl;
298                 return;
299         }
300
301         //Encoding const * encoding = view()->cursor().getEncoding();
302         //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
303         // FIXME: encoded_last_key shadows the member variable of the same
304         // name. Is that intended?
305         char_type encoded_last_key = keysym->getUCSEncoded();
306
307         // Do a one-deep top-level lookup for
308         // cancel and meta-fake keys. RVDK_PATCH_5
309         cancel_meta_seq->reset();
310
311         FuncRequest func = cancel_meta_seq->addkey(keysym, state);
312         LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
313                            << " action first set to [" << func.action << ']'
314                            << endl;
315
316         // When not cancel or meta-fake, do the normal lookup.
317         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
318         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
319         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
320                 // remove Caps Lock and Mod2 as a modifiers
321                 func = keyseq->addkey(keysym, (state | meta_fake_bit));
322                 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
323                                    << "action now set to ["
324                                    << func.action << ']' << endl;
325         }
326
327         // Dont remove this unless you know what you are doing.
328         meta_fake_bit = key_modifier::none;
329
330         // Can this happen now ?
331         if (func.action == LFUN_NOACTION) {
332                 func = FuncRequest(LFUN_COMMAND_PREFIX);
333         }
334
335         LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
336                << " Key [action="
337                << func.action << "]["
338                << to_utf8(keyseq->print(false)) << ']'
339                << endl;
340
341         // already here we know if it any point in going further
342         // why not return already here if action == -1 and
343         // num_bytes == 0? (Lgb)
344
345         if (keyseq->length() > 1) {
346                 lyx_view_->message(keyseq->print(true));
347         }
348
349
350         // Maybe user can only reach the key via holding down shift.
351         // Let's see. But only if shift is the only modifier
352         if (func.action == LFUN_UNKNOWN_ACTION &&
353             state == key_modifier::shift) {
354                 LYXERR(Debug::KEY) << "Trying without shift" << endl;
355                 func = keyseq->addkey(keysym, key_modifier::none);
356                 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
357         }
358
359         if (func.action == LFUN_UNKNOWN_ACTION) {
360                 // Hmm, we didn't match any of the keysequences. See
361                 // if it's normal insertable text not already covered
362                 // by a binding
363                 if (keysym->isText() && keyseq->length() == 1) {
364                         LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
365                         func = FuncRequest(LFUN_SELF_INSERT,
366                                            FuncRequest::KEYBOARD);
367                 } else {
368                         LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
369                         lyx_view_->message(_("Unknown function."));
370                         return;
371                 }
372         }
373
374         if (func.action == LFUN_SELF_INSERT) {
375                 if (encoded_last_key != 0) {
376                         docstring const arg(1, encoded_last_key);
377                         dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
378                                              FuncRequest::KEYBOARD));
379                         LYXERR(Debug::KEY)
380                                 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
381                 }
382         } else {
383                 dispatch(func);
384         }
385 }
386
387
388 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
389 {
390         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
391         FuncStatus flag;
392
393         Cursor & cur = view()->cursor();
394
395         /* In LyX/Mac, when a dialog is open, the menus of the
396            application can still be accessed without giving focus to
397            the main window. In this case, we want to disable the menu
398            entries that are buffer-related.
399
400            Note that this code is not perfect, as bug 1941 attests:
401            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
402         */
403         Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
404         if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
405                 buf = 0;
406
407         if (cmd.action == LFUN_NOACTION) {
408                 flag.message(from_utf8(N_("Nothing to do")));
409                 flag.enabled(false);
410                 return flag;
411         }
412
413         switch (cmd.action) {
414         case LFUN_UNKNOWN_ACTION:
415 #ifndef HAVE_LIBAIKSAURUS
416         case LFUN_THESAURUS_ENTRY:
417 #endif
418                 flag.unknown(true);
419                 flag.enabled(false);
420                 break;
421
422         default:
423                 break;
424         }
425
426         if (flag.unknown()) {
427                 flag.message(from_utf8(N_("Unknown action")));
428                 return flag;
429         }
430
431         if (!flag.enabled()) {
432                 if (flag.message().empty())
433                         flag.message(from_utf8(N_("Command disabled")));
434                 return flag;
435         }
436
437         // Check whether we need a buffer
438         if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
439                 // no, exit directly
440                 flag.message(from_utf8(N_("Command not allowed with"
441                                     "out any document open")));
442                 flag.enabled(false);
443                 return flag;
444         }
445
446         // I would really like to avoid having this switch and rather try to
447         // encode this in the function itself.
448         // -- And I'd rather let an inset decide which LFUNs it is willing
449         // to handle (Andre')
450         bool enable = true;
451         switch (cmd.action) {
452         case LFUN_BUFFER_TOGGLE_READ_ONLY:
453                 flag.setOnOff(buf->isReadonly());
454                 break;
455
456         case LFUN_BUFFER_SWITCH:
457                 // toggle on the current buffer, but do not toggle off
458                 // the other ones (is that a good idea?)
459                 if (to_utf8(cmd.argument()) == buf->fileName())
460                         flag.setOnOff(true);
461                 break;
462
463         case LFUN_BUFFER_EXPORT:
464                 enable = cmd.argument() == "custom"
465                         || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
466                 break;
467
468         case LFUN_BUFFER_CHKTEX:
469                 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
470                 break;
471
472         case LFUN_BUILD_PROGRAM:
473                 enable = Exporter::isExportable(*buf, "program");
474                 break;
475
476         case LFUN_LAYOUT_TABULAR:
477                 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
478                 break;
479
480         case LFUN_LAYOUT:
481         case LFUN_LAYOUT_PARAGRAPH:
482                 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
483                 break;
484
485         case LFUN_VC_REGISTER:
486                 enable = !buf->lyxvc().inUse();
487                 break;
488         case LFUN_VC_CHECK_IN:
489                 enable = buf->lyxvc().inUse() && !buf->isReadonly();
490                 break;
491         case LFUN_VC_CHECK_OUT:
492                 enable = buf->lyxvc().inUse() && buf->isReadonly();
493                 break;
494         case LFUN_VC_REVERT:
495         case LFUN_VC_UNDO_LAST:
496                 enable = buf->lyxvc().inUse();
497                 break;
498         case LFUN_BUFFER_RELOAD:
499                 enable = !buf->isUnnamed() && fs::exists(buf->fileName())
500                         && (!buf->isClean() || buf->isExternallyModified(Buffer::timestamp_method));
501                 break;
502
503         case LFUN_INSET_SETTINGS: {
504                 enable = false;
505                 if (!cur)
506                         break;
507                 Inset::Code code = cur.inset().lyxCode();
508                 switch (code) {
509                         case Inset::TABULAR_CODE:
510                                 enable = cmd.argument() == "tabular";
511                                 break;
512                         case Inset::ERT_CODE:
513                                 enable = cmd.argument() == "ert";
514                                 break;
515                         case Inset::FLOAT_CODE:
516                                 enable = cmd.argument() == "float";
517                                 break;
518                         case Inset::WRAP_CODE:
519                                 enable = cmd.argument() == "wrap";
520                                 break;
521                         case Inset::NOTE_CODE:
522                                 enable = cmd.argument() == "note";
523                                 break;
524                         case Inset::BRANCH_CODE:
525                                 enable = cmd.argument() == "branch";
526                                 break;
527                         case Inset::BOX_CODE:
528                                 enable = cmd.argument() == "box";
529                                 break;
530                         case Inset::LISTINGS_CODE:
531                                 enable = cmd.argument() == "listings";
532                                 break;
533                         default:
534                                 break;
535                 }
536                 break;
537         }
538
539         case LFUN_INSET_APPLY: {
540                 string const name = cmd.getArg(0);
541                 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
542                 if (inset) {
543                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
544                         FuncStatus fs;
545                         if (!inset->getStatus(cur, fr, fs)) {
546                                 // Every inset is supposed to handle this
547                                 BOOST_ASSERT(false);
548                         }
549                         flag |= fs;
550                 } else {
551                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
552                         flag |= getStatus(fr);
553                 }
554                 enable = flag.enabled();
555                 break;
556         }
557
558         case LFUN_DIALOG_TOGGLE:
559                 flag.setOnOff(lyx_view_->getDialogs().visible(cmd.getArg(0)));
560                 // fall through to set "enable"
561         case LFUN_DIALOG_SHOW: {
562                 string const name = cmd.getArg(0);
563                 if (!buf)
564                         enable = name == "aboutlyx"
565                                 || name == "file" //FIXME: should be removed.
566                                 || name == "prefs"
567                                 || name == "texinfo";
568                 else if (name == "print")
569                         enable = Exporter::isExportable(*buf, "dvi")
570                                 && lyxrc.print_command != "none";
571                 else if (name == "character")
572                         enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
573                                 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
574                 else if (name == "latexlog")
575                         enable = isFileReadable(FileName(buf->getLogName().second));
576                 else if (name == "spellchecker")
577 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
578                         enable = !buf->isReadonly();
579 #else
580                         enable = false;
581 #endif
582                 else if (name == "vclog")
583                         enable = buf->lyxvc().inUse();
584                 break;
585         }
586
587         case LFUN_DIALOG_SHOW_NEW_INSET:
588                 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
589                         cur.inset().lyxCode() != Inset::LISTINGS_CODE;
590                 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
591                         FuncStatus flag;
592                         if (cur.inset().getStatus(cur, cmd, flag))
593                                 return flag;
594                 }
595                 break;
596
597         case LFUN_DIALOG_UPDATE: {
598                 string const name = cmd.getArg(0);
599                 if (!buf)
600                         enable = name == "prefs";
601                 break;
602         }
603
604         case LFUN_CITATION_INSERT: {
605                 FuncRequest fr(LFUN_INSET_INSERT, "citation");
606                 enable = getStatus(fr).enabled();
607                 break;
608         }
609
610         case LFUN_BUFFER_WRITE: {
611                 enable = view()->buffer()->isUnnamed()
612                         || !view()->buffer()->isClean();
613                 break;
614         }
615
616
617         case LFUN_BUFFER_WRITE_ALL: {
618         // We enable the command only if there are some modified buffers
619                 Buffer * first = theBufferList().first();
620                 bool modified = false;
621                 if (first) {
622                         Buffer * b = first;
623                 
624                 // We cannot use a for loop as the buffer list is a cycle.
625                         do {
626                                 if (!b->isClean()) {
627                                         modified = true;
628                                         break;
629                                 }
630                                 b = theBufferList().next(b);
631                         } while (b != first); 
632                 }
633         
634                 enable = modified;
635
636                 break;
637         }
638
639         case LFUN_BOOKMARK_GOTO: {
640                 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
641                 enable = LyX::ref().session().bookmarks().isValid(num);
642                 break;
643         }
644
645         case LFUN_BOOKMARK_CLEAR:
646                 enable = LyX::ref().session().bookmarks().size() > 0;
647                 break;
648
649         case LFUN_TOOLBAR_TOGGLE: {
650                 bool const current = lyx_view_->getToolbars().visible(cmd.getArg(0));
651                 flag.setOnOff(current);
652                 break;
653         }
654         case LFUN_WINDOW_CLOSE: {
655                 enable = (theApp()->gui().viewIds().size() > 1);
656                 break;
657         }
658
659         // this one is difficult to get right. As a half-baked
660         // solution, we consider only the first action of the sequence
661         case LFUN_COMMAND_SEQUENCE: {
662                 // argument contains ';'-terminated commands
663                 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
664                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
665                 func.origin = cmd.origin;
666                 flag = getStatus(func);
667         }
668
669         case LFUN_BUFFER_NEW:
670         case LFUN_BUFFER_NEW_TEMPLATE:
671         case LFUN_WORD_FIND_FORWARD:
672         case LFUN_WORD_FIND_BACKWARD:
673         case LFUN_COMMAND_PREFIX:
674         case LFUN_COMMAND_EXECUTE:
675         case LFUN_CANCEL:
676         case LFUN_META_PREFIX:
677         case LFUN_BUFFER_CLOSE:
678         case LFUN_BUFFER_WRITE_AS:
679         case LFUN_BUFFER_UPDATE:
680         case LFUN_BUFFER_VIEW:
681         case LFUN_BUFFER_IMPORT:
682         case LFUN_BUFFER_AUTO_SAVE:
683         case LFUN_RECONFIGURE:
684         case LFUN_HELP_OPEN:
685         case LFUN_FILE_NEW:
686         case LFUN_FILE_OPEN:
687         case LFUN_DROP_LAYOUTS_CHOICE:
688         case LFUN_MENU_OPEN:
689         case LFUN_SERVER_GET_NAME:
690         case LFUN_SERVER_NOTIFY:
691         case LFUN_SERVER_GOTO_FILE_ROW:
692         case LFUN_DIALOG_HIDE:
693         case LFUN_DIALOG_DISCONNECT_INSET:
694         case LFUN_BUFFER_CHILD_OPEN:
695         case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
696         case LFUN_KEYMAP_OFF:
697         case LFUN_KEYMAP_PRIMARY:
698         case LFUN_KEYMAP_SECONDARY:
699         case LFUN_KEYMAP_TOGGLE:
700         case LFUN_REPEAT:
701         case LFUN_BUFFER_EXPORT_CUSTOM:
702         case LFUN_BUFFER_PRINT:
703         case LFUN_PREFERENCES_SAVE:
704         case LFUN_SCREEN_FONT_UPDATE:
705         case LFUN_SET_COLOR:
706         case LFUN_MESSAGE:
707         case LFUN_EXTERNAL_EDIT:
708         case LFUN_GRAPHICS_EDIT:
709         case LFUN_ALL_INSETS_TOGGLE:
710         case LFUN_BUFFER_LANGUAGE:
711         case LFUN_TEXTCLASS_APPLY:
712         case LFUN_TEXTCLASS_LOAD:
713         case LFUN_BUFFER_SAVE_AS_DEFAULT:
714         case LFUN_BUFFER_PARAMS_APPLY:
715         case LFUN_LYXRC_APPLY:
716         case LFUN_BUFFER_NEXT:
717         case LFUN_BUFFER_PREVIOUS:
718         case LFUN_WINDOW_NEW:
719         case LFUN_LYX_QUIT:
720                 // these are handled in our dispatch()
721                 break;
722
723         default:
724                 if (!getLocalStatus(cur, cmd, flag))
725                         flag = view()->getStatus(cmd);
726         }
727
728         if (!enable)
729                 flag.enabled(false);
730
731         // Can we use a readonly buffer?
732         if (buf && buf->isReadonly()
733             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
734             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
735                 flag.message(from_utf8(N_("Document is read-only")));
736                 flag.enabled(false);
737         }
738
739         // Are we in a DELETED change-tracking region?
740         if (buf && lookupChangeType(cur, true) == Change::DELETED
741             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
742             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
743                 flag.message(from_utf8(N_("This portion of the document is deleted.")));
744                 flag.enabled(false);
745         }
746
747         // the default error message if we disable the command
748         if (!flag.enabled() && flag.message().empty())
749                 flag.message(from_utf8(N_("Command disabled")));
750
751         return flag;
752 }
753
754
755 bool LyXFunc::ensureBufferClean(BufferView * bv)
756 {
757         Buffer & buf = *bv->buffer();
758         if (buf.isClean())
759                 return true;
760
761         docstring const file = makeDisplayPath(buf.fileName(), 30);
762         docstring text = bformat(_("The document %1$s has unsaved "
763                                              "changes.\n\nDo you want to save "
764                                              "the document?"), file);
765         int const ret = Alert::prompt(_("Save changed document?"),
766                                       text, 0, 1, _("&Save"),
767                                       _("&Cancel"));
768
769         if (ret == 0)
770                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
771
772         return buf.isClean();
773 }
774
775
776 namespace {
777
778 void showPrintError(string const & name)
779 {
780         docstring str = bformat(_("Could not print the document %1$s.\n"
781                                             "Check that your printer is set up correctly."),
782                              makeDisplayPath(name, 50));
783         Alert::error(_("Print document failed"), str);
784 }
785
786
787 void loadTextclass(string const & name)
788 {
789         std::pair<bool, textclass_type> const tc_pair =
790                 textclasslist.numberOfClass(name);
791
792         if (!tc_pair.first) {
793                 lyxerr << "Document class \"" << name
794                        << "\" does not exist."
795                        << std::endl;
796                 return;
797         }
798
799         textclass_type const tc = tc_pair.second;
800
801         if (!textclasslist[tc].load()) {
802                 docstring s = bformat(_("The document could not be converted\n"
803                                                   "into the document class %1$s."),
804                                    from_utf8(textclasslist[tc].name()));
805                 Alert::error(_("Could not change class"), s);
806         }
807 }
808
809
810 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
811
812 } //namespace anon
813
814
815 void LyXFunc::dispatch(FuncRequest const & cmd)
816 {
817         string const argument = to_utf8(cmd.argument());
818         kb_action const action = cmd.action;
819
820         LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
821         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
822
823         // we have not done anything wrong yet.
824         errorstat = false;
825         dispatch_buffer.erase();
826
827         // redraw the screen at the end (first of the two drawing steps).
828         //This is done unless explicitely requested otherwise
829         Update::flags updateFlags = Update::FitCursor;
830
831         FuncStatus const flag = getStatus(cmd);
832         if (!flag.enabled()) {
833                 // We cannot use this function here
834                 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
835                        << lyxaction.getActionName(action)
836                        << " [" << action << "] is disabled at this location"
837                        << endl;
838                 setErrorMessage(flag.message());
839         } else {
840                 switch (action) {
841
842                 case LFUN_WORD_FIND_FORWARD:
843                 case LFUN_WORD_FIND_BACKWARD: {
844                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
845                         static docstring last_search;
846                         docstring searched_string;
847
848                         if (!cmd.argument().empty()) {
849                                 last_search = cmd.argument();
850                                 searched_string = cmd.argument();
851                         } else {
852                                 searched_string = last_search;
853                         }
854
855                         if (searched_string.empty())
856                                 break;
857
858                         bool const fw = action == LFUN_WORD_FIND_FORWARD;
859                         docstring const data =
860                                 find2string(searched_string, true, false, fw);
861                         find(view(), FuncRequest(LFUN_WORD_FIND, data));
862                         break;
863                 }
864
865                 case LFUN_COMMAND_PREFIX:
866                         BOOST_ASSERT(lyx_view_);
867                         lyx_view_->message(keyseq->printOptions(true));
868                         break;
869
870                 case LFUN_COMMAND_EXECUTE:
871                         BOOST_ASSERT(lyx_view_);
872                         lyx_view_->getToolbars().display("minibuffer", true);
873                         lyx_view_->focus_command_buffer();
874                         break;
875
876                 case LFUN_CANCEL:
877                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
878                         keyseq->reset();
879                         meta_fake_bit = key_modifier::none;
880                         if (view()->buffer())
881                                 // cancel any selection
882                                 dispatch(FuncRequest(LFUN_MARK_OFF));
883                         setMessage(from_ascii(N_("Cancel")));
884                         break;
885
886                 case LFUN_META_PREFIX:
887                         meta_fake_bit = key_modifier::alt;
888                         setMessage(keyseq->print(true));
889                         break;
890
891                 case LFUN_BUFFER_TOGGLE_READ_ONLY:
892                         BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
893                         if (lyx_view_->buffer()->lyxvc().inUse())
894                                 lyx_view_->buffer()->lyxvc().toggleReadOnly();
895                         else
896                                 lyx_view_->buffer()->setReadonly(
897                                         !lyx_view_->buffer()->isReadonly());
898                         break;
899
900                 // --- Menus -----------------------------------------------
901                 case LFUN_BUFFER_NEW:
902                         menuNew(argument, false);
903                         break;
904
905                 case LFUN_BUFFER_NEW_TEMPLATE:
906                         menuNew(argument, true);
907                         break;
908
909                 case LFUN_BUFFER_CLOSE:
910                         closeBuffer();
911                         view()->update();
912                         break;
913
914                 case LFUN_BUFFER_WRITE:
915                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
916                         if (!lyx_view_->buffer()->isUnnamed()) {
917                                 docstring const str = bformat(_("Saving document %1$s..."),
918                                          makeDisplayPath(lyx_view_->buffer()->fileName()));
919                                 lyx_view_->message(str);
920                                 menuWrite(lyx_view_->buffer());
921                                 lyx_view_->message(str + _(" done."));
922                         } else {
923                                 writeAs(lyx_view_->buffer());
924                         }
925                         updateFlags = Update::None;
926                         break;
927
928                 case LFUN_BUFFER_WRITE_AS:
929                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
930                         writeAs(lyx_view_->buffer(), argument);
931                         updateFlags = Update::None;
932                         break;
933
934                 case LFUN_BUFFER_WRITE_ALL: {
935                         Buffer * first = theBufferList().first();
936                         if (first) {
937                                 Buffer * b = first;
938                                 lyx_view_->message(_("Saving all documents..."));
939                 
940                                 // We cannot use a for loop as the buffer list cycles.
941                                 do {
942                                         if (!b->isClean()) {
943                                                 if (!b->isUnnamed()) {
944                                                         menuWrite(b);
945                                                         lyxerr[Debug::ACTION] << "Saved " << b->fileName() << endl;
946                                                 } else
947                                                         writeAs(b);
948                                         }
949                                         b = theBufferList().next(b);
950                                 } while (b != first); 
951                                 lyx_view_->message(_("All documents saved."));
952                         } 
953         
954                         updateFlags = Update::None;
955                         break;
956                 }
957
958                 case LFUN_BUFFER_RELOAD: {
959                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
960                         docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
961                         docstring text = bformat(_("Any changes will be lost. Are you sure "
962                                                              "you want to revert to the saved version of the document %1$s?"), file);
963                         int const ret = Alert::prompt(_("Revert to saved document?"),
964                                 text, 1, 1, _("&Revert"), _("&Cancel"));
965
966                         if (ret == 0)
967                                 reloadBuffer();
968                         break;
969                 }
970
971                 case LFUN_BUFFER_UPDATE:
972                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
973                         Exporter::Export(lyx_view_->buffer(), argument, true);
974                         break;
975
976                 case LFUN_BUFFER_VIEW:
977                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
978                         Exporter::preview(lyx_view_->buffer(), argument);
979                         break;
980
981                 case LFUN_BUILD_PROGRAM:
982                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
983                         Exporter::Export(lyx_view_->buffer(), "program", true);
984                         break;
985
986                 case LFUN_BUFFER_CHKTEX:
987                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
988                         lyx_view_->buffer()->runChktex();
989                         break;
990
991                 case LFUN_BUFFER_EXPORT:
992                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
993                         if (argument == "custom")
994                                 lyx_view_->getDialogs().show("sendto");
995                         else {
996                                 Exporter::Export(lyx_view_->buffer(), argument, false);
997                         }
998                         break;
999
1000                 case LFUN_BUFFER_EXPORT_CUSTOM: {
1001                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1002                         string format_name;
1003                         string command = split(argument, format_name, ' ');
1004                         Format const * format = formats.getFormat(format_name);
1005                         if (!format) {
1006                                 lyxerr << "Format \"" << format_name
1007                                        << "\" not recognized!"
1008                                        << std::endl;
1009                                 break;
1010                         }
1011
1012                         Buffer * buffer = lyx_view_->buffer();
1013
1014                         // The name of the file created by the conversion process
1015                         string filename;
1016
1017                         // Output to filename
1018                         if (format->name() == "lyx") {
1019                                 string const latexname =
1020                                         buffer->getLatexName(false);
1021                                 filename = changeExtension(latexname,
1022                                                            format->extension());
1023                                 filename = addName(buffer->temppath(), filename);
1024
1025                                 if (!buffer->writeFile(FileName(filename)))
1026                                         break;
1027
1028                         } else {
1029                                 Exporter::Export(buffer, format_name, true, filename);
1030                         }
1031
1032                         // Substitute $$FName for filename
1033                         if (!contains(command, "$$FName"))
1034                                 command = "( " + command + " ) < $$FName";
1035                         command = subst(command, "$$FName", filename);
1036
1037                         // Execute the command in the background
1038                         Systemcall call;
1039                         call.startscript(Systemcall::DontWait, command);
1040                         break;
1041                 }
1042
1043                 case LFUN_BUFFER_PRINT: {
1044                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1045                         // FIXME: cmd.getArg() might fail if one of the arguments
1046                         // contains double quotes
1047                         string target = cmd.getArg(0);
1048                         string target_name = cmd.getArg(1);
1049                         string command = cmd.getArg(2);
1050
1051                         if (target.empty()
1052                             || target_name.empty()
1053                             || command.empty()) {
1054                                 lyxerr << "Unable to parse \""
1055                                        << argument << '"' << endl;
1056                                 break;
1057                         }
1058                         if (target != "printer" && target != "file") {
1059                                 lyxerr << "Unrecognized target \""
1060                                        << target << '"' << endl;
1061                                 break;
1062                         }
1063
1064                         Buffer * buffer = lyx_view_->buffer();
1065
1066                         if (!Exporter::Export(buffer, "dvi", true)) {
1067                                 showPrintError(buffer->fileName());
1068                                 break;
1069                         }
1070
1071                         // Push directory path.
1072                         string const path(buffer->temppath());
1073                         // Prevent the compiler from optimizing away p
1074                         FileName pp(path);
1075                         support::Path p(pp);
1076
1077                         // there are three cases here:
1078                         // 1. we print to a file
1079                         // 2. we print directly to a printer
1080                         // 3. we print using a spool command (print to file first)
1081                         Systemcall one;
1082                         int res = 0;
1083                         string const dviname =
1084                                 changeExtension(buffer->getLatexName(true),
1085                                                 "dvi");
1086
1087                         if (target == "printer") {
1088                                 if (!lyxrc.print_spool_command.empty()) {
1089                                         // case 3: print using a spool
1090                                         string const psname =
1091                                                 changeExtension(dviname,".ps");
1092                                         command += ' ' + lyxrc.print_to_file
1093                                                 + quoteName(psname)
1094                                                 + ' '
1095                                                 + quoteName(dviname);
1096
1097                                         string command2 =
1098                                                 lyxrc.print_spool_command + ' ';
1099                                         if (target_name != "default") {
1100                                                 command2 += lyxrc.print_spool_printerprefix
1101                                                         + target_name
1102                                                         + ' ';
1103                                         }
1104                                         command2 += quoteName(psname);
1105                                         // First run dvips.
1106                                         // If successful, then spool command
1107                                         res = one.startscript(
1108                                                 Systemcall::Wait,
1109                                                 command);
1110
1111                                         if (res == 0)
1112                                                 res = one.startscript(
1113                                                         Systemcall::DontWait,
1114                                                         command2);
1115                                 } else {
1116                                         // case 2: print directly to a printer
1117                                         if (target_name != "default")
1118                                                 command += ' ' + lyxrc.print_to_printer + target_name + ' ';
1119                                         res = one.startscript(
1120                                                 Systemcall::DontWait,
1121                                                 command + quoteName(dviname));
1122                                 }
1123
1124                         } else {
1125                                 // case 1: print to a file
1126                                 FileName const filename(makeAbsPath(target_name,
1127                                                         lyx_view_->buffer()->filePath()));
1128                                 FileName const dvifile(makeAbsPath(dviname, path));
1129                                 if (fs::exists(filename.toFilesystemEncoding())) {
1130                                         docstring text = bformat(
1131                                                 _("The file %1$s already exists.\n\n"
1132                                                   "Do you want to overwrite that file?"),
1133                                                 makeDisplayPath(filename.absFilename()));
1134                                         if (Alert::prompt(_("Overwrite file?"),
1135                                             text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0)
1136                                                 break;
1137                                 }
1138                                 command += ' ' + lyxrc.print_to_file
1139                                         + quoteName(filename.toFilesystemEncoding())
1140                                         + ' '
1141                                         + quoteName(dvifile.toFilesystemEncoding());
1142                                 res = one.startscript(Systemcall::DontWait,
1143                                                       command);
1144                         }
1145
1146                         if (res != 0)
1147                                 showPrintError(buffer->fileName());
1148                         break;
1149                 }
1150
1151                 case LFUN_BUFFER_IMPORT:
1152                         doImport(argument);
1153                         break;
1154
1155                 case LFUN_LYX_QUIT:
1156                         // quitting is triggered by the gui code
1157                         // (leaving the event loop).
1158                         lyx_view_->message(from_utf8(N_("Exiting.")));
1159                         if (theBufferList().quitWriteAll())
1160                                 theApp()->gui().closeAllViews();
1161                         break;
1162
1163                 case LFUN_BUFFER_AUTO_SAVE:
1164                         autoSave(view());
1165                         break;
1166
1167                 case LFUN_RECONFIGURE:
1168                         BOOST_ASSERT(lyx_view_);
1169                         reconfigure(*lyx_view_);
1170                         break;
1171
1172                 case LFUN_HELP_OPEN: {
1173                         BOOST_ASSERT(lyx_view_);
1174                         string const arg = argument;
1175                         if (arg.empty()) {
1176                                 setErrorMessage(from_ascii(N_("Missing argument")));
1177                                 break;
1178                         }
1179                         FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1180                         if (fname.empty()) {
1181                                 lyxerr << "LyX: unable to find documentation file `"
1182                                                          << arg << "'. Bad installation?" << endl;
1183                                 break;
1184                         }
1185                         lyx_view_->message(bformat(_("Opening help file %1$s..."),
1186                                 makeDisplayPath(fname.absFilename())));
1187                         lyx_view_->loadLyXFile(fname, false);
1188                         break;
1189                 }
1190
1191                 // --- version control -------------------------------
1192                 case LFUN_VC_REGISTER:
1193                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1194                         if (!ensureBufferClean(view()))
1195                                 break;
1196                         if (!lyx_view_->buffer()->lyxvc().inUse()) {
1197                                 lyx_view_->buffer()->lyxvc().registrer();
1198                                 reloadBuffer();
1199                         }
1200                         updateFlags = Update::Force;
1201                         break;
1202
1203                 case LFUN_VC_CHECK_IN:
1204                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1205                         if (!ensureBufferClean(view()))
1206                                 break;
1207                         if (lyx_view_->buffer()->lyxvc().inUse()
1208                                         && !lyx_view_->buffer()->isReadonly()) {
1209                                 lyx_view_->buffer()->lyxvc().checkIn();
1210                                 reloadBuffer();
1211                         }
1212                         break;
1213
1214                 case LFUN_VC_CHECK_OUT:
1215                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1216                         if (!ensureBufferClean(view()))
1217                                 break;
1218                         if (lyx_view_->buffer()->lyxvc().inUse()
1219                                         && lyx_view_->buffer()->isReadonly()) {
1220                                 lyx_view_->buffer()->lyxvc().checkOut();
1221                                 reloadBuffer();
1222                         }
1223                         break;
1224
1225                 case LFUN_VC_REVERT:
1226                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1227                         lyx_view_->buffer()->lyxvc().revert();
1228                         reloadBuffer();
1229                         break;
1230
1231                 case LFUN_VC_UNDO_LAST:
1232                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1233                         lyx_view_->buffer()->lyxvc().undoLast();
1234                         reloadBuffer();
1235                         break;
1236
1237                 // --- buffers ----------------------------------------
1238                 case LFUN_BUFFER_SWITCH:
1239                         BOOST_ASSERT(lyx_view_);
1240                         lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1241                         updateFlags = Update::Force;
1242                         break;
1243
1244                 case LFUN_BUFFER_NEXT:
1245                         BOOST_ASSERT(lyx_view_);
1246                         lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1247                         updateFlags = Update::Force;
1248                         break;
1249
1250                 case LFUN_BUFFER_PREVIOUS:
1251                         BOOST_ASSERT(lyx_view_);
1252                         lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1253                         updateFlags = Update::Force;
1254                         break;
1255
1256                 case LFUN_FILE_NEW:
1257                         BOOST_ASSERT(lyx_view_);
1258                         newFile(view(), argument);
1259                         break;
1260
1261                 case LFUN_FILE_OPEN:
1262                         BOOST_ASSERT(lyx_view_);
1263                         open(argument);
1264                         break;
1265
1266                 case LFUN_DROP_LAYOUTS_CHOICE:
1267                         BOOST_ASSERT(lyx_view_);
1268                         lyx_view_->getToolbars().openLayoutList();
1269                         break;
1270
1271                 case LFUN_MENU_OPEN:
1272                         BOOST_ASSERT(lyx_view_);
1273                         lyx_view_->getMenubar().openByName(from_utf8(argument));
1274                         break;
1275
1276                 // --- lyxserver commands ----------------------------
1277                 case LFUN_SERVER_GET_NAME:
1278                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1279                         setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1280                         LYXERR(Debug::INFO) << "FNAME["
1281                                                          << lyx_view_->buffer()->fileName()
1282                                                          << "] " << endl;
1283                         break;
1284
1285                 case LFUN_SERVER_NOTIFY:
1286                         dispatch_buffer = keyseq->print(false);
1287                         theServer().notifyClient(to_utf8(dispatch_buffer));
1288                         break;
1289
1290                 case LFUN_SERVER_GOTO_FILE_ROW: {
1291                         BOOST_ASSERT(lyx_view_);
1292                         string file_name;
1293                         int row;
1294                         istringstream is(argument);
1295                         is >> file_name >> row;
1296                         if (prefixIs(file_name, package().temp_dir().absFilename())) {
1297                                 // Needed by inverse dvi search. If it is a file
1298                                 // in tmpdir, call the apropriated function
1299                                 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1300                         } else {
1301                                 // Must replace extension of the file to be .lyx
1302                                 // and get full path
1303                                 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1304                                 // Either change buffer or load the file
1305                                 if (theBufferList().exists(s.absFilename())) {
1306                                         lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1307                                 } else {
1308                                         lyx_view_->loadLyXFile(s);
1309                                 }
1310                         }
1311
1312                         view()->setCursorFromRow(row);
1313
1314                         updateFlags = Update::FitCursor;
1315                         break;
1316                 }
1317
1318                 case LFUN_DIALOG_SHOW: {
1319                         BOOST_ASSERT(lyx_view_);
1320                         string const name = cmd.getArg(0);
1321                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1322
1323                         if (name == "character") {
1324                                 data = freefont2string();
1325                                 if (!data.empty())
1326                                         lyx_view_->getDialogs().show("character", data);
1327                         } else if (name == "latexlog") {
1328                                 pair<Buffer::LogType, string> const logfile =
1329                                         lyx_view_->buffer()->getLogName();
1330                                 switch (logfile.first) {
1331                                 case Buffer::latexlog:
1332                                         data = "latex ";
1333                                         break;
1334                                 case Buffer::buildlog:
1335                                         data = "literate ";
1336                                         break;
1337                                 }
1338                                 data += Lexer::quoteString(logfile.second);
1339                                 lyx_view_->getDialogs().show("log", data);
1340                         } else if (name == "vclog") {
1341                                 string const data = "vc " +
1342                                         Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1343                                 lyx_view_->getDialogs().show("log", data);
1344                         } else
1345                                 lyx_view_->getDialogs().show(name, data);
1346                         break;
1347                 }
1348
1349                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1350                         BOOST_ASSERT(lyx_view_);
1351                         string const name = cmd.getArg(0);
1352                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1353                         if (name == "bibitem" ||
1354                             name == "bibtex" ||
1355                             name == "index" ||
1356                             name == "label" ||
1357                             name == "nomenclature" ||
1358                             name == "ref" ||
1359                             name == "toc" ||
1360                             name == "url") {
1361                                 InsetCommandParams p(name);
1362                                 data = InsetCommandMailer::params2string(name, p);
1363                         } else if (name == "include") {
1364                                 // data is the include type: one of "include",
1365                                 // "input", "verbatiminput" or "verbatiminput*"
1366                                 if (data.empty())
1367                                         // default type is requested
1368                                         data = "include";
1369                                 InsetCommandParams p(data);
1370                                 data = InsetIncludeMailer::params2string(p);
1371                         } else if (name == "box") {
1372                                 // \c data == "Boxed" || "Frameless" etc
1373                                 InsetBoxParams p(data);
1374                                 data = InsetBoxMailer::params2string(p);
1375                         } else if (name == "branch") {
1376                                 InsetBranchParams p;
1377                                 data = InsetBranchMailer::params2string(p);
1378                         } else if (name == "citation") {
1379                                 InsetCommandParams p("cite");
1380                                 data = InsetCommandMailer::params2string(name, p);
1381                         } else if (name == "ert") {
1382                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1383                         } else if (name == "external") {
1384                                 InsetExternalParams p;
1385                                 Buffer const & buffer = *lyx_view_->buffer();
1386                                 data = InsetExternalMailer::params2string(p, buffer);
1387                         } else if (name == "float") {
1388                                 InsetFloatParams p;
1389                                 data = InsetFloatMailer::params2string(p);
1390                         } else if (name == "listings") {
1391                                 InsetListingsParams p;
1392                                 data = InsetListingsMailer::params2string(p);
1393                         } else if (name == "graphics") {
1394                                 InsetGraphicsParams p;
1395                                 Buffer const & buffer = *lyx_view_->buffer();
1396                                 data = InsetGraphicsMailer::params2string(p, buffer);
1397                         } else if (name == "note") {
1398                                 InsetNoteParams p;
1399                                 data = InsetNoteMailer::params2string(p);
1400                         } else if (name == "vspace") {
1401                                 VSpace space;
1402                                 data = InsetVSpaceMailer::params2string(space);
1403                         } else if (name == "wrap") {
1404                                 InsetWrapParams p;
1405                                 data = InsetWrapMailer::params2string(p);
1406                         }
1407                         lyx_view_->getDialogs().show(name, data, 0);
1408                         break;
1409                 }
1410
1411                 case LFUN_DIALOG_UPDATE: {
1412                         BOOST_ASSERT(lyx_view_);
1413                         string const & name = argument;
1414                         // Can only update a dialog connected to an existing inset
1415                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1416                         if (inset) {
1417                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1418                                 inset->dispatch(view()->cursor(), fr);
1419                         } else if (name == "paragraph") {
1420                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1421                         } else if (name == "prefs") {
1422                                 lyx_view_->getDialogs().update(name, string());
1423                         }
1424                         break;
1425                 }
1426
1427                 case LFUN_DIALOG_HIDE:
1428                         LyX::cref().hideDialogs(argument, 0);
1429                         break;
1430
1431                 case LFUN_DIALOG_TOGGLE: {
1432                         BOOST_ASSERT(lyx_view_);
1433                         if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1434                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1435                         else
1436                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1437                         break;
1438                 }
1439
1440                 case LFUN_DIALOG_DISCONNECT_INSET:
1441                         BOOST_ASSERT(lyx_view_);
1442                         lyx_view_->getDialogs().disconnect(argument);
1443                         break;
1444
1445
1446                 case LFUN_CITATION_INSERT: {
1447                         BOOST_ASSERT(lyx_view_);
1448                         if (!argument.empty()) {
1449                                 // we can have one optional argument, delimited by '|'
1450                                 // citation-insert <key>|<text_before>
1451                                 // this should be enhanced to also support text_after
1452                                 // and citation style
1453                                 string arg = argument;
1454                                 string opt1;
1455                                 if (contains(argument, "|")) {
1456                                         arg = token(argument, '|', 0);
1457                                         opt1 = token(argument, '|', 1);
1458                                 }
1459                                 InsetCommandParams icp("cite");
1460                                 icp["key"] = from_utf8(arg);
1461                                 if (!opt1.empty())
1462                                         icp["before"] = from_utf8(opt1);
1463                                 string icstr = InsetCommandMailer::params2string("citation", icp);
1464                                 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1465                                 dispatch(fr);
1466                         } else
1467                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1468                         break;
1469                 }
1470
1471                 case LFUN_BUFFER_CHILD_OPEN: {
1472                         // takes an optional argument, "|bool", at the end
1473                         // indicating whether this file is being opened automatically
1474                         // by LyX itself, in which case we will not want to switch
1475                         // buffers after opening. The default is false, so in practice
1476                         // it is used only when true.
1477                         BOOST_ASSERT(lyx_view_);
1478                         int const arglength = argument.length();
1479                         FileName filename;
1480                         bool autoOpen = false;
1481                         if (argument.substr(arglength - 5, 5) == "|true") {
1482                                 autoOpen = true;
1483                                 filename = makeAbsPath(argument.substr(0, arglength - 5), 
1484                                         lyx_view_->buffer()->filePath());
1485                         } else if (argument.substr(arglength - 6, 6) == "|false") {
1486                                 filename = makeAbsPath(argument.substr(0, arglength - 6), 
1487                                         lyx_view_->buffer()->filePath());
1488                         } else filename = 
1489                                 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1490                         view()->saveBookmark(false);
1491                         if (theBufferList().exists(filename.absFilename())) {
1492                                 Buffer * buf = theBufferList().getBuffer(filename.absFilename());
1493                                 if (!autoOpen)
1494                                         lyx_view_->setBuffer(buf, true);
1495                                 else
1496                                         buf->setParentName(lyx_view_->buffer()->fileName());
1497                         } else
1498                                 lyx_view_->loadLyXFile(filename, true, true, autoOpen);
1499
1500                         // If a screen update is required (in case where auto_open is false), 
1501                         // loadLyXFile() would have taken care of it already. Otherwise we shall 
1502                         // reset the update flag because it can cause a circular problem.
1503                         // See bug 3970.
1504                         updateFlags = Update::None;
1505                         break;
1506                 }
1507
1508                 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1509                         BOOST_ASSERT(lyx_view_);
1510                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1511                         break;
1512
1513                 case LFUN_KEYMAP_OFF:
1514                         BOOST_ASSERT(lyx_view_);
1515                         lyx_view_->view()->getIntl().keyMapOn(false);
1516                         break;
1517
1518                 case LFUN_KEYMAP_PRIMARY:
1519                         BOOST_ASSERT(lyx_view_);
1520                         lyx_view_->view()->getIntl().keyMapPrim();
1521                         break;
1522
1523                 case LFUN_KEYMAP_SECONDARY:
1524                         BOOST_ASSERT(lyx_view_);
1525                         lyx_view_->view()->getIntl().keyMapSec();
1526                         break;
1527
1528                 case LFUN_KEYMAP_TOGGLE:
1529                         BOOST_ASSERT(lyx_view_);
1530                         lyx_view_->view()->getIntl().toggleKeyMap();
1531                         break;
1532
1533                 case LFUN_REPEAT: {
1534                         // repeat command
1535                         string countstr;
1536                         string rest = split(argument, countstr, ' ');
1537                         istringstream is(countstr);
1538                         int count = 0;
1539                         is >> count;
1540                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1541                         for (int i = 0; i < count; ++i)
1542                                 dispatch(lyxaction.lookupFunc(rest));
1543                         break;
1544                 }
1545
1546                 case LFUN_COMMAND_SEQUENCE: {
1547                         // argument contains ';'-terminated commands
1548                         string arg = argument;
1549                         while (!arg.empty()) {
1550                                 string first;
1551                                 arg = split(arg, first, ';');
1552                                 FuncRequest func(lyxaction.lookupFunc(first));
1553                                 func.origin = cmd.origin;
1554                                 dispatch(func);
1555                         }
1556                         break;
1557                 }
1558
1559                 case LFUN_PREFERENCES_SAVE: {
1560                         lyxrc.write(makeAbsPath("preferences",
1561                                                 package().user_support().absFilename()),
1562                                     false);
1563                         break;
1564                 }
1565
1566                 case LFUN_SCREEN_FONT_UPDATE:
1567                         BOOST_ASSERT(lyx_view_);
1568                         // handle the screen font changes.
1569                         theFontLoader().update();
1570                         /// FIXME: only the current view will be updated. the Gui
1571                         /// class is able to furnish the list of views.
1572                         updateFlags = Update::Force;
1573                         break;
1574
1575                 case LFUN_SET_COLOR: {
1576                         string lyx_name;
1577                         string const x11_name = split(argument, lyx_name, ' ');
1578                         if (lyx_name.empty() || x11_name.empty()) {
1579                                 setErrorMessage(from_ascii(N_(
1580                                                 "Syntax: set-color <lyx_name>"
1581                                                 " <x11_name>")));
1582                                 break;
1583                         }
1584
1585                         bool const graphicsbg_changed =
1586                                 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1587                                  x11_name != lcolor.getX11Name(Color::graphicsbg));
1588
1589                         if (!lcolor.setColor(lyx_name, x11_name)) {
1590                                 setErrorMessage(
1591                                                 bformat(_("Set-color \"%1$s\" failed "
1592                                                                        "- color is undefined or "
1593                                                                        "may not be redefined"),
1594                                                                            from_utf8(lyx_name)));
1595                                 break;
1596                         }
1597
1598                         theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1599
1600                         if (graphicsbg_changed) {
1601 #ifdef WITH_WARNINGS
1602 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1603 #endif
1604 #if 0
1605                                 graphics::GCache::get().changeDisplay(true);
1606 #endif
1607                         }
1608                         break;
1609                 }
1610
1611                 case LFUN_MESSAGE:
1612                         BOOST_ASSERT(lyx_view_);
1613                         lyx_view_->message(from_utf8(argument));
1614                         break;
1615
1616                 case LFUN_EXTERNAL_EDIT: {
1617                         BOOST_ASSERT(lyx_view_);
1618                         FuncRequest fr(action, argument);
1619                         InsetExternal().dispatch(view()->cursor(), fr);
1620                         break;
1621                 }
1622
1623                 case LFUN_GRAPHICS_EDIT: {
1624                         FuncRequest fr(action, argument);
1625                         InsetGraphics().dispatch(view()->cursor(), fr);
1626                         break;
1627                 }
1628
1629                 case LFUN_INSET_APPLY: {
1630                         BOOST_ASSERT(lyx_view_);
1631                         string const name = cmd.getArg(0);
1632                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1633                         if (inset) {
1634                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1635                                 inset->dispatch(view()->cursor(), fr);
1636                         } else {
1637                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1638                                 dispatch(fr);
1639                         }
1640                         // ideally, the update flag should be set by the insets,
1641                         // but this is not possible currently
1642                         updateFlags = Update::Force | Update::FitCursor;
1643                         break;
1644                 }
1645
1646                 case LFUN_ALL_INSETS_TOGGLE: {
1647                         BOOST_ASSERT(lyx_view_);
1648                         string action;
1649                         string const name = split(argument, action, ' ');
1650                         Inset::Code const inset_code =
1651                                 Inset::translate(name);
1652
1653                         Cursor & cur = view()->cursor();
1654                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1655
1656                         Inset & inset = lyx_view_->buffer()->inset();
1657                         InsetIterator it  = inset_iterator_begin(inset);
1658                         InsetIterator const end = inset_iterator_end(inset);
1659                         for (; it != end; ++it) {
1660                                 if (!it->asInsetMath()
1661                                     && (inset_code == Inset::NO_CODE
1662                                     || inset_code == it->lyxCode())) {
1663                                         Cursor tmpcur = cur;
1664                                         tmpcur.pushLeft(*it);
1665                                         it->dispatch(tmpcur, fr);
1666                                 }
1667                         }
1668                         updateFlags = Update::Force | Update::FitCursor;
1669                         break;
1670                 }
1671
1672                 case LFUN_BUFFER_LANGUAGE: {
1673                         BOOST_ASSERT(lyx_view_);
1674                         Buffer & buffer = *lyx_view_->buffer();
1675                         Language const * oldL = buffer.params().language;
1676                         Language const * newL = languages.getLanguage(argument);
1677                         if (!newL || oldL == newL)
1678                                 break;
1679
1680                         if (oldL->rightToLeft() == newL->rightToLeft()
1681                             && !buffer.isMultiLingual())
1682                                 buffer.changeLanguage(oldL, newL);
1683                         break;
1684                 }
1685
1686                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1687                         string const fname =
1688                                 addName(addPath(package().user_support().absFilename(), "templates/"),
1689                                         "defaults.lyx");
1690                         Buffer defaults(fname);
1691
1692                         istringstream ss(argument);
1693                         Lexer lex(0,0);
1694                         lex.setStream(ss);
1695                         int const unknown_tokens = defaults.readHeader(lex);
1696
1697                         if (unknown_tokens != 0) {
1698                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1699                                        << unknown_tokens << " unknown token"
1700                                        << (unknown_tokens == 1 ? "" : "s")
1701                                        << endl;
1702                         }
1703
1704                         if (defaults.writeFile(FileName(defaults.fileName())))
1705                                 setMessage(bformat(_("Document defaults saved in %1$s"),
1706                                                    makeDisplayPath(fname)));
1707                         else
1708                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1709                         break;
1710                 }
1711
1712                 case LFUN_BUFFER_PARAMS_APPLY: {
1713                         BOOST_ASSERT(lyx_view_);
1714                         biblio::CiteEngine const engine =
1715                                 lyx_view_->buffer()->params().getEngine();
1716
1717                         istringstream ss(argument);
1718                         Lexer lex(0,0);
1719                         lex.setStream(ss);
1720                         int const unknown_tokens =
1721                                 lyx_view_->buffer()->readHeader(lex);
1722
1723                         if (unknown_tokens != 0) {
1724                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1725                                        << unknown_tokens << " unknown token"
1726                                        << (unknown_tokens == 1 ? "" : "s")
1727                                        << endl;
1728                         }
1729                         if (engine == lyx_view_->buffer()->params().getEngine())
1730                                 break;
1731
1732                         Cursor & cur = view()->cursor();
1733                         FuncRequest fr(LFUN_INSET_REFRESH);
1734
1735                         Inset & inset = lyx_view_->buffer()->inset();
1736                         InsetIterator it  = inset_iterator_begin(inset);
1737                         InsetIterator const end = inset_iterator_end(inset);
1738                         for (; it != end; ++it)
1739                                 if (it->lyxCode() == Inset::CITE_CODE)
1740                                         it->dispatch(cur, fr);
1741                         break;
1742                 }
1743
1744                 case LFUN_TEXTCLASS_APPLY: {
1745                         BOOST_ASSERT(lyx_view_);
1746                         Buffer * buffer = lyx_view_->buffer();
1747
1748                         textclass_type const old_class =
1749                                 buffer->params().textclass;
1750
1751                         loadTextclass(argument);
1752
1753                         std::pair<bool, textclass_type> const tc_pair =
1754                                 textclasslist.numberOfClass(argument);
1755
1756                         if (!tc_pair.first)
1757                                 break;
1758
1759                         textclass_type const new_class = tc_pair.second;
1760                         if (old_class == new_class)
1761                                 // nothing to do
1762                                 break;
1763
1764                         lyx_view_->message(_("Converting document to new document class..."));
1765                         recordUndoFullDocument(view());
1766                         buffer->params().textclass = new_class;
1767                         StableDocIterator backcur(view()->cursor());
1768                         ErrorList & el = buffer->errorList("Class Switch");
1769                         cap::switchBetweenClasses(
1770                                 old_class, new_class,
1771                                 static_cast<InsetText &>(buffer->inset()), el);
1772
1773                         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1774
1775                         buffer->errors("Class Switch");
1776                         updateLabels(*buffer);
1777                         updateFlags = Update::Force | Update::FitCursor;
1778                         break;
1779                 }
1780
1781                 case LFUN_TEXTCLASS_LOAD:
1782                         loadTextclass(argument);
1783                         break;
1784
1785                 case LFUN_LYXRC_APPLY: {
1786                         LyXRC const lyxrc_orig = lyxrc;
1787
1788                         istringstream ss(argument);
1789                         bool const success = lyxrc.read(ss) == 0;
1790
1791                         if (!success) {
1792                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1793                                        << "Unable to read lyxrc data"
1794                                        << endl;
1795                                 break;
1796                         }
1797
1798                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1799
1800                         /// We force the redraw in any case because there might be
1801                         /// some screen font changes.
1802                         /// FIXME: only the current view will be updated. the Gui
1803                         /// class is able to furnish the list of views.
1804                         updateFlags = Update::Force;
1805                         break;
1806                 }
1807
1808                 case LFUN_WINDOW_NEW:
1809                         LyX::ref().newLyXView();
1810                         break;
1811
1812                 case LFUN_WINDOW_CLOSE:
1813                         BOOST_ASSERT(lyx_view_);
1814                         BOOST_ASSERT(theApp());
1815                         // update bookmark pit of the current buffer before window close
1816                         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1817                                 gotoBookmark(i+1, false, false);
1818                         // ask the user for saving changes or cancel quit
1819                         if (!theBufferList().quitWriteAll())
1820                                 break;
1821                         lyx_view_->close();
1822                         return;
1823
1824                 case LFUN_BOOKMARK_GOTO:
1825                         // go to bookmark, open unopened file and switch to buffer if necessary
1826                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1827                         break;
1828
1829                 case LFUN_BOOKMARK_CLEAR:
1830                         LyX::ref().session().bookmarks().clear();
1831                         break;
1832
1833                 case LFUN_TOOLBAR_TOGGLE: {
1834                         BOOST_ASSERT(lyx_view_);
1835                         string const name = cmd.getArg(0);
1836                         bool const allowauto = cmd.getArg(1) == "allowauto";
1837                         lyx_view_->toggleToolbarState(name, allowauto);
1838                         ToolbarInfo * tbi = lyx_view_->getToolbarInfo(name);
1839                         if (!tbi) {
1840                                 setMessage(bformat(_("Unknown toolbar \"%1$s\""),
1841                                                    from_utf8(name)));
1842                                 break;
1843                         }
1844                         docstring state;
1845                         if (tbi->flags & ToolbarInfo::ON)
1846                                 state = _("on");
1847                         else if (tbi->flags & ToolbarInfo::OFF)
1848                                 state = _("off");
1849                         else if (tbi->flags & ToolbarInfo::AUTO)
1850                                 state = _("auto");
1851
1852                         setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1853                                            _(tbi->gui_name), state));
1854                         break;
1855                 }
1856
1857                 default: {
1858                         BOOST_ASSERT(lyx_view_);
1859                         view()->cursor().dispatch(cmd);
1860                         updateFlags = view()->cursor().result().update();
1861                         if (!view()->cursor().result().dispatched())
1862                                 updateFlags = view()->dispatch(cmd);
1863                         break;
1864                 }
1865                 }
1866
1867                 if (lyx_view_ && view()->buffer()) {
1868                         // BufferView::update() updates the ViewMetricsInfo and
1869                         // also initializes the position cache for all insets in
1870                         // (at least partially) visible top-level paragraphs.
1871                         // We will redraw the screen only if needed.
1872                         if (view()->update(updateFlags)) {
1873                                 // Buffer::changed() signals that a repaint is needed.
1874                                 // The frontend (WorkArea) knows which area to repaint
1875                                 // thanks to the ViewMetricsInfo updated above.
1876                                 view()->buffer()->changed();
1877                         }
1878
1879                         lyx_view_->updateStatusBar();
1880
1881                         // if we executed a mutating lfun, mark the buffer as dirty
1882                         if (flag.enabled()
1883                             && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1884                             && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1885                                 view()->buffer()->markDirty();
1886
1887                         //Do we have a selection?
1888                         theSelection().haveSelection(view()->cursor().selection());
1889
1890                         if (view()->cursor().inTexted()) {
1891                                 lyx_view_->updateLayoutChoice();
1892                         }
1893                 }
1894         }
1895         if (!quitting && lyx_view_) {
1896                 lyx_view_->updateMenubar();
1897                 lyx_view_->updateToolbars();
1898                 // Some messages may already be translated, so we cannot use _()
1899                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1900         }
1901 }
1902
1903
1904 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1905 {
1906         const bool verbose = (cmd.origin == FuncRequest::MENU
1907                               || cmd.origin == FuncRequest::TOOLBAR
1908                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1909
1910         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1911                 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1912                 if (!msg.empty())
1913                         lyx_view_->message(msg);
1914                 return;
1915         }
1916
1917         docstring dispatch_msg = msg;
1918         if (!dispatch_msg.empty())
1919                 dispatch_msg += ' ';
1920
1921         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1922
1923         bool argsadded = false;
1924
1925         if (!cmd.argument().empty()) {
1926                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1927                         comname += ' ' + cmd.argument();
1928                         argsadded = true;
1929                 }
1930         }
1931
1932         docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1933
1934         if (!shortcuts.empty())
1935                 comname += ": " + shortcuts;
1936         else if (!argsadded && !cmd.argument().empty())
1937                 comname += ' ' + cmd.argument();
1938
1939         if (!comname.empty()) {
1940                 comname = rtrim(comname);
1941                 dispatch_msg += '(' + rtrim(comname) + ')';
1942         }
1943
1944         LYXERR(Debug::ACTION) << "verbose dispatch msg "
1945                 << to_utf8(dispatch_msg) << endl;
1946         if (!dispatch_msg.empty())
1947                 lyx_view_->message(dispatch_msg);
1948 }
1949
1950
1951 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1952 {
1953         // FIXME: initpath is not used. What to do?
1954         string initpath = lyxrc.document_path;
1955         string filename(name);
1956
1957         if (view()->buffer()) {
1958                 string const trypath = lyx_view_->buffer()->filePath();
1959                 // If directory is writeable, use this as default.
1960                 if (isDirWriteable(FileName(trypath)))
1961                         initpath = trypath;
1962         }
1963
1964         static int newfile_number;
1965
1966         if (filename.empty()) {
1967                 filename = addName(lyxrc.document_path,
1968                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1969                 while (theBufferList().exists(filename) ||
1970                        fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1971                         ++newfile_number;
1972                         filename = addName(lyxrc.document_path,
1973                                            "newfile" +  convert<string>(newfile_number) +
1974                                     ".lyx");
1975                 }
1976         }
1977
1978         // The template stuff
1979         string templname;
1980         if (fromTemplate) {
1981                 FileDialog fileDlg(_("Select template file"),
1982                         LFUN_SELECT_FILE_SYNC,
1983                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1984                         make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1985
1986                 FileDialog::Result result =
1987                         fileDlg.open(from_utf8(lyxrc.template_path),
1988                                      FileFilterList(_("LyX Documents (*.lyx)")),
1989                                      docstring());
1990
1991                 if (result.first == FileDialog::Later)
1992                         return;
1993                 if (result.second.empty())
1994                         return;
1995                 templname = to_utf8(result.second);
1996         }
1997
1998         Buffer * const b = newFile(filename, templname, !name.empty());
1999         if (b) {
2000                 updateLabels(*b);
2001                 lyx_view_->setBuffer(b);
2002         }
2003 }
2004
2005
2006 void LyXFunc::open(string const & fname)
2007 {
2008         string initpath = lyxrc.document_path;
2009
2010         if (view()->buffer()) {
2011                 string const trypath = lyx_view_->buffer()->filePath();
2012                 // If directory is writeable, use this as default.
2013                 if (isDirWriteable(FileName(trypath)))
2014                         initpath = trypath;
2015         }
2016
2017         string filename;
2018
2019         if (fname.empty()) {
2020                 FileDialog fileDlg(_("Select document to open"),
2021                         LFUN_FILE_OPEN,
2022                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2023                         make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2024
2025                 FileDialog::Result result =
2026                         fileDlg.open(from_utf8(initpath),
2027                                      FileFilterList(_("LyX Documents (*.lyx)")),
2028                                      docstring());
2029
2030                 if (result.first == FileDialog::Later)
2031                         return;
2032
2033                 filename = to_utf8(result.second);
2034
2035                 // check selected filename
2036                 if (filename.empty()) {
2037                         lyx_view_->message(_("Canceled."));
2038                         return;
2039                 }
2040         } else
2041                 filename = fname;
2042
2043         // get absolute path of file and add ".lyx" to the filename if
2044         // necessary
2045         FileName const fullname = fileSearch(string(), filename, "lyx");
2046         if (!fullname.empty())
2047                 filename = fullname.absFilename();
2048
2049         // if the file doesn't exist, let the user create one
2050         if (!fs::exists(fullname.toFilesystemEncoding())) {
2051                 // the user specifically chose this name. Believe him.
2052                 Buffer * const b = newFile(filename, string(), true);
2053                 if (b)
2054                         lyx_view_->setBuffer(b);
2055                 return;
2056         }
2057
2058         docstring const disp_fn = makeDisplayPath(filename);
2059         lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
2060
2061         docstring str2;
2062         if (lyx_view_->loadLyXFile(fullname)) {
2063                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2064         } else {
2065                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2066         }
2067         lyx_view_->message(str2);
2068 }
2069
2070
2071 void LyXFunc::doImport(string const & argument)
2072 {
2073         string format;
2074         string filename = split(argument, format, ' ');
2075
2076         LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
2077                             << " file: " << filename << endl;
2078
2079         // need user interaction
2080         if (filename.empty()) {
2081                 string initpath = lyxrc.document_path;
2082
2083                 if (view()->buffer()) {
2084                         string const trypath = lyx_view_->buffer()->filePath();
2085                         // If directory is writeable, use this as default.
2086                         if (isDirWriteable(FileName(trypath)))
2087                                 initpath = trypath;
2088                 }
2089
2090                 docstring const text = bformat(_("Select %1$s file to import"),
2091                         formats.prettyName(format));
2092
2093                 FileDialog fileDlg(text,
2094                         LFUN_BUFFER_IMPORT,
2095                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2096                         make_pair(_("Examples|#E#e"),
2097                                   from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2098
2099                 docstring filter = formats.prettyName(format);
2100                 filter += " (*.";
2101                 // FIXME UNICODE
2102                 filter += from_utf8(formats.extension(format));
2103                 filter += ')';
2104
2105                 FileDialog::Result result =
2106                         fileDlg.open(from_utf8(initpath),
2107                                      FileFilterList(filter),
2108                                      docstring());
2109
2110                 if (result.first == FileDialog::Later)
2111                         return;
2112
2113                 filename = to_utf8(result.second);
2114
2115                 // check selected filename
2116                 if (filename.empty())
2117                         lyx_view_->message(_("Canceled."));
2118         }
2119
2120         if (filename.empty())
2121                 return;
2122
2123         // get absolute path of file
2124         FileName const fullname(makeAbsPath(filename));
2125
2126         FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2127
2128         // Check if the document already is open
2129         if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2130                 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2131                         lyx_view_->message(_("Canceled."));
2132                         return;
2133                 }
2134         }
2135
2136         // if the file exists already, and we didn't do
2137         // -i lyx thefile.lyx, warn
2138         if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2139                 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2140
2141                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2142                                                      "Do you want to overwrite that document?"), file);
2143                 int const ret = Alert::prompt(_("Overwrite document?"),
2144                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2145
2146                 if (ret == 1) {
2147                         lyx_view_->message(_("Canceled."));
2148                         return;
2149                 }
2150         }
2151
2152         ErrorList errorList;
2153         Importer::Import(lyx_view_, fullname, format, errorList);
2154         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2155 }
2156
2157
2158 void LyXFunc::closeBuffer()
2159 {
2160         // goto bookmark to update bookmark pit.
2161         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2162                 gotoBookmark(i+1, false, false);
2163         
2164         theBufferList().close(lyx_view_->buffer(), true);
2165 }
2166
2167
2168 void LyXFunc::reloadBuffer()
2169 {
2170         FileName filename(lyx_view_->buffer()->fileName());
2171         closeBuffer();
2172         lyx_view_->loadLyXFile(filename);
2173 }
2174
2175 // Each "lyx_view_" should have it's own message method. lyxview and
2176 // the minibuffer would use the minibuffer, but lyxserver would
2177 // send an ERROR signal to its client.  Alejandro 970603
2178 // This function is bit problematic when it comes to NLS, to make the
2179 // lyx servers client be language indepenent we must not translate
2180 // strings sent to this func.
2181 void LyXFunc::setErrorMessage(docstring const & m) const
2182 {
2183         dispatch_buffer = m;
2184         errorstat = true;
2185 }
2186
2187
2188 void LyXFunc::setMessage(docstring const & m) const
2189 {
2190         dispatch_buffer = m;
2191 }
2192
2193
2194 docstring const LyXFunc::viewStatusMessage()
2195 {
2196         // When meta-fake key is pressed, show the key sequence so far + "M-".
2197         if (wasMetaKey())
2198                 return keyseq->print(true) + "M-";
2199
2200         // Else, when a non-complete key sequence is pressed,
2201         // show the available options.
2202         if (keyseq->length() > 0 && !keyseq->deleted())
2203                 return keyseq->printOptions(true);
2204
2205         if (!view()->buffer())
2206                 return _("Welcome to LyX!");
2207
2208         return view()->cursor().currentState();
2209 }
2210
2211
2212 BufferView * LyXFunc::view() const
2213 {
2214         BOOST_ASSERT(lyx_view_);
2215         return lyx_view_->view();
2216 }
2217
2218
2219 bool LyXFunc::wasMetaKey() const
2220 {
2221         return (meta_fake_bit != key_modifier::none);
2222 }
2223
2224
2225 namespace {
2226
2227 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2228 {
2229         // Why the switch you might ask. It is a trick to ensure that all
2230         // the elements in the LyXRCTags enum is handled. As you can see
2231         // there are no breaks at all. So it is just a huge fall-through.
2232         // The nice thing is that we will get a warning from the compiler
2233         // if we forget an element.
2234         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2235         switch (tag) {
2236         case LyXRC::RC_ACCEPT_COMPOUND:
2237         case LyXRC::RC_ALT_LANG:
2238         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2239         case LyXRC::RC_PLAINTEXT_LINELEN:
2240         case LyXRC::RC_AUTOREGIONDELETE:
2241         case LyXRC::RC_AUTORESET_OPTIONS:
2242         case LyXRC::RC_AUTOSAVE:
2243         case LyXRC::RC_AUTO_NUMBER:
2244         case LyXRC::RC_BACKUPDIR_PATH:
2245         case LyXRC::RC_BIBTEX_COMMAND:
2246         case LyXRC::RC_BINDFILE:
2247         case LyXRC::RC_CHECKLASTFILES:
2248         case LyXRC::RC_USELASTFILEPOS:
2249         case LyXRC::RC_LOADSESSION:
2250         case LyXRC::RC_CHKTEX_COMMAND:
2251         case LyXRC::RC_CONVERTER:
2252         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2253         case LyXRC::RC_COPIER:
2254         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2255         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2256         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2257         case LyXRC::RC_DATE_INSERT_FORMAT:
2258         case LyXRC::RC_DEFAULT_LANGUAGE:
2259         case LyXRC::RC_DEFAULT_PAPERSIZE:
2260         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2261         case LyXRC::RC_DISPLAY_GRAPHICS:
2262         case LyXRC::RC_DOCUMENTPATH:
2263                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2264                         string const encoded = FileName(
2265                                 lyxrc_new.document_path).toFilesystemEncoding();
2266                         if (fs::exists(encoded) && fs::is_directory(encoded))
2267                                 support::package().document_dir() = FileName(lyxrc.document_path);
2268                 }
2269         case LyXRC::RC_ESC_CHARS:
2270         case LyXRC::RC_FONT_ENCODING:
2271         case LyXRC::RC_FORMAT:
2272         case LyXRC::RC_INDEX_COMMAND:
2273         case LyXRC::RC_INPUT:
2274         case LyXRC::RC_KBMAP:
2275         case LyXRC::RC_KBMAP_PRIMARY:
2276         case LyXRC::RC_KBMAP_SECONDARY:
2277         case LyXRC::RC_LABEL_INIT_LENGTH:
2278         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2279         case LyXRC::RC_LANGUAGE_AUTO_END:
2280         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2281         case LyXRC::RC_LANGUAGE_COMMAND_END:
2282         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2283         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2284         case LyXRC::RC_LANGUAGE_PACKAGE:
2285         case LyXRC::RC_LANGUAGE_USE_BABEL:
2286         case LyXRC::RC_MAKE_BACKUP:
2287         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2288         case LyXRC::RC_NUMLASTFILES:
2289         case LyXRC::RC_PATH_PREFIX:
2290                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2291                         support::prependEnvPath("PATH", lyxrc.path_prefix);
2292                 }
2293         case LyXRC::RC_PERS_DICT:
2294         case LyXRC::RC_PREVIEW:
2295         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2296         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2297         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2298         case LyXRC::RC_PRINTCOPIESFLAG:
2299         case LyXRC::RC_PRINTER:
2300         case LyXRC::RC_PRINTEVENPAGEFLAG:
2301         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2302         case LyXRC::RC_PRINTFILEEXTENSION:
2303         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2304         case LyXRC::RC_PRINTODDPAGEFLAG:
2305         case LyXRC::RC_PRINTPAGERANGEFLAG:
2306         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2307         case LyXRC::RC_PRINTPAPERFLAG:
2308         case LyXRC::RC_PRINTREVERSEFLAG:
2309         case LyXRC::RC_PRINTSPOOL_COMMAND:
2310         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2311         case LyXRC::RC_PRINTTOFILE:
2312         case LyXRC::RC_PRINTTOPRINTER:
2313         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2314         case LyXRC::RC_PRINT_COMMAND:
2315         case LyXRC::RC_RTL_SUPPORT:
2316         case LyXRC::RC_SCREEN_DPI:
2317         case LyXRC::RC_SCREEN_FONT_ROMAN:
2318         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2319         case LyXRC::RC_SCREEN_FONT_SANS:
2320         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2321         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2322         case LyXRC::RC_SCREEN_FONT_SIZES:
2323         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2324         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2325         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2326         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2327         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2328         case LyXRC::RC_SCREEN_ZOOM:
2329         case LyXRC::RC_SERVERPIPE:
2330         case LyXRC::RC_SET_COLOR:
2331         case LyXRC::RC_SHOW_BANNER:
2332         case LyXRC::RC_SPELL_COMMAND:
2333         case LyXRC::RC_TEMPDIRPATH:
2334         case LyXRC::RC_TEMPLATEPATH:
2335         case LyXRC::RC_TEX_ALLOWS_SPACES:
2336         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2337                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2338                         support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2339                 }
2340         case LyXRC::RC_UIFILE:
2341         case LyXRC::RC_USER_EMAIL:
2342         case LyXRC::RC_USER_NAME:
2343         case LyXRC::RC_USETEMPDIR:
2344         case LyXRC::RC_USE_ALT_LANG:
2345         case LyXRC::RC_USE_CONVERTER_CACHE:
2346         case LyXRC::RC_USE_ESC_CHARS:
2347         case LyXRC::RC_USE_INP_ENC:
2348         case LyXRC::RC_USE_PERS_DICT:
2349         case LyXRC::RC_USE_SPELL_LIB:
2350         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2351         case LyXRC::RC_VIEWER:
2352         case LyXRC::RC_LAST:
2353                 break;
2354         }
2355 }
2356
2357 } // namespace anon
2358
2359
2360 } // namespace lyx