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