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