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