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