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