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