]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
Fix for bug 3401. Updated LFUN_CITATION_INSERT to new format.
[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                                 InsetCommandParams icp("cite");
1382                                 icp["key"] = from_utf8(arg);
1383                                 if (!opt1.empty())
1384                                         icp["before"] = from_utf8(opt1);
1385                                 string icstr = InsetCommandMailer::params2string("citation", icp);
1386                                 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1387                                 dispatch(fr);
1388                         } else
1389                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1390                         break;
1391                 }
1392
1393                 case LFUN_BUFFER_CHILD_OPEN: {
1394                         BOOST_ASSERT(lyx_view_);
1395                         FileName const filename =
1396                                 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1397                         setMessage(bformat(_("Opening child document %1$s..."),
1398                                            makeDisplayPath(filename.absFilename())));
1399                         view()->saveBookmark(false);
1400                         string const parentfilename = lyx_view_->buffer()->fileName();
1401                         if (theBufferList().exists(filename.absFilename()))
1402                                 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1403                         else
1404                                 lyx_view_->loadLyXFile(filename);
1405                         // Set the parent name of the child document.
1406                         // This makes insertion of citations and references in the child work,
1407                         // when the target is in the parent or another child document.
1408                         lyx_view_->buffer()->setParentName(parentfilename);
1409                         break;
1410                 }
1411
1412                 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1413                         BOOST_ASSERT(lyx_view_);
1414                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1415                         break;
1416
1417                 case LFUN_KEYMAP_OFF:
1418                         BOOST_ASSERT(lyx_view_);
1419                         lyx_view_->view()->getIntl().keyMapOn(false);
1420                         break;
1421
1422                 case LFUN_KEYMAP_PRIMARY:
1423                         BOOST_ASSERT(lyx_view_);
1424                         lyx_view_->view()->getIntl().keyMapPrim();
1425                         break;
1426
1427                 case LFUN_KEYMAP_SECONDARY:
1428                         BOOST_ASSERT(lyx_view_);
1429                         lyx_view_->view()->getIntl().keyMapSec();
1430                         break;
1431
1432                 case LFUN_KEYMAP_TOGGLE:
1433                         BOOST_ASSERT(lyx_view_);
1434                         lyx_view_->view()->getIntl().toggleKeyMap();
1435                         break;
1436
1437                 case LFUN_REPEAT: {
1438                         // repeat command
1439                         string countstr;
1440                         string rest = split(argument, countstr, ' ');
1441                         istringstream is(countstr);
1442                         int count = 0;
1443                         is >> count;
1444                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1445                         for (int i = 0; i < count; ++i)
1446                                 dispatch(lyxaction.lookupFunc(rest));
1447                         break;
1448                 }
1449
1450                 case LFUN_COMMAND_SEQUENCE: {
1451                         // argument contains ';'-terminated commands
1452                         string arg = argument;
1453                         while (!arg.empty()) {
1454                                 string first;
1455                                 arg = split(arg, first, ';');
1456                                 FuncRequest func(lyxaction.lookupFunc(first));
1457                                 func.origin = cmd.origin;
1458                                 dispatch(func);
1459                         }
1460                         break;
1461                 }
1462
1463                 case LFUN_PREFERENCES_SAVE: {
1464                         lyxrc.write(makeAbsPath("preferences",
1465                                                 package().user_support()),
1466                                     false);
1467                         break;
1468                 }
1469
1470                 case LFUN_SCREEN_FONT_UPDATE:
1471                         BOOST_ASSERT(lyx_view_);
1472                         // handle the screen font changes.
1473                         theFontLoader().update();
1474                         /// FIXME: only the current view will be updated. the Gui
1475                         /// class is able to furnish the list of views.
1476                         updateFlags = Update::Force;
1477                         break;
1478
1479                 case LFUN_SET_COLOR: {
1480                         string lyx_name;
1481                         string const x11_name = split(argument, lyx_name, ' ');
1482                         if (lyx_name.empty() || x11_name.empty()) {
1483                                 setErrorMessage(from_ascii(N_(
1484                                                 "Syntax: set-color <lyx_name>"
1485                                                 " <x11_name>")));
1486                                 break;
1487                         }
1488
1489                         bool const graphicsbg_changed =
1490                                 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1491                                  x11_name != lcolor.getX11Name(LColor::graphicsbg));
1492
1493                         if (!lcolor.setColor(lyx_name, x11_name)) {
1494                                 setErrorMessage(
1495                                                 bformat(_("Set-color \"%1$s\" failed "
1496                                                                        "- color is undefined or "
1497                                                                        "may not be redefined"),
1498                                                                            from_utf8(lyx_name)));
1499                                 break;
1500                         }
1501
1502                         theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1503
1504                         if (graphicsbg_changed) {
1505 #ifdef WITH_WARNINGS
1506 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1507 #endif
1508 #if 0
1509                                 graphics::GCache::get().changeDisplay(true);
1510 #endif
1511                         }
1512                         break;
1513                 }
1514
1515                 case LFUN_MESSAGE:
1516                         BOOST_ASSERT(lyx_view_);
1517                         lyx_view_->message(from_utf8(argument));
1518                         break;
1519
1520                 case LFUN_EXTERNAL_EDIT: {
1521                         BOOST_ASSERT(lyx_view_);
1522                         FuncRequest fr(action, argument);
1523                         InsetExternal().dispatch(view()->cursor(), fr);
1524                         break;
1525                 }
1526
1527                 case LFUN_GRAPHICS_EDIT: {
1528                         FuncRequest fr(action, argument);
1529                         InsetGraphics().dispatch(view()->cursor(), fr);
1530                         break;
1531                 }
1532
1533                 case LFUN_INSET_APPLY: {
1534                         BOOST_ASSERT(lyx_view_);
1535                         string const name = cmd.getArg(0);
1536                         InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1537                         if (inset) {
1538                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1539                                 inset->dispatch(view()->cursor(), fr);
1540                         } else {
1541                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1542                                 dispatch(fr);
1543                         }
1544                         // ideally, the update flag should be set by the insets,
1545                         // but this is not possible currently
1546                         updateFlags = Update::Force | Update::FitCursor;
1547                         break;
1548                 }
1549
1550                 case LFUN_ALL_INSETS_TOGGLE: {
1551                         BOOST_ASSERT(lyx_view_);
1552                         string action;
1553                         string const name = split(argument, action, ' ');
1554                         InsetBase::Code const inset_code =
1555                                 InsetBase::translate(name);
1556
1557                         LCursor & cur = view()->cursor();
1558                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1559
1560                         InsetBase & inset = lyx_view_->buffer()->inset();
1561                         InsetIterator it  = inset_iterator_begin(inset);
1562                         InsetIterator const end = inset_iterator_end(inset);
1563                         for (; it != end; ++it) {
1564                                 if (inset_code == InsetBase::NO_CODE
1565                                     || inset_code == it->lyxCode()) {
1566                                         LCursor tmpcur = cur;
1567                                         tmpcur.pushLeft(*it);
1568                                         it->dispatch(tmpcur, fr);
1569                                 }
1570                         }
1571                         updateFlags = Update::Force | Update::FitCursor;
1572                         break;
1573                 }
1574
1575                 case LFUN_BUFFER_LANGUAGE: {
1576                         BOOST_ASSERT(lyx_view_);
1577                         Buffer & buffer = *lyx_view_->buffer();
1578                         Language const * oldL = buffer.params().language;
1579                         Language const * newL = languages.getLanguage(argument);
1580                         if (!newL || oldL == newL)
1581                                 break;
1582
1583                         if (oldL->rightToLeft() == newL->rightToLeft()
1584                             && !buffer.isMultiLingual())
1585                                 buffer.changeLanguage(oldL, newL);
1586                         break;
1587                 }
1588
1589                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1590                         string const fname =
1591                                 addName(addPath(package().user_support(), "templates/"),
1592                                         "defaults.lyx");
1593                         Buffer defaults(fname);
1594
1595                         istringstream ss(argument);
1596                         LyXLex lex(0,0);
1597                         lex.setStream(ss);
1598                         int const unknown_tokens = defaults.readHeader(lex);
1599
1600                         if (unknown_tokens != 0) {
1601                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1602                                        << unknown_tokens << " unknown token"
1603                                        << (unknown_tokens == 1 ? "" : "s")
1604                                        << endl;
1605                         }
1606
1607                         if (defaults.writeFile(FileName(defaults.fileName())))
1608                                 setMessage(bformat(_("Document defaults saved in %1$s"),
1609                                                    makeDisplayPath(fname)));
1610                         else
1611                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1612                         break;
1613                 }
1614
1615                 case LFUN_BUFFER_PARAMS_APPLY: {
1616                         BOOST_ASSERT(lyx_view_);
1617                         biblio::CiteEngine const engine =
1618                                 lyx_view_->buffer()->params().getEngine();
1619
1620                         istringstream ss(argument);
1621                         LyXLex lex(0,0);
1622                         lex.setStream(ss);
1623                         int const unknown_tokens =
1624                                 lyx_view_->buffer()->readHeader(lex);
1625
1626                         if (unknown_tokens != 0) {
1627                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1628                                        << unknown_tokens << " unknown token"
1629                                        << (unknown_tokens == 1 ? "" : "s")
1630                                        << endl;
1631                         }
1632                         if (engine == lyx_view_->buffer()->params().getEngine())
1633                                 break;
1634
1635                         LCursor & cur = view()->cursor();
1636                         FuncRequest fr(LFUN_INSET_REFRESH);
1637
1638                         InsetBase & inset = lyx_view_->buffer()->inset();
1639                         InsetIterator it  = inset_iterator_begin(inset);
1640                         InsetIterator const end = inset_iterator_end(inset);
1641                         for (; it != end; ++it)
1642                                 if (it->lyxCode() == InsetBase::CITE_CODE)
1643                                         it->dispatch(cur, fr);
1644                         break;
1645                 }
1646
1647                 case LFUN_TEXTCLASS_APPLY: {
1648                         BOOST_ASSERT(lyx_view_);
1649                         Buffer * buffer = lyx_view_->buffer();
1650
1651                         textclass_type const old_class =
1652                                 buffer->params().textclass;
1653
1654                         loadTextclass(argument);
1655
1656                         std::pair<bool, textclass_type> const tc_pair =
1657                                 textclasslist.numberOfClass(argument);
1658
1659                         if (!tc_pair.first)
1660                                 break;
1661
1662                         textclass_type const new_class = tc_pair.second;
1663                         if (old_class == new_class)
1664                                 // nothing to do
1665                                 break;
1666
1667                         lyx_view_->message(_("Converting document to new document class..."));
1668                         recordUndoFullDocument(view());
1669                         buffer->params().textclass = new_class;
1670                         StableDocIterator backcur(view()->cursor());
1671                         ErrorList & el = buffer->errorList("Class Switch");
1672                         cap::switchBetweenClasses(
1673                                 old_class, new_class,
1674                                 static_cast<InsetText &>(buffer->inset()), el);
1675
1676                         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1677
1678                         buffer->errors("Class Switch");
1679                         updateLabels(*buffer);
1680                         updateFlags = Update::Force | Update::FitCursor;
1681                         break;
1682                 }
1683
1684                 case LFUN_TEXTCLASS_LOAD:
1685                         loadTextclass(argument);
1686                         break;
1687
1688                 case LFUN_LYXRC_APPLY: {
1689                         LyXRC const lyxrc_orig = lyxrc;
1690
1691                         istringstream ss(argument);
1692                         bool const success = lyxrc.read(ss) == 0;
1693
1694                         if (!success) {
1695                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1696                                        << "Unable to read lyxrc data"
1697                                        << endl;
1698                                 break;
1699                         }
1700
1701                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1702
1703                         /// We force the redraw in any case because there might be
1704                         /// some screen font changes.
1705                         /// FIXME: only the current view will be updated. the Gui
1706                         /// class is able to furnish the list of views.
1707                         updateFlags = Update::Force;
1708                         break;
1709                 }
1710
1711                 case LFUN_WINDOW_NEW:
1712                         LyX::ref().newLyXView();
1713                         break;
1714
1715                 case LFUN_WINDOW_CLOSE:
1716                         BOOST_ASSERT(lyx_view_);
1717                         BOOST_ASSERT(theApp());
1718                         // update bookmark pit of the current buffer before window close
1719                         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1720                                 gotoBookmark(i+1, false, false);
1721                         // ask the user for saving changes or cancel quit
1722                         if (!theBufferList().quitWriteAll())
1723                                 break;
1724                         lyx_view_->close();
1725                         return;
1726
1727                 case LFUN_BOOKMARK_GOTO:
1728                         // go to bookmark, open unopened file and switch to buffer if necessary
1729                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1730                         break;
1731
1732                 case LFUN_BOOKMARK_CLEAR:
1733                         LyX::ref().session().bookmarks().clear();
1734                         break;
1735
1736                 case LFUN_TOOLBAR_TOGGLE_STATE:
1737                         lyx_view_->toggleToolbarState(argument);
1738                         break;
1739
1740                 default: {
1741                         BOOST_ASSERT(lyx_view_);
1742                         view()->cursor().dispatch(cmd);
1743                         updateFlags = view()->cursor().result().update();
1744                         if (!view()->cursor().result().dispatched())
1745                                 updateFlags = view()->dispatch(cmd);
1746                         break;
1747                 }
1748                 }
1749
1750                 if (lyx_view_ && view()->buffer()) {
1751                         // BufferView::update() updates the ViewMetricsInfo and
1752                         // also initializes the position cache for all insets in
1753                         // (at least partially) visible top-level paragraphs.
1754                         // We will redraw the screen only if needed.
1755                         if (view()->update(updateFlags)) {
1756                                 // Buffer::changed() signals that a repaint is needed.
1757                                 // The frontend (WorkArea) knows which area to repaint
1758                                 // thanks to the ViewMetricsInfo updated above.
1759                                 view()->buffer()->changed();
1760                         }
1761
1762                         lyx_view_->updateStatusBar();
1763
1764                         // if we executed a mutating lfun, mark the buffer as dirty
1765                         if (flag.enabled()
1766                             && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1767                             && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1768                                 view()->buffer()->markDirty();
1769
1770                         if (view()->cursor().inTexted()) {
1771                                 lyx_view_->updateLayoutChoice();
1772                         }
1773                 }
1774         }
1775         if (!quitting) {
1776                 lyx_view_->updateMenubar();
1777                 lyx_view_->updateToolbars();
1778                 // Some messages may already be translated, so we cannot use _()
1779                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1780         }
1781 }
1782
1783
1784 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1785 {
1786         const bool verbose = (cmd.origin == FuncRequest::MENU
1787                               || cmd.origin == FuncRequest::TOOLBAR
1788                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1789
1790         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1791                 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1792                 if (!msg.empty())
1793                         lyx_view_->message(msg);
1794                 return;
1795         }
1796
1797         docstring dispatch_msg = msg;
1798         if (!dispatch_msg.empty())
1799                 dispatch_msg += ' ';
1800
1801         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1802
1803         bool argsadded = false;
1804
1805         if (!cmd.argument().empty()) {
1806                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1807                         comname += ' ' + cmd.argument();
1808                         argsadded = true;
1809                 }
1810         }
1811
1812         docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1813
1814         if (!shortcuts.empty())
1815                 comname += ": " + shortcuts;
1816         else if (!argsadded && !cmd.argument().empty())
1817                 comname += ' ' + cmd.argument();
1818
1819         if (!comname.empty()) {
1820                 comname = rtrim(comname);
1821                 dispatch_msg += '(' + rtrim(comname) + ')';
1822         }
1823
1824         LYXERR(Debug::ACTION) << "verbose dispatch msg "
1825                 << to_utf8(dispatch_msg) << endl;
1826         if (!dispatch_msg.empty())
1827                 lyx_view_->message(dispatch_msg);
1828 }
1829
1830
1831 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1832 {
1833         // FIXME: initpath is not used. What to do?
1834         string initpath = lyxrc.document_path;
1835         string filename(name);
1836
1837         if (view()->buffer()) {
1838                 string const trypath = lyx_view_->buffer()->filePath();
1839                 // If directory is writeable, use this as default.
1840                 if (isDirWriteable(FileName(trypath)))
1841                         initpath = trypath;
1842         }
1843
1844         static int newfile_number;
1845
1846         if (filename.empty()) {
1847                 filename = addName(lyxrc.document_path,
1848                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1849                 while (theBufferList().exists(filename) ||
1850                        fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1851                         ++newfile_number;
1852                         filename = addName(lyxrc.document_path,
1853                                            "newfile" +  convert<string>(newfile_number) +
1854                                     ".lyx");
1855                 }
1856         }
1857
1858         // The template stuff
1859         string templname;
1860         if (fromTemplate) {
1861                 FileDialog fileDlg(_("Select template file"),
1862                         LFUN_SELECT_FILE_SYNC,
1863                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1864                         make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1865
1866                 FileDialog::Result result =
1867                         fileDlg.open(from_utf8(lyxrc.template_path),
1868                                      FileFilterList(_("LyX Documents (*.lyx)")),
1869                                      docstring());
1870
1871                 if (result.first == FileDialog::Later)
1872                         return;
1873                 if (result.second.empty())
1874                         return;
1875                 templname = to_utf8(result.second);
1876         }
1877
1878         Buffer * const b = newFile(filename, templname, !name.empty());
1879         if (b) {
1880                 updateLabels(*b);
1881                 lyx_view_->setBuffer(b);
1882         }
1883 }
1884
1885
1886 void LyXFunc::open(string const & fname)
1887 {
1888         string initpath = lyxrc.document_path;
1889
1890         if (view()->buffer()) {
1891                 string const trypath = lyx_view_->buffer()->filePath();
1892                 // If directory is writeable, use this as default.
1893                 if (isDirWriteable(FileName(trypath)))
1894                         initpath = trypath;
1895         }
1896
1897         string filename;
1898
1899         if (fname.empty()) {
1900                 FileDialog fileDlg(_("Select document to open"),
1901                         LFUN_FILE_OPEN,
1902                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1903                         make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1904
1905                 FileDialog::Result result =
1906                         fileDlg.open(from_utf8(initpath),
1907                                      FileFilterList(_("LyX Documents (*.lyx)")),
1908                                      docstring());
1909
1910                 if (result.first == FileDialog::Later)
1911                         return;
1912
1913                 filename = to_utf8(result.second);
1914
1915                 // check selected filename
1916                 if (filename.empty()) {
1917                         lyx_view_->message(_("Canceled."));
1918                         return;
1919                 }
1920         } else
1921                 filename = fname;
1922
1923         // get absolute path of file and add ".lyx" to the filename if
1924         // necessary
1925         FileName const fullname = fileSearch(string(), filename, "lyx");
1926         if (!fullname.empty())
1927                 filename = fullname.absFilename();
1928
1929         // if the file doesn't exist, let the user create one
1930         if (!fs::exists(fullname.toFilesystemEncoding())) {
1931                 // the user specifically chose this name. Believe him.
1932                 Buffer * const b = newFile(filename, string(), true);
1933                 if (b)
1934                         lyx_view_->setBuffer(b);
1935                 return;
1936         }
1937
1938         docstring const disp_fn = makeDisplayPath(filename);
1939         lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1940
1941         docstring str2;
1942         if (lyx_view_->loadLyXFile(fullname)) {
1943                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1944         } else {
1945                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1946         }
1947         lyx_view_->message(str2);
1948 }
1949
1950
1951 void LyXFunc::doImport(string const & argument)
1952 {
1953         string format;
1954         string filename = split(argument, format, ' ');
1955
1956         LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1957                             << " file: " << filename << endl;
1958
1959         // need user interaction
1960         if (filename.empty()) {
1961                 string initpath = lyxrc.document_path;
1962
1963                 if (view()->buffer()) {
1964                         string const trypath = lyx_view_->buffer()->filePath();
1965                         // If directory is writeable, use this as default.
1966                         if (isDirWriteable(FileName(trypath)))
1967                                 initpath = trypath;
1968                 }
1969
1970                 docstring const text = bformat(_("Select %1$s file to import"),
1971                         formats.prettyName(format));
1972
1973                 FileDialog fileDlg(text,
1974                         LFUN_BUFFER_IMPORT,
1975                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1976                         make_pair(_("Examples|#E#e"),
1977                                   from_utf8(addPath(package().system_support(), "examples"))));
1978
1979                 docstring filter = formats.prettyName(format);
1980                 filter += " (*.";
1981                 // FIXME UNICODE
1982                 filter += from_utf8(formats.extension(format));
1983                 filter += ')';
1984
1985                 FileDialog::Result result =
1986                         fileDlg.open(from_utf8(initpath),
1987                                      FileFilterList(filter),
1988                                      docstring());
1989
1990                 if (result.first == FileDialog::Later)
1991                         return;
1992
1993                 filename = to_utf8(result.second);
1994
1995                 // check selected filename
1996                 if (filename.empty())
1997                         lyx_view_->message(_("Canceled."));
1998         }
1999
2000         if (filename.empty())
2001                 return;
2002
2003         // get absolute path of file
2004         FileName const fullname(makeAbsPath(filename));
2005
2006         FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2007
2008         // Check if the document already is open
2009         if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2010                 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2011                         lyx_view_->message(_("Canceled."));
2012                         return;
2013                 }
2014         }
2015
2016         // if the file exists already, and we didn't do
2017         // -i lyx thefile.lyx, warn
2018         if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2019                 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2020
2021                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2022                                                      "Do you want to over-write that document?"), file);
2023                 int const ret = Alert::prompt(_("Over-write document?"),
2024                         text, 0, 1, _("&Over-write"), _("&Cancel"));
2025
2026                 if (ret == 1) {
2027                         lyx_view_->message(_("Canceled."));
2028                         return;
2029                 }
2030         }
2031
2032         ErrorList errorList;
2033         Importer::Import(lyx_view_, fullname, format, errorList);
2034         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2035 }
2036
2037
2038 void LyXFunc::closeBuffer()
2039 {
2040         // save current cursor position
2041         LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2042                 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2043         // goto bookmark to update bookmark pit.
2044         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2045                 gotoBookmark(i+1, false, false);
2046         if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2047                 if (theBufferList().empty()) {
2048                         // need this otherwise SEGV may occur while
2049                         // trying to set variables that don't exist
2050                         // since there's no current buffer
2051                         lyx_view_->getDialogs().hideBufferDependent();
2052                 } else {
2053                         lyx_view_->setBuffer(theBufferList().first());
2054                 }
2055         }
2056 }
2057
2058
2059 void LyXFunc::reloadBuffer()
2060 {
2061         FileName filename(lyx_view_->buffer()->fileName());
2062         closeBuffer();
2063         lyx_view_->loadLyXFile(filename);
2064 }
2065
2066 // Each "lyx_view_" should have it's own message method. lyxview and
2067 // the minibuffer would use the minibuffer, but lyxserver would
2068 // send an ERROR signal to its client.  Alejandro 970603
2069 // This function is bit problematic when it comes to NLS, to make the
2070 // lyx servers client be language indepenent we must not translate
2071 // strings sent to this func.
2072 void LyXFunc::setErrorMessage(docstring const & m) const
2073 {
2074         dispatch_buffer = m;
2075         errorstat = true;
2076 }
2077
2078
2079 void LyXFunc::setMessage(docstring const & m) const
2080 {
2081         dispatch_buffer = m;
2082 }
2083
2084
2085 docstring const LyXFunc::viewStatusMessage()
2086 {
2087         // When meta-fake key is pressed, show the key sequence so far + "M-".
2088         if (wasMetaKey())
2089                 return keyseq->print(true) + "M-";
2090
2091         // Else, when a non-complete key sequence is pressed,
2092         // show the available options.
2093         if (keyseq->length() > 0 && !keyseq->deleted())
2094                 return keyseq->printOptions(true);
2095
2096         if (!view()->buffer())
2097                 return _("Welcome to LyX!");
2098
2099         return view()->cursor().currentState();
2100 }
2101
2102
2103 BufferView * LyXFunc::view() const
2104 {
2105         BOOST_ASSERT(lyx_view_);
2106         return lyx_view_->view();
2107 }
2108
2109
2110 bool LyXFunc::wasMetaKey() const
2111 {
2112         return (meta_fake_bit != key_modifier::none);
2113 }
2114
2115
2116 namespace {
2117
2118 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2119 {
2120         // Why the switch you might ask. It is a trick to ensure that all
2121         // the elements in the LyXRCTags enum is handled. As you can see
2122         // there are no breaks at all. So it is just a huge fall-through.
2123         // The nice thing is that we will get a warning from the compiler
2124         // if we forget an element.
2125         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2126         switch (tag) {
2127         case LyXRC::RC_ACCEPT_COMPOUND:
2128         case LyXRC::RC_ALT_LANG:
2129         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2130         case LyXRC::RC_PLAINTEXT_LINELEN:
2131         case LyXRC::RC_AUTOREGIONDELETE:
2132         case LyXRC::RC_AUTORESET_OPTIONS:
2133         case LyXRC::RC_AUTOSAVE:
2134         case LyXRC::RC_AUTO_NUMBER:
2135         case LyXRC::RC_BACKUPDIR_PATH:
2136         case LyXRC::RC_BIBTEX_COMMAND:
2137         case LyXRC::RC_BINDFILE:
2138         case LyXRC::RC_CHECKLASTFILES:
2139         case LyXRC::RC_USELASTFILEPOS:
2140         case LyXRC::RC_LOADSESSION:
2141         case LyXRC::RC_CHKTEX_COMMAND:
2142         case LyXRC::RC_CONVERTER:
2143         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2144         case LyXRC::RC_COPIER:
2145         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2146         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2147         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2148         case LyXRC::RC_DATE_INSERT_FORMAT:
2149         case LyXRC::RC_DEFAULT_LANGUAGE:
2150         case LyXRC::RC_DEFAULT_PAPERSIZE:
2151         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2152         case LyXRC::RC_DISPLAY_GRAPHICS:
2153         case LyXRC::RC_DOCUMENTPATH:
2154                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2155                         string const encoded = FileName(
2156                                 lyxrc_new.document_path).toFilesystemEncoding();
2157                         if (fs::exists(encoded) && fs::is_directory(encoded))
2158                                 support::package().document_dir() = lyxrc.document_path;
2159                 }
2160         case LyXRC::RC_ESC_CHARS:
2161         case LyXRC::RC_FONT_ENCODING:
2162         case LyXRC::RC_FORMAT:
2163         case LyXRC::RC_INDEX_COMMAND:
2164         case LyXRC::RC_INPUT:
2165         case LyXRC::RC_KBMAP:
2166         case LyXRC::RC_KBMAP_PRIMARY:
2167         case LyXRC::RC_KBMAP_SECONDARY:
2168         case LyXRC::RC_LABEL_INIT_LENGTH:
2169         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2170         case LyXRC::RC_LANGUAGE_AUTO_END:
2171         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2172         case LyXRC::RC_LANGUAGE_COMMAND_END:
2173         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2174         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2175         case LyXRC::RC_LANGUAGE_PACKAGE:
2176         case LyXRC::RC_LANGUAGE_USE_BABEL:
2177         case LyXRC::RC_MAKE_BACKUP:
2178         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2179         case LyXRC::RC_NUMLASTFILES:
2180         case LyXRC::RC_PATH_PREFIX:
2181                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2182                         support::prependEnvPath("PATH", lyxrc.path_prefix);
2183                 }
2184         case LyXRC::RC_PERS_DICT:
2185         case LyXRC::RC_PREVIEW:
2186         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2187         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2188         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2189         case LyXRC::RC_PRINTCOPIESFLAG:
2190         case LyXRC::RC_PRINTER:
2191         case LyXRC::RC_PRINTEVENPAGEFLAG:
2192         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2193         case LyXRC::RC_PRINTFILEEXTENSION:
2194         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2195         case LyXRC::RC_PRINTODDPAGEFLAG:
2196         case LyXRC::RC_PRINTPAGERANGEFLAG:
2197         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2198         case LyXRC::RC_PRINTPAPERFLAG:
2199         case LyXRC::RC_PRINTREVERSEFLAG:
2200         case LyXRC::RC_PRINTSPOOL_COMMAND:
2201         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2202         case LyXRC::RC_PRINTTOFILE:
2203         case LyXRC::RC_PRINTTOPRINTER:
2204         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2205         case LyXRC::RC_PRINT_COMMAND:
2206         case LyXRC::RC_RTL_SUPPORT:
2207         case LyXRC::RC_SCREEN_DPI:
2208         case LyXRC::RC_SCREEN_FONT_ROMAN:
2209         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2210         case LyXRC::RC_SCREEN_FONT_SANS:
2211         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2212         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2213         case LyXRC::RC_SCREEN_FONT_SIZES:
2214         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2215         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2216         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2217         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2218         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2219         case LyXRC::RC_SCREEN_ZOOM:
2220         case LyXRC::RC_SERVERPIPE:
2221         case LyXRC::RC_SET_COLOR:
2222         case LyXRC::RC_SHOW_BANNER:
2223         case LyXRC::RC_SPELL_COMMAND:
2224         case LyXRC::RC_TEMPDIRPATH:
2225         case LyXRC::RC_TEMPLATEPATH:
2226         case LyXRC::RC_TEX_ALLOWS_SPACES:
2227         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2228                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2229                         support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2230                 }
2231         case LyXRC::RC_UIFILE:
2232         case LyXRC::RC_USER_EMAIL:
2233         case LyXRC::RC_USER_NAME:
2234         case LyXRC::RC_USETEMPDIR:
2235         case LyXRC::RC_USE_ALT_LANG:
2236         case LyXRC::RC_USE_CONVERTER_CACHE:
2237         case LyXRC::RC_USE_ESC_CHARS:
2238         case LyXRC::RC_USE_INP_ENC:
2239         case LyXRC::RC_USE_PERS_DICT:
2240         case LyXRC::RC_USE_SPELL_LIB:
2241         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2242         case LyXRC::RC_VIEWER:
2243         case LyXRC::RC_LAST:
2244                 break;
2245         }
2246 }
2247
2248 } // namespace anon
2249
2250
2251 } // namespace lyx