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