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