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