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