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