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