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