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