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