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