]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
compil fix.
[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                         InsetCode code = insetCode(name);
1405                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1406                         bool insetCodeOK = true;
1407                         switch (code) {
1408                         case BIBITEM_CODE:
1409                         case BIBTEX_CODE:
1410                         case INDEX_CODE:
1411                         case LABEL_CODE:
1412                         case NOMENCL_CODE:
1413                         case REF_CODE:
1414                         case TOC_CODE:
1415                         case HYPERLINK_CODE: {
1416                                 InsetCommandParams p(name);
1417                                 data = InsetCommandMailer::params2string(name, p);
1418                                 break;
1419                         } 
1420                         case INCLUDE_CODE: {
1421                                 // data is the include type: one of "include",
1422                                 // "input", "verbatiminput" or "verbatiminput*"
1423                                 if (data.empty())
1424                                         // default type is requested
1425                                         data = "include";
1426                                 InsetCommandParams p("include", data);
1427                                 data = InsetIncludeMailer::params2string(p);
1428                                 break;
1429                         } 
1430                         case BOX_CODE: {
1431                                 // \c data == "Boxed" || "Frameless" etc
1432                                 InsetBoxParams p(data);
1433                                 data = InsetBoxMailer::params2string(p);
1434                                 break;
1435                         } 
1436                         case BRANCH_CODE: {
1437                                 InsetBranchParams p;
1438                                 data = InsetBranchMailer::params2string(p);
1439                                 break;
1440                         } 
1441                         case CITE_CODE: {
1442                                 InsetCommandParams p("cite");
1443                                 data = InsetCommandMailer::params2string(name, p);
1444                                 break;
1445                         } 
1446                         case ERT_CODE: {
1447                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1448                                 break;
1449                         } 
1450                         case EXTERNAL_CODE: {
1451                                 InsetExternalParams p;
1452                                 Buffer const & buffer = *lyx_view_->buffer();
1453                                 data = InsetExternalMailer::params2string(p, buffer);
1454                                 break;
1455                         } 
1456                         case FLOAT_CODE:  {
1457                                 InsetFloatParams p;
1458                                 data = InsetFloatMailer::params2string(p);
1459                                 break;
1460                         } 
1461                         case LISTINGS_CODE: {
1462                                 InsetListingsParams p;
1463                                 data = InsetListingsMailer::params2string(p);
1464                                 break;
1465                         } 
1466                         case GRAPHICS_CODE: {
1467                                 InsetGraphicsParams p;
1468                                 Buffer const & buffer = *lyx_view_->buffer();
1469                                 data = InsetGraphicsMailer::params2string(p, buffer);
1470                                 break;
1471                         } 
1472                         case NOTE_CODE: {
1473                                 InsetNoteParams p;
1474                                 data = InsetNoteMailer::params2string(p);
1475                                 break;
1476                         } 
1477                         case VSPACE_CODE: {
1478                                 VSpace space;
1479                                 data = InsetVSpaceMailer::params2string(space);
1480                                 break;
1481                         } 
1482                         case WRAP_CODE: {
1483                                 InsetWrapParams p;
1484                                 data = InsetWrapMailer::params2string(p);
1485                                 break;
1486                         }
1487                         default:
1488                                 lyxerr << "Inset type '" << name << 
1489                                         "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << std:: endl;
1490                                 insetCodeOK = false;
1491                                 break;
1492                         } // end switch(code)
1493                         if (insetCodeOK)
1494                                 lyx_view_->getDialogs().show(name, data, 0);
1495                         break;
1496                 }
1497
1498                 case LFUN_DIALOG_UPDATE: {
1499                         BOOST_ASSERT(lyx_view_);
1500                         string const & name = argument;
1501                         // Can only update a dialog connected to an existing inset
1502                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1503                         if (inset) {
1504                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1505                                 inset->dispatch(view()->cursor(), fr);
1506                         } else if (name == "paragraph") {
1507                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1508                         } else if (name == "prefs") {
1509                                 lyx_view_->getDialogs().update(name, string());
1510                         }
1511                         break;
1512                 }
1513
1514                 case LFUN_DIALOG_HIDE:
1515                         LyX::cref().hideDialogs(argument, 0);
1516                         break;
1517
1518                 case LFUN_DIALOG_TOGGLE: {
1519                         BOOST_ASSERT(lyx_view_);
1520                         if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1521                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1522                         else
1523                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1524                         break;
1525                 }
1526
1527                 case LFUN_DIALOG_DISCONNECT_INSET:
1528                         BOOST_ASSERT(lyx_view_);
1529                         lyx_view_->getDialogs().disconnect(argument);
1530                         break;
1531
1532
1533                 case LFUN_CITATION_INSERT: {
1534                         BOOST_ASSERT(lyx_view_);
1535                         if (!argument.empty()) {
1536                                 // we can have one optional argument, delimited by '|'
1537                                 // citation-insert <key>|<text_before>
1538                                 // this should be enhanced to also support text_after
1539                                 // and citation style
1540                                 string arg = argument;
1541                                 string opt1;
1542                                 if (contains(argument, "|")) {
1543                                         arg = token(argument, '|', 0);
1544                                         opt1 = token(argument, '|', 1);
1545                                 }
1546                                 InsetCommandParams icp("cite");
1547                                 icp["key"] = from_utf8(arg);
1548                                 if (!opt1.empty())
1549                                         icp["before"] = from_utf8(opt1);
1550                                 string icstr = InsetCommandMailer::params2string("citation", icp);
1551                                 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1552                                 dispatch(fr);
1553                         } else
1554                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1555                         break;
1556                 }
1557
1558                 case LFUN_BUFFER_CHILD_OPEN: {
1559                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1560                         Buffer * parent = lyx_view_->buffer();
1561                         FileName filename = makeAbsPath(argument, parent->filePath());
1562                         view()->saveBookmark(false);
1563                         Buffer * child = 0;
1564                         bool parsed = false;
1565                         if (theBufferList().exists(filename.absFilename())) {
1566                                 child = theBufferList().getBuffer(filename.absFilename());
1567                         } else {
1568                                 setMessage(bformat(_("Opening child document %1$s..."),
1569                                         makeDisplayPath(filename.absFilename())));
1570                                 child = lyx_view_->loadLyXFile(filename, true);
1571                                 parsed = true;
1572                         }
1573                         if (child) {
1574                                 // Set the parent name of the child document.
1575                                 // This makes insertion of citations and references in the child work,
1576                                 // when the target is in the parent or another child document.
1577                                 child->setParentName(parent->fileName());
1578                                 updateLabels(*child->getMasterBuffer());
1579                                 lyx_view_->setBuffer(child);
1580                                 if (parsed)
1581                                         lyx_view_->showErrorList("Parse");
1582                         }
1583
1584                         // If a screen update is required (in case where auto_open is false), 
1585                         // setBuffer() would have taken care of it already. Otherwise we shall 
1586                         // reset the update flag because it can cause a circular problem.
1587                         // See bug 3970.
1588                         updateFlags = Update::None;
1589                         break;
1590                 }
1591
1592                 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1593                         BOOST_ASSERT(lyx_view_);
1594                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1595                         break;
1596
1597                 case LFUN_KEYMAP_OFF:
1598                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
1599                         lyx_view_->view()->getIntl().keyMapOn(false);
1600                         break;
1601
1602                 case LFUN_KEYMAP_PRIMARY:
1603                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
1604                         lyx_view_->view()->getIntl().keyMapPrim();
1605                         break;
1606
1607                 case LFUN_KEYMAP_SECONDARY:
1608                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
1609                         lyx_view_->view()->getIntl().keyMapSec();
1610                         break;
1611
1612                 case LFUN_KEYMAP_TOGGLE:
1613                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
1614                         lyx_view_->view()->getIntl().toggleKeyMap();
1615                         break;
1616
1617                 case LFUN_REPEAT: {
1618                         // repeat command
1619                         string countstr;
1620                         string rest = split(argument, countstr, ' ');
1621                         istringstream is(countstr);
1622                         int count = 0;
1623                         is >> count;
1624                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1625                         for (int i = 0; i < count; ++i)
1626                                 dispatch(lyxaction.lookupFunc(rest));
1627                         break;
1628                 }
1629
1630                 case LFUN_COMMAND_SEQUENCE: {
1631                         // argument contains ';'-terminated commands
1632                         string arg = argument;
1633                         while (!arg.empty()) {
1634                                 string first;
1635                                 arg = split(arg, first, ';');
1636                                 FuncRequest func(lyxaction.lookupFunc(first));
1637                                 func.origin = cmd.origin;
1638                                 dispatch(func);
1639                         }
1640                         break;
1641                 }
1642
1643                 case LFUN_PREFERENCES_SAVE: {
1644                         lyxrc.write(makeAbsPath("preferences",
1645                                                 package().user_support().absFilename()),
1646                                     false);
1647                         break;
1648                 }
1649
1650                 case LFUN_SCREEN_FONT_UPDATE:
1651                         BOOST_ASSERT(lyx_view_);
1652                         // handle the screen font changes.
1653                         theFontLoader().update();
1654                         /// FIXME: only the current view will be updated. the Gui
1655                         /// class is able to furnish the list of views.
1656                         updateFlags = Update::Force;
1657                         break;
1658
1659                 case LFUN_SET_COLOR: {
1660                         string lyx_name;
1661                         string const x11_name = split(argument, lyx_name, ' ');
1662                         if (lyx_name.empty() || x11_name.empty()) {
1663                                 setErrorMessage(from_ascii(N_(
1664                                                 "Syntax: set-color <lyx_name>"
1665                                                 " <x11_name>")));
1666                                 break;
1667                         }
1668
1669                         bool const graphicsbg_changed =
1670                                 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1671                                  x11_name != lcolor.getX11Name(Color::graphicsbg));
1672
1673                         if (!lcolor.setColor(lyx_name, x11_name)) {
1674                                 setErrorMessage(
1675                                                 bformat(_("Set-color \"%1$s\" failed "
1676                                                                        "- color is undefined or "
1677                                                                        "may not be redefined"),
1678                                                                            from_utf8(lyx_name)));
1679                                 break;
1680                         }
1681
1682                         theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1683
1684                         if (graphicsbg_changed) {
1685                                 // FIXME: The graphics cache no longer has a changeDisplay method.
1686 #if 0
1687                                 graphics::GCache::get().changeDisplay(true);
1688 #endif
1689                         }
1690                         break;
1691                 }
1692
1693                 case LFUN_MESSAGE:
1694                         BOOST_ASSERT(lyx_view_);
1695                         lyx_view_->message(from_utf8(argument));
1696                         break;
1697
1698                 case LFUN_EXTERNAL_EDIT: {
1699                         BOOST_ASSERT(lyx_view_);
1700                         FuncRequest fr(action, argument);
1701                         InsetExternal().dispatch(view()->cursor(), fr);
1702                         break;
1703                 }
1704
1705                 case LFUN_GRAPHICS_EDIT: {
1706                         FuncRequest fr(action, argument);
1707                         InsetGraphics().dispatch(view()->cursor(), fr);
1708                         break;
1709                 }
1710
1711                 case LFUN_INSET_APPLY: {
1712                         BOOST_ASSERT(lyx_view_);
1713                         string const name = cmd.getArg(0);
1714                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1715                         if (inset) {
1716                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1717                                 inset->dispatch(view()->cursor(), fr);
1718                         } else {
1719                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1720                                 dispatch(fr);
1721                         }
1722                         // ideally, the update flag should be set by the insets,
1723                         // but this is not possible currently
1724                         updateFlags = Update::Force | Update::FitCursor;
1725                         break;
1726                 }
1727
1728                 case LFUN_ALL_INSETS_TOGGLE: {
1729                         BOOST_ASSERT(lyx_view_);
1730                         string action;
1731                         string const name = split(argument, action, ' ');
1732                         InsetCode const inset_code = insetCode(name);
1733
1734                         Cursor & cur = view()->cursor();
1735                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1736
1737                         Inset & inset = lyx_view_->buffer()->inset();
1738                         InsetIterator it  = inset_iterator_begin(inset);
1739                         InsetIterator const end = inset_iterator_end(inset);
1740                         for (; it != end; ++it) {
1741                                 if (!it->asInsetMath()
1742                                     && (inset_code == NO_CODE
1743                                     || inset_code == it->lyxCode())) {
1744                                         Cursor tmpcur = cur;
1745                                         tmpcur.pushLeft(*it);
1746                                         it->dispatch(tmpcur, fr);
1747                                 }
1748                         }
1749                         updateFlags = Update::Force | Update::FitCursor;
1750                         break;
1751                 }
1752
1753                 case LFUN_BUFFER_LANGUAGE: {
1754                         BOOST_ASSERT(lyx_view_);
1755                         Buffer & buffer = *lyx_view_->buffer();
1756                         Language const * oldL = buffer.params().language;
1757                         Language const * newL = languages.getLanguage(argument);
1758                         if (!newL || oldL == newL)
1759                                 break;
1760
1761                         if (oldL->rightToLeft() == newL->rightToLeft()
1762                             && !buffer.isMultiLingual())
1763                                 buffer.changeLanguage(oldL, newL);
1764                         break;
1765                 }
1766
1767                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1768                         string const fname =
1769                                 addName(addPath(package().user_support().absFilename(), "templates/"),
1770                                         "defaults.lyx");
1771                         Buffer defaults(fname);
1772
1773                         istringstream ss(argument);
1774                         Lexer lex(0,0);
1775                         lex.setStream(ss);
1776                         int const unknown_tokens = defaults.readHeader(lex);
1777
1778                         if (unknown_tokens != 0) {
1779                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1780                                        << unknown_tokens << " unknown token"
1781                                        << (unknown_tokens == 1 ? "" : "s")
1782                                        << endl;
1783                         }
1784
1785                         if (defaults.writeFile(FileName(defaults.fileName())))
1786                                 setMessage(bformat(_("Document defaults saved in %1$s"),
1787                                                    makeDisplayPath(fname)));
1788                         else
1789                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1790                         break;
1791                 }
1792
1793                 case LFUN_BUFFER_PARAMS_APPLY: {
1794                         BOOST_ASSERT(lyx_view_);
1795                         biblio::CiteEngine const oldEngine =
1796                                         lyx_view_->buffer()->params().getEngine();
1797                         
1798                         Buffer * buffer = lyx_view_->buffer();
1799
1800                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1801                         recordUndoFullDocument(view());
1802                         
1803                         istringstream ss(argument);
1804                         Lexer lex(0,0);
1805                         lex.setStream(ss);
1806                         int const unknown_tokens = buffer->readHeader(lex);
1807
1808                         if (unknown_tokens != 0) {
1809                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1810                                                 << unknown_tokens << " unknown token"
1811                                                 << (unknown_tokens == 1 ? "" : "s")
1812                                                 << endl;
1813                         }
1814                         
1815                         updateLayout(oldClass, buffer);
1816                         
1817                         biblio::CiteEngine const newEngine =
1818                                         lyx_view_->buffer()->params().getEngine();
1819                         
1820                         if (oldEngine != newEngine) {
1821                                 Cursor & cur = view()->cursor();
1822                                 FuncRequest fr(LFUN_INSET_REFRESH);
1823         
1824                                 Inset & inset = lyx_view_->buffer()->inset();
1825                                 InsetIterator it  = inset_iterator_begin(inset);
1826                                 InsetIterator const end = inset_iterator_end(inset);
1827                                 for (; it != end; ++it)
1828                                         if (it->lyxCode() == CITE_CODE)
1829                                                 it->dispatch(cur, fr);
1830                         }
1831                         
1832                         updateFlags = Update::Force | Update::FitCursor;
1833                         break;
1834                 }
1835                 
1836                 case LFUN_LAYOUT_MODULES_CLEAR: {
1837                         BOOST_ASSERT(lyx_view_);
1838                         Buffer * buffer = lyx_view_->buffer();
1839                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1840                         recordUndoFullDocument(view());
1841                         buffer->params().clearLayoutModules();
1842                         updateLayout(oldClass, buffer);
1843                         updateFlags = Update::Force | Update::FitCursor;
1844                         break;
1845                 }
1846                 
1847                 case LFUN_LAYOUT_MODULE_ADD: {
1848                         BOOST_ASSERT(lyx_view_);
1849                         Buffer * buffer = lyx_view_->buffer();
1850                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1851                         recordUndoFullDocument(view());
1852                         buffer->params().addLayoutModule(argument);
1853                         updateLayout(oldClass, buffer);
1854                         updateFlags = Update::Force | Update::FitCursor;
1855                         break;
1856                 }
1857
1858                 case LFUN_TEXTCLASS_APPLY: {
1859                         BOOST_ASSERT(lyx_view_);
1860                         Buffer * buffer = lyx_view_->buffer();
1861
1862                         loadTextClass(argument);
1863
1864                         std::pair<bool, textclass_type> const tc_pair =
1865                                 textclasslist.numberOfClass(argument);
1866
1867                         if (!tc_pair.first)
1868                                 break;
1869
1870                         textclass_type const old_class = buffer->params().getBaseClass();
1871                         textclass_type const new_class = tc_pair.second;
1872
1873                         if (old_class == new_class)
1874                                 // nothing to do
1875                                 break;
1876
1877                         //Save the old, possibly modular, layout for use in conversion.
1878                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1879                         recordUndoFullDocument(view());
1880                         buffer->params().setBaseClass(new_class);
1881                         updateLayout(oldClass, buffer);
1882                         updateFlags = Update::Force | Update::FitCursor;
1883                         break;
1884                 }
1885                 
1886                 case LFUN_LAYOUT_RELOAD: {
1887                         BOOST_ASSERT(lyx_view_);
1888                         Buffer * buffer = lyx_view_->buffer();
1889                         TextClassPtr oldClass = buffer->params().getTextClassPtr();
1890                         textclass_type const tc = buffer->params().getBaseClass();
1891                         textclasslist.reset(tc);
1892                         buffer->params().setBaseClass(tc);
1893                         updateLayout(oldClass, buffer);
1894                         updateFlags = Update::Force | Update::FitCursor;
1895                         break;
1896                 }
1897
1898                 case LFUN_TEXTCLASS_LOAD:
1899                         loadTextClass(argument);
1900                         break;
1901
1902                 case LFUN_LYXRC_APPLY: {
1903                         LyXRC const lyxrc_orig = lyxrc;
1904
1905                         istringstream ss(argument);
1906                         bool const success = lyxrc.read(ss) == 0;
1907
1908                         if (!success) {
1909                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1910                                        << "Unable to read lyxrc data"
1911                                        << endl;
1912                                 break;
1913                         }
1914
1915                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1916
1917                         /// We force the redraw in any case because there might be
1918                         /// some screen font changes.
1919                         /// FIXME: only the current view will be updated. the Gui
1920                         /// class is able to furnish the list of views.
1921                         updateFlags = Update::Force;
1922                         break;
1923                 }
1924
1925                 case LFUN_WINDOW_NEW:
1926                         LyX::ref().newLyXView();
1927                         break;
1928
1929                 case LFUN_WINDOW_CLOSE:
1930                         BOOST_ASSERT(lyx_view_);
1931                         BOOST_ASSERT(theApp());
1932                         // update bookmark pit of the current buffer before window close
1933                         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1934                                 gotoBookmark(i+1, false, false);
1935                         // ask the user for saving changes or cancel quit
1936                         if (!theBufferList().quitWriteAll())
1937                                 break;
1938                         lyx_view_->close();
1939                         return;
1940
1941                 case LFUN_BOOKMARK_GOTO:
1942                         // go to bookmark, open unopened file and switch to buffer if necessary
1943                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1944                         break;
1945
1946                 case LFUN_BOOKMARK_CLEAR:
1947                         LyX::ref().session().bookmarks().clear();
1948                         break;
1949
1950                 case LFUN_TOOLBAR_TOGGLE: {
1951                         BOOST_ASSERT(lyx_view_);
1952                         string const name = cmd.getArg(0);
1953                         bool const allowauto = cmd.getArg(1) == "allowauto";
1954                         lyx_view_->toggleToolbarState(name, allowauto);
1955                         ToolbarInfo * tbi = lyx_view_->getToolbarInfo(name);
1956                         if (!tbi) {
1957                                 setMessage(bformat(_("Unknown toolbar \"%1$s\""),
1958                                                    from_utf8(name)));
1959                                 break;
1960                         }
1961                         docstring state;
1962                         if (tbi->flags & ToolbarInfo::ON)
1963                                 state = _("on");
1964                         else if (tbi->flags & ToolbarInfo::OFF)
1965                                 state = _("off");
1966                         else if (tbi->flags & ToolbarInfo::AUTO)
1967                                 state = _("auto");
1968
1969                         setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"), 
1970                                            _(tbi->gui_name), state));
1971                         break;
1972                 }
1973
1974                 default: {
1975                         BOOST_ASSERT(lyx_view_);
1976                         view()->cursor().dispatch(cmd);
1977                         updateFlags = view()->cursor().result().update();
1978                         if (!view()->cursor().result().dispatched())
1979                                 updateFlags = view()->dispatch(cmd);
1980                         break;
1981                 }
1982                 }
1983
1984                 if (lyx_view_ && lyx_view_->buffer()) {
1985                         // BufferView::update() updates the ViewMetricsInfo and
1986                         // also initializes the position cache for all insets in
1987                         // (at least partially) visible top-level paragraphs.
1988                         // We will redraw the screen only if needed.
1989                         view()->processUpdateFlags(updateFlags);
1990                         lyx_view_->updateStatusBar();
1991
1992                         // if we executed a mutating lfun, mark the buffer as dirty
1993                         if (flag.enabled()
1994                             && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1995                             && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1996                                 lyx_view_->buffer()->markDirty();
1997
1998                         //Do we have a selection?
1999                         theSelection().haveSelection(view()->cursor().selection());
2000
2001                         if (view()->cursor().inTexted()) {
2002                                 lyx_view_->updateLayoutChoice();
2003                         }
2004                 }
2005         }
2006         if (!quitting && lyx_view_) {
2007                 lyx_view_->updateToolbars();
2008                 // Some messages may already be translated, so we cannot use _()
2009                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
2010         }
2011 }
2012
2013
2014 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
2015 {
2016         const bool verbose = (cmd.origin == FuncRequest::MENU
2017                               || cmd.origin == FuncRequest::TOOLBAR
2018                               || cmd.origin == FuncRequest::COMMANDBUFFER);
2019
2020         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
2021                 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
2022                 if (!msg.empty())
2023                         lyx_view_->message(msg);
2024                 return;
2025         }
2026
2027         docstring dispatch_msg = msg;
2028         if (!dispatch_msg.empty())
2029                 dispatch_msg += ' ';
2030
2031         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
2032
2033         bool argsadded = false;
2034
2035         if (!cmd.argument().empty()) {
2036                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
2037                         comname += ' ' + cmd.argument();
2038                         argsadded = true;
2039                 }
2040         }
2041
2042         docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
2043
2044         if (!shortcuts.empty())
2045                 comname += ": " + shortcuts;
2046         else if (!argsadded && !cmd.argument().empty())
2047                 comname += ' ' + cmd.argument();
2048
2049         if (!comname.empty()) {
2050                 comname = rtrim(comname);
2051                 dispatch_msg += '(' + rtrim(comname) + ')';
2052         }
2053
2054         LYXERR(Debug::ACTION) << "verbose dispatch msg "
2055                 << to_utf8(dispatch_msg) << endl;
2056         if (!dispatch_msg.empty())
2057                 lyx_view_->message(dispatch_msg);
2058 }
2059
2060
2061 void LyXFunc::menuNew(string const & name, bool fromTemplate)
2062 {
2063         // FIXME: initpath is not used. What to do?
2064         string initpath = lyxrc.document_path;
2065         string filename(name);
2066
2067         if (lyx_view_->buffer()) {
2068                 string const trypath = lyx_view_->buffer()->filePath();
2069                 // If directory is writeable, use this as default.
2070                 if (isDirWriteable(FileName(trypath)))
2071                         initpath = trypath;
2072         }
2073
2074         static int newfile_number;
2075
2076         if (filename.empty()) {
2077                 filename = addName(lyxrc.document_path,
2078                             "newfile" + convert<string>(++newfile_number) + ".lyx");
2079                 while (theBufferList().exists(filename) ||
2080                        fs::is_readable(FileName(filename).toFilesystemEncoding())) {
2081                         ++newfile_number;
2082                         filename = addName(lyxrc.document_path,
2083                                            "newfile" +  convert<string>(newfile_number) +
2084                                     ".lyx");
2085                 }
2086         }
2087
2088         // The template stuff
2089         string templname;
2090         if (fromTemplate) {
2091                 FileDialog fileDlg(_("Select template file"),
2092                         LFUN_SELECT_FILE_SYNC,
2093                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2094                         make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
2095
2096                 FileDialog::Result result =
2097                         fileDlg.open(from_utf8(lyxrc.template_path),
2098                                      FileFilterList(_("LyX Documents (*.lyx)")),
2099                                      docstring());
2100
2101                 if (result.first == FileDialog::Later)
2102                         return;
2103                 if (result.second.empty())
2104                         return;
2105                 templname = to_utf8(result.second);
2106         }
2107
2108         Buffer * const b = newFile(filename, templname, !name.empty());
2109         if (b)
2110                 lyx_view_->setBuffer(b);
2111 }
2112
2113
2114 void LyXFunc::open(string const & fname)
2115 {
2116         string initpath = lyxrc.document_path;
2117
2118         if (lyx_view_->buffer()) {
2119                 string const trypath = lyx_view_->buffer()->filePath();
2120                 // If directory is writeable, use this as default.
2121                 if (isDirWriteable(FileName(trypath)))
2122                         initpath = trypath;
2123         }
2124
2125         string filename;
2126
2127         if (fname.empty()) {
2128                 FileDialog fileDlg(_("Select document to open"),
2129                         LFUN_FILE_OPEN,
2130                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2131                         make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2132
2133                 FileDialog::Result result =
2134                         fileDlg.open(from_utf8(initpath),
2135                                      FileFilterList(_("LyX Documents (*.lyx)")),
2136                                      docstring());
2137
2138                 if (result.first == FileDialog::Later)
2139                         return;
2140
2141                 filename = to_utf8(result.second);
2142
2143                 // check selected filename
2144                 if (filename.empty()) {
2145                         lyx_view_->message(_("Canceled."));
2146                         return;
2147                 }
2148         } else
2149                 filename = fname;
2150
2151         // get absolute path of file and add ".lyx" to the filename if
2152         // necessary
2153         FileName const fullname = fileSearch(string(), filename, "lyx");
2154         if (!fullname.empty())
2155                 filename = fullname.absFilename();
2156
2157         // if the file doesn't exist, let the user create one
2158         if (!fs::exists(fullname.toFilesystemEncoding())) {
2159                 // the user specifically chose this name. Believe him.
2160                 Buffer * const b = newFile(filename, string(), true);
2161                 if (b)
2162                         lyx_view_->setBuffer(b);
2163                 return;
2164         }
2165
2166         docstring const disp_fn = makeDisplayPath(filename);
2167         lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
2168
2169         docstring str2;
2170         Buffer * buf = lyx_view_->loadLyXFile(fullname);
2171         if (buf) {
2172                 updateLabels(*buf);
2173                 lyx_view_->setBuffer(buf);
2174                 lyx_view_->showErrorList("Parse");
2175                 str2 = bformat(_("Document %1$s opened."), disp_fn);
2176         } else {
2177                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2178         }
2179         lyx_view_->message(str2);
2180 }
2181
2182
2183 void LyXFunc::doImport(string const & argument)
2184 {
2185         string format;
2186         string filename = split(argument, format, ' ');
2187
2188         LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
2189                             << " file: " << filename << endl;
2190
2191         // need user interaction
2192         if (filename.empty()) {
2193                 string initpath = lyxrc.document_path;
2194
2195                 if (lyx_view_->buffer()) {
2196                         string const trypath = lyx_view_->buffer()->filePath();
2197                         // If directory is writeable, use this as default.
2198                         if (isDirWriteable(FileName(trypath)))
2199                                 initpath = trypath;
2200                 }
2201
2202                 docstring const text = bformat(_("Select %1$s file to import"),
2203                         formats.prettyName(format));
2204
2205                 FileDialog fileDlg(text,
2206                         LFUN_BUFFER_IMPORT,
2207                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2208                         make_pair(_("Examples|#E#e"),
2209                                   from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2210
2211                 docstring filter = formats.prettyName(format);
2212                 filter += " (*.";
2213                 // FIXME UNICODE
2214                 filter += from_utf8(formats.extension(format));
2215                 filter += ')';
2216
2217                 FileDialog::Result result =
2218                         fileDlg.open(from_utf8(initpath),
2219                                      FileFilterList(filter),
2220                                      docstring());
2221
2222                 if (result.first == FileDialog::Later)
2223                         return;
2224
2225                 filename = to_utf8(result.second);
2226
2227                 // check selected filename
2228                 if (filename.empty())
2229                         lyx_view_->message(_("Canceled."));
2230         }
2231
2232         if (filename.empty())
2233                 return;
2234
2235         // get absolute path of file
2236         FileName const fullname(makeAbsPath(filename));
2237
2238         FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2239
2240         // Check if the document already is open
2241         if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2242                 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2243                         lyx_view_->message(_("Canceled."));
2244                         return;
2245                 }
2246         }
2247
2248         // if the file exists already, and we didn't do
2249         // -i lyx thefile.lyx, warn
2250         if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2251                 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2252
2253                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2254                                                      "Do you want to overwrite that document?"), file);
2255                 int const ret = Alert::prompt(_("Overwrite document?"),
2256                         text, 0, 1, _("&Overwrite"), _("&Cancel"));
2257
2258                 if (ret == 1) {
2259                         lyx_view_->message(_("Canceled."));
2260                         return;
2261                 }
2262         }
2263
2264         ErrorList errorList;
2265         Importer::Import(lyx_view_, fullname, format, errorList);
2266         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2267 }
2268
2269
2270 void LyXFunc::closeBuffer()
2271 {
2272         // goto bookmark to update bookmark pit.
2273         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2274                 gotoBookmark(i+1, false, false);
2275         
2276         theBufferList().close(lyx_view_->buffer(), true);
2277 }
2278
2279
2280 void LyXFunc::reloadBuffer()
2281 {
2282         FileName filename(lyx_view_->buffer()->fileName());
2283         docstring const disp_fn = makeDisplayPath(filename.absFilename());
2284         docstring str;
2285         closeBuffer();
2286         Buffer * buf = lyx_view_->loadLyXFile(filename);
2287         if (buf) {
2288                 updateLabels(*buf);
2289                 lyx_view_->setBuffer(buf);
2290                 lyx_view_->showErrorList("Parse");
2291                 str = bformat(_("Document %1$s reloaded."), disp_fn);
2292         } else {
2293                 str = bformat(_("Could not reload document %1$s"), disp_fn);
2294         }
2295         lyx_view_->message(str);
2296 }
2297
2298 // Each "lyx_view_" should have it's own message method. lyxview and
2299 // the minibuffer would use the minibuffer, but lyxserver would
2300 // send an ERROR signal to its client.  Alejandro 970603
2301 // This function is bit problematic when it comes to NLS, to make the
2302 // lyx servers client be language indepenent we must not translate
2303 // strings sent to this func.
2304 void LyXFunc::setErrorMessage(docstring const & m) const
2305 {
2306         dispatch_buffer = m;
2307         errorstat = true;
2308 }
2309
2310
2311 void LyXFunc::setMessage(docstring const & m) const
2312 {
2313         dispatch_buffer = m;
2314 }
2315
2316
2317 docstring const LyXFunc::viewStatusMessage()
2318 {
2319         // When meta-fake key is pressed, show the key sequence so far + "M-".
2320         if (wasMetaKey())
2321                 return keyseq.print(true) + "M-";
2322
2323         // Else, when a non-complete key sequence is pressed,
2324         // show the available options.
2325         if (keyseq.length() > 0 && !keyseq.deleted())
2326                 return keyseq.printOptions(true);
2327
2328         BOOST_ASSERT(lyx_view_);
2329         if (!lyx_view_->buffer())
2330                 return _("Welcome to LyX!");
2331
2332         return view()->cursor().currentState();
2333 }
2334
2335
2336 BufferView * LyXFunc::view() const
2337 {
2338         BOOST_ASSERT(lyx_view_);
2339         return lyx_view_->view();
2340 }
2341
2342
2343 bool LyXFunc::wasMetaKey() const
2344 {
2345         return (meta_fake_bit != NoModifier);
2346 }
2347
2348
2349 void LyXFunc::updateLayout(TextClassPtr const & oldlayout,
2350                            Buffer * buffer)
2351 {
2352         lyx_view_->message(_("Converting document to new document class..."));
2353         
2354         StableDocIterator backcur(view()->cursor());
2355         ErrorList & el = buffer->errorList("Class Switch");
2356         cap::switchBetweenClasses(
2357                         oldlayout, buffer->params().getTextClassPtr(),
2358                         static_cast<InsetText &>(buffer->inset()), el);
2359
2360         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
2361
2362         buffer->errors("Class Switch");
2363         updateLabels(*buffer);
2364 }
2365
2366
2367 namespace {
2368
2369 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2370 {
2371         // Why the switch you might ask. It is a trick to ensure that all
2372         // the elements in the LyXRCTags enum is handled. As you can see
2373         // there are no breaks at all. So it is just a huge fall-through.
2374         // The nice thing is that we will get a warning from the compiler
2375         // if we forget an element.
2376         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2377         switch (tag) {
2378         case LyXRC::RC_ACCEPT_COMPOUND:
2379         case LyXRC::RC_ALT_LANG:
2380         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2381         case LyXRC::RC_PLAINTEXT_LINELEN:
2382         case LyXRC::RC_AUTOREGIONDELETE:
2383         case LyXRC::RC_AUTORESET_OPTIONS:
2384         case LyXRC::RC_AUTOSAVE:
2385         case LyXRC::RC_AUTO_NUMBER:
2386         case LyXRC::RC_BACKUPDIR_PATH:
2387         case LyXRC::RC_BIBTEX_COMMAND:
2388         case LyXRC::RC_BINDFILE:
2389         case LyXRC::RC_CHECKLASTFILES:
2390         case LyXRC::RC_USELASTFILEPOS:
2391         case LyXRC::RC_LOADSESSION:
2392         case LyXRC::RC_CHKTEX_COMMAND:
2393         case LyXRC::RC_CONVERTER:
2394         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2395         case LyXRC::RC_COPIER:
2396         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2397         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2398         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2399         case LyXRC::RC_DATE_INSERT_FORMAT:
2400         case LyXRC::RC_DEFAULT_LANGUAGE:
2401         case LyXRC::RC_DEFAULT_PAPERSIZE:
2402         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2403         case LyXRC::RC_DISPLAY_GRAPHICS:
2404         case LyXRC::RC_DOCUMENTPATH:
2405                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2406                         string const encoded = FileName(
2407                                 lyxrc_new.document_path).toFilesystemEncoding();
2408                         if (fs::exists(encoded) && fs::is_directory(encoded))
2409                                 support::package().document_dir() = FileName(lyxrc.document_path);
2410                 }
2411         case LyXRC::RC_ESC_CHARS:
2412         case LyXRC::RC_FONT_ENCODING:
2413         case LyXRC::RC_FORMAT:
2414         case LyXRC::RC_INDEX_COMMAND:
2415         case LyXRC::RC_INPUT:
2416         case LyXRC::RC_KBMAP:
2417         case LyXRC::RC_KBMAP_PRIMARY:
2418         case LyXRC::RC_KBMAP_SECONDARY:
2419         case LyXRC::RC_LABEL_INIT_LENGTH:
2420         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2421         case LyXRC::RC_LANGUAGE_AUTO_END:
2422         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2423         case LyXRC::RC_LANGUAGE_COMMAND_END:
2424         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2425         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2426         case LyXRC::RC_LANGUAGE_PACKAGE:
2427         case LyXRC::RC_LANGUAGE_USE_BABEL:
2428         case LyXRC::RC_MAKE_BACKUP:
2429         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2430         case LyXRC::RC_NUMLASTFILES:
2431         case LyXRC::RC_PATH_PREFIX:
2432                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2433                         support::prependEnvPath("PATH", lyxrc.path_prefix);
2434                 }
2435         case LyXRC::RC_PERS_DICT:
2436         case LyXRC::RC_PREVIEW:
2437         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2438         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2439         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2440         case LyXRC::RC_PRINTCOPIESFLAG:
2441         case LyXRC::RC_PRINTER:
2442         case LyXRC::RC_PRINTEVENPAGEFLAG:
2443         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2444         case LyXRC::RC_PRINTFILEEXTENSION:
2445         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2446         case LyXRC::RC_PRINTODDPAGEFLAG:
2447         case LyXRC::RC_PRINTPAGERANGEFLAG:
2448         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2449         case LyXRC::RC_PRINTPAPERFLAG:
2450         case LyXRC::RC_PRINTREVERSEFLAG:
2451         case LyXRC::RC_PRINTSPOOL_COMMAND:
2452         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2453         case LyXRC::RC_PRINTTOFILE:
2454         case LyXRC::RC_PRINTTOPRINTER:
2455         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2456         case LyXRC::RC_PRINT_COMMAND:
2457         case LyXRC::RC_RTL_SUPPORT:
2458         case LyXRC::RC_SCREEN_DPI:
2459         case LyXRC::RC_SCREEN_FONT_ROMAN:
2460         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2461         case LyXRC::RC_SCREEN_FONT_SANS:
2462         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2463         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2464         case LyXRC::RC_SCREEN_FONT_SIZES:
2465         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2466         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2467         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2468         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2469         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2470         case LyXRC::RC_SCREEN_ZOOM:
2471         case LyXRC::RC_SERVERPIPE:
2472         case LyXRC::RC_SET_COLOR:
2473         case LyXRC::RC_SHOW_BANNER:
2474         case LyXRC::RC_SPELL_COMMAND:
2475         case LyXRC::RC_TEMPDIRPATH:
2476         case LyXRC::RC_TEMPLATEPATH:
2477         case LyXRC::RC_TEX_ALLOWS_SPACES:
2478         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2479                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2480                         support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2481                 }
2482         case LyXRC::RC_UIFILE:
2483         case LyXRC::RC_USER_EMAIL:
2484         case LyXRC::RC_USER_NAME:
2485         case LyXRC::RC_USETEMPDIR:
2486         case LyXRC::RC_USE_ALT_LANG:
2487         case LyXRC::RC_USE_CONVERTER_CACHE:
2488         case LyXRC::RC_USE_ESC_CHARS:
2489         case LyXRC::RC_USE_INP_ENC:
2490         case LyXRC::RC_USE_PERS_DICT:
2491         case LyXRC::RC_USE_SPELL_LIB:
2492         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2493         case LyXRC::RC_VIEWER:
2494         case LyXRC::RC_LAST:
2495                 break;
2496         }
2497 }
2498
2499 } // namespace anon
2500
2501
2502 } // namespace lyx