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