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