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