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