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