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