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