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