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