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