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