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