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