]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
correct numbering
[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 =
1698                                 insetCode(name);
1699
1700                         Cursor & cur = view()->cursor();
1701                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1702
1703                         Inset & inset = lyx_view_->buffer()->inset();
1704                         InsetIterator it  = inset_iterator_begin(inset);
1705                         InsetIterator const end = inset_iterator_end(inset);
1706                         for (; it != end; ++it) {
1707                                 if (!it->asInsetMath()
1708                                     && (inset_code == NO_CODE
1709                                     || inset_code == it->lyxCode())) {
1710                                         Cursor tmpcur = cur;
1711                                         tmpcur.pushLeft(*it);
1712                                         it->dispatch(tmpcur, fr);
1713                                 }
1714                         }
1715                         updateFlags = Update::Force | Update::FitCursor;
1716                         break;
1717                 }
1718
1719                 case LFUN_BUFFER_LANGUAGE: {
1720                         BOOST_ASSERT(lyx_view_);
1721                         Buffer & buffer = *lyx_view_->buffer();
1722                         Language const * oldL = buffer.params().language;
1723                         Language const * newL = languages.getLanguage(argument);
1724                         if (!newL || oldL == newL)
1725                                 break;
1726
1727                         if (oldL->rightToLeft() == newL->rightToLeft()
1728                             && !buffer.isMultiLingual())
1729                                 buffer.changeLanguage(oldL, newL);
1730                         break;
1731                 }
1732
1733                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1734                         string const fname =
1735                                 addName(addPath(package().user_support().absFilename(), "templates/"),
1736                                         "defaults.lyx");
1737                         Buffer defaults(fname);
1738
1739                         istringstream ss(argument);
1740                         Lexer lex(0,0);
1741                         lex.setStream(ss);
1742                         int const unknown_tokens = defaults.readHeader(lex);
1743
1744                         if (unknown_tokens != 0) {
1745                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1746                                        << unknown_tokens << " unknown token"
1747                                        << (unknown_tokens == 1 ? "" : "s")
1748                                        << endl;
1749                         }
1750
1751                         if (defaults.writeFile(FileName(defaults.fileName())))
1752                                 setMessage(bformat(_("Document defaults saved in %1$s"),
1753                                                    makeDisplayPath(fname)));
1754                         else
1755                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1756                         break;
1757                 }
1758
1759                 case LFUN_BUFFER_PARAMS_APPLY: {
1760                         BOOST_ASSERT(lyx_view_);
1761                         biblio::CiteEngine const oldEngine =
1762                                         lyx_view_->buffer()->params().getEngine();
1763                         
1764                         Buffer * buffer = lyx_view_->buffer();
1765
1766                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1767                         recordUndoFullDocument(view());
1768                         
1769                         istringstream ss(argument);
1770                         Lexer lex(0,0);
1771                         lex.setStream(ss);
1772                         int const unknown_tokens = buffer->readHeader(lex);
1773
1774                         if (unknown_tokens != 0) {
1775                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1776                                                 << unknown_tokens << " unknown token"
1777                                                 << (unknown_tokens == 1 ? "" : "s")
1778                                                 << endl;
1779                         }
1780                         
1781                         updateLayout(oldClass, buffer);
1782                         
1783                         biblio::CiteEngine const newEngine =
1784                                         lyx_view_->buffer()->params().getEngine();
1785                         
1786                         if (oldEngine != newEngine) {
1787                                 Cursor & cur = view()->cursor();
1788                                 FuncRequest fr(LFUN_INSET_REFRESH);
1789         
1790                                 Inset & inset = lyx_view_->buffer()->inset();
1791                                 InsetIterator it  = inset_iterator_begin(inset);
1792                                 InsetIterator const end = inset_iterator_end(inset);
1793                                 for (; it != end; ++it)
1794                                         if (it->lyxCode() == CITE_CODE)
1795                                                 it->dispatch(cur, fr);
1796                         }
1797                         
1798                         updateFlags = Update::Force | Update::FitCursor;
1799                         break;
1800                 }
1801                 
1802                 case LFUN_LAYOUT_MODULES_CLEAR: {
1803                         BOOST_ASSERT(lyx_view_);
1804                         Buffer * buffer = lyx_view_->buffer();
1805                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1806                         recordUndoFullDocument(view());
1807                         buffer->params().clearLayoutModules();
1808                         updateLayout(oldClass, buffer);
1809                         updateFlags = Update::Force | Update::FitCursor;
1810                         break;
1811                 }
1812                 
1813                 case LFUN_LAYOUT_MODULE_ADD: {
1814                         BOOST_ASSERT(lyx_view_);
1815                         Buffer * buffer = lyx_view_->buffer();
1816                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1817                         recordUndoFullDocument(view());
1818                         buffer->params().addLayoutModule(argument);
1819                         updateLayout(oldClass, buffer);
1820                         updateFlags = Update::Force | Update::FitCursor;
1821                         break;
1822                 }
1823
1824                 case LFUN_TEXTCLASS_APPLY: {
1825                         BOOST_ASSERT(lyx_view_);
1826                         Buffer * buffer = lyx_view_->buffer();
1827
1828                         loadTextClass(argument);
1829
1830                         std::pair<bool, textclass_type> const tc_pair =
1831                                 textclasslist.numberOfClass(argument);
1832
1833                         if (!tc_pair.first)
1834                                 break;
1835
1836                         textclass_type const old_class = buffer->params().getBaseClass();
1837                         textclass_type const new_class = tc_pair.second;
1838
1839                         if (old_class == new_class)
1840                                 // nothing to do
1841                                 break;
1842
1843                         //Save the old, possibly modular, layout for use in conversion.
1844                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1845                         recordUndoFullDocument(view());
1846                         buffer->params().setBaseClass(new_class);
1847                         updateLayout(oldClass, buffer);
1848                         updateFlags = Update::Force | Update::FitCursor;
1849                         break;
1850                 }
1851                 
1852                 case LFUN_LAYOUT_RELOAD: {
1853                         BOOST_ASSERT(lyx_view_);
1854                         Buffer * buffer = lyx_view_->buffer();
1855                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1856                         textclass_type const tc = buffer->params().getBaseClass();
1857                         textclasslist.reset(tc);
1858                         buffer->params().setBaseClass(tc);
1859                         updateLayout(oldClass, buffer);
1860                         updateFlags = Update::Force | Update::FitCursor;
1861                         break;
1862                 }
1863
1864                 case LFUN_TEXTCLASS_LOAD:
1865                         loadTextClass(argument);
1866                         break;
1867
1868                 case LFUN_LYXRC_APPLY: {
1869                         LyXRC const lyxrc_orig = lyxrc;
1870
1871                         istringstream ss(argument);
1872                         bool const success = lyxrc.read(ss) == 0;
1873
1874                         if (!success) {
1875                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1876                                        << "Unable to read lyxrc data"
1877                                        << endl;
1878                                 break;
1879                         }
1880
1881                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1882
1883                         /// We force the redraw in any case because there might be
1884                         /// some screen font changes.
1885                         /// FIXME: only the current view will be updated. the Gui
1886                         /// class is able to furnish the list of views.
1887                         updateFlags = Update::Force;
1888                         break;
1889                 }
1890
1891                 case LFUN_WINDOW_NEW:
1892                         LyX::ref().newLyXView();
1893                         break;
1894
1895                 case LFUN_WINDOW_CLOSE:
1896                         BOOST_ASSERT(lyx_view_);
1897                         BOOST_ASSERT(theApp());
1898                         // update bookmark pit of the current buffer before window close
1899                         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1900                                 gotoBookmark(i+1, false, false);
1901                         // ask the user for saving changes or cancel quit
1902                         if (!theBufferList().quitWriteAll())
1903                                 break;
1904                         lyx_view_->close();
1905                         return;
1906
1907                 case LFUN_BOOKMARK_GOTO:
1908                         // go to bookmark, open unopened file and switch to buffer if necessary
1909                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1910                         break;
1911
1912                 case LFUN_BOOKMARK_CLEAR:
1913                         LyX::ref().session().bookmarks().clear();
1914                         break;
1915
1916                 case LFUN_TOOLBAR_TOGGLE: {
1917                         BOOST_ASSERT(lyx_view_);
1918                         string const name = cmd.getArg(0);
1919                         bool const allowauto = cmd.getArg(1) == "allowauto";
1920                         lyx_view_->toggleToolbarState(name, allowauto);
1921                         ToolbarInfo * tbi = lyx_view_->getToolbarInfo(name);
1922                         if (!tbi) {
1923                                 setMessage(bformat(_("Unknown toolbar \"%1$s\""),
1924                                                    from_utf8(name)));
1925                                 break;
1926                         }
1927                         docstring state;
1928                         if (tbi->flags & ToolbarInfo::ON)
1929                                 state = _("on");
1930                         else if (tbi->flags & ToolbarInfo::OFF)
1931                                 state = _("off");
1932                         else if (tbi->flags & ToolbarInfo::AUTO)
1933                                 state = _("auto");
1934
1935                         setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1936                                            _(tbi->gui_name), state));
1937                         break;
1938                 }
1939
1940                 default: {
1941                         BOOST_ASSERT(lyx_view_);
1942                         view()->cursor().dispatch(cmd);
1943                         updateFlags = view()->cursor().result().update();
1944                         if (!view()->cursor().result().dispatched())
1945                                 updateFlags = view()->dispatch(cmd);
1946                         break;
1947                 }
1948                 }
1949
1950                 if (lyx_view_ && lyx_view_->buffer()) {
1951                         // BufferView::update() updates the ViewMetricsInfo and
1952                         // also initializes the position cache for all insets in
1953                         // (at least partially) visible top-level paragraphs.
1954                         // We will redraw the screen only if needed.
1955                         view()->processUpdateFlags(updateFlags);
1956                         lyx_view_->updateStatusBar();
1957
1958                         // if we executed a mutating lfun, mark the buffer as dirty
1959                         if (flag.enabled()
1960                             && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1961                             && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1962                                 lyx_view_->buffer()->markDirty();
1963
1964                         //Do we have a selection?
1965                         theSelection().haveSelection(view()->cursor().selection());
1966
1967                         if (view()->cursor().inTexted()) {
1968                                 lyx_view_->updateLayoutChoice();
1969                         }
1970                 }
1971         }
1972         if (!quitting && lyx_view_) {
1973                 lyx_view_->updateToolbars();
1974                 // Some messages may already be translated, so we cannot use _()
1975                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1976         }
1977 }
1978
1979
1980 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1981 {
1982         const bool verbose = (cmd.origin == FuncRequest::MENU
1983                               || cmd.origin == FuncRequest::TOOLBAR
1984                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1985
1986         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1987                 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1988                 if (!msg.empty())
1989                         lyx_view_->message(msg);
1990                 return;
1991         }
1992
1993         docstring dispatch_msg = msg;
1994         if (!dispatch_msg.empty())
1995                 dispatch_msg += ' ';
1996
1997         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1998
1999         bool argsadded = false;
2000
2001         if (!cmd.argument().empty()) {
2002                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
2003                         comname += ' ' + cmd.argument();
2004                         argsadded = true;
2005                 }
2006         }
2007
2008         docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
2009
2010         if (!shortcuts.empty())
2011                 comname += ": " + shortcuts;
2012         else if (!argsadded && !cmd.argument().empty())
2013                 comname += ' ' + cmd.argument();
2014
2015         if (!comname.empty()) {
2016                 comname = rtrim(comname);
2017                 dispatch_msg += '(' + rtrim(comname) + ')';
2018         }
2019
2020         LYXERR(Debug::ACTION) << "verbose dispatch msg "
2021                 << to_utf8(dispatch_msg) << endl;
2022         if (!dispatch_msg.empty())
2023                 lyx_view_->message(dispatch_msg);
2024 }
2025
2026
2027 void LyXFunc::menuNew(string const & name, bool fromTemplate)
2028 {
2029         // FIXME: initpath is not used. What to do?
2030         string initpath = lyxrc.document_path;
2031         string filename(name);
2032
2033         if (lyx_view_->buffer()) {
2034                 string const trypath = lyx_view_->buffer()->filePath();
2035                 // If directory is writeable, use this as default.
2036                 if (isDirWriteable(FileName(trypath)))
2037                         initpath = trypath;
2038         }
2039
2040         static int newfile_number;
2041
2042         if (filename.empty()) {
2043                 filename = addName(lyxrc.document_path,
2044                             "newfile" + convert<string>(++newfile_number) + ".lyx");
2045                 while (theBufferList().exists(filename) ||
2046                        fs::is_readable(FileName(filename).toFilesystemEncoding())) {
2047                         ++newfile_number;
2048                         filename = addName(lyxrc.document_path,
2049                                            "newfile" +  convert<string>(newfile_number) +
2050                                     ".lyx");
2051                 }
2052         }
2053
2054         // The template stuff
2055         string templname;
2056         if (fromTemplate) {
2057                 FileDialog fileDlg(_("Select template file"),
2058                         LFUN_SELECT_FILE_SYNC,
2059                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2060                         make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
2061
2062                 FileDialog::Result result =
2063                         fileDlg.open(from_utf8(lyxrc.template_path),
2064                                      FileFilterList(_("LyX Documents (*.lyx)")),
2065                                      docstring());
2066
2067                 if (result.first == FileDialog::Later)
2068                         return;
2069                 if (result.second.empty())
2070                         return;
2071                 templname = to_utf8(result.second);
2072         }
2073
2074         Buffer * const b = newFile(filename, templname, !name.empty());
2075         if (b)
2076                 lyx_view_->setBuffer(b);
2077 }
2078
2079
2080 void LyXFunc::open(string const & fname)
2081 {
2082         string initpath = lyxrc.document_path;
2083
2084         if (lyx_view_->buffer()) {
2085                 string const trypath = lyx_view_->buffer()->filePath();
2086                 // If directory is writeable, use this as default.
2087                 if (isDirWriteable(FileName(trypath)))
2088                         initpath = trypath;
2089         }
2090
2091         string filename;
2092
2093         if (fname.empty()) {
2094                 FileDialog fileDlg(_("Select document to open"),
2095                         LFUN_FILE_OPEN,
2096                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2097                         make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2098
2099                 FileDialog::Result result =
2100                         fileDlg.open(from_utf8(initpath),
2101                                      FileFilterList(_("LyX Documents (*.lyx)")),
2102                                      docstring());
2103
2104                 if (result.first == FileDialog::Later)
2105                         return;
2106
2107                 filename = to_utf8(result.second);
2108
2109                 // check selected filename
2110                 if (filename.empty()) {
2111                         lyx_view_->message(_("Canceled."));
2112                         return;
2113                 }
2114         } else
2115                 filename = fname;
2116
2117         // get absolute path of file and add ".lyx" to the filename if
2118         // necessary
2119         FileName const fullname = fileSearch(string(), filename, "lyx");
2120         if (!fullname.empty())
2121                 filename = fullname.absFilename();
2122
2123         // if the file doesn't exist, let the user create one
2124         if (!fs::exists(fullname.toFilesystemEncoding())) {
2125                 // the user specifically chose this name. Believe him.
2126                 Buffer * const b = newFile(filename, string(), true);
2127                 if (b)
2128                         lyx_view_->setBuffer(b);
2129                 return;
2130         }
2131
2132         docstring const disp_fn = makeDisplayPath(filename);
2133         lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
2134
2135         docstring str2;
2136         Buffer * buf = lyx_view_->loadLyXFile(fullname);
2137         if (buf) {
2138                 updateLabels(*buf);
2139                 lyx_view_->setBuffer(buf);
2140                 lyx_view_->showErrorList("Parse");
2141                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2142         } else {
2143                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2144         }
2145         lyx_view_->message(str2);
2146 }
2147
2148
2149 void LyXFunc::doImport(string const & argument)
2150 {
2151         string format;
2152         string filename = split(argument, format, ' ');
2153
2154         LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
2155                             << " file: " << filename << endl;
2156
2157         // need user interaction
2158         if (filename.empty()) {
2159                 string initpath = lyxrc.document_path;
2160
2161                 if (lyx_view_->buffer()) {
2162                         string const trypath = lyx_view_->buffer()->filePath();
2163                         // If directory is writeable, use this as default.
2164                         if (isDirWriteable(FileName(trypath)))
2165                                 initpath = trypath;
2166                 }
2167
2168                 docstring const text = bformat(_("Select %1$s file to import"),
2169                         formats.prettyName(format));
2170
2171                 FileDialog fileDlg(text,
2172                         LFUN_BUFFER_IMPORT,
2173                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2174                         make_pair(_("Examples|#E#e"),
2175                                   from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2176
2177                 docstring filter = formats.prettyName(format);
2178                 filter += " (*.";
2179                 // FIXME UNICODE
2180                 filter += from_utf8(formats.extension(format));
2181                 filter += ')';
2182
2183                 FileDialog::Result result =
2184                         fileDlg.open(from_utf8(initpath),
2185                                      FileFilterList(filter),
2186                                      docstring());
2187
2188                 if (result.first == FileDialog::Later)
2189                         return;
2190
2191                 filename = to_utf8(result.second);
2192
2193                 // check selected filename
2194                 if (filename.empty())
2195                         lyx_view_->message(_("Canceled."));
2196         }
2197
2198         if (filename.empty())
2199                 return;
2200
2201         // get absolute path of file
2202         FileName const fullname(makeAbsPath(filename));
2203
2204         FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2205
2206         // Check if the document already is open
2207         if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2208                 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2209                         lyx_view_->message(_("Canceled."));
2210                         return;
2211                 }
2212         }
2213
2214         // if the file exists already, and we didn't do
2215         // -i lyx thefile.lyx, warn
2216         if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2217                 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2218
2219                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2220                                                      "Do you want to overwrite that document?"), file);
2221                 int const ret = Alert::prompt(_("Overwrite document?"),
2222                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2223
2224                 if (ret == 1) {
2225                         lyx_view_->message(_("Canceled."));
2226                         return;
2227                 }
2228         }
2229
2230         ErrorList errorList;
2231         Importer::Import(lyx_view_, fullname, format, errorList);
2232         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2233 }
2234
2235
2236 void LyXFunc::closeBuffer()
2237 {
2238         // goto bookmark to update bookmark pit.
2239         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2240                 gotoBookmark(i+1, false, false);
2241         
2242         theBufferList().close(lyx_view_->buffer(), true);
2243 }
2244
2245
2246 void LyXFunc::reloadBuffer()
2247 {
2248         FileName filename(lyx_view_->buffer()->fileName());
2249         docstring const disp_fn = makeDisplayPath(filename.absFilename());
2250         docstring str;
2251         closeBuffer();
2252         Buffer * buf = lyx_view_->loadLyXFile(filename);
2253         if (buf) {
2254                 updateLabels(*buf);
2255                 lyx_view_->setBuffer(buf);
2256                 lyx_view_->showErrorList("Parse");
2257                 str = bformat(_("Document %1$s reloaded."), disp_fn);
2258         } else {
2259                 str = bformat(_("Could not reload document %1$s"), disp_fn);
2260         }
2261         lyx_view_->message(str);
2262 }
2263
2264 // Each "lyx_view_" should have it's own message method. lyxview and
2265 // the minibuffer would use the minibuffer, but lyxserver would
2266 // send an ERROR signal to its client.  Alejandro 970603
2267 // This function is bit problematic when it comes to NLS, to make the
2268 // lyx servers client be language indepenent we must not translate
2269 // strings sent to this func.
2270 void LyXFunc::setErrorMessage(docstring const & m) const
2271 {
2272         dispatch_buffer = m;
2273         errorstat = true;
2274 }
2275
2276
2277 void LyXFunc::setMessage(docstring const & m) const
2278 {
2279         dispatch_buffer = m;
2280 }
2281
2282
2283 docstring const LyXFunc::viewStatusMessage()
2284 {
2285         // When meta-fake key is pressed, show the key sequence so far + "M-".
2286         if (wasMetaKey())
2287                 return keyseq.print(true) + "M-";
2288
2289         // Else, when a non-complete key sequence is pressed,
2290         // show the available options.
2291         if (keyseq.length() > 0 && !keyseq.deleted())
2292                 return keyseq.printOptions(true);
2293
2294         BOOST_ASSERT(lyx_view_);
2295         if (!lyx_view_->buffer())
2296                 return _("Welcome to LyX!");
2297
2298         return view()->cursor().currentState();
2299 }
2300
2301
2302 BufferView * LyXFunc::view() const
2303 {
2304         BOOST_ASSERT(lyx_view_);
2305         return lyx_view_->view();
2306 }
2307
2308
2309 bool LyXFunc::wasMetaKey() const
2310 {
2311         return (meta_fake_bit != NoModifier);
2312 }
2313
2314
2315 void LyXFunc::updateLayout(TextClassPtr const & oldlayout,
2316                            Buffer * buffer)
2317 {
2318         lyx_view_->message(_("Converting document to new document class..."));
2319         
2320         StableDocIterator backcur(view()->cursor());
2321         ErrorList & el = buffer->errorList("Class Switch");
2322         cap::switchBetweenClasses(
2323                         oldlayout, buffer->params().getTextClassPtr(),
2324                         static_cast<InsetText &>(buffer->inset()), el);
2325
2326         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
2327
2328         buffer->errors("Class Switch");
2329         updateLabels(*buffer);
2330 }
2331
2332
2333 namespace {
2334
2335 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2336 {
2337         // Why the switch you might ask. It is a trick to ensure that all
2338         // the elements in the LyXRCTags enum is handled. As you can see
2339         // there are no breaks at all. So it is just a huge fall-through.
2340         // The nice thing is that we will get a warning from the compiler
2341         // if we forget an element.
2342         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2343         switch (tag) {
2344         case LyXRC::RC_ACCEPT_COMPOUND:
2345         case LyXRC::RC_ALT_LANG:
2346         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2347         case LyXRC::RC_PLAINTEXT_LINELEN:
2348         case LyXRC::RC_AUTOREGIONDELETE:
2349         case LyXRC::RC_AUTORESET_OPTIONS:
2350         case LyXRC::RC_AUTOSAVE:
2351         case LyXRC::RC_AUTO_NUMBER:
2352         case LyXRC::RC_BACKUPDIR_PATH:
2353         case LyXRC::RC_BIBTEX_COMMAND:
2354         case LyXRC::RC_BINDFILE:
2355         case LyXRC::RC_CHECKLASTFILES:
2356         case LyXRC::RC_USELASTFILEPOS:
2357         case LyXRC::RC_LOADSESSION:
2358         case LyXRC::RC_CHKTEX_COMMAND:
2359         case LyXRC::RC_CONVERTER:
2360         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2361         case LyXRC::RC_COPIER:
2362         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2363         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2364         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2365         case LyXRC::RC_DATE_INSERT_FORMAT:
2366         case LyXRC::RC_DEFAULT_LANGUAGE:
2367         case LyXRC::RC_DEFAULT_PAPERSIZE:
2368         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2369         case LyXRC::RC_DISPLAY_GRAPHICS:
2370         case LyXRC::RC_DOCUMENTPATH:
2371                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2372                         string const encoded = FileName(
2373                                 lyxrc_new.document_path).toFilesystemEncoding();
2374                         if (fs::exists(encoded) && fs::is_directory(encoded))
2375                                 support::package().document_dir() = FileName(lyxrc.document_path);
2376                 }
2377         case LyXRC::RC_ESC_CHARS:
2378         case LyXRC::RC_FONT_ENCODING:
2379         case LyXRC::RC_FORMAT:
2380         case LyXRC::RC_INDEX_COMMAND:
2381         case LyXRC::RC_INPUT:
2382         case LyXRC::RC_KBMAP:
2383         case LyXRC::RC_KBMAP_PRIMARY:
2384         case LyXRC::RC_KBMAP_SECONDARY:
2385         case LyXRC::RC_LABEL_INIT_LENGTH:
2386         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2387         case LyXRC::RC_LANGUAGE_AUTO_END:
2388         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2389         case LyXRC::RC_LANGUAGE_COMMAND_END:
2390         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2391         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2392         case LyXRC::RC_LANGUAGE_PACKAGE:
2393         case LyXRC::RC_LANGUAGE_USE_BABEL:
2394         case LyXRC::RC_MAKE_BACKUP:
2395         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2396         case LyXRC::RC_NUMLASTFILES:
2397         case LyXRC::RC_PATH_PREFIX:
2398                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2399                         support::prependEnvPath("PATH", lyxrc.path_prefix);
2400                 }
2401         case LyXRC::RC_PERS_DICT:
2402         case LyXRC::RC_PREVIEW:
2403         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2404         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2405         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2406         case LyXRC::RC_PRINTCOPIESFLAG:
2407         case LyXRC::RC_PRINTER:
2408         case LyXRC::RC_PRINTEVENPAGEFLAG:
2409         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2410         case LyXRC::RC_PRINTFILEEXTENSION:
2411         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2412         case LyXRC::RC_PRINTODDPAGEFLAG:
2413         case LyXRC::RC_PRINTPAGERANGEFLAG:
2414         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2415         case LyXRC::RC_PRINTPAPERFLAG:
2416         case LyXRC::RC_PRINTREVERSEFLAG:
2417         case LyXRC::RC_PRINTSPOOL_COMMAND:
2418         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2419         case LyXRC::RC_PRINTTOFILE:
2420         case LyXRC::RC_PRINTTOPRINTER:
2421         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2422         case LyXRC::RC_PRINT_COMMAND:
2423         case LyXRC::RC_RTL_SUPPORT:
2424         case LyXRC::RC_SCREEN_DPI:
2425         case LyXRC::RC_SCREEN_FONT_ROMAN:
2426         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2427         case LyXRC::RC_SCREEN_FONT_SANS:
2428         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2429         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2430         case LyXRC::RC_SCREEN_FONT_SIZES:
2431         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2432         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2433         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2434         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2435         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2436         case LyXRC::RC_SCREEN_ZOOM:
2437         case LyXRC::RC_SERVERPIPE:
2438         case LyXRC::RC_SET_COLOR:
2439         case LyXRC::RC_SHOW_BANNER:
2440         case LyXRC::RC_SPELL_COMMAND:
2441         case LyXRC::RC_TEMPDIRPATH:
2442         case LyXRC::RC_TEMPLATEPATH:
2443         case LyXRC::RC_TEX_ALLOWS_SPACES:
2444         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2445                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2446                         support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2447                 }
2448         case LyXRC::RC_UIFILE:
2449         case LyXRC::RC_USER_EMAIL:
2450         case LyXRC::RC_USER_NAME:
2451         case LyXRC::RC_USETEMPDIR:
2452         case LyXRC::RC_USE_ALT_LANG:
2453         case LyXRC::RC_USE_CONVERTER_CACHE:
2454         case LyXRC::RC_USE_ESC_CHARS:
2455         case LyXRC::RC_USE_INP_ENC:
2456         case LyXRC::RC_USE_PERS_DICT:
2457         case LyXRC::RC_USE_SPELL_LIB:
2458         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2459         case LyXRC::RC_VIEWER:
2460         case LyXRC::RC_LAST:
2461                 break;
2462         }
2463 }
2464
2465 } // namespace anon
2466
2467
2468 } // namespace lyx