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