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