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