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