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