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