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