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