]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
* Only enter inset which return true on isActive(). This is the behavior in the curso...
[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                         BOOST_ASSERT(lyx_view_);
1414                         FileName const filename =
1415                                 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1416                         view()->saveBookmark(false);
1417                         string const parentfilename = lyx_view_->buffer()->fileName();
1418                         if (theBufferList().exists(filename.absFilename()))
1419                                 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1420                         else
1421                                 if (lyx_view_->loadLyXFile(filename)) {
1422                                         // Set the parent name of the child document.
1423                                         // This makes insertion of citations and references in the child work,
1424                                         // when the target is in the parent or another child document.
1425                                         lyx_view_->buffer()->setParentName(parentfilename);
1426                                         setMessage(bformat(_("Opening child document %1$s..."),
1427                                                  makeDisplayPath(filename.absFilename())));
1428                                 } else
1429                                         setMessage(_("Document not loaded."));
1430                         break;
1431                 }
1432
1433                 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1434                         BOOST_ASSERT(lyx_view_);
1435                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1436                         break;
1437
1438                 case LFUN_KEYMAP_OFF:
1439                         BOOST_ASSERT(lyx_view_);
1440                         lyx_view_->view()->getIntl().keyMapOn(false);
1441                         break;
1442
1443                 case LFUN_KEYMAP_PRIMARY:
1444                         BOOST_ASSERT(lyx_view_);
1445                         lyx_view_->view()->getIntl().keyMapPrim();
1446                         break;
1447
1448                 case LFUN_KEYMAP_SECONDARY:
1449                         BOOST_ASSERT(lyx_view_);
1450                         lyx_view_->view()->getIntl().keyMapSec();
1451                         break;
1452
1453                 case LFUN_KEYMAP_TOGGLE:
1454                         BOOST_ASSERT(lyx_view_);
1455                         lyx_view_->view()->getIntl().toggleKeyMap();
1456                         break;
1457
1458                 case LFUN_REPEAT: {
1459                         // repeat command
1460                         string countstr;
1461                         string rest = split(argument, countstr, ' ');
1462                         istringstream is(countstr);
1463                         int count = 0;
1464                         is >> count;
1465                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1466                         for (int i = 0; i < count; ++i)
1467                                 dispatch(lyxaction.lookupFunc(rest));
1468                         break;
1469                 }
1470
1471                 case LFUN_COMMAND_SEQUENCE: {
1472                         // argument contains ';'-terminated commands
1473                         string arg = argument;
1474                         while (!arg.empty()) {
1475                                 string first;
1476                                 arg = split(arg, first, ';');
1477                                 FuncRequest func(lyxaction.lookupFunc(first));
1478                                 func.origin = cmd.origin;
1479                                 dispatch(func);
1480                         }
1481                         break;
1482                 }
1483
1484                 case LFUN_PREFERENCES_SAVE: {
1485                         lyxrc.write(makeAbsPath("preferences",
1486                                                 package().user_support().absFilename()),
1487                                     false);
1488                         break;
1489                 }
1490
1491                 case LFUN_SCREEN_FONT_UPDATE:
1492                         BOOST_ASSERT(lyx_view_);
1493                         // handle the screen font changes.
1494                         theFontLoader().update();
1495                         /// FIXME: only the current view will be updated. the Gui
1496                         /// class is able to furnish the list of views.
1497                         updateFlags = Update::Force;
1498                         break;
1499
1500                 case LFUN_SET_COLOR: {
1501                         string lyx_name;
1502                         string const x11_name = split(argument, lyx_name, ' ');
1503                         if (lyx_name.empty() || x11_name.empty()) {
1504                                 setErrorMessage(from_ascii(N_(
1505                                                 "Syntax: set-color <lyx_name>"
1506                                                 " <x11_name>")));
1507                                 break;
1508                         }
1509
1510                         bool const graphicsbg_changed =
1511                                 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1512                                  x11_name != lcolor.getX11Name(Color::graphicsbg));
1513
1514                         if (!lcolor.setColor(lyx_name, x11_name)) {
1515                                 setErrorMessage(
1516                                                 bformat(_("Set-color \"%1$s\" failed "
1517                                                                        "- color is undefined or "
1518                                                                        "may not be redefined"),
1519                                                                            from_utf8(lyx_name)));
1520                                 break;
1521                         }
1522
1523                         theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1524
1525                         if (graphicsbg_changed) {
1526 #ifdef WITH_WARNINGS
1527 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1528 #endif
1529 #if 0
1530                                 graphics::GCache::get().changeDisplay(true);
1531 #endif
1532                         }
1533                         break;
1534                 }
1535
1536                 case LFUN_MESSAGE:
1537                         BOOST_ASSERT(lyx_view_);
1538                         lyx_view_->message(from_utf8(argument));
1539                         break;
1540
1541                 case LFUN_EXTERNAL_EDIT: {
1542                         BOOST_ASSERT(lyx_view_);
1543                         FuncRequest fr(action, argument);
1544                         InsetExternal().dispatch(view()->cursor(), fr);
1545                         break;
1546                 }
1547
1548                 case LFUN_GRAPHICS_EDIT: {
1549                         FuncRequest fr(action, argument);
1550                         InsetGraphics().dispatch(view()->cursor(), fr);
1551                         break;
1552                 }
1553
1554                 case LFUN_INSET_APPLY: {
1555                         BOOST_ASSERT(lyx_view_);
1556                         string const name = cmd.getArg(0);
1557                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1558                         if (inset) {
1559                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1560                                 inset->dispatch(view()->cursor(), fr);
1561                         } else {
1562                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1563                                 dispatch(fr);
1564                         }
1565                         // ideally, the update flag should be set by the insets,
1566                         // but this is not possible currently
1567                         updateFlags = Update::Force | Update::FitCursor;
1568                         break;
1569                 }
1570
1571                 case LFUN_ALL_INSETS_TOGGLE: {
1572                         BOOST_ASSERT(lyx_view_);
1573                         string action;
1574                         string const name = split(argument, action, ' ');
1575                         Inset::Code const inset_code =
1576                                 Inset::translate(name);
1577
1578                         Cursor & cur = view()->cursor();
1579                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1580
1581                         Inset & inset = lyx_view_->buffer()->inset();
1582                         InsetIterator it  = inset_iterator_begin(inset);
1583                         InsetIterator const end = inset_iterator_end(inset);
1584                         for (; it != end; ++it) {
1585                                 if (!it->asInsetMath()
1586                                     && (inset_code == Inset::NO_CODE
1587                                     || inset_code == it->lyxCode())) {
1588                                         Cursor tmpcur = cur;
1589                                         tmpcur.pushLeft(*it);
1590                                         it->dispatch(tmpcur, fr);
1591                                 }
1592                         }
1593                         updateFlags = Update::Force | Update::FitCursor;
1594                         break;
1595                 }
1596
1597                 case LFUN_BUFFER_LANGUAGE: {
1598                         BOOST_ASSERT(lyx_view_);
1599                         Buffer & buffer = *lyx_view_->buffer();
1600                         Language const * oldL = buffer.params().language;
1601                         Language const * newL = languages.getLanguage(argument);
1602                         if (!newL || oldL == newL)
1603                                 break;
1604
1605                         if (oldL->rightToLeft() == newL->rightToLeft()
1606                             && !buffer.isMultiLingual())
1607                                 buffer.changeLanguage(oldL, newL);
1608                         break;
1609                 }
1610
1611                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1612                         string const fname =
1613                                 addName(addPath(package().user_support().absFilename(), "templates/"),
1614                                         "defaults.lyx");
1615                         Buffer defaults(fname);
1616
1617                         istringstream ss(argument);
1618                         Lexer lex(0,0);
1619                         lex.setStream(ss);
1620                         int const unknown_tokens = defaults.readHeader(lex);
1621
1622                         if (unknown_tokens != 0) {
1623                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1624                                        << unknown_tokens << " unknown token"
1625                                        << (unknown_tokens == 1 ? "" : "s")
1626                                        << endl;
1627                         }
1628
1629                         if (defaults.writeFile(FileName(defaults.fileName())))
1630                                 setMessage(bformat(_("Document defaults saved in %1$s"),
1631                                                    makeDisplayPath(fname)));
1632                         else
1633                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1634                         break;
1635                 }
1636
1637                 case LFUN_BUFFER_PARAMS_APPLY: {
1638                         BOOST_ASSERT(lyx_view_);
1639                         biblio::CiteEngine const engine =
1640                                 lyx_view_->buffer()->params().getEngine();
1641
1642                         istringstream ss(argument);
1643                         Lexer lex(0,0);
1644                         lex.setStream(ss);
1645                         int const unknown_tokens =
1646                                 lyx_view_->buffer()->readHeader(lex);
1647
1648                         if (unknown_tokens != 0) {
1649                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1650                                        << unknown_tokens << " unknown token"
1651                                        << (unknown_tokens == 1 ? "" : "s")
1652                                        << endl;
1653                         }
1654                         if (engine == lyx_view_->buffer()->params().getEngine())
1655                                 break;
1656
1657                         Cursor & cur = view()->cursor();
1658                         FuncRequest fr(LFUN_INSET_REFRESH);
1659
1660                         Inset & inset = lyx_view_->buffer()->inset();
1661                         InsetIterator it  = inset_iterator_begin(inset);
1662                         InsetIterator const end = inset_iterator_end(inset);
1663                         for (; it != end; ++it)
1664                                 if (it->lyxCode() == Inset::CITE_CODE)
1665                                         it->dispatch(cur, fr);
1666                         break;
1667                 }
1668
1669                 case LFUN_TEXTCLASS_APPLY: {
1670                         BOOST_ASSERT(lyx_view_);
1671                         Buffer * buffer = lyx_view_->buffer();
1672
1673                         textclass_type const old_class =
1674                                 buffer->params().textclass;
1675
1676                         loadTextclass(argument);
1677
1678                         std::pair<bool, textclass_type> const tc_pair =
1679                                 textclasslist.numberOfClass(argument);
1680
1681                         if (!tc_pair.first)
1682                                 break;
1683
1684                         textclass_type const new_class = tc_pair.second;
1685                         if (old_class == new_class)
1686                                 // nothing to do
1687                                 break;
1688
1689                         lyx_view_->message(_("Converting document to new document class..."));
1690                         recordUndoFullDocument(view());
1691                         buffer->params().textclass = new_class;
1692                         StableDocIterator backcur(view()->cursor());
1693                         ErrorList & el = buffer->errorList("Class Switch");
1694                         cap::switchBetweenClasses(
1695                                 old_class, new_class,
1696                                 static_cast<InsetText &>(buffer->inset()), el);
1697
1698                         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1699
1700                         buffer->errors("Class Switch");
1701                         updateLabels(*buffer);
1702                         updateFlags = Update::Force | Update::FitCursor;
1703                         break;
1704                 }
1705
1706                 case LFUN_TEXTCLASS_LOAD:
1707                         loadTextclass(argument);
1708                         break;
1709
1710                 case LFUN_LYXRC_APPLY: {
1711                         LyXRC const lyxrc_orig = lyxrc;
1712
1713                         istringstream ss(argument);
1714                         bool const success = lyxrc.read(ss) == 0;
1715
1716                         if (!success) {
1717                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1718                                        << "Unable to read lyxrc data"
1719                                        << endl;
1720                                 break;
1721                         }
1722
1723                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1724
1725                         /// We force the redraw in any case because there might be
1726                         /// some screen font changes.
1727                         /// FIXME: only the current view will be updated. the Gui
1728                         /// class is able to furnish the list of views.
1729                         updateFlags = Update::Force;
1730                         break;
1731                 }
1732
1733                 case LFUN_WINDOW_NEW:
1734                         LyX::ref().newLyXView();
1735                         break;
1736
1737                 case LFUN_WINDOW_CLOSE:
1738                         BOOST_ASSERT(lyx_view_);
1739                         BOOST_ASSERT(theApp());
1740                         // update bookmark pit of the current buffer before window close
1741                         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1742                                 gotoBookmark(i+1, false, false);
1743                         // ask the user for saving changes or cancel quit
1744                         if (!theBufferList().quitWriteAll())
1745                                 break;
1746                         lyx_view_->close();
1747                         return;
1748
1749                 case LFUN_BOOKMARK_GOTO:
1750                         // go to bookmark, open unopened file and switch to buffer if necessary
1751                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1752                         break;
1753
1754                 case LFUN_BOOKMARK_CLEAR:
1755                         LyX::ref().session().bookmarks().clear();
1756                         break;
1757
1758                 case LFUN_TOOLBAR_TOGGLE: {
1759                         BOOST_ASSERT(lyx_view_);
1760                         string const name = cmd.getArg(0);
1761                         bool const allowauto = cmd.getArg(1) == "allowauto";
1762                         lyx_view_->toggleToolbarState(name, allowauto);
1763                         ToolbarInfo::Flags const flags = 
1764                                 lyx_view_->getToolbarState(name);
1765                         docstring state;
1766                         if (flags & ToolbarInfo::ON)
1767                                 state = _("on");
1768                         else if (flags & ToolbarInfo::OFF)
1769                                 state = _("off");
1770                         else if (flags & ToolbarInfo::AUTO)
1771                                 state = _("auto");
1772
1773                         setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1774                                                    from_ascii(name), state));
1775                         break;
1776                 }
1777
1778                 default: {
1779                         BOOST_ASSERT(lyx_view_);
1780                         view()->cursor().dispatch(cmd);
1781                         updateFlags = view()->cursor().result().update();
1782                         if (!view()->cursor().result().dispatched())
1783                                 updateFlags = view()->dispatch(cmd);
1784                         break;
1785                 }
1786                 }
1787
1788                 if (lyx_view_ && view()->buffer()) {
1789                         // BufferView::update() updates the ViewMetricsInfo and
1790                         // also initializes the position cache for all insets in
1791                         // (at least partially) visible top-level paragraphs.
1792                         // We will redraw the screen only if needed.
1793                         if (view()->update(updateFlags)) {
1794                                 // Buffer::changed() signals that a repaint is needed.
1795                                 // The frontend (WorkArea) knows which area to repaint
1796                                 // thanks to the ViewMetricsInfo updated above.
1797                                 view()->buffer()->changed();
1798                         }
1799
1800                         lyx_view_->updateStatusBar();
1801
1802                         // if we executed a mutating lfun, mark the buffer as dirty
1803                         if (flag.enabled()
1804                             && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1805                             && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1806                                 view()->buffer()->markDirty();
1807
1808                         if (view()->cursor().inTexted()) {
1809                                 lyx_view_->updateLayoutChoice();
1810                         }
1811                 }
1812         }
1813         if (!quitting) {
1814                 lyx_view_->updateMenubar();
1815                 lyx_view_->updateToolbars();
1816                 // Some messages may already be translated, so we cannot use _()
1817                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1818         }
1819 }
1820
1821
1822 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1823 {
1824         const bool verbose = (cmd.origin == FuncRequest::MENU
1825                               || cmd.origin == FuncRequest::TOOLBAR
1826                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1827
1828         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1829                 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1830                 if (!msg.empty())
1831                         lyx_view_->message(msg);
1832                 return;
1833         }
1834
1835         docstring dispatch_msg = msg;
1836         if (!dispatch_msg.empty())
1837                 dispatch_msg += ' ';
1838
1839         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1840
1841         bool argsadded = false;
1842
1843         if (!cmd.argument().empty()) {
1844                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1845                         comname += ' ' + cmd.argument();
1846                         argsadded = true;
1847                 }
1848         }
1849
1850         docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1851
1852         if (!shortcuts.empty())
1853                 comname += ": " + shortcuts;
1854         else if (!argsadded && !cmd.argument().empty())
1855                 comname += ' ' + cmd.argument();
1856
1857         if (!comname.empty()) {
1858                 comname = rtrim(comname);
1859                 dispatch_msg += '(' + rtrim(comname) + ')';
1860         }
1861
1862         LYXERR(Debug::ACTION) << "verbose dispatch msg "
1863                 << to_utf8(dispatch_msg) << endl;
1864         if (!dispatch_msg.empty())
1865                 lyx_view_->message(dispatch_msg);
1866 }
1867
1868
1869 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1870 {
1871         // FIXME: initpath is not used. What to do?
1872         string initpath = lyxrc.document_path;
1873         string filename(name);
1874
1875         if (view()->buffer()) {
1876                 string const trypath = lyx_view_->buffer()->filePath();
1877                 // If directory is writeable, use this as default.
1878                 if (isDirWriteable(FileName(trypath)))
1879                         initpath = trypath;
1880         }
1881
1882         static int newfile_number;
1883
1884         if (filename.empty()) {
1885                 filename = addName(lyxrc.document_path,
1886                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1887                 while (theBufferList().exists(filename) ||
1888                        fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1889                         ++newfile_number;
1890                         filename = addName(lyxrc.document_path,
1891                                            "newfile" +  convert<string>(newfile_number) +
1892                                     ".lyx");
1893                 }
1894         }
1895
1896         // The template stuff
1897         string templname;
1898         if (fromTemplate) {
1899                 FileDialog fileDlg(_("Select template file"),
1900                         LFUN_SELECT_FILE_SYNC,
1901                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1902                         make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1903
1904                 FileDialog::Result result =
1905                         fileDlg.open(from_utf8(lyxrc.template_path),
1906                                      FileFilterList(_("LyX Documents (*.lyx)")),
1907                                      docstring());
1908
1909                 if (result.first == FileDialog::Later)
1910                         return;
1911                 if (result.second.empty())
1912                         return;
1913                 templname = to_utf8(result.second);
1914         }
1915
1916         Buffer * const b = newFile(filename, templname, !name.empty());
1917         if (b) {
1918                 updateLabels(*b);
1919                 lyx_view_->setBuffer(b);
1920         }
1921 }
1922
1923
1924 void LyXFunc::open(string const & fname)
1925 {
1926         string initpath = lyxrc.document_path;
1927
1928         if (view()->buffer()) {
1929                 string const trypath = lyx_view_->buffer()->filePath();
1930                 // If directory is writeable, use this as default.
1931                 if (isDirWriteable(FileName(trypath)))
1932                         initpath = trypath;
1933         }
1934
1935         string filename;
1936
1937         if (fname.empty()) {
1938                 FileDialog fileDlg(_("Select document to open"),
1939                         LFUN_FILE_OPEN,
1940                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1941                         make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1942
1943                 FileDialog::Result result =
1944                         fileDlg.open(from_utf8(initpath),
1945                                      FileFilterList(_("LyX Documents (*.lyx)")),
1946                                      docstring());
1947
1948                 if (result.first == FileDialog::Later)
1949                         return;
1950
1951                 filename = to_utf8(result.second);
1952
1953                 // check selected filename
1954                 if (filename.empty()) {
1955                         lyx_view_->message(_("Canceled."));
1956                         return;
1957                 }
1958         } else
1959                 filename = fname;
1960
1961         // get absolute path of file and add ".lyx" to the filename if
1962         // necessary
1963         FileName const fullname = fileSearch(string(), filename, "lyx");
1964         if (!fullname.empty())
1965                 filename = fullname.absFilename();
1966
1967         // if the file doesn't exist, let the user create one
1968         if (!fs::exists(fullname.toFilesystemEncoding())) {
1969                 // the user specifically chose this name. Believe him.
1970                 Buffer * const b = newFile(filename, string(), true);
1971                 if (b)
1972                         lyx_view_->setBuffer(b);
1973                 return;
1974         }
1975
1976         docstring const disp_fn = makeDisplayPath(filename);
1977         lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1978
1979         docstring str2;
1980         if (lyx_view_->loadLyXFile(fullname)) {
1981                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1982         } else {
1983                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1984         }
1985         lyx_view_->message(str2);
1986 }
1987
1988
1989 void LyXFunc::doImport(string const & argument)
1990 {
1991         string format;
1992         string filename = split(argument, format, ' ');
1993
1994         LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1995                             << " file: " << filename << endl;
1996
1997         // need user interaction
1998         if (filename.empty()) {
1999                 string initpath = lyxrc.document_path;
2000
2001                 if (view()->buffer()) {
2002                         string const trypath = lyx_view_->buffer()->filePath();
2003                         // If directory is writeable, use this as default.
2004                         if (isDirWriteable(FileName(trypath)))
2005                                 initpath = trypath;
2006                 }
2007
2008                 docstring const text = bformat(_("Select %1$s file to import"),
2009                         formats.prettyName(format));
2010
2011                 FileDialog fileDlg(text,
2012                         LFUN_BUFFER_IMPORT,
2013                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2014                         make_pair(_("Examples|#E#e"),
2015                                   from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2016
2017                 docstring filter = formats.prettyName(format);
2018                 filter += " (*.";
2019                 // FIXME UNICODE
2020                 filter += from_utf8(formats.extension(format));
2021                 filter += ')';
2022
2023                 FileDialog::Result result =
2024                         fileDlg.open(from_utf8(initpath),
2025                                      FileFilterList(filter),
2026                                      docstring());
2027
2028                 if (result.first == FileDialog::Later)
2029                         return;
2030
2031                 filename = to_utf8(result.second);
2032
2033                 // check selected filename
2034                 if (filename.empty())
2035                         lyx_view_->message(_("Canceled."));
2036         }
2037
2038         if (filename.empty())
2039                 return;
2040
2041         // get absolute path of file
2042         FileName const fullname(makeAbsPath(filename));
2043
2044         FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2045
2046         // Check if the document already is open
2047         if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2048                 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2049                         lyx_view_->message(_("Canceled."));
2050                         return;
2051                 }
2052         }
2053
2054         // if the file exists already, and we didn't do
2055         // -i lyx thefile.lyx, warn
2056         if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2057                 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2058
2059                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2060                                                      "Do you want to overwrite that document?"), file);
2061                 int const ret = Alert::prompt(_("Overwrite document?"),
2062                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2063
2064                 if (ret == 1) {
2065                         lyx_view_->message(_("Canceled."));
2066                         return;
2067                 }
2068         }
2069
2070         ErrorList errorList;
2071         Importer::Import(lyx_view_, fullname, format, errorList);
2072         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2073 }
2074
2075
2076 void LyXFunc::closeBuffer()
2077 {
2078         // goto bookmark to update bookmark pit.
2079         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2080                 gotoBookmark(i+1, false, false);
2081         if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2082                 if (theBufferList().empty()) {
2083                         // need this otherwise SEGV may occur while
2084                         // trying to set variables that don't exist
2085                         // since there's no current buffer
2086                         lyx_view_->getDialogs().hideBufferDependent();
2087                 } else {
2088                         lyx_view_->setBuffer(theBufferList().first());
2089                 }
2090         }
2091 }
2092
2093
2094 void LyXFunc::reloadBuffer()
2095 {
2096         FileName filename(lyx_view_->buffer()->fileName());
2097         closeBuffer();
2098         lyx_view_->loadLyXFile(filename);
2099 }
2100
2101 // Each "lyx_view_" should have it's own message method. lyxview and
2102 // the minibuffer would use the minibuffer, but lyxserver would
2103 // send an ERROR signal to its client.  Alejandro 970603
2104 // This function is bit problematic when it comes to NLS, to make the
2105 // lyx servers client be language indepenent we must not translate
2106 // strings sent to this func.
2107 void LyXFunc::setErrorMessage(docstring const & m) const
2108 {
2109         dispatch_buffer = m;
2110         errorstat = true;
2111 }
2112
2113
2114 void LyXFunc::setMessage(docstring const & m) const
2115 {
2116         dispatch_buffer = m;
2117 }
2118
2119
2120 docstring const LyXFunc::viewStatusMessage()
2121 {
2122         // When meta-fake key is pressed, show the key sequence so far + "M-".
2123         if (wasMetaKey())
2124                 return keyseq->print(true) + "M-";
2125
2126         // Else, when a non-complete key sequence is pressed,
2127         // show the available options.
2128         if (keyseq->length() > 0 && !keyseq->deleted())
2129                 return keyseq->printOptions(true);
2130
2131         if (!view()->buffer())
2132                 return _("Welcome to LyX!");
2133
2134         return view()->cursor().currentState();
2135 }
2136
2137
2138 BufferView * LyXFunc::view() const
2139 {
2140         BOOST_ASSERT(lyx_view_);
2141         return lyx_view_->view();
2142 }
2143
2144
2145 bool LyXFunc::wasMetaKey() const
2146 {
2147         return (meta_fake_bit != key_modifier::none);
2148 }
2149
2150
2151 namespace {
2152
2153 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2154 {
2155         // Why the switch you might ask. It is a trick to ensure that all
2156         // the elements in the LyXRCTags enum is handled. As you can see
2157         // there are no breaks at all. So it is just a huge fall-through.
2158         // The nice thing is that we will get a warning from the compiler
2159         // if we forget an element.
2160         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2161         switch (tag) {
2162         case LyXRC::RC_ACCEPT_COMPOUND:
2163         case LyXRC::RC_ALT_LANG:
2164         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2165         case LyXRC::RC_PLAINTEXT_LINELEN:
2166         case LyXRC::RC_AUTOREGIONDELETE:
2167         case LyXRC::RC_AUTORESET_OPTIONS:
2168         case LyXRC::RC_AUTOSAVE:
2169         case LyXRC::RC_AUTO_NUMBER:
2170         case LyXRC::RC_BACKUPDIR_PATH:
2171         case LyXRC::RC_BIBTEX_COMMAND:
2172         case LyXRC::RC_BINDFILE:
2173         case LyXRC::RC_CHECKLASTFILES:
2174         case LyXRC::RC_USELASTFILEPOS:
2175         case LyXRC::RC_LOADSESSION:
2176         case LyXRC::RC_CHKTEX_COMMAND:
2177         case LyXRC::RC_CONVERTER:
2178         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2179         case LyXRC::RC_COPIER:
2180         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2181         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2182         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2183         case LyXRC::RC_DATE_INSERT_FORMAT:
2184         case LyXRC::RC_DEFAULT_LANGUAGE:
2185         case LyXRC::RC_DEFAULT_PAPERSIZE:
2186         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2187         case LyXRC::RC_DISPLAY_GRAPHICS:
2188         case LyXRC::RC_DOCUMENTPATH:
2189                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2190                         string const encoded = FileName(
2191                                 lyxrc_new.document_path).toFilesystemEncoding();
2192                         if (fs::exists(encoded) && fs::is_directory(encoded))
2193                                 support::package().document_dir() = FileName(lyxrc.document_path);
2194                 }
2195         case LyXRC::RC_ESC_CHARS:
2196         case LyXRC::RC_FONT_ENCODING:
2197         case LyXRC::RC_FORMAT:
2198         case LyXRC::RC_INDEX_COMMAND:
2199         case LyXRC::RC_INPUT:
2200         case LyXRC::RC_KBMAP:
2201         case LyXRC::RC_KBMAP_PRIMARY:
2202         case LyXRC::RC_KBMAP_SECONDARY:
2203         case LyXRC::RC_LABEL_INIT_LENGTH:
2204         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2205         case LyXRC::RC_LANGUAGE_AUTO_END:
2206         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2207         case LyXRC::RC_LANGUAGE_COMMAND_END:
2208         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2209         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2210         case LyXRC::RC_LANGUAGE_PACKAGE:
2211         case LyXRC::RC_LANGUAGE_USE_BABEL:
2212         case LyXRC::RC_MAKE_BACKUP:
2213         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2214         case LyXRC::RC_NUMLASTFILES:
2215         case LyXRC::RC_PATH_PREFIX:
2216                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2217                         support::prependEnvPath("PATH", lyxrc.path_prefix);
2218                 }
2219         case LyXRC::RC_PERS_DICT:
2220         case LyXRC::RC_PREVIEW:
2221         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2222         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2223         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2224         case LyXRC::RC_PRINTCOPIESFLAG:
2225         case LyXRC::RC_PRINTER:
2226         case LyXRC::RC_PRINTEVENPAGEFLAG:
2227         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2228         case LyXRC::RC_PRINTFILEEXTENSION:
2229         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2230         case LyXRC::RC_PRINTODDPAGEFLAG:
2231         case LyXRC::RC_PRINTPAGERANGEFLAG:
2232         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2233         case LyXRC::RC_PRINTPAPERFLAG:
2234         case LyXRC::RC_PRINTREVERSEFLAG:
2235         case LyXRC::RC_PRINTSPOOL_COMMAND:
2236         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2237         case LyXRC::RC_PRINTTOFILE:
2238         case LyXRC::RC_PRINTTOPRINTER:
2239         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2240         case LyXRC::RC_PRINT_COMMAND:
2241         case LyXRC::RC_RTL_SUPPORT:
2242         case LyXRC::RC_SCREEN_DPI:
2243         case LyXRC::RC_SCREEN_FONT_ROMAN:
2244         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2245         case LyXRC::RC_SCREEN_FONT_SANS:
2246         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2247         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2248         case LyXRC::RC_SCREEN_FONT_SIZES:
2249         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2250         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2251         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2252         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2253         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2254         case LyXRC::RC_SCREEN_ZOOM:
2255         case LyXRC::RC_SERVERPIPE:
2256         case LyXRC::RC_SET_COLOR:
2257         case LyXRC::RC_SHOW_BANNER:
2258         case LyXRC::RC_SPELL_COMMAND:
2259         case LyXRC::RC_TEMPDIRPATH:
2260         case LyXRC::RC_TEMPLATEPATH:
2261         case LyXRC::RC_TEX_ALLOWS_SPACES:
2262         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2263                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2264                         support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2265                 }
2266         case LyXRC::RC_UIFILE:
2267         case LyXRC::RC_USER_EMAIL:
2268         case LyXRC::RC_USER_NAME:
2269         case LyXRC::RC_USETEMPDIR:
2270         case LyXRC::RC_USE_ALT_LANG:
2271         case LyXRC::RC_USE_CONVERTER_CACHE:
2272         case LyXRC::RC_USE_ESC_CHARS:
2273         case LyXRC::RC_USE_INP_ENC:
2274         case LyXRC::RC_USE_PERS_DICT:
2275         case LyXRC::RC_USE_SPELL_LIB:
2276         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2277         case LyXRC::RC_VIEWER:
2278         case LyXRC::RC_LAST:
2279                 break;
2280         }
2281 }
2282
2283 } // namespace anon
2284
2285
2286 } // namespace lyx