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