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