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