]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
Remind scons users to check config.log when configure fails
[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() && !buf->isClean();
500                 break;
501
502         case LFUN_INSET_SETTINGS: {
503                 enable = false;
504                 if (!cur)
505                         break;
506                 Inset::Code code = cur.inset().lyxCode();
507                 switch (code) {
508                         case Inset::TABULAR_CODE:
509                                 enable = cmd.argument() == "tabular";
510                                 break;
511                         case Inset::ERT_CODE:
512                                 enable = cmd.argument() == "ert";
513                                 break;
514                         case Inset::FLOAT_CODE:
515                                 enable = cmd.argument() == "float";
516                                 break;
517                         case Inset::WRAP_CODE:
518                                 enable = cmd.argument() == "wrap";
519                                 break;
520                         case Inset::NOTE_CODE:
521                                 enable = cmd.argument() == "note";
522                                 break;
523                         case Inset::BRANCH_CODE:
524                                 enable = cmd.argument() == "branch";
525                                 break;
526                         case Inset::BOX_CODE:
527                                 enable = cmd.argument() == "box";
528                                 break;
529                         case Inset::LISTINGS_CODE:
530                                 enable = cmd.argument() == "listings";
531                                 break;
532                         default:
533                                 break;
534                 }
535                 break;
536         }
537
538         case LFUN_INSET_APPLY: {
539                 string const name = cmd.getArg(0);
540                 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
541                 if (inset) {
542                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
543                         FuncStatus fs;
544                         if (!inset->getStatus(cur, fr, fs)) {
545                                 // Every inset is supposed to handle this
546                                 BOOST_ASSERT(false);
547                         }
548                         flag |= fs;
549                 } else {
550                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
551                         flag |= getStatus(fr);
552                 }
553                 enable = flag.enabled();
554                 break;
555         }
556
557         case LFUN_DIALOG_TOGGLE:
558                 flag.setOnOff(lyx_view_->getDialogs().visible(cmd.getArg(0)));
559                 // fall through to set "enable"
560         case LFUN_DIALOG_SHOW: {
561                 string const name = cmd.getArg(0);
562                 if (!buf)
563                         enable = name == "aboutlyx"
564                                 || name == "file" //FIXME: should be removed.
565                                 || name == "prefs"
566                                 || name == "texinfo";
567                 else if (name == "print")
568                         enable = Exporter::isExportable(*buf, "dvi")
569                                 && lyxrc.print_command != "none";
570                 else if (name == "character")
571                         enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
572                                 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
573                 else if (name == "latexlog")
574                         enable = isFileReadable(FileName(buf->getLogName().second));
575                 else if (name == "spellchecker")
576 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
577                         enable = !buf->isReadonly();
578 #else
579                         enable = false;
580 #endif
581                 else if (name == "vclog")
582                         enable = buf->lyxvc().inUse();
583                 break;
584         }
585
586         case LFUN_DIALOG_SHOW_NEW_INSET:
587                 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
588                         cur.inset().lyxCode() != Inset::LISTINGS_CODE;
589                 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
590                         FuncStatus flag;
591                         if (cur.inset().getStatus(cur, cmd, flag))
592                                 return flag;
593                 }
594                 break;
595
596         case LFUN_DIALOG_UPDATE: {
597                 string const name = cmd.getArg(0);
598                 if (!buf)
599                         enable = name == "prefs";
600                 break;
601         }
602
603         case LFUN_CITATION_INSERT: {
604                 FuncRequest fr(LFUN_INSET_INSERT, "citation");
605                 enable = getStatus(fr).enabled();
606                 break;
607         }
608
609         case LFUN_BUFFER_WRITE: {
610                 enable = view()->buffer()->isUnnamed()
611                         || !view()->buffer()->isClean();
612                 break;
613         }
614
615
616         case LFUN_BUFFER_WRITE_ALL: {
617         // We enable the command only if there are some modified buffers
618                 Buffer * first = theBufferList().first();
619                 bool modified = false;
620                 if (first) {
621                         Buffer * b = first;
622                 
623                 // We cannot use a for loop as the buffer list is a cycle.
624                         do {
625                                 if (!b->isClean()) {
626                                         modified = true;
627                                         break;
628                                 }
629                                 b = theBufferList().next(b);
630                         } while (b != first); 
631                 }
632         
633                 enable = modified;
634
635                 break;
636         }
637
638         case LFUN_BOOKMARK_GOTO: {
639                 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
640                 enable = LyX::ref().session().bookmarks().isValid(num);
641                 break;
642         }
643
644         case LFUN_BOOKMARK_CLEAR:
645                 enable = LyX::ref().session().bookmarks().size() > 0;
646                 break;
647
648         case LFUN_TOOLBAR_TOGGLE: {
649                 bool const current = lyx_view_->getToolbars().visible(cmd.getArg(0));
650                 flag.setOnOff(current);
651                 break;
652         }
653         case LFUN_WINDOW_CLOSE: {
654                 enable = (theApp()->gui().viewIds().size() > 1);
655                 break;
656         }
657
658         // this one is difficult to get right. As a half-baked
659         // solution, we consider only the first action of the sequence
660         case LFUN_COMMAND_SEQUENCE: {
661                 // argument contains ';'-terminated commands
662                 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
663                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
664                 func.origin = cmd.origin;
665                 flag = getStatus(func);
666         }
667
668         case LFUN_BUFFER_NEW:
669         case LFUN_BUFFER_NEW_TEMPLATE:
670         case LFUN_WORD_FIND_FORWARD:
671         case LFUN_WORD_FIND_BACKWARD:
672         case LFUN_COMMAND_PREFIX:
673         case LFUN_COMMAND_EXECUTE:
674         case LFUN_CANCEL:
675         case LFUN_META_PREFIX:
676         case LFUN_BUFFER_CLOSE:
677         case LFUN_BUFFER_WRITE_AS:
678         case LFUN_BUFFER_UPDATE:
679         case LFUN_BUFFER_VIEW:
680         case LFUN_BUFFER_IMPORT:
681         case LFUN_BUFFER_AUTO_SAVE:
682         case LFUN_RECONFIGURE:
683         case LFUN_HELP_OPEN:
684         case LFUN_FILE_NEW:
685         case LFUN_FILE_OPEN:
686         case LFUN_DROP_LAYOUTS_CHOICE:
687         case LFUN_MENU_OPEN:
688         case LFUN_SERVER_GET_NAME:
689         case LFUN_SERVER_NOTIFY:
690         case LFUN_SERVER_GOTO_FILE_ROW:
691         case LFUN_DIALOG_HIDE:
692         case LFUN_DIALOG_DISCONNECT_INSET:
693         case LFUN_BUFFER_CHILD_OPEN:
694         case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
695         case LFUN_KEYMAP_OFF:
696         case LFUN_KEYMAP_PRIMARY:
697         case LFUN_KEYMAP_SECONDARY:
698         case LFUN_KEYMAP_TOGGLE:
699         case LFUN_REPEAT:
700         case LFUN_BUFFER_EXPORT_CUSTOM:
701         case LFUN_BUFFER_PRINT:
702         case LFUN_PREFERENCES_SAVE:
703         case LFUN_SCREEN_FONT_UPDATE:
704         case LFUN_SET_COLOR:
705         case LFUN_MESSAGE:
706         case LFUN_EXTERNAL_EDIT:
707         case LFUN_GRAPHICS_EDIT:
708         case LFUN_ALL_INSETS_TOGGLE:
709         case LFUN_BUFFER_LANGUAGE:
710         case LFUN_TEXTCLASS_APPLY:
711         case LFUN_TEXTCLASS_LOAD:
712         case LFUN_BUFFER_SAVE_AS_DEFAULT:
713         case LFUN_BUFFER_PARAMS_APPLY:
714         case LFUN_LYXRC_APPLY:
715         case LFUN_BUFFER_NEXT:
716         case LFUN_BUFFER_PREVIOUS:
717         case LFUN_WINDOW_NEW:
718         case LFUN_LYX_QUIT:
719                 // these are handled in our dispatch()
720                 break;
721
722         default:
723                 if (!getLocalStatus(cur, cmd, flag))
724                         flag = view()->getStatus(cmd);
725         }
726
727         if (!enable)
728                 flag.enabled(false);
729
730         // Can we use a readonly buffer?
731         if (buf && buf->isReadonly()
732             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
733             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
734                 flag.message(from_utf8(N_("Document is read-only")));
735                 flag.enabled(false);
736         }
737
738         // Are we in a DELETED change-tracking region?
739         if (buf && lookupChangeType(cur, true) == Change::DELETED
740             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
741             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
742                 flag.message(from_utf8(N_("This portion of the document is deleted.")));
743                 flag.enabled(false);
744         }
745
746         // the default error message if we disable the command
747         if (!flag.enabled() && flag.message().empty())
748                 flag.message(from_utf8(N_("Command disabled")));
749
750         return flag;
751 }
752
753
754 bool LyXFunc::ensureBufferClean(BufferView * bv)
755 {
756         Buffer & buf = *bv->buffer();
757         if (buf.isClean())
758                 return true;
759
760         docstring const file = makeDisplayPath(buf.fileName(), 30);
761         docstring text = bformat(_("The document %1$s has unsaved "
762                                              "changes.\n\nDo you want to save "
763                                              "the document?"), file);
764         int const ret = Alert::prompt(_("Save changed document?"),
765                                       text, 0, 1, _("&Save"),
766                                       _("&Cancel"));
767
768         if (ret == 0)
769                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
770
771         return buf.isClean();
772 }
773
774
775 namespace {
776
777 void showPrintError(string const & name)
778 {
779         docstring str = bformat(_("Could not print the document %1$s.\n"
780                                             "Check that your printer is set up correctly."),
781                              makeDisplayPath(name, 50));
782         Alert::error(_("Print document failed"), str);
783 }
784
785
786 void loadTextclass(string const & name)
787 {
788         std::pair<bool, textclass_type> const tc_pair =
789                 textclasslist.numberOfClass(name);
790
791         if (!tc_pair.first) {
792                 lyxerr << "Document class \"" << name
793                        << "\" does not exist."
794                        << std::endl;
795                 return;
796         }
797
798         textclass_type const tc = tc_pair.second;
799
800         if (!textclasslist[tc].load()) {
801                 docstring s = bformat(_("The document could not be converted\n"
802                                                   "into the document class %1$s."),
803                                    from_utf8(textclasslist[tc].name()));
804                 Alert::error(_("Could not change class"), s);
805         }
806 }
807
808
809 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
810
811 } //namespace anon
812
813
814 void LyXFunc::dispatch(FuncRequest const & cmd)
815 {
816         string const argument = to_utf8(cmd.argument());
817         kb_action const action = cmd.action;
818
819         LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
820         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
821
822         // we have not done anything wrong yet.
823         errorstat = false;
824         dispatch_buffer.erase();
825
826         // redraw the screen at the end (first of the two drawing steps).
827         //This is done unless explicitely requested otherwise
828         Update::flags updateFlags = Update::FitCursor;
829
830         FuncStatus const flag = getStatus(cmd);
831         if (!flag.enabled()) {
832                 // We cannot use this function here
833                 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
834                        << lyxaction.getActionName(action)
835                        << " [" << action << "] is disabled at this location"
836                        << endl;
837                 setErrorMessage(flag.message());
838         } else {
839                 switch (action) {
840
841                 case LFUN_WORD_FIND_FORWARD:
842                 case LFUN_WORD_FIND_BACKWARD: {
843                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
844                         static docstring last_search;
845                         docstring searched_string;
846
847                         if (!cmd.argument().empty()) {
848                                 last_search = cmd.argument();
849                                 searched_string = cmd.argument();
850                         } else {
851                                 searched_string = last_search;
852                         }
853
854                         if (searched_string.empty())
855                                 break;
856
857                         bool const fw = action == LFUN_WORD_FIND_FORWARD;
858                         docstring const data =
859                                 find2string(searched_string, true, false, fw);
860                         find(view(), FuncRequest(LFUN_WORD_FIND, data));
861                         break;
862                 }
863
864                 case LFUN_COMMAND_PREFIX:
865                         BOOST_ASSERT(lyx_view_);
866                         lyx_view_->message(keyseq->printOptions(true));
867                         break;
868
869                 case LFUN_COMMAND_EXECUTE:
870                         BOOST_ASSERT(lyx_view_);
871                         lyx_view_->getToolbars().display("minibuffer", true);
872                         lyx_view_->focus_command_buffer();
873                         break;
874
875                 case LFUN_CANCEL:
876                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
877                         keyseq->reset();
878                         meta_fake_bit = key_modifier::none;
879                         if (view()->buffer())
880                                 // cancel any selection
881                                 dispatch(FuncRequest(LFUN_MARK_OFF));
882                         setMessage(from_ascii(N_("Cancel")));
883                         break;
884
885                 case LFUN_META_PREFIX:
886                         meta_fake_bit = key_modifier::alt;
887                         setMessage(keyseq->print(true));
888                         break;
889
890                 case LFUN_BUFFER_TOGGLE_READ_ONLY:
891                         BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
892                         if (lyx_view_->buffer()->lyxvc().inUse())
893                                 lyx_view_->buffer()->lyxvc().toggleReadOnly();
894                         else
895                                 lyx_view_->buffer()->setReadonly(
896                                         !lyx_view_->buffer()->isReadonly());
897                         break;
898
899                 // --- Menus -----------------------------------------------
900                 case LFUN_BUFFER_NEW:
901                         menuNew(argument, false);
902                         break;
903
904                 case LFUN_BUFFER_NEW_TEMPLATE:
905                         menuNew(argument, true);
906                         break;
907
908                 case LFUN_BUFFER_CLOSE:
909                         closeBuffer();
910                         view()->update();
911                         break;
912
913                 case LFUN_BUFFER_WRITE:
914                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
915                         if (!lyx_view_->buffer()->isUnnamed()) {
916                                 docstring const str = bformat(_("Saving document %1$s..."),
917                                          makeDisplayPath(lyx_view_->buffer()->fileName()));
918                                 lyx_view_->message(str);
919                                 menuWrite(lyx_view_->buffer());
920                                 lyx_view_->message(str + _(" done."));
921                         } else {
922                                 writeAs(lyx_view_->buffer());
923                         }
924                         updateFlags = Update::None;
925                         break;
926
927                 case LFUN_BUFFER_WRITE_AS:
928                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
929                         writeAs(lyx_view_->buffer(), argument);
930                         updateFlags = Update::None;
931                         break;
932
933                 case LFUN_BUFFER_WRITE_ALL: {
934                         Buffer * first = theBufferList().first();
935                         if (first) {
936                                 Buffer * b = first;
937                                 lyx_view_->message(_("Saving all documents..."));
938                 
939                                 // We cannot use a for loop as the buffer list cycles.
940                                 do {
941                                         if (!b->isClean()) {
942                                                 if (!b->isUnnamed()) {
943                                                         menuWrite(b);
944                                                         lyxerr[Debug::ACTION] << "Saved " << b->fileName() << endl;
945                                                 } else
946                                                         writeAs(b);
947                                         }
948                                         b = theBufferList().next(b);
949                                 } while (b != first); 
950                                 lyx_view_->message(_("All documents saved."));
951                         } 
952         
953                         updateFlags = Update::None;
954                         break;
955                 }
956
957                 case LFUN_BUFFER_RELOAD: {
958                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
959                         docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
960                         docstring text = bformat(_("Any changes will be lost. Are you sure "
961                                                              "you want to revert to the saved version of the document %1$s?"), file);
962                         int const ret = Alert::prompt(_("Revert to saved document?"),
963                                 text, 1, 1, _("&Revert"), _("&Cancel"));
964
965                         if (ret == 0)
966                                 reloadBuffer();
967                         break;
968                 }
969
970                 case LFUN_BUFFER_UPDATE:
971                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
972                         Exporter::Export(lyx_view_->buffer(), argument, true);
973                         break;
974
975                 case LFUN_BUFFER_VIEW:
976                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
977                         Exporter::preview(lyx_view_->buffer(), argument);
978                         break;
979
980                 case LFUN_BUILD_PROGRAM:
981                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
982                         Exporter::Export(lyx_view_->buffer(), "program", true);
983                         break;
984
985                 case LFUN_BUFFER_CHKTEX:
986                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
987                         lyx_view_->buffer()->runChktex();
988                         break;
989
990                 case LFUN_BUFFER_EXPORT:
991                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
992                         if (argument == "custom")
993                                 lyx_view_->getDialogs().show("sendto");
994                         else {
995                                 Exporter::Export(lyx_view_->buffer(), argument, false);
996                         }
997                         break;
998
999                 case LFUN_BUFFER_EXPORT_CUSTOM: {
1000                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1001                         string format_name;
1002                         string command = split(argument, format_name, ' ');
1003                         Format const * format = formats.getFormat(format_name);
1004                         if (!format) {
1005                                 lyxerr << "Format \"" << format_name
1006                                        << "\" not recognized!"
1007                                        << std::endl;
1008                                 break;
1009                         }
1010
1011                         Buffer * buffer = lyx_view_->buffer();
1012
1013                         // The name of the file created by the conversion process
1014                         string filename;
1015
1016                         // Output to filename
1017                         if (format->name() == "lyx") {
1018                                 string const latexname =
1019                                         buffer->getLatexName(false);
1020                                 filename = changeExtension(latexname,
1021                                                            format->extension());
1022                                 filename = addName(buffer->temppath(), filename);
1023
1024                                 if (!buffer->writeFile(FileName(filename)))
1025                                         break;
1026
1027                         } else {
1028                                 Exporter::Export(buffer, format_name, true, filename);
1029                         }
1030
1031                         // Substitute $$FName for filename
1032                         if (!contains(command, "$$FName"))
1033                                 command = "( " + command + " ) < $$FName";
1034                         command = subst(command, "$$FName", filename);
1035
1036                         // Execute the command in the background
1037                         Systemcall call;
1038                         call.startscript(Systemcall::DontWait, command);
1039                         break;
1040                 }
1041
1042                 case LFUN_BUFFER_PRINT: {
1043                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1044                         // FIXME: cmd.getArg() might fail if one of the arguments
1045                         // contains double quotes
1046                         string target = cmd.getArg(0);
1047                         string target_name = cmd.getArg(1);
1048                         string command = cmd.getArg(2);
1049
1050                         if (target.empty()
1051                             || target_name.empty()
1052                             || command.empty()) {
1053                                 lyxerr << "Unable to parse \""
1054                                        << argument << '"' << endl;
1055                                 break;
1056                         }
1057                         if (target != "printer" && target != "file") {
1058                                 lyxerr << "Unrecognized target \""
1059                                        << target << '"' << endl;
1060                                 break;
1061                         }
1062
1063                         Buffer * buffer = lyx_view_->buffer();
1064
1065                         if (!Exporter::Export(buffer, "dvi", true)) {
1066                                 showPrintError(buffer->fileName());
1067                                 break;
1068                         }
1069
1070                         // Push directory path.
1071                         string const path(buffer->temppath());
1072                         // Prevent the compiler from optimizing away p
1073                         FileName pp(path);
1074                         support::Path p(pp);
1075
1076                         // there are three cases here:
1077                         // 1. we print to a file
1078                         // 2. we print directly to a printer
1079                         // 3. we print using a spool command (print to file first)
1080                         Systemcall one;
1081                         int res = 0;
1082                         string const dviname =
1083                                 changeExtension(buffer->getLatexName(true),
1084                                                 "dvi");
1085
1086                         if (target == "printer") {
1087                                 if (!lyxrc.print_spool_command.empty()) {
1088                                         // case 3: print using a spool
1089                                         string const psname =
1090                                                 changeExtension(dviname,".ps");
1091                                         command += ' ' + lyxrc.print_to_file
1092                                                 + quoteName(psname)
1093                                                 + ' '
1094                                                 + quoteName(dviname);
1095
1096                                         string command2 =
1097                                                 lyxrc.print_spool_command + ' ';
1098                                         if (target_name != "default") {
1099                                                 command2 += lyxrc.print_spool_printerprefix
1100                                                         + target_name
1101                                                         + ' ';
1102                                         }
1103                                         command2 += quoteName(psname);
1104                                         // First run dvips.
1105                                         // If successful, then spool command
1106                                         res = one.startscript(
1107                                                 Systemcall::Wait,
1108                                                 command);
1109
1110                                         if (res == 0)
1111                                                 res = one.startscript(
1112                                                         Systemcall::DontWait,
1113                                                         command2);
1114                                 } else {
1115                                         // case 2: print directly to a printer
1116                                         if (target_name != "default")
1117                                                 command += ' ' + lyxrc.print_to_printer + target_name + ' ';
1118                                         res = one.startscript(
1119                                                 Systemcall::DontWait,
1120                                                 command + quoteName(dviname));
1121                                 }
1122
1123                         } else {
1124                                 // case 1: print to a file
1125                                 FileName const filename(makeAbsPath(target_name,
1126                                                         lyx_view_->buffer()->filePath()));
1127                                 FileName const dvifile(makeAbsPath(dviname, path));
1128                                 if (fs::exists(filename.toFilesystemEncoding())) {
1129                                         docstring text = bformat(
1130                                                 _("The file %1$s already exists.\n\n"
1131                                                   "Do you want to overwrite that file?"),
1132                                                 makeDisplayPath(filename.absFilename()));
1133                                         if (Alert::prompt(_("Overwrite file?"),
1134                                             text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0)
1135                                                 break;
1136                                 }
1137                                 command += ' ' + lyxrc.print_to_file
1138                                         + quoteName(filename.toFilesystemEncoding())
1139                                         + ' '
1140                                         + quoteName(dvifile.toFilesystemEncoding());
1141                                 res = one.startscript(Systemcall::DontWait,
1142                                                       command);
1143                         }
1144
1145                         if (res != 0)
1146                                 showPrintError(buffer->fileName());
1147                         break;
1148                 }
1149
1150                 case LFUN_BUFFER_IMPORT:
1151                         doImport(argument);
1152                         break;
1153
1154                 case LFUN_LYX_QUIT:
1155                         // quitting is triggered by the gui code
1156                         // (leaving the event loop).
1157                         lyx_view_->message(from_utf8(N_("Exiting.")));
1158                         if (theBufferList().quitWriteAll())
1159                                 theApp()->gui().closeAllViews();
1160                         break;
1161
1162                 case LFUN_BUFFER_AUTO_SAVE:
1163                         autoSave(view());
1164                         break;
1165
1166                 case LFUN_RECONFIGURE:
1167                         BOOST_ASSERT(lyx_view_);
1168                         reconfigure(*lyx_view_);
1169                         break;
1170
1171                 case LFUN_HELP_OPEN: {
1172                         BOOST_ASSERT(lyx_view_);
1173                         string const arg = argument;
1174                         if (arg.empty()) {
1175                                 setErrorMessage(from_ascii(N_("Missing argument")));
1176                                 break;
1177                         }
1178                         FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1179                         if (fname.empty()) {
1180                                 lyxerr << "LyX: unable to find documentation file `"
1181                                                          << arg << "'. Bad installation?" << endl;
1182                                 break;
1183                         }
1184                         lyx_view_->message(bformat(_("Opening help file %1$s..."),
1185                                 makeDisplayPath(fname.absFilename())));
1186                         lyx_view_->loadLyXFile(fname, false);
1187                         break;
1188                 }
1189
1190                 // --- version control -------------------------------
1191                 case LFUN_VC_REGISTER:
1192                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1193                         if (!ensureBufferClean(view()))
1194                                 break;
1195                         if (!lyx_view_->buffer()->lyxvc().inUse()) {
1196                                 lyx_view_->buffer()->lyxvc().registrer();
1197                                 reloadBuffer();
1198                         }
1199                         updateFlags = Update::Force;
1200                         break;
1201
1202                 case LFUN_VC_CHECK_IN:
1203                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1204                         if (!ensureBufferClean(view()))
1205                                 break;
1206                         if (lyx_view_->buffer()->lyxvc().inUse()
1207                                         && !lyx_view_->buffer()->isReadonly()) {
1208                                 lyx_view_->buffer()->lyxvc().checkIn();
1209                                 reloadBuffer();
1210                         }
1211                         break;
1212
1213                 case LFUN_VC_CHECK_OUT:
1214                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1215                         if (!ensureBufferClean(view()))
1216                                 break;
1217                         if (lyx_view_->buffer()->lyxvc().inUse()
1218                                         && lyx_view_->buffer()->isReadonly()) {
1219                                 lyx_view_->buffer()->lyxvc().checkOut();
1220                                 reloadBuffer();
1221                         }
1222                         break;
1223
1224                 case LFUN_VC_REVERT:
1225                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1226                         lyx_view_->buffer()->lyxvc().revert();
1227                         reloadBuffer();
1228                         break;
1229
1230                 case LFUN_VC_UNDO_LAST:
1231                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1232                         lyx_view_->buffer()->lyxvc().undoLast();
1233                         reloadBuffer();
1234                         break;
1235
1236                 // --- buffers ----------------------------------------
1237                 case LFUN_BUFFER_SWITCH:
1238                         BOOST_ASSERT(lyx_view_);
1239                         lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1240                         updateFlags = Update::Force;
1241                         break;
1242
1243                 case LFUN_BUFFER_NEXT:
1244                         BOOST_ASSERT(lyx_view_);
1245                         lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1246                         updateFlags = Update::Force;
1247                         break;
1248
1249                 case LFUN_BUFFER_PREVIOUS:
1250                         BOOST_ASSERT(lyx_view_);
1251                         lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1252                         updateFlags = Update::Force;
1253                         break;
1254
1255                 case LFUN_FILE_NEW:
1256                         BOOST_ASSERT(lyx_view_);
1257                         newFile(view(), argument);
1258                         break;
1259
1260                 case LFUN_FILE_OPEN:
1261                         BOOST_ASSERT(lyx_view_);
1262                         open(argument);
1263                         break;
1264
1265                 case LFUN_DROP_LAYOUTS_CHOICE:
1266                         BOOST_ASSERT(lyx_view_);
1267                         lyx_view_->getToolbars().openLayoutList();
1268                         break;
1269
1270                 case LFUN_MENU_OPEN:
1271                         BOOST_ASSERT(lyx_view_);
1272                         lyx_view_->getMenubar().openByName(from_utf8(argument));
1273                         break;
1274
1275                 // --- lyxserver commands ----------------------------
1276                 case LFUN_SERVER_GET_NAME:
1277                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1278                         setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1279                         LYXERR(Debug::INFO) << "FNAME["
1280                                                          << lyx_view_->buffer()->fileName()
1281                                                          << "] " << endl;
1282                         break;
1283
1284                 case LFUN_SERVER_NOTIFY:
1285                         dispatch_buffer = keyseq->print(false);
1286                         theServer().notifyClient(to_utf8(dispatch_buffer));
1287                         break;
1288
1289                 case LFUN_SERVER_GOTO_FILE_ROW: {
1290                         BOOST_ASSERT(lyx_view_);
1291                         string file_name;
1292                         int row;
1293                         istringstream is(argument);
1294                         is >> file_name >> row;
1295                         if (prefixIs(file_name, package().temp_dir().absFilename())) {
1296                                 // Needed by inverse dvi search. If it is a file
1297                                 // in tmpdir, call the apropriated function
1298                                 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1299                         } else {
1300                                 // Must replace extension of the file to be .lyx
1301                                 // and get full path
1302                                 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1303                                 // Either change buffer or load the file
1304                                 if (theBufferList().exists(s.absFilename())) {
1305                                         lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1306                                 } else {
1307                                         lyx_view_->loadLyXFile(s);
1308                                 }
1309                         }
1310
1311                         view()->setCursorFromRow(row);
1312
1313                         updateFlags = Update::FitCursor;
1314                         break;
1315                 }
1316
1317                 case LFUN_DIALOG_SHOW: {
1318                         BOOST_ASSERT(lyx_view_);
1319                         string const name = cmd.getArg(0);
1320                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1321
1322                         if (name == "character") {
1323                                 data = freefont2string();
1324                                 if (!data.empty())
1325                                         lyx_view_->getDialogs().show("character", data);
1326                         } else if (name == "latexlog") {
1327                                 pair<Buffer::LogType, string> const logfile =
1328                                         lyx_view_->buffer()->getLogName();
1329                                 switch (logfile.first) {
1330                                 case Buffer::latexlog:
1331                                         data = "latex ";
1332                                         break;
1333                                 case Buffer::buildlog:
1334                                         data = "literate ";
1335                                         break;
1336                                 }
1337                                 data += Lexer::quoteString(logfile.second);
1338                                 lyx_view_->getDialogs().show("log", data);
1339                         } else if (name == "vclog") {
1340                                 string const data = "vc " +
1341                                         Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1342                                 lyx_view_->getDialogs().show("log", data);
1343                         } else
1344                                 lyx_view_->getDialogs().show(name, data);
1345                         break;
1346                 }
1347
1348                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1349                         BOOST_ASSERT(lyx_view_);
1350                         string const name = cmd.getArg(0);
1351                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1352                         if (name == "bibitem" ||
1353                             name == "bibtex" ||
1354                             name == "index" ||
1355                             name == "label" ||
1356                             name == "nomenclature" ||
1357                             name == "ref" ||
1358                             name == "toc" ||
1359                             name == "url") {
1360                                 InsetCommandParams p(name);
1361                                 data = InsetCommandMailer::params2string(name, p);
1362                         } else if (name == "include") {
1363                                 // data is the include type: one of "include",
1364                                 // "input", "verbatiminput" or "verbatiminput*"
1365                                 if (data.empty())
1366                                         // default type is requested
1367                                         data = "include";
1368                                 InsetCommandParams p(data);
1369                                 data = InsetIncludeMailer::params2string(p);
1370                         } else if (name == "box") {
1371                                 // \c data == "Boxed" || "Frameless" etc
1372                                 InsetBoxParams p(data);
1373                                 data = InsetBoxMailer::params2string(p);
1374                         } else if (name == "branch") {
1375                                 InsetBranchParams p;
1376                                 data = InsetBranchMailer::params2string(p);
1377                         } else if (name == "citation") {
1378                                 InsetCommandParams p("cite");
1379                                 data = InsetCommandMailer::params2string(name, p);
1380                         } else if (name == "ert") {
1381                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1382                         } else if (name == "external") {
1383                                 InsetExternalParams p;
1384                                 Buffer const & buffer = *lyx_view_->buffer();
1385                                 data = InsetExternalMailer::params2string(p, buffer);
1386                         } else if (name == "float") {
1387                                 InsetFloatParams p;
1388                                 data = InsetFloatMailer::params2string(p);
1389                         } else if (name == "listings") {
1390                                 InsetListingsParams p;
1391                                 data = InsetListingsMailer::params2string(p);
1392                         } else if (name == "graphics") {
1393                                 InsetGraphicsParams p;
1394                                 Buffer const & buffer = *lyx_view_->buffer();
1395                                 data = InsetGraphicsMailer::params2string(p, buffer);
1396                         } else if (name == "note") {
1397                                 InsetNoteParams p;
1398                                 data = InsetNoteMailer::params2string(p);
1399                         } else if (name == "vspace") {
1400                                 VSpace space;
1401                                 data = InsetVSpaceMailer::params2string(space);
1402                         } else if (name == "wrap") {
1403                                 InsetWrapParams p;
1404                                 data = InsetWrapMailer::params2string(p);
1405                         }
1406                         lyx_view_->getDialogs().show(name, data, 0);
1407                         break;
1408                 }
1409
1410                 case LFUN_DIALOG_UPDATE: {
1411                         BOOST_ASSERT(lyx_view_);
1412                         string const & name = argument;
1413                         // Can only update a dialog connected to an existing inset
1414                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1415                         if (inset) {
1416                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1417                                 inset->dispatch(view()->cursor(), fr);
1418                         } else if (name == "paragraph") {
1419                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1420                         } else if (name == "prefs") {
1421                                 lyx_view_->getDialogs().update(name, string());
1422                         }
1423                         break;
1424                 }
1425
1426                 case LFUN_DIALOG_HIDE:
1427                         LyX::cref().hideDialogs(argument, 0);
1428                         break;
1429
1430                 case LFUN_DIALOG_TOGGLE: {
1431                         BOOST_ASSERT(lyx_view_);
1432                         if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1433                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1434                         else
1435                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1436                         break;
1437                 }
1438
1439                 case LFUN_DIALOG_DISCONNECT_INSET:
1440                         BOOST_ASSERT(lyx_view_);
1441                         lyx_view_->getDialogs().disconnect(argument);
1442                         break;
1443
1444
1445                 case LFUN_CITATION_INSERT: {
1446                         BOOST_ASSERT(lyx_view_);
1447                         if (!argument.empty()) {
1448                                 // we can have one optional argument, delimited by '|'
1449                                 // citation-insert <key>|<text_before>
1450                                 // this should be enhanced to also support text_after
1451                                 // and citation style
1452                                 string arg = argument;
1453                                 string opt1;
1454                                 if (contains(argument, "|")) {
1455                                         arg = token(argument, '|', 0);
1456                                         opt1 = token(argument, '|', 1);
1457                                 }
1458                                 InsetCommandParams icp("cite");
1459                                 icp["key"] = from_utf8(arg);
1460                                 if (!opt1.empty())
1461                                         icp["before"] = from_utf8(opt1);
1462                                 string icstr = InsetCommandMailer::params2string("citation", icp);
1463                                 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1464                                 dispatch(fr);
1465                         } else
1466                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1467                         break;
1468                 }
1469
1470                 case LFUN_BUFFER_CHILD_OPEN: {
1471                         // takes an optional argument, "|bool", at the end
1472                         // indicating whether this file is being opened automatically
1473                         // by LyX itself, in which case we will not want to switch
1474                         // buffers after opening. The default is false, so in practice
1475                         // it is used only when true.
1476                         BOOST_ASSERT(lyx_view_);
1477                         int const arglength = argument.length();
1478                         FileName filename;
1479                         bool autoOpen = false;
1480                         if (argument.substr(arglength - 5, 5) == "|true") {
1481                                 autoOpen = true;
1482                                 filename = makeAbsPath(argument.substr(0, arglength - 5), 
1483                                         lyx_view_->buffer()->filePath());
1484                         } else if (argument.substr(arglength - 6, 6) == "|false") {
1485                                 filename = makeAbsPath(argument.substr(0, arglength - 6), 
1486                                         lyx_view_->buffer()->filePath());
1487                         } else filename = 
1488                                 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1489                         view()->saveBookmark(false);
1490                         if (theBufferList().exists(filename.absFilename())) {
1491                                 Buffer * buf = theBufferList().getBuffer(filename.absFilename());
1492                                 if (!autoOpen)
1493                                         lyx_view_->setBuffer(buf, true);
1494                                 else
1495                                         buf->setParentName(lyx_view_->buffer()->fileName());
1496                         } else
1497                                 lyx_view_->loadLyXFile(filename, true, true, autoOpen);
1498
1499                         // If a screen update is required (in case where auto_open is false), 
1500                         // loadLyXFile() would have taken care of it already. Otherwise we shall 
1501                         // reset the update flag because it can cause a circular problem.
1502                         // See bug 3970.
1503                         updateFlags = Update::None;
1504                         break;
1505                 }
1506
1507                 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1508                         BOOST_ASSERT(lyx_view_);
1509                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1510                         break;
1511
1512                 case LFUN_KEYMAP_OFF:
1513                         BOOST_ASSERT(lyx_view_);
1514                         lyx_view_->view()->getIntl().keyMapOn(false);
1515                         break;
1516
1517                 case LFUN_KEYMAP_PRIMARY:
1518                         BOOST_ASSERT(lyx_view_);
1519                         lyx_view_->view()->getIntl().keyMapPrim();
1520                         break;
1521
1522                 case LFUN_KEYMAP_SECONDARY:
1523                         BOOST_ASSERT(lyx_view_);
1524                         lyx_view_->view()->getIntl().keyMapSec();
1525                         break;
1526
1527                 case LFUN_KEYMAP_TOGGLE:
1528                         BOOST_ASSERT(lyx_view_);
1529                         lyx_view_->view()->getIntl().toggleKeyMap();
1530                         break;
1531
1532                 case LFUN_REPEAT: {
1533                         // repeat command
1534                         string countstr;
1535                         string rest = split(argument, countstr, ' ');
1536                         istringstream is(countstr);
1537                         int count = 0;
1538                         is >> count;
1539                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1540                         for (int i = 0; i < count; ++i)
1541                                 dispatch(lyxaction.lookupFunc(rest));
1542                         break;
1543                 }
1544
1545                 case LFUN_COMMAND_SEQUENCE: {
1546                         // argument contains ';'-terminated commands
1547                         string arg = argument;
1548                         while (!arg.empty()) {
1549                                 string first;
1550                                 arg = split(arg, first, ';');
1551                                 FuncRequest func(lyxaction.lookupFunc(first));
1552                                 func.origin = cmd.origin;
1553                                 dispatch(func);
1554                         }
1555                         break;
1556                 }
1557
1558                 case LFUN_PREFERENCES_SAVE: {
1559                         lyxrc.write(makeAbsPath("preferences",
1560                                                 package().user_support().absFilename()),
1561                                     false);
1562                         break;
1563                 }
1564
1565                 case LFUN_SCREEN_FONT_UPDATE:
1566                         BOOST_ASSERT(lyx_view_);
1567                         // handle the screen font changes.
1568                         theFontLoader().update();
1569                         /// FIXME: only the current view will be updated. the Gui
1570                         /// class is able to furnish the list of views.
1571                         updateFlags = Update::Force;
1572                         break;
1573
1574                 case LFUN_SET_COLOR: {
1575                         string lyx_name;
1576                         string const x11_name = split(argument, lyx_name, ' ');
1577                         if (lyx_name.empty() || x11_name.empty()) {
1578                                 setErrorMessage(from_ascii(N_(
1579                                                 "Syntax: set-color <lyx_name>"
1580                                                 " <x11_name>")));
1581                                 break;
1582                         }
1583
1584                         bool const graphicsbg_changed =
1585                                 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1586                                  x11_name != lcolor.getX11Name(Color::graphicsbg));
1587
1588                         if (!lcolor.setColor(lyx_name, x11_name)) {
1589                                 setErrorMessage(
1590                                                 bformat(_("Set-color \"%1$s\" failed "
1591                                                                        "- color is undefined or "
1592                                                                        "may not be redefined"),
1593                                                                            from_utf8(lyx_name)));
1594                                 break;
1595                         }
1596
1597                         theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1598
1599                         if (graphicsbg_changed) {
1600 #ifdef WITH_WARNINGS
1601 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1602 #endif
1603 #if 0
1604                                 graphics::GCache::get().changeDisplay(true);
1605 #endif
1606                         }
1607                         break;
1608                 }
1609
1610                 case LFUN_MESSAGE:
1611                         BOOST_ASSERT(lyx_view_);
1612                         lyx_view_->message(from_utf8(argument));
1613                         break;
1614
1615                 case LFUN_EXTERNAL_EDIT: {
1616                         BOOST_ASSERT(lyx_view_);
1617                         FuncRequest fr(action, argument);
1618                         InsetExternal().dispatch(view()->cursor(), fr);
1619                         break;
1620                 }
1621
1622                 case LFUN_GRAPHICS_EDIT: {
1623                         FuncRequest fr(action, argument);
1624                         InsetGraphics().dispatch(view()->cursor(), fr);
1625                         break;
1626                 }
1627
1628                 case LFUN_INSET_APPLY: {
1629                         BOOST_ASSERT(lyx_view_);
1630                         string const name = cmd.getArg(0);
1631                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1632                         if (inset) {
1633                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1634                                 inset->dispatch(view()->cursor(), fr);
1635                         } else {
1636                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1637                                 dispatch(fr);
1638                         }
1639                         // ideally, the update flag should be set by the insets,
1640                         // but this is not possible currently
1641                         updateFlags = Update::Force | Update::FitCursor;
1642                         break;
1643                 }
1644
1645                 case LFUN_ALL_INSETS_TOGGLE: {
1646                         BOOST_ASSERT(lyx_view_);
1647                         string action;
1648                         string const name = split(argument, action, ' ');
1649                         Inset::Code const inset_code =
1650                                 Inset::translate(name);
1651
1652                         Cursor & cur = view()->cursor();
1653                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1654
1655                         Inset & inset = lyx_view_->buffer()->inset();
1656                         InsetIterator it  = inset_iterator_begin(inset);
1657                         InsetIterator const end = inset_iterator_end(inset);
1658                         for (; it != end; ++it) {
1659                                 if (!it->asInsetMath()
1660                                     && (inset_code == Inset::NO_CODE
1661                                     || inset_code == it->lyxCode())) {
1662                                         Cursor tmpcur = cur;
1663                                         tmpcur.pushLeft(*it);
1664                                         it->dispatch(tmpcur, fr);
1665                                 }
1666                         }
1667                         updateFlags = Update::Force | Update::FitCursor;
1668                         break;
1669                 }
1670
1671                 case LFUN_BUFFER_LANGUAGE: {
1672                         BOOST_ASSERT(lyx_view_);
1673                         Buffer & buffer = *lyx_view_->buffer();
1674                         Language const * oldL = buffer.params().language;
1675                         Language const * newL = languages.getLanguage(argument);
1676                         if (!newL || oldL == newL)
1677                                 break;
1678
1679                         if (oldL->rightToLeft() == newL->rightToLeft()
1680                             && !buffer.isMultiLingual())
1681                                 buffer.changeLanguage(oldL, newL);
1682                         break;
1683                 }
1684
1685                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1686                         string const fname =
1687                                 addName(addPath(package().user_support().absFilename(), "templates/"),
1688                                         "defaults.lyx");
1689                         Buffer defaults(fname);
1690
1691                         istringstream ss(argument);
1692                         Lexer lex(0,0);
1693                         lex.setStream(ss);
1694                         int const unknown_tokens = defaults.readHeader(lex);
1695
1696                         if (unknown_tokens != 0) {
1697                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1698                                        << unknown_tokens << " unknown token"
1699                                        << (unknown_tokens == 1 ? "" : "s")
1700                                        << endl;
1701                         }
1702
1703                         if (defaults.writeFile(FileName(defaults.fileName())))
1704                                 setMessage(bformat(_("Document defaults saved in %1$s"),
1705                                                    makeDisplayPath(fname)));
1706                         else
1707                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1708                         break;
1709                 }
1710
1711                 case LFUN_BUFFER_PARAMS_APPLY: {
1712                         BOOST_ASSERT(lyx_view_);
1713                         biblio::CiteEngine const engine =
1714                                 lyx_view_->buffer()->params().getEngine();
1715
1716                         istringstream ss(argument);
1717                         Lexer lex(0,0);
1718                         lex.setStream(ss);
1719                         int const unknown_tokens =
1720                                 lyx_view_->buffer()->readHeader(lex);
1721
1722                         if (unknown_tokens != 0) {
1723                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1724                                        << unknown_tokens << " unknown token"
1725                                        << (unknown_tokens == 1 ? "" : "s")
1726                                        << endl;
1727                         }
1728                         if (engine == lyx_view_->buffer()->params().getEngine())
1729                                 break;
1730
1731                         Cursor & cur = view()->cursor();
1732                         FuncRequest fr(LFUN_INSET_REFRESH);
1733
1734                         Inset & inset = lyx_view_->buffer()->inset();
1735                         InsetIterator it  = inset_iterator_begin(inset);
1736                         InsetIterator const end = inset_iterator_end(inset);
1737                         for (; it != end; ++it)
1738                                 if (it->lyxCode() == Inset::CITE_CODE)
1739                                         it->dispatch(cur, fr);
1740                         break;
1741                 }
1742
1743                 case LFUN_TEXTCLASS_APPLY: {
1744                         BOOST_ASSERT(lyx_view_);
1745                         Buffer * buffer = lyx_view_->buffer();
1746
1747                         textclass_type const old_class =
1748                                 buffer->params().textclass;
1749
1750                         loadTextclass(argument);
1751
1752                         std::pair<bool, textclass_type> const tc_pair =
1753                                 textclasslist.numberOfClass(argument);
1754
1755                         if (!tc_pair.first)
1756                                 break;
1757
1758                         textclass_type const new_class = tc_pair.second;
1759                         if (old_class == new_class)
1760                                 // nothing to do
1761                                 break;
1762
1763                         lyx_view_->message(_("Converting document to new document class..."));
1764                         recordUndoFullDocument(view());
1765                         buffer->params().textclass = new_class;
1766                         StableDocIterator backcur(view()->cursor());
1767                         ErrorList & el = buffer->errorList("Class Switch");
1768                         cap::switchBetweenClasses(
1769                                 old_class, new_class,
1770                                 static_cast<InsetText &>(buffer->inset()), el);
1771
1772                         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1773
1774                         buffer->errors("Class Switch");
1775                         updateLabels(*buffer);
1776                         updateFlags = Update::Force | Update::FitCursor;
1777                         break;
1778                 }
1779
1780                 case LFUN_TEXTCLASS_LOAD:
1781                         loadTextclass(argument);
1782                         break;
1783
1784                 case LFUN_LYXRC_APPLY: {
1785                         LyXRC const lyxrc_orig = lyxrc;
1786
1787                         istringstream ss(argument);
1788                         bool const success = lyxrc.read(ss) == 0;
1789
1790                         if (!success) {
1791                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1792                                        << "Unable to read lyxrc data"
1793                                        << endl;
1794                                 break;
1795                         }
1796
1797                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1798
1799                         /// We force the redraw in any case because there might be
1800                         /// some screen font changes.
1801                         /// FIXME: only the current view will be updated. the Gui
1802                         /// class is able to furnish the list of views.
1803                         updateFlags = Update::Force;
1804                         break;
1805                 }
1806
1807                 case LFUN_WINDOW_NEW:
1808                         LyX::ref().newLyXView();
1809                         break;
1810
1811                 case LFUN_WINDOW_CLOSE:
1812                         BOOST_ASSERT(lyx_view_);
1813                         BOOST_ASSERT(theApp());
1814                         // update bookmark pit of the current buffer before window close
1815                         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1816                                 gotoBookmark(i+1, false, false);
1817                         // ask the user for saving changes or cancel quit
1818                         if (!theBufferList().quitWriteAll())
1819                                 break;
1820                         lyx_view_->close();
1821                         return;
1822
1823                 case LFUN_BOOKMARK_GOTO:
1824                         // go to bookmark, open unopened file and switch to buffer if necessary
1825                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1826                         break;
1827
1828                 case LFUN_BOOKMARK_CLEAR:
1829                         LyX::ref().session().bookmarks().clear();
1830                         break;
1831
1832                 case LFUN_TOOLBAR_TOGGLE: {
1833                         BOOST_ASSERT(lyx_view_);
1834                         string const name = cmd.getArg(0);
1835                         bool const allowauto = cmd.getArg(1) == "allowauto";
1836                         lyx_view_->toggleToolbarState(name, allowauto);
1837                         ToolbarInfo * tbi = lyx_view_->getToolbarInfo(name);
1838                         if (!tbi) {
1839                                 setMessage(bformat(_("Unknown toolbar \"%1$s\""),
1840                                                    from_utf8(name)));
1841                                 break;
1842                         }
1843                         docstring state;
1844                         if (tbi->flags & ToolbarInfo::ON)
1845                                 state = _("on");
1846                         else if (tbi->flags & ToolbarInfo::OFF)
1847                                 state = _("off");
1848                         else if (tbi->flags & ToolbarInfo::AUTO)
1849                                 state = _("auto");
1850
1851                         setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1852                                            _(tbi->gui_name), state));
1853                         break;
1854                 }
1855
1856                 default: {
1857                         BOOST_ASSERT(lyx_view_);
1858                         view()->cursor().dispatch(cmd);
1859                         updateFlags = view()->cursor().result().update();
1860                         if (!view()->cursor().result().dispatched())
1861                                 updateFlags = view()->dispatch(cmd);
1862                         break;
1863                 }
1864                 }
1865
1866                 if (lyx_view_ && view()->buffer()) {
1867                         // BufferView::update() updates the ViewMetricsInfo and
1868                         // also initializes the position cache for all insets in
1869                         // (at least partially) visible top-level paragraphs.
1870                         // We will redraw the screen only if needed.
1871                         if (view()->update(updateFlags)) {
1872                                 // Buffer::changed() signals that a repaint is needed.
1873                                 // The frontend (WorkArea) knows which area to repaint
1874                                 // thanks to the ViewMetricsInfo updated above.
1875                                 view()->buffer()->changed();
1876                         }
1877
1878                         lyx_view_->updateStatusBar();
1879
1880                         // if we executed a mutating lfun, mark the buffer as dirty
1881                         if (flag.enabled()
1882                             && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1883                             && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1884                                 view()->buffer()->markDirty();
1885
1886                         //Do we have a selection?
1887                         theSelection().haveSelection(view()->cursor().selection());
1888
1889                         if (view()->cursor().inTexted()) {
1890                                 lyx_view_->updateLayoutChoice();
1891                         }
1892                 }
1893         }
1894         if (!quitting && lyx_view_) {
1895                 lyx_view_->updateMenubar();
1896                 lyx_view_->updateToolbars();
1897                 // Some messages may already be translated, so we cannot use _()
1898                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1899         }
1900 }
1901
1902
1903 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1904 {
1905         const bool verbose = (cmd.origin == FuncRequest::MENU
1906                               || cmd.origin == FuncRequest::TOOLBAR
1907                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1908
1909         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1910                 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1911                 if (!msg.empty())
1912                         lyx_view_->message(msg);
1913                 return;
1914         }
1915
1916         docstring dispatch_msg = msg;
1917         if (!dispatch_msg.empty())
1918                 dispatch_msg += ' ';
1919
1920         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1921
1922         bool argsadded = false;
1923
1924         if (!cmd.argument().empty()) {
1925                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1926                         comname += ' ' + cmd.argument();
1927                         argsadded = true;
1928                 }
1929         }
1930
1931         docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1932
1933         if (!shortcuts.empty())
1934                 comname += ": " + shortcuts;
1935         else if (!argsadded && !cmd.argument().empty())
1936                 comname += ' ' + cmd.argument();
1937
1938         if (!comname.empty()) {
1939                 comname = rtrim(comname);
1940                 dispatch_msg += '(' + rtrim(comname) + ')';
1941         }
1942
1943         LYXERR(Debug::ACTION) << "verbose dispatch msg "
1944                 << to_utf8(dispatch_msg) << endl;
1945         if (!dispatch_msg.empty())
1946                 lyx_view_->message(dispatch_msg);
1947 }
1948
1949
1950 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1951 {
1952         // FIXME: initpath is not used. What to do?
1953         string initpath = lyxrc.document_path;
1954         string filename(name);
1955
1956         if (view()->buffer()) {
1957                 string const trypath = lyx_view_->buffer()->filePath();
1958                 // If directory is writeable, use this as default.
1959                 if (isDirWriteable(FileName(trypath)))
1960                         initpath = trypath;
1961         }
1962
1963         static int newfile_number;
1964
1965         if (filename.empty()) {
1966                 filename = addName(lyxrc.document_path,
1967                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1968                 while (theBufferList().exists(filename) ||
1969                        fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1970                         ++newfile_number;
1971                         filename = addName(lyxrc.document_path,
1972                                            "newfile" +  convert<string>(newfile_number) +
1973                                     ".lyx");
1974                 }
1975         }
1976
1977         // The template stuff
1978         string templname;
1979         if (fromTemplate) {
1980                 FileDialog fileDlg(_("Select template file"),
1981                         LFUN_SELECT_FILE_SYNC,
1982                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1983                         make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1984
1985                 FileDialog::Result result =
1986                         fileDlg.open(from_utf8(lyxrc.template_path),
1987                                      FileFilterList(_("LyX Documents (*.lyx)")),
1988                                      docstring());
1989
1990                 if (result.first == FileDialog::Later)
1991                         return;
1992                 if (result.second.empty())
1993                         return;
1994                 templname = to_utf8(result.second);
1995         }
1996
1997         Buffer * const b = newFile(filename, templname, !name.empty());
1998         if (b) {
1999                 updateLabels(*b);
2000                 lyx_view_->setBuffer(b);
2001         }
2002 }
2003
2004
2005 void LyXFunc::open(string const & fname)
2006 {
2007         string initpath = lyxrc.document_path;
2008
2009         if (view()->buffer()) {
2010                 string const trypath = lyx_view_->buffer()->filePath();
2011                 // If directory is writeable, use this as default.
2012                 if (isDirWriteable(FileName(trypath)))
2013                         initpath = trypath;
2014         }
2015
2016         string filename;
2017
2018         if (fname.empty()) {
2019                 FileDialog fileDlg(_("Select document to open"),
2020                         LFUN_FILE_OPEN,
2021                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2022                         make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2023
2024                 FileDialog::Result result =
2025                         fileDlg.open(from_utf8(initpath),
2026                                      FileFilterList(_("LyX Documents (*.lyx)")),
2027                                      docstring());
2028
2029                 if (result.first == FileDialog::Later)
2030                         return;
2031
2032                 filename = to_utf8(result.second);
2033
2034                 // check selected filename
2035                 if (filename.empty()) {
2036                         lyx_view_->message(_("Canceled."));
2037                         return;
2038                 }
2039         } else
2040                 filename = fname;
2041
2042         // get absolute path of file and add ".lyx" to the filename if
2043         // necessary
2044         FileName const fullname = fileSearch(string(), filename, "lyx");
2045         if (!fullname.empty())
2046                 filename = fullname.absFilename();
2047
2048         // if the file doesn't exist, let the user create one
2049         if (!fs::exists(fullname.toFilesystemEncoding())) {
2050                 // the user specifically chose this name. Believe him.
2051                 Buffer * const b = newFile(filename, string(), true);
2052                 if (b)
2053                         lyx_view_->setBuffer(b);
2054                 return;
2055         }
2056
2057         docstring const disp_fn = makeDisplayPath(filename);
2058         lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
2059
2060         docstring str2;
2061         if (lyx_view_->loadLyXFile(fullname)) {
2062                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2063         } else {
2064                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2065         }
2066         lyx_view_->message(str2);
2067 }
2068
2069
2070 void LyXFunc::doImport(string const & argument)
2071 {
2072         string format;
2073         string filename = split(argument, format, ' ');
2074
2075         LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
2076                             << " file: " << filename << endl;
2077
2078         // need user interaction
2079         if (filename.empty()) {
2080                 string initpath = lyxrc.document_path;
2081
2082                 if (view()->buffer()) {
2083                         string const trypath = lyx_view_->buffer()->filePath();
2084                         // If directory is writeable, use this as default.
2085                         if (isDirWriteable(FileName(trypath)))
2086                                 initpath = trypath;
2087                 }
2088
2089                 docstring const text = bformat(_("Select %1$s file to import"),
2090                         formats.prettyName(format));
2091
2092                 FileDialog fileDlg(text,
2093                         LFUN_BUFFER_IMPORT,
2094                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2095                         make_pair(_("Examples|#E#e"),
2096                                   from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2097
2098                 docstring filter = formats.prettyName(format);
2099                 filter += " (*.";
2100                 // FIXME UNICODE
2101                 filter += from_utf8(formats.extension(format));
2102                 filter += ')';
2103
2104                 FileDialog::Result result =
2105                         fileDlg.open(from_utf8(initpath),
2106                                      FileFilterList(filter),
2107                                      docstring());
2108
2109                 if (result.first == FileDialog::Later)
2110                         return;
2111
2112                 filename = to_utf8(result.second);
2113
2114                 // check selected filename
2115                 if (filename.empty())
2116                         lyx_view_->message(_("Canceled."));
2117         }
2118
2119         if (filename.empty())
2120                 return;
2121
2122         // get absolute path of file
2123         FileName const fullname(makeAbsPath(filename));
2124
2125         FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2126
2127         // Check if the document already is open
2128         if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2129                 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2130                         lyx_view_->message(_("Canceled."));
2131                         return;
2132                 }
2133         }
2134
2135         // if the file exists already, and we didn't do
2136         // -i lyx thefile.lyx, warn
2137         if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2138                 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2139
2140                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2141                                                      "Do you want to overwrite that document?"), file);
2142                 int const ret = Alert::prompt(_("Overwrite document?"),
2143                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2144
2145                 if (ret == 1) {
2146                         lyx_view_->message(_("Canceled."));
2147                         return;
2148                 }
2149         }
2150
2151         ErrorList errorList;
2152         Importer::Import(lyx_view_, fullname, format, errorList);
2153         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2154 }
2155
2156
2157 void LyXFunc::closeBuffer()
2158 {
2159         // goto bookmark to update bookmark pit.
2160         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2161                 gotoBookmark(i+1, false, false);
2162         
2163         theBufferList().close(lyx_view_->buffer(), true);
2164 }
2165
2166
2167 void LyXFunc::reloadBuffer()
2168 {
2169         FileName filename(lyx_view_->buffer()->fileName());
2170         closeBuffer();
2171         lyx_view_->loadLyXFile(filename);
2172 }
2173
2174 // Each "lyx_view_" should have it's own message method. lyxview and
2175 // the minibuffer would use the minibuffer, but lyxserver would
2176 // send an ERROR signal to its client.  Alejandro 970603
2177 // This function is bit problematic when it comes to NLS, to make the
2178 // lyx servers client be language indepenent we must not translate
2179 // strings sent to this func.
2180 void LyXFunc::setErrorMessage(docstring const & m) const
2181 {
2182         dispatch_buffer = m;
2183         errorstat = true;
2184 }
2185
2186
2187 void LyXFunc::setMessage(docstring const & m) const
2188 {
2189         dispatch_buffer = m;
2190 }
2191
2192
2193 docstring const LyXFunc::viewStatusMessage()
2194 {
2195         // When meta-fake key is pressed, show the key sequence so far + "M-".
2196         if (wasMetaKey())
2197                 return keyseq->print(true) + "M-";
2198
2199         // Else, when a non-complete key sequence is pressed,
2200         // show the available options.
2201         if (keyseq->length() > 0 && !keyseq->deleted())
2202                 return keyseq->printOptions(true);
2203
2204         if (!view()->buffer())
2205                 return _("Welcome to LyX!");
2206
2207         return view()->cursor().currentState();
2208 }
2209
2210
2211 BufferView * LyXFunc::view() const
2212 {
2213         BOOST_ASSERT(lyx_view_);
2214         return lyx_view_->view();
2215 }
2216
2217
2218 bool LyXFunc::wasMetaKey() const
2219 {
2220         return (meta_fake_bit != key_modifier::none);
2221 }
2222
2223
2224 namespace {
2225
2226 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2227 {
2228         // Why the switch you might ask. It is a trick to ensure that all
2229         // the elements in the LyXRCTags enum is handled. As you can see
2230         // there are no breaks at all. So it is just a huge fall-through.
2231         // The nice thing is that we will get a warning from the compiler
2232         // if we forget an element.
2233         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2234         switch (tag) {
2235         case LyXRC::RC_ACCEPT_COMPOUND:
2236         case LyXRC::RC_ALT_LANG:
2237         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2238         case LyXRC::RC_PLAINTEXT_LINELEN:
2239         case LyXRC::RC_AUTOREGIONDELETE:
2240         case LyXRC::RC_AUTORESET_OPTIONS:
2241         case LyXRC::RC_AUTOSAVE:
2242         case LyXRC::RC_AUTO_NUMBER:
2243         case LyXRC::RC_BACKUPDIR_PATH:
2244         case LyXRC::RC_BIBTEX_COMMAND:
2245         case LyXRC::RC_BINDFILE:
2246         case LyXRC::RC_CHECKLASTFILES:
2247         case LyXRC::RC_USELASTFILEPOS:
2248         case LyXRC::RC_LOADSESSION:
2249         case LyXRC::RC_CHKTEX_COMMAND:
2250         case LyXRC::RC_CONVERTER:
2251         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2252         case LyXRC::RC_COPIER:
2253         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2254         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2255         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2256         case LyXRC::RC_DATE_INSERT_FORMAT:
2257         case LyXRC::RC_DEFAULT_LANGUAGE:
2258         case LyXRC::RC_DEFAULT_PAPERSIZE:
2259         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2260         case LyXRC::RC_DISPLAY_GRAPHICS:
2261         case LyXRC::RC_DOCUMENTPATH:
2262                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2263                         string const encoded = FileName(
2264                                 lyxrc_new.document_path).toFilesystemEncoding();
2265                         if (fs::exists(encoded) && fs::is_directory(encoded))
2266                                 support::package().document_dir() = FileName(lyxrc.document_path);
2267                 }
2268         case LyXRC::RC_ESC_CHARS:
2269         case LyXRC::RC_FONT_ENCODING:
2270         case LyXRC::RC_FORMAT:
2271         case LyXRC::RC_INDEX_COMMAND:
2272         case LyXRC::RC_INPUT:
2273         case LyXRC::RC_KBMAP:
2274         case LyXRC::RC_KBMAP_PRIMARY:
2275         case LyXRC::RC_KBMAP_SECONDARY:
2276         case LyXRC::RC_LABEL_INIT_LENGTH:
2277         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2278         case LyXRC::RC_LANGUAGE_AUTO_END:
2279         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2280         case LyXRC::RC_LANGUAGE_COMMAND_END:
2281         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2282         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2283         case LyXRC::RC_LANGUAGE_PACKAGE:
2284         case LyXRC::RC_LANGUAGE_USE_BABEL:
2285         case LyXRC::RC_MAKE_BACKUP:
2286         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2287         case LyXRC::RC_NUMLASTFILES:
2288         case LyXRC::RC_PATH_PREFIX:
2289                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2290                         support::prependEnvPath("PATH", lyxrc.path_prefix);
2291                 }
2292         case LyXRC::RC_PERS_DICT:
2293         case LyXRC::RC_PREVIEW:
2294         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2295         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2296         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2297         case LyXRC::RC_PRINTCOPIESFLAG:
2298         case LyXRC::RC_PRINTER:
2299         case LyXRC::RC_PRINTEVENPAGEFLAG:
2300         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2301         case LyXRC::RC_PRINTFILEEXTENSION:
2302         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2303         case LyXRC::RC_PRINTODDPAGEFLAG:
2304         case LyXRC::RC_PRINTPAGERANGEFLAG:
2305         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2306         case LyXRC::RC_PRINTPAPERFLAG:
2307         case LyXRC::RC_PRINTREVERSEFLAG:
2308         case LyXRC::RC_PRINTSPOOL_COMMAND:
2309         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2310         case LyXRC::RC_PRINTTOFILE:
2311         case LyXRC::RC_PRINTTOPRINTER:
2312         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2313         case LyXRC::RC_PRINT_COMMAND:
2314         case LyXRC::RC_RTL_SUPPORT:
2315         case LyXRC::RC_SCREEN_DPI:
2316         case LyXRC::RC_SCREEN_FONT_ROMAN:
2317         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2318         case LyXRC::RC_SCREEN_FONT_SANS:
2319         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2320         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2321         case LyXRC::RC_SCREEN_FONT_SIZES:
2322         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2323         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2324         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2325         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2326         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2327         case LyXRC::RC_SCREEN_ZOOM:
2328         case LyXRC::RC_SERVERPIPE:
2329         case LyXRC::RC_SET_COLOR:
2330         case LyXRC::RC_SHOW_BANNER:
2331         case LyXRC::RC_SPELL_COMMAND:
2332         case LyXRC::RC_TEMPDIRPATH:
2333         case LyXRC::RC_TEMPLATEPATH:
2334         case LyXRC::RC_TEX_ALLOWS_SPACES:
2335         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2336                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2337                         support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2338                 }
2339         case LyXRC::RC_UIFILE:
2340         case LyXRC::RC_USER_EMAIL:
2341         case LyXRC::RC_USER_NAME:
2342         case LyXRC::RC_USETEMPDIR:
2343         case LyXRC::RC_USE_ALT_LANG:
2344         case LyXRC::RC_USE_CONVERTER_CACHE:
2345         case LyXRC::RC_USE_ESC_CHARS:
2346         case LyXRC::RC_USE_INP_ENC:
2347         case LyXRC::RC_USE_PERS_DICT:
2348         case LyXRC::RC_USE_SPELL_LIB:
2349         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2350         case LyXRC::RC_VIEWER:
2351         case LyXRC::RC_LAST:
2352                 break;
2353         }
2354 }
2355
2356 } // namespace anon
2357
2358
2359 } // namespace lyx