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