3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
25 #include "BranchList.h"
27 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
31 #include "bufferview_funcs.h"
33 #include "CutAndPaste.h"
35 #include "DispatchResult.h"
37 #include "ErrorList.h"
40 #include "FuncRequest.h"
41 #include "FuncStatus.h"
44 #include "InsetIterator.h"
52 #include "LyXAction.h"
58 #include "TextClassList.h"
60 #include "Paragraph.h"
61 #include "ParIterator.h"
62 #include "ParagraphParameters.h"
65 #include "insets/InsetBox.h"
66 #include "insets/InsetBranch.h"
67 #include "insets/InsetCommand.h"
68 #include "insets/InsetERT.h"
69 #include "insets/InsetExternal.h"
70 #include "insets/InsetFloat.h"
71 #include "insets/InsetListings.h"
72 #include "insets/InsetGraphics.h"
73 #include "insets/InsetInclude.h"
74 #include "insets/InsetNote.h"
75 #include "insets/InsetTabular.h"
76 #include "insets/InsetVSpace.h"
77 #include "insets/InsetWrap.h"
79 #include "frontends/Application.h"
80 #include "frontends/alert.h"
81 #include "frontends/Dialogs.h"
82 #include "frontends/FileDialog.h"
83 #include "frontends/FontLoader.h"
84 #include "frontends/Gui.h"
85 #include "frontends/KeySymbol.h"
86 #include "frontends/LyXView.h"
87 #include "frontends/Menubar.h"
88 #include "frontends/Toolbars.h"
89 #include "frontends/Selection.h"
91 #include "support/environment.h"
92 #include "support/FileFilterList.h"
93 #include "support/filetools.h"
94 #include "support/ForkedcallsController.h"
95 #include "support/fs_extras.h"
96 #include "support/lstrings.h"
97 #include "support/Path.h"
98 #include "support/Package.h"
99 #include "support/Systemcall.h"
100 #include "support/convert.h"
101 #include "support/os.h"
103 #include <boost/current_function.hpp>
104 #include <boost/filesystem/operations.hpp>
111 using bv_funcs::freefont2string;
113 using support::absolutePath;
114 using support::addName;
115 using support::addPath;
116 using support::bformat;
117 using support::changeExtension;
118 using support::contains;
119 using support::FileFilterList;
120 using support::FileName;
121 using support::fileSearch;
122 using support::ForkedcallsController;
123 using support::i18nLibFileSearch;
124 using support::isDirWriteable;
125 using support::isFileReadable;
126 using support::isStrInt;
127 using support::makeAbsPath;
128 using support::makeDisplayPath;
129 using support::package;
130 using support::quoteName;
131 using support::rtrim;
132 using support::split;
133 using support::subst;
134 using support::Systemcall;
135 using support::token;
137 using support::prefixIs;
140 using std::make_pair;
143 using std::istringstream;
144 using std::ostringstream;
146 namespace Alert = frontend::Alert;
147 namespace fs = boost::filesystem;
152 bool getLocalStatus(Cursor cursor,
153 FuncRequest const & cmd, FuncStatus & status)
155 // Try to fix cursor in case it is broken.
156 cursor.fixIfBroken();
158 // This is, of course, a mess. Better create a new doc iterator and use
159 // this in Inset::getStatus. This might require an additional
160 // BufferView * arg, though (which should be avoided)
161 //Cursor safe = *this;
163 for ( ; cursor.depth(); cursor.pop()) {
164 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
165 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
166 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
167 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
169 // The inset's getStatus() will return 'true' if it made
170 // a definitive decision on whether it want to handle the
171 // request or not. The result of this decision is put into
172 // the 'status' parameter.
173 if (cursor.inset().getStatus(cursor, cmd, status)) {
182 /** Return the change status at cursor position, taking in account the
183 * status at each level of the document iterator (a table in a deleted
184 * footnote is deleted).
185 * When \param outer is true, the top slice is not looked at.
187 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
189 size_t const depth = dit.depth() - (outer ? 1 : 0);
191 for (size_t i = 0 ; i < depth ; ++i) {
192 CursorSlice const & slice = dit[i];
193 if (!slice.inset().inMathed()
194 && slice.pos() < slice.paragraph().size()) {
195 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
196 if (ch != Change::UNCHANGED)
200 return Change::UNCHANGED;
208 meta_fake_bit(key_modifier::none)
213 void LyXFunc::initKeySequences(KeyMap * kb)
215 keyseq.reset(new KeySequence(kb, kb));
216 cancel_meta_seq.reset(new KeySequence(kb, kb));
220 void LyXFunc::setLyXView(LyXView * lv)
222 if (!quitting && lyx_view_ && lyx_view_ != lv)
223 // save current selection to the selection buffer to allow
224 // middle-button paste in another window
225 cap::saveSelection(lyx_view_->view()->cursor());
230 void LyXFunc::handleKeyFunc(kb_action action)
232 char_type c = encoded_last_key;
234 if (keyseq->length())
237 lyx_view_->view()->getIntl().getTransManager().deadkey(
238 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
239 // Need to clear, in case the minibuffer calls these
242 // copied verbatim from do_accent_char
243 view()->cursor().resetAnchor();
248 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
250 BOOST_ASSERT(lyx_view_);
251 if (!LyX::ref().session().bookmarks().isValid(idx))
253 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
254 BOOST_ASSERT(!bm.filename.empty());
255 string const file = bm.filename.absFilename();
256 // if the file is not opened, open it.
257 if (!theBufferList().exists(file)) {
259 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
263 // open may fail, so we need to test it again
264 if (theBufferList().exists(file)) {
265 // if the current buffer is not that one, switch to it.
266 if (lyx_view_->buffer()->fileName() != file) {
268 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
272 // moveToPosition use par_id, and par_pit and return new par_id.
276 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
277 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
278 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
279 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
280 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
285 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
287 LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
289 // Do nothing if we have nothing (JMarc)
290 if (!keysym->isOK()) {
291 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
296 if (keysym->isModifier()) {
297 LYXERR(Debug::KEY) << "isModifier true" << endl;
301 //Encoding const * encoding = view()->cursor().getEncoding();
302 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
303 // FIXME: encoded_last_key shadows the member variable of the same
304 // name. Is that intended?
305 char_type encoded_last_key = keysym->getUCSEncoded();
307 // Do a one-deep top-level lookup for
308 // cancel and meta-fake keys. RVDK_PATCH_5
309 cancel_meta_seq->reset();
311 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
312 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
313 << " action first set to [" << func.action << ']'
316 // When not cancel or meta-fake, do the normal lookup.
317 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
318 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
319 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
320 // remove Caps Lock and Mod2 as a modifiers
321 func = keyseq->addkey(keysym, (state | meta_fake_bit));
322 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
323 << "action now set to ["
324 << func.action << ']' << endl;
327 // Dont remove this unless you know what you are doing.
328 meta_fake_bit = key_modifier::none;
330 // Can this happen now ?
331 if (func.action == LFUN_NOACTION) {
332 func = FuncRequest(LFUN_COMMAND_PREFIX);
335 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
337 << func.action << "]["
338 << to_utf8(keyseq->print(false)) << ']'
341 // already here we know if it any point in going further
342 // why not return already here if action == -1 and
343 // num_bytes == 0? (Lgb)
345 if (keyseq->length() > 1) {
346 lyx_view_->message(keyseq->print(true));
350 // Maybe user can only reach the key via holding down shift.
351 // Let's see. But only if shift is the only modifier
352 if (func.action == LFUN_UNKNOWN_ACTION &&
353 state == key_modifier::shift) {
354 LYXERR(Debug::KEY) << "Trying without shift" << endl;
355 func = keyseq->addkey(keysym, key_modifier::none);
356 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
359 if (func.action == LFUN_UNKNOWN_ACTION) {
360 // Hmm, we didn't match any of the keysequences. See
361 // if it's normal insertable text not already covered
363 if (keysym->isText() && keyseq->length() == 1) {
364 LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
365 func = FuncRequest(LFUN_SELF_INSERT,
366 FuncRequest::KEYBOARD);
368 LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
369 lyx_view_->message(_("Unknown function."));
374 if (func.action == LFUN_SELF_INSERT) {
375 if (encoded_last_key != 0) {
376 docstring const arg(1, encoded_last_key);
377 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
378 FuncRequest::KEYBOARD));
380 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
388 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
390 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
393 Cursor & cur = view()->cursor();
395 /* In LyX/Mac, when a dialog is open, the menus of the
396 application can still be accessed without giving focus to
397 the main window. In this case, we want to disable the menu
398 entries that are buffer-related.
400 Note that this code is not perfect, as bug 1941 attests:
401 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
403 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
404 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
407 if (cmd.action == LFUN_NOACTION) {
408 flag.message(from_utf8(N_("Nothing to do")));
413 switch (cmd.action) {
414 case LFUN_UNKNOWN_ACTION:
415 #ifndef HAVE_LIBAIKSAURUS
416 case LFUN_THESAURUS_ENTRY:
426 if (flag.unknown()) {
427 flag.message(from_utf8(N_("Unknown action")));
431 if (!flag.enabled()) {
432 if (flag.message().empty())
433 flag.message(from_utf8(N_("Command disabled")));
437 // Check whether we need a buffer
438 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
440 flag.message(from_utf8(N_("Command not allowed with"
441 "out any document open")));
446 // I would really like to avoid having this switch and rather try to
447 // encode this in the function itself.
448 // -- And I'd rather let an inset decide which LFUNs it is willing
449 // to handle (Andre')
451 switch (cmd.action) {
452 case LFUN_BUFFER_TOGGLE_READ_ONLY:
453 flag.setOnOff(buf->isReadonly());
456 case LFUN_BUFFER_SWITCH:
457 // toggle on the current buffer, but do not toggle off
458 // the other ones (is that a good idea?)
459 if (to_utf8(cmd.argument()) == buf->fileName())
463 case LFUN_BUFFER_EXPORT:
464 enable = cmd.argument() == "custom"
465 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
468 case LFUN_BUFFER_CHKTEX:
469 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
472 case LFUN_BUILD_PROGRAM:
473 enable = Exporter::isExportable(*buf, "program");
476 case LFUN_LAYOUT_TABULAR:
477 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
481 case LFUN_LAYOUT_PARAGRAPH:
482 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
485 case LFUN_VC_REGISTER:
486 enable = !buf->lyxvc().inUse();
488 case LFUN_VC_CHECK_IN:
489 enable = buf->lyxvc().inUse() && !buf->isReadonly();
491 case LFUN_VC_CHECK_OUT:
492 enable = buf->lyxvc().inUse() && buf->isReadonly();
495 case LFUN_VC_UNDO_LAST:
496 enable = buf->lyxvc().inUse();
498 case LFUN_BUFFER_RELOAD:
499 enable = !buf->isUnnamed() && !buf->isClean();
502 case LFUN_INSET_SETTINGS: {
506 Inset::Code code = cur.inset().lyxCode();
508 case Inset::TABULAR_CODE:
509 enable = cmd.argument() == "tabular";
511 case Inset::ERT_CODE:
512 enable = cmd.argument() == "ert";
514 case Inset::FLOAT_CODE:
515 enable = cmd.argument() == "float";
517 case Inset::WRAP_CODE:
518 enable = cmd.argument() == "wrap";
520 case Inset::NOTE_CODE:
521 enable = cmd.argument() == "note";
523 case Inset::BRANCH_CODE:
524 enable = cmd.argument() == "branch";
526 case Inset::BOX_CODE:
527 enable = cmd.argument() == "box";
529 case Inset::LISTINGS_CODE:
530 enable = cmd.argument() == "listings";
538 case LFUN_INSET_APPLY: {
539 string const name = cmd.getArg(0);
540 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
542 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
544 if (!inset->getStatus(cur, fr, fs)) {
545 // Every inset is supposed to handle this
550 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
551 flag |= getStatus(fr);
553 enable = flag.enabled();
557 case LFUN_DIALOG_TOGGLE:
558 flag.setOnOff(lyx_view_->getDialogs().visible(cmd.getArg(0)));
559 // fall through to set "enable"
560 case LFUN_DIALOG_SHOW: {
561 string const name = cmd.getArg(0);
563 enable = name == "aboutlyx"
564 || name == "file" //FIXME: should be removed.
566 || name == "texinfo";
567 else if (name == "print")
568 enable = Exporter::isExportable(*buf, "dvi")
569 && lyxrc.print_command != "none";
570 else if (name == "character")
571 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
572 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
573 else if (name == "latexlog")
574 enable = isFileReadable(FileName(buf->getLogName().second));
575 else if (name == "spellchecker")
576 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
577 enable = !buf->isReadonly();
581 else if (name == "vclog")
582 enable = buf->lyxvc().inUse();
586 case LFUN_DIALOG_SHOW_NEW_INSET:
587 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
588 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
589 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
591 if (cur.inset().getStatus(cur, cmd, flag))
596 case LFUN_DIALOG_UPDATE: {
597 string const name = cmd.getArg(0);
599 enable = name == "prefs";
603 case LFUN_CITATION_INSERT: {
604 FuncRequest fr(LFUN_INSET_INSERT, "citation");
605 enable = getStatus(fr).enabled();
609 case LFUN_BUFFER_WRITE: {
610 enable = view()->buffer()->isUnnamed()
611 || !view()->buffer()->isClean();
615 case LFUN_BOOKMARK_GOTO: {
616 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
617 enable = LyX::ref().session().bookmarks().isValid(num);
621 case LFUN_BOOKMARK_CLEAR:
622 enable = LyX::ref().session().bookmarks().size() > 0;
625 case LFUN_TOOLBAR_TOGGLE: {
626 bool const current = lyx_view_->getToolbars().visible(cmd.getArg(0));
627 flag.setOnOff(current);
630 case LFUN_WINDOW_CLOSE: {
631 enable = (theApp()->gui().viewIds().size() > 1);
635 // this one is difficult to get right. As a half-baked
636 // solution, we consider only the first action of the sequence
637 case LFUN_COMMAND_SEQUENCE: {
638 // argument contains ';'-terminated commands
639 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
640 FuncRequest func(lyxaction.lookupFunc(firstcmd));
641 func.origin = cmd.origin;
642 flag = getStatus(func);
645 case LFUN_BUFFER_NEW:
646 case LFUN_BUFFER_NEW_TEMPLATE:
647 case LFUN_WORD_FIND_FORWARD:
648 case LFUN_WORD_FIND_BACKWARD:
649 case LFUN_COMMAND_PREFIX:
650 case LFUN_COMMAND_EXECUTE:
652 case LFUN_META_PREFIX:
653 case LFUN_BUFFER_CLOSE:
654 case LFUN_BUFFER_WRITE_AS:
655 case LFUN_BUFFER_UPDATE:
656 case LFUN_BUFFER_VIEW:
657 case LFUN_BUFFER_IMPORT:
658 case LFUN_BUFFER_AUTO_SAVE:
659 case LFUN_RECONFIGURE:
663 case LFUN_DROP_LAYOUTS_CHOICE:
665 case LFUN_SERVER_GET_NAME:
666 case LFUN_SERVER_NOTIFY:
667 case LFUN_SERVER_GOTO_FILE_ROW:
668 case LFUN_DIALOG_HIDE:
669 case LFUN_DIALOG_DISCONNECT_INSET:
670 case LFUN_BUFFER_CHILD_OPEN:
671 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
672 case LFUN_KEYMAP_OFF:
673 case LFUN_KEYMAP_PRIMARY:
674 case LFUN_KEYMAP_SECONDARY:
675 case LFUN_KEYMAP_TOGGLE:
677 case LFUN_BUFFER_EXPORT_CUSTOM:
678 case LFUN_BUFFER_PRINT:
679 case LFUN_PREFERENCES_SAVE:
680 case LFUN_SCREEN_FONT_UPDATE:
683 case LFUN_EXTERNAL_EDIT:
684 case LFUN_GRAPHICS_EDIT:
685 case LFUN_ALL_INSETS_TOGGLE:
686 case LFUN_BUFFER_LANGUAGE:
687 case LFUN_TEXTCLASS_APPLY:
688 case LFUN_TEXTCLASS_LOAD:
689 case LFUN_BUFFER_SAVE_AS_DEFAULT:
690 case LFUN_BUFFER_PARAMS_APPLY:
691 case LFUN_LYXRC_APPLY:
692 case LFUN_BUFFER_NEXT:
693 case LFUN_BUFFER_PREVIOUS:
694 case LFUN_WINDOW_NEW:
696 // these are handled in our dispatch()
700 if (!getLocalStatus(cur, cmd, flag))
701 flag = view()->getStatus(cmd);
707 // Can we use a readonly buffer?
708 if (buf && buf->isReadonly()
709 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
710 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
711 flag.message(from_utf8(N_("Document is read-only")));
715 // Are we in a DELETED change-tracking region?
716 if (buf && lookupChangeType(cur, true) == Change::DELETED
717 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
718 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
719 flag.message(from_utf8(N_("This portion of the document is deleted.")));
723 // the default error message if we disable the command
724 if (!flag.enabled() && flag.message().empty())
725 flag.message(from_utf8(N_("Command disabled")));
731 bool LyXFunc::ensureBufferClean(BufferView * bv)
733 Buffer & buf = *bv->buffer();
737 docstring const file = makeDisplayPath(buf.fileName(), 30);
738 docstring text = bformat(_("The document %1$s has unsaved "
739 "changes.\n\nDo you want to save "
740 "the document?"), file);
741 int const ret = Alert::prompt(_("Save changed document?"),
742 text, 0, 1, _("&Save"),
746 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
748 return buf.isClean();
754 void showPrintError(string const & name)
756 docstring str = bformat(_("Could not print the document %1$s.\n"
757 "Check that your printer is set up correctly."),
758 makeDisplayPath(name, 50));
759 Alert::error(_("Print document failed"), str);
763 void loadTextclass(string const & name)
765 std::pair<bool, textclass_type> const tc_pair =
766 textclasslist.numberOfClass(name);
768 if (!tc_pair.first) {
769 lyxerr << "Document class \"" << name
770 << "\" does not exist."
775 textclass_type const tc = tc_pair.second;
777 if (!textclasslist[tc].load()) {
778 docstring s = bformat(_("The document could not be converted\n"
779 "into the document class %1$s."),
780 from_utf8(textclasslist[tc].name()));
781 Alert::error(_("Could not change class"), s);
786 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
791 void LyXFunc::dispatch(FuncRequest const & cmd)
793 string const argument = to_utf8(cmd.argument());
794 kb_action const action = cmd.action;
796 LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
797 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
799 // we have not done anything wrong yet.
801 dispatch_buffer.erase();
803 // redraw the screen at the end (first of the two drawing steps).
804 //This is done unless explicitely requested otherwise
805 Update::flags updateFlags = Update::FitCursor;
807 FuncStatus const flag = getStatus(cmd);
808 if (!flag.enabled()) {
809 // We cannot use this function here
810 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
811 << lyxaction.getActionName(action)
812 << " [" << action << "] is disabled at this location"
814 setErrorMessage(flag.message());
818 case LFUN_WORD_FIND_FORWARD:
819 case LFUN_WORD_FIND_BACKWARD: {
820 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
821 static docstring last_search;
822 docstring searched_string;
824 if (!cmd.argument().empty()) {
825 last_search = cmd.argument();
826 searched_string = cmd.argument();
828 searched_string = last_search;
831 if (searched_string.empty())
834 bool const fw = action == LFUN_WORD_FIND_FORWARD;
835 docstring const data =
836 find2string(searched_string, true, false, fw);
837 find(view(), FuncRequest(LFUN_WORD_FIND, data));
841 case LFUN_COMMAND_PREFIX:
842 BOOST_ASSERT(lyx_view_);
843 lyx_view_->message(keyseq->printOptions(true));
846 case LFUN_COMMAND_EXECUTE:
847 BOOST_ASSERT(lyx_view_);
848 lyx_view_->getToolbars().display("minibuffer", true);
849 lyx_view_->focus_command_buffer();
853 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
855 meta_fake_bit = key_modifier::none;
856 if (view()->buffer())
857 // cancel any selection
858 dispatch(FuncRequest(LFUN_MARK_OFF));
859 setMessage(from_ascii(N_("Cancel")));
862 case LFUN_META_PREFIX:
863 meta_fake_bit = key_modifier::alt;
864 setMessage(keyseq->print(true));
867 case LFUN_BUFFER_TOGGLE_READ_ONLY:
868 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
869 if (lyx_view_->buffer()->lyxvc().inUse())
870 lyx_view_->buffer()->lyxvc().toggleReadOnly();
872 lyx_view_->buffer()->setReadonly(
873 !lyx_view_->buffer()->isReadonly());
876 // --- Menus -----------------------------------------------
877 case LFUN_BUFFER_NEW:
878 menuNew(argument, false);
881 case LFUN_BUFFER_NEW_TEMPLATE:
882 menuNew(argument, true);
885 case LFUN_BUFFER_CLOSE:
890 case LFUN_BUFFER_WRITE:
891 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
892 if (!lyx_view_->buffer()->isUnnamed()) {
893 docstring const str = bformat(_("Saving document %1$s..."),
894 makeDisplayPath(lyx_view_->buffer()->fileName()));
895 lyx_view_->message(str);
896 menuWrite(lyx_view_->buffer());
897 lyx_view_->message(str + _(" done."));
899 writeAs(lyx_view_->buffer());
901 updateFlags = Update::None;
904 case LFUN_BUFFER_WRITE_AS:
905 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
906 writeAs(lyx_view_->buffer(), argument);
907 updateFlags = Update::None;
910 case LFUN_BUFFER_RELOAD: {
911 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
912 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
913 docstring text = bformat(_("Any changes will be lost. Are you sure "
914 "you want to revert to the saved version of the document %1$s?"), file);
915 int const ret = Alert::prompt(_("Revert to saved document?"),
916 text, 1, 1, _("&Revert"), _("&Cancel"));
923 case LFUN_BUFFER_UPDATE:
924 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
925 Exporter::Export(lyx_view_->buffer(), argument, true);
928 case LFUN_BUFFER_VIEW:
929 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
930 Exporter::preview(lyx_view_->buffer(), argument);
933 case LFUN_BUILD_PROGRAM:
934 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
935 Exporter::Export(lyx_view_->buffer(), "program", true);
938 case LFUN_BUFFER_CHKTEX:
939 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
940 lyx_view_->buffer()->runChktex();
943 case LFUN_BUFFER_EXPORT:
944 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
945 if (argument == "custom")
946 lyx_view_->getDialogs().show("sendto");
948 Exporter::Export(lyx_view_->buffer(), argument, false);
952 case LFUN_BUFFER_EXPORT_CUSTOM: {
953 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
955 string command = split(argument, format_name, ' ');
956 Format const * format = formats.getFormat(format_name);
958 lyxerr << "Format \"" << format_name
959 << "\" not recognized!"
964 Buffer * buffer = lyx_view_->buffer();
966 // The name of the file created by the conversion process
969 // Output to filename
970 if (format->name() == "lyx") {
971 string const latexname =
972 buffer->getLatexName(false);
973 filename = changeExtension(latexname,
974 format->extension());
975 filename = addName(buffer->temppath(), filename);
977 if (!buffer->writeFile(FileName(filename)))
981 Exporter::Export(buffer, format_name, true, filename);
984 // Substitute $$FName for filename
985 if (!contains(command, "$$FName"))
986 command = "( " + command + " ) < $$FName";
987 command = subst(command, "$$FName", filename);
989 // Execute the command in the background
991 call.startscript(Systemcall::DontWait, command);
995 case LFUN_BUFFER_PRINT: {
996 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
997 // FIXME: cmd.getArg() might fail if one of the arguments
998 // contains double quotes
999 string target = cmd.getArg(0);
1000 string target_name = cmd.getArg(1);
1001 string command = cmd.getArg(2);
1004 || target_name.empty()
1005 || command.empty()) {
1006 lyxerr << "Unable to parse \""
1007 << argument << '"' << endl;
1010 if (target != "printer" && target != "file") {
1011 lyxerr << "Unrecognized target \""
1012 << target << '"' << endl;
1016 Buffer * buffer = lyx_view_->buffer();
1018 if (!Exporter::Export(buffer, "dvi", true)) {
1019 showPrintError(buffer->fileName());
1023 // Push directory path.
1024 string const path(buffer->temppath());
1025 // Prevent the compiler from optimizing away p
1027 support::Path p(pp);
1029 // there are three cases here:
1030 // 1. we print to a file
1031 // 2. we print directly to a printer
1032 // 3. we print using a spool command (print to file first)
1035 string const dviname =
1036 changeExtension(buffer->getLatexName(true),
1039 if (target == "printer") {
1040 if (!lyxrc.print_spool_command.empty()) {
1041 // case 3: print using a spool
1042 string const psname =
1043 changeExtension(dviname,".ps");
1044 command += ' ' + lyxrc.print_to_file
1047 + quoteName(dviname);
1050 lyxrc.print_spool_command + ' ';
1051 if (target_name != "default") {
1052 command2 += lyxrc.print_spool_printerprefix
1056 command2 += quoteName(psname);
1058 // If successful, then spool command
1059 res = one.startscript(
1064 res = one.startscript(
1065 Systemcall::DontWait,
1068 // case 2: print directly to a printer
1069 if (target_name != "default")
1070 command += ' ' + lyxrc.print_to_printer + target_name + ' ';
1071 res = one.startscript(
1072 Systemcall::DontWait,
1073 command + quoteName(dviname));
1077 // case 1: print to a file
1078 FileName const filename(makeAbsPath(target_name,
1079 lyx_view_->buffer()->filePath()));
1080 FileName const dvifile(makeAbsPath(dviname, path));
1081 if (fs::exists(filename.toFilesystemEncoding())) {
1082 docstring text = bformat(
1083 _("The file %1$s already exists.\n\n"
1084 "Do you want to overwrite that file?"),
1085 makeDisplayPath(filename.absFilename()));
1086 if (Alert::prompt(_("Overwrite file?"),
1087 text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0)
1090 command += ' ' + lyxrc.print_to_file
1091 + quoteName(filename.toFilesystemEncoding())
1093 + quoteName(dvifile.toFilesystemEncoding());
1094 res = one.startscript(Systemcall::DontWait,
1099 showPrintError(buffer->fileName());
1103 case LFUN_BUFFER_IMPORT:
1108 // quitting is triggered by the gui code
1109 // (leaving the event loop).
1110 lyx_view_->message(from_utf8(N_("Exiting.")));
1111 if (theBufferList().quitWriteAll())
1112 theApp()->gui().closeAllViews();
1115 case LFUN_BUFFER_AUTO_SAVE:
1119 case LFUN_RECONFIGURE:
1120 BOOST_ASSERT(lyx_view_);
1121 reconfigure(*lyx_view_);
1124 case LFUN_HELP_OPEN: {
1125 BOOST_ASSERT(lyx_view_);
1126 string const arg = argument;
1128 setErrorMessage(from_ascii(N_("Missing argument")));
1131 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1132 if (fname.empty()) {
1133 lyxerr << "LyX: unable to find documentation file `"
1134 << arg << "'. Bad installation?" << endl;
1137 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1138 makeDisplayPath(fname.absFilename())));
1139 lyx_view_->loadLyXFile(fname, false);
1143 // --- version control -------------------------------
1144 case LFUN_VC_REGISTER:
1145 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1146 if (!ensureBufferClean(view()))
1148 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1149 lyx_view_->buffer()->lyxvc().registrer();
1152 updateFlags = Update::Force;
1155 case LFUN_VC_CHECK_IN:
1156 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1157 if (!ensureBufferClean(view()))
1159 if (lyx_view_->buffer()->lyxvc().inUse()
1160 && !lyx_view_->buffer()->isReadonly()) {
1161 lyx_view_->buffer()->lyxvc().checkIn();
1166 case LFUN_VC_CHECK_OUT:
1167 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1168 if (!ensureBufferClean(view()))
1170 if (lyx_view_->buffer()->lyxvc().inUse()
1171 && lyx_view_->buffer()->isReadonly()) {
1172 lyx_view_->buffer()->lyxvc().checkOut();
1177 case LFUN_VC_REVERT:
1178 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1179 lyx_view_->buffer()->lyxvc().revert();
1183 case LFUN_VC_UNDO_LAST:
1184 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1185 lyx_view_->buffer()->lyxvc().undoLast();
1189 // --- buffers ----------------------------------------
1190 case LFUN_BUFFER_SWITCH:
1191 BOOST_ASSERT(lyx_view_);
1192 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1193 updateFlags = Update::Force;
1196 case LFUN_BUFFER_NEXT:
1197 BOOST_ASSERT(lyx_view_);
1198 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1199 updateFlags = Update::Force;
1202 case LFUN_BUFFER_PREVIOUS:
1203 BOOST_ASSERT(lyx_view_);
1204 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1205 updateFlags = Update::Force;
1209 BOOST_ASSERT(lyx_view_);
1210 newFile(view(), argument);
1213 case LFUN_FILE_OPEN:
1214 BOOST_ASSERT(lyx_view_);
1218 case LFUN_DROP_LAYOUTS_CHOICE:
1219 BOOST_ASSERT(lyx_view_);
1220 lyx_view_->getToolbars().openLayoutList();
1223 case LFUN_MENU_OPEN:
1224 BOOST_ASSERT(lyx_view_);
1225 lyx_view_->getMenubar().openByName(from_utf8(argument));
1228 // --- lyxserver commands ----------------------------
1229 case LFUN_SERVER_GET_NAME:
1230 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1231 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1232 LYXERR(Debug::INFO) << "FNAME["
1233 << lyx_view_->buffer()->fileName()
1237 case LFUN_SERVER_NOTIFY:
1238 dispatch_buffer = keyseq->print(false);
1239 theServer().notifyClient(to_utf8(dispatch_buffer));
1242 case LFUN_SERVER_GOTO_FILE_ROW: {
1243 BOOST_ASSERT(lyx_view_);
1246 istringstream is(argument);
1247 is >> file_name >> row;
1248 if (prefixIs(file_name, package().temp_dir().absFilename())) {
1249 // Needed by inverse dvi search. If it is a file
1250 // in tmpdir, call the apropriated function
1251 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1253 // Must replace extension of the file to be .lyx
1254 // and get full path
1255 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1256 // Either change buffer or load the file
1257 if (theBufferList().exists(s.absFilename())) {
1258 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1260 lyx_view_->loadLyXFile(s);
1264 view()->setCursorFromRow(row);
1266 updateFlags = Update::FitCursor;
1270 case LFUN_DIALOG_SHOW: {
1271 BOOST_ASSERT(lyx_view_);
1272 string const name = cmd.getArg(0);
1273 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1275 if (name == "character") {
1276 data = freefont2string();
1278 lyx_view_->getDialogs().show("character", data);
1279 } else if (name == "latexlog") {
1280 pair<Buffer::LogType, string> const logfile =
1281 lyx_view_->buffer()->getLogName();
1282 switch (logfile.first) {
1283 case Buffer::latexlog:
1286 case Buffer::buildlog:
1290 data += Lexer::quoteString(logfile.second);
1291 lyx_view_->getDialogs().show("log", data);
1292 } else if (name == "vclog") {
1293 string const data = "vc " +
1294 Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1295 lyx_view_->getDialogs().show("log", data);
1297 lyx_view_->getDialogs().show(name, data);
1301 case LFUN_DIALOG_SHOW_NEW_INSET: {
1302 BOOST_ASSERT(lyx_view_);
1303 string const name = cmd.getArg(0);
1304 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1305 if (name == "bibitem" ||
1309 name == "nomenclature" ||
1313 InsetCommandParams p(name);
1314 data = InsetCommandMailer::params2string(name, p);
1315 } else if (name == "include") {
1316 // data is the include type: one of "include",
1317 // "input", "verbatiminput" or "verbatiminput*"
1319 // default type is requested
1321 InsetCommandParams p(data);
1322 data = InsetIncludeMailer::params2string(p);
1323 } else if (name == "box") {
1324 // \c data == "Boxed" || "Frameless" etc
1325 InsetBoxParams p(data);
1326 data = InsetBoxMailer::params2string(p);
1327 } else if (name == "branch") {
1328 InsetBranchParams p;
1329 data = InsetBranchMailer::params2string(p);
1330 } else if (name == "citation") {
1331 InsetCommandParams p("cite");
1332 data = InsetCommandMailer::params2string(name, p);
1333 } else if (name == "ert") {
1334 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1335 } else if (name == "external") {
1336 InsetExternalParams p;
1337 Buffer const & buffer = *lyx_view_->buffer();
1338 data = InsetExternalMailer::params2string(p, buffer);
1339 } else if (name == "float") {
1341 data = InsetFloatMailer::params2string(p);
1342 } else if (name == "listings") {
1343 InsetListingsParams p;
1344 data = InsetListingsMailer::params2string(p);
1345 } else if (name == "graphics") {
1346 InsetGraphicsParams p;
1347 Buffer const & buffer = *lyx_view_->buffer();
1348 data = InsetGraphicsMailer::params2string(p, buffer);
1349 } else if (name == "note") {
1351 data = InsetNoteMailer::params2string(p);
1352 } else if (name == "vspace") {
1354 data = InsetVSpaceMailer::params2string(space);
1355 } else if (name == "wrap") {
1357 data = InsetWrapMailer::params2string(p);
1359 lyx_view_->getDialogs().show(name, data, 0);
1363 case LFUN_DIALOG_UPDATE: {
1364 BOOST_ASSERT(lyx_view_);
1365 string const & name = argument;
1366 // Can only update a dialog connected to an existing inset
1367 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1369 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1370 inset->dispatch(view()->cursor(), fr);
1371 } else if (name == "paragraph") {
1372 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1373 } else if (name == "prefs") {
1374 lyx_view_->getDialogs().update(name, string());
1379 case LFUN_DIALOG_HIDE:
1380 LyX::cref().hideDialogs(argument, 0);
1383 case LFUN_DIALOG_TOGGLE: {
1384 BOOST_ASSERT(lyx_view_);
1385 if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1386 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1388 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1392 case LFUN_DIALOG_DISCONNECT_INSET:
1393 BOOST_ASSERT(lyx_view_);
1394 lyx_view_->getDialogs().disconnect(argument);
1398 case LFUN_CITATION_INSERT: {
1399 BOOST_ASSERT(lyx_view_);
1400 if (!argument.empty()) {
1401 // we can have one optional argument, delimited by '|'
1402 // citation-insert <key>|<text_before>
1403 // this should be enhanced to also support text_after
1404 // and citation style
1405 string arg = argument;
1407 if (contains(argument, "|")) {
1408 arg = token(argument, '|', 0);
1409 opt1 = token(argument, '|', 1);
1411 InsetCommandParams icp("cite");
1412 icp["key"] = from_utf8(arg);
1414 icp["before"] = from_utf8(opt1);
1415 string icstr = InsetCommandMailer::params2string("citation", icp);
1416 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1419 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1423 case LFUN_BUFFER_CHILD_OPEN: {
1424 // takes an optional argument, "|bool", at the end
1425 // indicating whether this file is being opened automatically
1426 // by LyX itself, in which case we will not want to switch
1427 // buffers after opening. The default is false, so in practice
1428 // it is used only when true.
1429 BOOST_ASSERT(lyx_view_);
1430 int const arglength = argument.length();
1432 bool autoOpen = false;
1433 if (argument.substr(arglength - 5, 5) == "|true") {
1435 filename = makeAbsPath(argument.substr(0, arglength - 5),
1436 lyx_view_->buffer()->filePath());
1437 } else if (argument.substr(arglength - 6, 6) == "|false") {
1438 filename = makeAbsPath(argument.substr(0, arglength - 6),
1439 lyx_view_->buffer()->filePath());
1441 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1442 view()->saveBookmark(false);
1443 if (theBufferList().exists(filename.absFilename())) {
1444 Buffer * buf = theBufferList().getBuffer(filename.absFilename());
1446 lyx_view_->setBuffer(buf, true);
1448 buf->setParentName(lyx_view_->buffer()->fileName());
1450 lyx_view_->loadLyXFile(filename, true, true, autoOpen);
1452 // If a screen update is required (in case where auto_open is false),
1453 // loadLyXFile() would have taken care of it already. Otherwise we shall
1454 // reset the update flag because it can cause a circular problem.
1456 updateFlags = Update::None;
1460 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1461 BOOST_ASSERT(lyx_view_);
1462 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1465 case LFUN_KEYMAP_OFF:
1466 BOOST_ASSERT(lyx_view_);
1467 lyx_view_->view()->getIntl().keyMapOn(false);
1470 case LFUN_KEYMAP_PRIMARY:
1471 BOOST_ASSERT(lyx_view_);
1472 lyx_view_->view()->getIntl().keyMapPrim();
1475 case LFUN_KEYMAP_SECONDARY:
1476 BOOST_ASSERT(lyx_view_);
1477 lyx_view_->view()->getIntl().keyMapSec();
1480 case LFUN_KEYMAP_TOGGLE:
1481 BOOST_ASSERT(lyx_view_);
1482 lyx_view_->view()->getIntl().toggleKeyMap();
1488 string rest = split(argument, countstr, ' ');
1489 istringstream is(countstr);
1492 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1493 for (int i = 0; i < count; ++i)
1494 dispatch(lyxaction.lookupFunc(rest));
1498 case LFUN_COMMAND_SEQUENCE: {
1499 // argument contains ';'-terminated commands
1500 string arg = argument;
1501 while (!arg.empty()) {
1503 arg = split(arg, first, ';');
1504 FuncRequest func(lyxaction.lookupFunc(first));
1505 func.origin = cmd.origin;
1511 case LFUN_PREFERENCES_SAVE: {
1512 lyxrc.write(makeAbsPath("preferences",
1513 package().user_support().absFilename()),
1518 case LFUN_SCREEN_FONT_UPDATE:
1519 BOOST_ASSERT(lyx_view_);
1520 // handle the screen font changes.
1521 theFontLoader().update();
1522 /// FIXME: only the current view will be updated. the Gui
1523 /// class is able to furnish the list of views.
1524 updateFlags = Update::Force;
1527 case LFUN_SET_COLOR: {
1529 string const x11_name = split(argument, lyx_name, ' ');
1530 if (lyx_name.empty() || x11_name.empty()) {
1531 setErrorMessage(from_ascii(N_(
1532 "Syntax: set-color <lyx_name>"
1537 bool const graphicsbg_changed =
1538 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1539 x11_name != lcolor.getX11Name(Color::graphicsbg));
1541 if (!lcolor.setColor(lyx_name, x11_name)) {
1543 bformat(_("Set-color \"%1$s\" failed "
1544 "- color is undefined or "
1545 "may not be redefined"),
1546 from_utf8(lyx_name)));
1550 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1552 if (graphicsbg_changed) {
1553 #ifdef WITH_WARNINGS
1554 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1557 graphics::GCache::get().changeDisplay(true);
1564 BOOST_ASSERT(lyx_view_);
1565 lyx_view_->message(from_utf8(argument));
1568 case LFUN_EXTERNAL_EDIT: {
1569 BOOST_ASSERT(lyx_view_);
1570 FuncRequest fr(action, argument);
1571 InsetExternal().dispatch(view()->cursor(), fr);
1575 case LFUN_GRAPHICS_EDIT: {
1576 FuncRequest fr(action, argument);
1577 InsetGraphics().dispatch(view()->cursor(), fr);
1581 case LFUN_INSET_APPLY: {
1582 BOOST_ASSERT(lyx_view_);
1583 string const name = cmd.getArg(0);
1584 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1586 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1587 inset->dispatch(view()->cursor(), fr);
1589 FuncRequest fr(LFUN_INSET_INSERT, argument);
1592 // ideally, the update flag should be set by the insets,
1593 // but this is not possible currently
1594 updateFlags = Update::Force | Update::FitCursor;
1598 case LFUN_ALL_INSETS_TOGGLE: {
1599 BOOST_ASSERT(lyx_view_);
1601 string const name = split(argument, action, ' ');
1602 Inset::Code const inset_code =
1603 Inset::translate(name);
1605 Cursor & cur = view()->cursor();
1606 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1608 Inset & inset = lyx_view_->buffer()->inset();
1609 InsetIterator it = inset_iterator_begin(inset);
1610 InsetIterator const end = inset_iterator_end(inset);
1611 for (; it != end; ++it) {
1612 if (!it->asInsetMath()
1613 && (inset_code == Inset::NO_CODE
1614 || inset_code == it->lyxCode())) {
1615 Cursor tmpcur = cur;
1616 tmpcur.pushLeft(*it);
1617 it->dispatch(tmpcur, fr);
1620 updateFlags = Update::Force | Update::FitCursor;
1624 case LFUN_BUFFER_LANGUAGE: {
1625 BOOST_ASSERT(lyx_view_);
1626 Buffer & buffer = *lyx_view_->buffer();
1627 Language const * oldL = buffer.params().language;
1628 Language const * newL = languages.getLanguage(argument);
1629 if (!newL || oldL == newL)
1632 if (oldL->rightToLeft() == newL->rightToLeft()
1633 && !buffer.isMultiLingual())
1634 buffer.changeLanguage(oldL, newL);
1638 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1639 string const fname =
1640 addName(addPath(package().user_support().absFilename(), "templates/"),
1642 Buffer defaults(fname);
1644 istringstream ss(argument);
1647 int const unknown_tokens = defaults.readHeader(lex);
1649 if (unknown_tokens != 0) {
1650 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1651 << unknown_tokens << " unknown token"
1652 << (unknown_tokens == 1 ? "" : "s")
1656 if (defaults.writeFile(FileName(defaults.fileName())))
1657 setMessage(bformat(_("Document defaults saved in %1$s"),
1658 makeDisplayPath(fname)));
1660 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1664 case LFUN_BUFFER_PARAMS_APPLY: {
1665 BOOST_ASSERT(lyx_view_);
1666 biblio::CiteEngine const engine =
1667 lyx_view_->buffer()->params().getEngine();
1669 istringstream ss(argument);
1672 int const unknown_tokens =
1673 lyx_view_->buffer()->readHeader(lex);
1675 if (unknown_tokens != 0) {
1676 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1677 << unknown_tokens << " unknown token"
1678 << (unknown_tokens == 1 ? "" : "s")
1681 if (engine == lyx_view_->buffer()->params().getEngine())
1684 Cursor & cur = view()->cursor();
1685 FuncRequest fr(LFUN_INSET_REFRESH);
1687 Inset & inset = lyx_view_->buffer()->inset();
1688 InsetIterator it = inset_iterator_begin(inset);
1689 InsetIterator const end = inset_iterator_end(inset);
1690 for (; it != end; ++it)
1691 if (it->lyxCode() == Inset::CITE_CODE)
1692 it->dispatch(cur, fr);
1696 case LFUN_TEXTCLASS_APPLY: {
1697 BOOST_ASSERT(lyx_view_);
1698 Buffer * buffer = lyx_view_->buffer();
1700 textclass_type const old_class =
1701 buffer->params().textclass;
1703 loadTextclass(argument);
1705 std::pair<bool, textclass_type> const tc_pair =
1706 textclasslist.numberOfClass(argument);
1711 textclass_type const new_class = tc_pair.second;
1712 if (old_class == new_class)
1716 lyx_view_->message(_("Converting document to new document class..."));
1717 recordUndoFullDocument(view());
1718 buffer->params().textclass = new_class;
1719 StableDocIterator backcur(view()->cursor());
1720 ErrorList & el = buffer->errorList("Class Switch");
1721 cap::switchBetweenClasses(
1722 old_class, new_class,
1723 static_cast<InsetText &>(buffer->inset()), el);
1725 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1727 buffer->errors("Class Switch");
1728 updateLabels(*buffer);
1729 updateFlags = Update::Force | Update::FitCursor;
1733 case LFUN_TEXTCLASS_LOAD:
1734 loadTextclass(argument);
1737 case LFUN_LYXRC_APPLY: {
1738 LyXRC const lyxrc_orig = lyxrc;
1740 istringstream ss(argument);
1741 bool const success = lyxrc.read(ss) == 0;
1744 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1745 << "Unable to read lyxrc data"
1750 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1752 /// We force the redraw in any case because there might be
1753 /// some screen font changes.
1754 /// FIXME: only the current view will be updated. the Gui
1755 /// class is able to furnish the list of views.
1756 updateFlags = Update::Force;
1760 case LFUN_WINDOW_NEW:
1761 LyX::ref().newLyXView();
1764 case LFUN_WINDOW_CLOSE:
1765 BOOST_ASSERT(lyx_view_);
1766 BOOST_ASSERT(theApp());
1767 // update bookmark pit of the current buffer before window close
1768 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1769 gotoBookmark(i+1, false, false);
1770 // ask the user for saving changes or cancel quit
1771 if (!theBufferList().quitWriteAll())
1776 case LFUN_BOOKMARK_GOTO:
1777 // go to bookmark, open unopened file and switch to buffer if necessary
1778 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1781 case LFUN_BOOKMARK_CLEAR:
1782 LyX::ref().session().bookmarks().clear();
1785 case LFUN_TOOLBAR_TOGGLE: {
1786 BOOST_ASSERT(lyx_view_);
1787 string const name = cmd.getArg(0);
1788 bool const allowauto = cmd.getArg(1) == "allowauto";
1789 lyx_view_->toggleToolbarState(name, allowauto);
1790 ToolbarInfo * tbi = lyx_view_->getToolbarInfo(name);
1792 setMessage(bformat(_("Unknown toolbar \"%1$s\""),
1797 if (tbi->flags & ToolbarInfo::ON)
1799 else if (tbi->flags & ToolbarInfo::OFF)
1801 else if (tbi->flags & ToolbarInfo::AUTO)
1804 setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"),
1805 _(tbi->gui_name), state));
1810 BOOST_ASSERT(lyx_view_);
1811 view()->cursor().dispatch(cmd);
1812 updateFlags = view()->cursor().result().update();
1813 if (!view()->cursor().result().dispatched())
1814 updateFlags = view()->dispatch(cmd);
1819 if (lyx_view_ && view()->buffer()) {
1820 // BufferView::update() updates the ViewMetricsInfo and
1821 // also initializes the position cache for all insets in
1822 // (at least partially) visible top-level paragraphs.
1823 // We will redraw the screen only if needed.
1824 if (view()->update(updateFlags)) {
1825 // Buffer::changed() signals that a repaint is needed.
1826 // The frontend (WorkArea) knows which area to repaint
1827 // thanks to the ViewMetricsInfo updated above.
1828 view()->buffer()->changed();
1831 lyx_view_->updateStatusBar();
1833 // if we executed a mutating lfun, mark the buffer as dirty
1835 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1836 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1837 view()->buffer()->markDirty();
1839 //Do we have a selection?
1840 theSelection().haveSelection(view()->cursor().selection());
1842 if (view()->cursor().inTexted()) {
1843 lyx_view_->updateLayoutChoice();
1847 if (!quitting && lyx_view_) {
1848 lyx_view_->updateMenubar();
1849 lyx_view_->updateToolbars();
1850 // Some messages may already be translated, so we cannot use _()
1851 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1856 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1858 const bool verbose = (cmd.origin == FuncRequest::MENU
1859 || cmd.origin == FuncRequest::TOOLBAR
1860 || cmd.origin == FuncRequest::COMMANDBUFFER);
1862 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1863 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1865 lyx_view_->message(msg);
1869 docstring dispatch_msg = msg;
1870 if (!dispatch_msg.empty())
1871 dispatch_msg += ' ';
1873 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1875 bool argsadded = false;
1877 if (!cmd.argument().empty()) {
1878 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1879 comname += ' ' + cmd.argument();
1884 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1886 if (!shortcuts.empty())
1887 comname += ": " + shortcuts;
1888 else if (!argsadded && !cmd.argument().empty())
1889 comname += ' ' + cmd.argument();
1891 if (!comname.empty()) {
1892 comname = rtrim(comname);
1893 dispatch_msg += '(' + rtrim(comname) + ')';
1896 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1897 << to_utf8(dispatch_msg) << endl;
1898 if (!dispatch_msg.empty())
1899 lyx_view_->message(dispatch_msg);
1903 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1905 // FIXME: initpath is not used. What to do?
1906 string initpath = lyxrc.document_path;
1907 string filename(name);
1909 if (view()->buffer()) {
1910 string const trypath = lyx_view_->buffer()->filePath();
1911 // If directory is writeable, use this as default.
1912 if (isDirWriteable(FileName(trypath)))
1916 static int newfile_number;
1918 if (filename.empty()) {
1919 filename = addName(lyxrc.document_path,
1920 "newfile" + convert<string>(++newfile_number) + ".lyx");
1921 while (theBufferList().exists(filename) ||
1922 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1924 filename = addName(lyxrc.document_path,
1925 "newfile" + convert<string>(newfile_number) +
1930 // The template stuff
1933 FileDialog fileDlg(_("Select template file"),
1934 LFUN_SELECT_FILE_SYNC,
1935 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1936 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1938 FileDialog::Result result =
1939 fileDlg.open(from_utf8(lyxrc.template_path),
1940 FileFilterList(_("LyX Documents (*.lyx)")),
1943 if (result.first == FileDialog::Later)
1945 if (result.second.empty())
1947 templname = to_utf8(result.second);
1950 Buffer * const b = newFile(filename, templname, !name.empty());
1953 lyx_view_->setBuffer(b);
1958 void LyXFunc::open(string const & fname)
1960 string initpath = lyxrc.document_path;
1962 if (view()->buffer()) {
1963 string const trypath = lyx_view_->buffer()->filePath();
1964 // If directory is writeable, use this as default.
1965 if (isDirWriteable(FileName(trypath)))
1971 if (fname.empty()) {
1972 FileDialog fileDlg(_("Select document to open"),
1974 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1975 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1977 FileDialog::Result result =
1978 fileDlg.open(from_utf8(initpath),
1979 FileFilterList(_("LyX Documents (*.lyx)")),
1982 if (result.first == FileDialog::Later)
1985 filename = to_utf8(result.second);
1987 // check selected filename
1988 if (filename.empty()) {
1989 lyx_view_->message(_("Canceled."));
1995 // get absolute path of file and add ".lyx" to the filename if
1997 FileName const fullname = fileSearch(string(), filename, "lyx");
1998 if (!fullname.empty())
1999 filename = fullname.absFilename();
2001 // if the file doesn't exist, let the user create one
2002 if (!fs::exists(fullname.toFilesystemEncoding())) {
2003 // the user specifically chose this name. Believe him.
2004 Buffer * const b = newFile(filename, string(), true);
2006 lyx_view_->setBuffer(b);
2010 docstring const disp_fn = makeDisplayPath(filename);
2011 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
2014 if (lyx_view_->loadLyXFile(fullname)) {
2015 str2 = bformat(_("Document %1$s opened."), disp_fn);
2017 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2019 lyx_view_->message(str2);
2023 void LyXFunc::doImport(string const & argument)
2026 string filename = split(argument, format, ' ');
2028 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
2029 << " file: " << filename << endl;
2031 // need user interaction
2032 if (filename.empty()) {
2033 string initpath = lyxrc.document_path;
2035 if (view()->buffer()) {
2036 string const trypath = lyx_view_->buffer()->filePath();
2037 // If directory is writeable, use this as default.
2038 if (isDirWriteable(FileName(trypath)))
2042 docstring const text = bformat(_("Select %1$s file to import"),
2043 formats.prettyName(format));
2045 FileDialog fileDlg(text,
2047 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2048 make_pair(_("Examples|#E#e"),
2049 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2051 docstring filter = formats.prettyName(format);
2054 filter += from_utf8(formats.extension(format));
2057 FileDialog::Result result =
2058 fileDlg.open(from_utf8(initpath),
2059 FileFilterList(filter),
2062 if (result.first == FileDialog::Later)
2065 filename = to_utf8(result.second);
2067 // check selected filename
2068 if (filename.empty())
2069 lyx_view_->message(_("Canceled."));
2072 if (filename.empty())
2075 // get absolute path of file
2076 FileName const fullname(makeAbsPath(filename));
2078 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2080 // Check if the document already is open
2081 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2082 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2083 lyx_view_->message(_("Canceled."));
2088 // if the file exists already, and we didn't do
2089 // -i lyx thefile.lyx, warn
2090 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2091 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2093 docstring text = bformat(_("The document %1$s already exists.\n\n"
2094 "Do you want to overwrite that document?"), file);
2095 int const ret = Alert::prompt(_("Overwrite document?"),
2096 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2099 lyx_view_->message(_("Canceled."));
2104 ErrorList errorList;
2105 Importer::Import(lyx_view_, fullname, format, errorList);
2106 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2110 void LyXFunc::closeBuffer()
2112 // goto bookmark to update bookmark pit.
2113 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2114 gotoBookmark(i+1, false, false);
2116 theBufferList().close(lyx_view_->buffer(), true);
2120 void LyXFunc::reloadBuffer()
2122 FileName filename(lyx_view_->buffer()->fileName());
2124 lyx_view_->loadLyXFile(filename);
2127 // Each "lyx_view_" should have it's own message method. lyxview and
2128 // the minibuffer would use the minibuffer, but lyxserver would
2129 // send an ERROR signal to its client. Alejandro 970603
2130 // This function is bit problematic when it comes to NLS, to make the
2131 // lyx servers client be language indepenent we must not translate
2132 // strings sent to this func.
2133 void LyXFunc::setErrorMessage(docstring const & m) const
2135 dispatch_buffer = m;
2140 void LyXFunc::setMessage(docstring const & m) const
2142 dispatch_buffer = m;
2146 docstring const LyXFunc::viewStatusMessage()
2148 // When meta-fake key is pressed, show the key sequence so far + "M-".
2150 return keyseq->print(true) + "M-";
2152 // Else, when a non-complete key sequence is pressed,
2153 // show the available options.
2154 if (keyseq->length() > 0 && !keyseq->deleted())
2155 return keyseq->printOptions(true);
2157 if (!view()->buffer())
2158 return _("Welcome to LyX!");
2160 return view()->cursor().currentState();
2164 BufferView * LyXFunc::view() const
2166 BOOST_ASSERT(lyx_view_);
2167 return lyx_view_->view();
2171 bool LyXFunc::wasMetaKey() const
2173 return (meta_fake_bit != key_modifier::none);
2179 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2181 // Why the switch you might ask. It is a trick to ensure that all
2182 // the elements in the LyXRCTags enum is handled. As you can see
2183 // there are no breaks at all. So it is just a huge fall-through.
2184 // The nice thing is that we will get a warning from the compiler
2185 // if we forget an element.
2186 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2188 case LyXRC::RC_ACCEPT_COMPOUND:
2189 case LyXRC::RC_ALT_LANG:
2190 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2191 case LyXRC::RC_PLAINTEXT_LINELEN:
2192 case LyXRC::RC_AUTOREGIONDELETE:
2193 case LyXRC::RC_AUTORESET_OPTIONS:
2194 case LyXRC::RC_AUTOSAVE:
2195 case LyXRC::RC_AUTO_NUMBER:
2196 case LyXRC::RC_BACKUPDIR_PATH:
2197 case LyXRC::RC_BIBTEX_COMMAND:
2198 case LyXRC::RC_BINDFILE:
2199 case LyXRC::RC_CHECKLASTFILES:
2200 case LyXRC::RC_USELASTFILEPOS:
2201 case LyXRC::RC_LOADSESSION:
2202 case LyXRC::RC_CHKTEX_COMMAND:
2203 case LyXRC::RC_CONVERTER:
2204 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2205 case LyXRC::RC_COPIER:
2206 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2207 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2208 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2209 case LyXRC::RC_DATE_INSERT_FORMAT:
2210 case LyXRC::RC_DEFAULT_LANGUAGE:
2211 case LyXRC::RC_DEFAULT_PAPERSIZE:
2212 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2213 case LyXRC::RC_DISPLAY_GRAPHICS:
2214 case LyXRC::RC_DOCUMENTPATH:
2215 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2216 string const encoded = FileName(
2217 lyxrc_new.document_path).toFilesystemEncoding();
2218 if (fs::exists(encoded) && fs::is_directory(encoded))
2219 support::package().document_dir() = FileName(lyxrc.document_path);
2221 case LyXRC::RC_ESC_CHARS:
2222 case LyXRC::RC_FONT_ENCODING:
2223 case LyXRC::RC_FORMAT:
2224 case LyXRC::RC_INDEX_COMMAND:
2225 case LyXRC::RC_INPUT:
2226 case LyXRC::RC_KBMAP:
2227 case LyXRC::RC_KBMAP_PRIMARY:
2228 case LyXRC::RC_KBMAP_SECONDARY:
2229 case LyXRC::RC_LABEL_INIT_LENGTH:
2230 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2231 case LyXRC::RC_LANGUAGE_AUTO_END:
2232 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2233 case LyXRC::RC_LANGUAGE_COMMAND_END:
2234 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2235 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2236 case LyXRC::RC_LANGUAGE_PACKAGE:
2237 case LyXRC::RC_LANGUAGE_USE_BABEL:
2238 case LyXRC::RC_MAKE_BACKUP:
2239 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2240 case LyXRC::RC_NUMLASTFILES:
2241 case LyXRC::RC_PATH_PREFIX:
2242 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2243 support::prependEnvPath("PATH", lyxrc.path_prefix);
2245 case LyXRC::RC_PERS_DICT:
2246 case LyXRC::RC_PREVIEW:
2247 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2248 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2249 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2250 case LyXRC::RC_PRINTCOPIESFLAG:
2251 case LyXRC::RC_PRINTER:
2252 case LyXRC::RC_PRINTEVENPAGEFLAG:
2253 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2254 case LyXRC::RC_PRINTFILEEXTENSION:
2255 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2256 case LyXRC::RC_PRINTODDPAGEFLAG:
2257 case LyXRC::RC_PRINTPAGERANGEFLAG:
2258 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2259 case LyXRC::RC_PRINTPAPERFLAG:
2260 case LyXRC::RC_PRINTREVERSEFLAG:
2261 case LyXRC::RC_PRINTSPOOL_COMMAND:
2262 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2263 case LyXRC::RC_PRINTTOFILE:
2264 case LyXRC::RC_PRINTTOPRINTER:
2265 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2266 case LyXRC::RC_PRINT_COMMAND:
2267 case LyXRC::RC_RTL_SUPPORT:
2268 case LyXRC::RC_SCREEN_DPI:
2269 case LyXRC::RC_SCREEN_FONT_ROMAN:
2270 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2271 case LyXRC::RC_SCREEN_FONT_SANS:
2272 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2273 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2274 case LyXRC::RC_SCREEN_FONT_SIZES:
2275 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2276 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2277 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2278 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2279 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2280 case LyXRC::RC_SCREEN_ZOOM:
2281 case LyXRC::RC_SERVERPIPE:
2282 case LyXRC::RC_SET_COLOR:
2283 case LyXRC::RC_SHOW_BANNER:
2284 case LyXRC::RC_SPELL_COMMAND:
2285 case LyXRC::RC_TEMPDIRPATH:
2286 case LyXRC::RC_TEMPLATEPATH:
2287 case LyXRC::RC_TEX_ALLOWS_SPACES:
2288 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2289 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2290 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2292 case LyXRC::RC_UIFILE:
2293 case LyXRC::RC_USER_EMAIL:
2294 case LyXRC::RC_USER_NAME:
2295 case LyXRC::RC_USETEMPDIR:
2296 case LyXRC::RC_USE_ALT_LANG:
2297 case LyXRC::RC_USE_CONVERTER_CACHE:
2298 case LyXRC::RC_USE_ESC_CHARS:
2299 case LyXRC::RC_USE_INP_ENC:
2300 case LyXRC::RC_USE_PERS_DICT:
2301 case LyXRC::RC_USE_SPELL_LIB:
2302 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2303 case LyXRC::RC_VIEWER:
2304 case LyXRC::RC_LAST: