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