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.
24 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "BufferList.h"
28 #include "BufferParams.h"
29 #include "BufferView.h"
30 #include "bufferview_funcs.h"
32 #include "CutAndPaste.h"
34 #include "DispatchResult.h"
36 #include "ErrorList.h"
39 #include "FuncRequest.h"
40 #include "FuncStatus.h"
43 #include "InsetIterator.h"
51 #include "LyXAction.h"
57 #include "LyXTextClassList.h"
59 #include "Paragraph.h"
60 #include "ParIterator.h"
61 #include "ParagraphParameters.h"
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/InsetGraphics.h"
71 #include "insets/InsetInclude.h"
72 #include "insets/InsetNote.h"
73 #include "insets/InsetTabular.h"
74 #include "insets/InsetVSpace.h"
75 #include "insets/InsetWrap.h"
77 #include "frontends/Application.h"
78 #include "frontends/alert.h"
79 #include "frontends/Dialogs.h"
80 #include "frontends/FileDialog.h"
81 #include "frontends/FontLoader.h"
82 #include "frontends/Gui.h"
83 #include "frontends/KeySymbol.h"
84 #include "frontends/LyXView.h"
85 #include "frontends/Menubar.h"
86 #include "frontends/Toolbars.h"
88 #include "support/environment.h"
89 #include "support/FileFilterList.h"
90 #include "support/filetools.h"
91 #include "support/ForkedcallsController.h"
92 #include "support/fs_extras.h"
93 #include "support/lstrings.h"
94 #include "support/Path.h"
95 #include "support/Package.h"
96 #include "support/Systemcall.h"
97 #include "support/convert.h"
98 #include "support/os.h"
100 #include <boost/current_function.hpp>
101 #include <boost/filesystem/operations.hpp>
108 using bv_funcs::freefont2string;
110 using support::absolutePath;
111 using support::addName;
112 using support::addPath;
113 using support::bformat;
114 using support::changeExtension;
115 using support::contains;
116 using support::FileFilterList;
117 using support::FileName;
118 using support::fileSearch;
119 using support::ForkedcallsController;
120 using support::i18nLibFileSearch;
121 using support::isDirWriteable;
122 using support::isFileReadable;
123 using support::isStrInt;
124 using support::makeAbsPath;
125 using support::makeDisplayPath;
126 using support::package;
127 using support::quoteName;
128 using support::rtrim;
129 using support::split;
130 using support::subst;
131 using support::Systemcall;
132 using support::token;
134 using support::prefixIs;
137 using std::make_pair;
140 using std::istringstream;
141 using std::ostringstream;
143 namespace Alert = frontend::Alert;
144 namespace fs = boost::filesystem;
149 bool getLocalStatus(Cursor cursor,
150 FuncRequest const & cmd, FuncStatus & status)
152 // Try to fix cursor in case it is broken.
153 cursor.fixIfBroken();
155 // This is, of course, a mess. Better create a new doc iterator and use
156 // this in Inset::getStatus. This might require an additional
157 // BufferView * arg, though (which should be avoided)
158 //Cursor safe = *this;
160 for ( ; cursor.depth(); cursor.pop()) {
161 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
162 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
163 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
164 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
166 // The inset's getStatus() will return 'true' if it made
167 // a definitive decision on whether it want to handle the
168 // request or not. The result of this decision is put into
169 // the 'status' parameter.
170 if (cursor.inset().getStatus(cursor, cmd, status)) {
179 /** Return the change status at cursor position, taking in account the
180 * status at each level of the document iterator (a table in a deleted
181 * footnote is deleted).
182 * When \param outer is true, the top slice is not looked at.
184 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
186 size_t const depth = dit.depth() - (outer ? 1 : 0);
188 for (size_t i = 0 ; i < depth ; ++i) {
189 CursorSlice const & slice = dit[i];
190 if (!slice.inset().inMathed()
191 && slice.pos() < slice.paragraph().size()) {
192 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
193 if (ch != Change::UNCHANGED)
197 return Change::UNCHANGED;
205 meta_fake_bit(key_modifier::none)
210 void LyXFunc::initKeySequences(KeyMap * kb)
212 keyseq.reset(new KeySequence(kb, kb));
213 cancel_meta_seq.reset(new KeySequence(kb, kb));
217 void LyXFunc::setLyXView(LyXView * lv)
223 void LyXFunc::handleKeyFunc(kb_action action)
225 char_type c = encoded_last_key;
227 if (keyseq->length())
230 lyx_view_->view()->getIntl().getTransManager().deadkey(
231 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
232 // Need to clear, in case the minibuffer calls these
235 // copied verbatim from do_accent_char
236 view()->cursor().resetAnchor();
241 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
243 BOOST_ASSERT(lyx_view_);
244 if (!LyX::ref().session().bookmarks().isValid(idx))
246 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
247 BOOST_ASSERT(!bm.filename.empty());
248 string const file = bm.filename.absFilename();
249 // if the file is not opened, open it.
250 if (!theBufferList().exists(file)) {
252 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
256 // open may fail, so we need to test it again
257 if (theBufferList().exists(file)) {
258 // if the current buffer is not that one, switch to it.
259 if (lyx_view_->buffer()->fileName() != file) {
261 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
265 // moveToPosition use par_id, and par_pit and return new par_id.
269 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
270 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
271 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
272 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
273 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
278 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
280 LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
282 // Do nothing if we have nothing (JMarc)
283 if (!keysym->isOK()) {
284 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
289 if (keysym->isModifier()) {
290 LYXERR(Debug::KEY) << "isModifier true" << endl;
294 //Encoding const * encoding = view()->cursor().getEncoding();
295 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
296 // FIXME: encoded_last_key shadows the member variable of the same
297 // name. Is that intended?
298 char_type encoded_last_key = keysym->getUCSEncoded();
300 // Do a one-deep top-level lookup for
301 // cancel and meta-fake keys. RVDK_PATCH_5
302 cancel_meta_seq->reset();
304 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
305 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
306 << " action first set to [" << func.action << ']'
309 // When not cancel or meta-fake, do the normal lookup.
310 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
311 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
312 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
313 // remove Caps Lock and Mod2 as a modifiers
314 func = keyseq->addkey(keysym, (state | meta_fake_bit));
315 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
316 << "action now set to ["
317 << func.action << ']' << endl;
320 // Dont remove this unless you know what you are doing.
321 meta_fake_bit = key_modifier::none;
323 // Can this happen now ?
324 if (func.action == LFUN_NOACTION) {
325 func = FuncRequest(LFUN_COMMAND_PREFIX);
328 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
330 << func.action << "]["
331 << to_utf8(keyseq->print(false)) << ']'
334 // already here we know if it any point in going further
335 // why not return already here if action == -1 and
336 // num_bytes == 0? (Lgb)
338 if (keyseq->length() > 1) {
339 lyx_view_->message(keyseq->print(true));
343 // Maybe user can only reach the key via holding down shift.
344 // Let's see. But only if shift is the only modifier
345 if (func.action == LFUN_UNKNOWN_ACTION &&
346 state == key_modifier::shift) {
347 LYXERR(Debug::KEY) << "Trying without shift" << endl;
348 func = keyseq->addkey(keysym, key_modifier::none);
349 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
352 if (func.action == LFUN_UNKNOWN_ACTION) {
353 // Hmm, we didn't match any of the keysequences. See
354 // if it's normal insertable text not already covered
356 if (keysym->isText() && keyseq->length() == 1) {
357 LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
358 func = FuncRequest(LFUN_SELF_INSERT,
359 FuncRequest::KEYBOARD);
361 LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
362 lyx_view_->message(_("Unknown function."));
367 if (func.action == LFUN_SELF_INSERT) {
368 if (encoded_last_key != 0) {
369 docstring const arg(1, encoded_last_key);
370 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
371 FuncRequest::KEYBOARD));
373 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
381 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
383 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
386 Cursor & cur = view()->cursor();
388 /* In LyX/Mac, when a dialog is open, the menus of the
389 application can still be accessed without giving focus to
390 the main window. In this case, we want to disable the menu
391 entries that are buffer-related.
393 Note that this code is not perfect, as bug 1941 attests:
394 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
396 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
397 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
400 if (cmd.action == LFUN_NOACTION) {
401 flag.message(from_utf8(N_("Nothing to do")));
406 switch (cmd.action) {
407 case LFUN_UNKNOWN_ACTION:
408 #ifndef HAVE_LIBAIKSAURUS
409 case LFUN_THESAURUS_ENTRY:
419 if (flag.unknown()) {
420 flag.message(from_utf8(N_("Unknown action")));
424 if (!flag.enabled()) {
425 if (flag.message().empty())
426 flag.message(from_utf8(N_("Command disabled")));
430 // Check whether we need a buffer
431 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
433 flag.message(from_utf8(N_("Command not allowed with"
434 "out any document open")));
439 // I would really like to avoid having this switch and rather try to
440 // encode this in the function itself.
441 // -- And I'd rather let an inset decide which LFUNs it is willing
442 // to handle (Andre')
444 switch (cmd.action) {
445 case LFUN_BUFFER_TOGGLE_READ_ONLY:
446 flag.setOnOff(buf->isReadonly());
449 case LFUN_BUFFER_SWITCH:
450 // toggle on the current buffer, but do not toggle off
451 // the other ones (is that a good idea?)
452 if (to_utf8(cmd.argument()) == buf->fileName())
456 case LFUN_BUFFER_EXPORT:
457 enable = cmd.argument() == "custom"
458 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
461 case LFUN_BUFFER_CHKTEX:
462 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
465 case LFUN_BUILD_PROGRAM:
466 enable = Exporter::isExportable(*buf, "program");
469 case LFUN_LAYOUT_TABULAR:
470 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
474 case LFUN_LAYOUT_PARAGRAPH:
475 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
478 case LFUN_VC_REGISTER:
479 enable = !buf->lyxvc().inUse();
481 case LFUN_VC_CHECK_IN:
482 enable = buf->lyxvc().inUse() && !buf->isReadonly();
484 case LFUN_VC_CHECK_OUT:
485 enable = buf->lyxvc().inUse() && buf->isReadonly();
488 case LFUN_VC_UNDO_LAST:
489 enable = buf->lyxvc().inUse();
491 case LFUN_BUFFER_RELOAD:
492 enable = !buf->isUnnamed() && !buf->isClean();
495 case LFUN_INSET_SETTINGS: {
499 Inset::Code code = cur.inset().lyxCode();
501 case Inset::TABULAR_CODE:
502 enable = cmd.argument() == "tabular";
504 case Inset::ERT_CODE:
505 enable = cmd.argument() == "ert";
507 case Inset::FLOAT_CODE:
508 enable = cmd.argument() == "float";
510 case Inset::WRAP_CODE:
511 enable = cmd.argument() == "wrap";
513 case Inset::NOTE_CODE:
514 enable = cmd.argument() == "note";
516 case Inset::BRANCH_CODE:
517 enable = cmd.argument() == "branch";
519 case Inset::BOX_CODE:
520 enable = cmd.argument() == "box";
528 case LFUN_INSET_APPLY: {
529 string const name = cmd.getArg(0);
530 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
532 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
534 if (!inset->getStatus(cur, fr, fs)) {
535 // Every inset is supposed to handle this
540 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
541 flag |= getStatus(fr);
543 enable = flag.enabled();
547 case LFUN_DIALOG_SHOW: {
548 string const name = cmd.getArg(0);
550 enable = name == "aboutlyx"
551 || name == "file" //FIXME: should be removed.
553 || name == "texinfo";
554 else if (name == "print")
555 enable = Exporter::isExportable(*buf, "dvi")
556 && lyxrc.print_command != "none";
557 else if (name == "character")
558 enable = cur.inset().lyxCode() != Inset::ERT_CODE;
559 else if (name == "latexlog")
560 enable = isFileReadable(FileName(buf->getLogName().second));
561 else if (name == "spellchecker")
562 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
563 enable = !buf->isReadonly();
567 else if (name == "vclog")
568 enable = buf->lyxvc().inUse();
572 case LFUN_DIALOG_SHOW_NEW_INSET:
573 enable = cur.inset().lyxCode() != Inset::ERT_CODE;
574 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
576 if (cur.inset().getStatus(cur, cmd, flag))
581 case LFUN_DIALOG_UPDATE: {
582 string const name = cmd.getArg(0);
584 enable = name == "prefs";
588 case LFUN_CITATION_INSERT: {
589 FuncRequest fr(LFUN_INSET_INSERT, "citation");
590 enable = getStatus(fr).enabled();
594 case LFUN_BUFFER_WRITE: {
595 enable = view()->buffer()->isUnnamed()
596 || !view()->buffer()->isClean();
600 case LFUN_BOOKMARK_GOTO: {
601 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
602 enable = LyX::ref().session().bookmarks().isValid(num);
606 case LFUN_BOOKMARK_CLEAR:
607 enable = LyX::ref().session().bookmarks().size() > 0;
610 case LFUN_TOOLBAR_TOGGLE_STATE: {
611 ToolbarInfo::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
612 if (!(flags & ToolbarInfo::AUTO))
613 flag.setOnOff(flags & ToolbarInfo::ON);
617 // this one is difficult to get right. As a half-baked
618 // solution, we consider only the first action of the sequence
619 case LFUN_COMMAND_SEQUENCE: {
620 // argument contains ';'-terminated commands
621 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
622 FuncRequest func(lyxaction.lookupFunc(firstcmd));
623 func.origin = cmd.origin;
624 flag = getStatus(func);
627 case LFUN_BUFFER_NEW:
628 case LFUN_BUFFER_NEW_TEMPLATE:
629 case LFUN_WORD_FIND_FORWARD:
630 case LFUN_WORD_FIND_BACKWARD:
631 case LFUN_COMMAND_PREFIX:
632 case LFUN_COMMAND_EXECUTE:
634 case LFUN_META_PREFIX:
635 case LFUN_BUFFER_CLOSE:
636 case LFUN_BUFFER_WRITE_AS:
637 case LFUN_BUFFER_UPDATE:
638 case LFUN_BUFFER_VIEW:
639 case LFUN_BUFFER_IMPORT:
641 case LFUN_BUFFER_AUTO_SAVE:
642 case LFUN_RECONFIGURE:
646 case LFUN_DROP_LAYOUTS_CHOICE:
648 case LFUN_SERVER_GET_NAME:
649 case LFUN_SERVER_NOTIFY:
650 case LFUN_SERVER_GOTO_FILE_ROW:
651 case LFUN_DIALOG_HIDE:
652 case LFUN_DIALOG_DISCONNECT_INSET:
653 case LFUN_BUFFER_CHILD_OPEN:
654 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
655 case LFUN_KEYMAP_OFF:
656 case LFUN_KEYMAP_PRIMARY:
657 case LFUN_KEYMAP_SECONDARY:
658 case LFUN_KEYMAP_TOGGLE:
660 case LFUN_BUFFER_EXPORT_CUSTOM:
661 case LFUN_BUFFER_PRINT:
662 case LFUN_PREFERENCES_SAVE:
663 case LFUN_SCREEN_FONT_UPDATE:
666 case LFUN_EXTERNAL_EDIT:
667 case LFUN_GRAPHICS_EDIT:
668 case LFUN_ALL_INSETS_TOGGLE:
669 case LFUN_BUFFER_LANGUAGE:
670 case LFUN_TEXTCLASS_APPLY:
671 case LFUN_TEXTCLASS_LOAD:
672 case LFUN_BUFFER_SAVE_AS_DEFAULT:
673 case LFUN_BUFFER_PARAMS_APPLY:
674 case LFUN_LYXRC_APPLY:
675 case LFUN_BUFFER_NEXT:
676 case LFUN_BUFFER_PREVIOUS:
677 case LFUN_WINDOW_NEW:
678 case LFUN_WINDOW_CLOSE:
680 // these are handled in our dispatch()
684 if (!getLocalStatus(cur, cmd, flag))
685 flag = view()->getStatus(cmd);
691 // Can we use a readonly buffer?
692 if (buf && buf->isReadonly()
693 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
694 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
695 flag.message(from_utf8(N_("Document is read-only")));
699 // Are we in a DELETED change-tracking region?
700 if (buf && lookupChangeType(cur, true) == Change::DELETED
701 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
702 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
703 flag.message(from_utf8(N_("This portion of the document is deleted.")));
707 // the default error message if we disable the command
708 if (!flag.enabled() && flag.message().empty())
709 flag.message(from_utf8(N_("Command disabled")));
715 bool LyXFunc::ensureBufferClean(BufferView * bv)
717 Buffer & buf = *bv->buffer();
721 docstring const file = makeDisplayPath(buf.fileName(), 30);
722 docstring text = bformat(_("The document %1$s has unsaved "
723 "changes.\n\nDo you want to save "
724 "the document?"), file);
725 int const ret = Alert::prompt(_("Save changed document?"),
726 text, 0, 1, _("&Save"),
730 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
732 return buf.isClean();
738 void showPrintError(string const & name)
740 docstring str = bformat(_("Could not print the document %1$s.\n"
741 "Check that your printer is set up correctly."),
742 makeDisplayPath(name, 50));
743 Alert::error(_("Print document failed"), str);
747 void loadTextclass(string const & name)
749 std::pair<bool, textclass_type> const tc_pair =
750 textclasslist.numberOfClass(name);
752 if (!tc_pair.first) {
753 lyxerr << "Document class \"" << name
754 << "\" does not exist."
759 textclass_type const tc = tc_pair.second;
761 if (!textclasslist[tc].load()) {
762 docstring s = bformat(_("The document could not be converted\n"
763 "into the document class %1$s."),
764 from_utf8(textclasslist[tc].name()));
765 Alert::error(_("Could not change class"), s);
770 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
775 void LyXFunc::dispatch(FuncRequest const & cmd)
777 string const argument = to_utf8(cmd.argument());
778 kb_action const action = cmd.action;
780 LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
781 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
783 // we have not done anything wrong yet.
785 dispatch_buffer.erase();
787 // redraw the screen at the end (first of the two drawing steps).
788 //This is done unless explicitely requested otherwise
789 Update::flags updateFlags = Update::FitCursor;
791 FuncStatus const flag = getStatus(cmd);
792 if (!flag.enabled()) {
793 // We cannot use this function here
794 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
795 << lyxaction.getActionName(action)
796 << " [" << action << "] is disabled at this location"
798 setErrorMessage(flag.message());
802 case LFUN_WORD_FIND_FORWARD:
803 case LFUN_WORD_FIND_BACKWARD: {
804 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
805 static docstring last_search;
806 docstring searched_string;
808 if (!cmd.argument().empty()) {
809 last_search = cmd.argument();
810 searched_string = cmd.argument();
812 searched_string = last_search;
815 if (searched_string.empty())
818 bool const fw = action == LFUN_WORD_FIND_FORWARD;
819 docstring const data =
820 find2string(searched_string, true, false, fw);
821 find(view(), FuncRequest(LFUN_WORD_FIND, data));
825 case LFUN_COMMAND_PREFIX:
826 BOOST_ASSERT(lyx_view_);
827 lyx_view_->message(keyseq->printOptions(true));
830 case LFUN_COMMAND_EXECUTE:
831 BOOST_ASSERT(lyx_view_);
832 lyx_view_->getToolbars().display("minibuffer", true);
833 lyx_view_->focus_command_buffer();
837 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
839 meta_fake_bit = key_modifier::none;
840 if (view()->buffer())
841 // cancel any selection
842 dispatch(FuncRequest(LFUN_MARK_OFF));
843 setMessage(from_ascii(N_("Cancel")));
846 case LFUN_META_PREFIX:
847 meta_fake_bit = key_modifier::alt;
848 setMessage(keyseq->print(true));
851 case LFUN_BUFFER_TOGGLE_READ_ONLY:
852 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
853 if (lyx_view_->buffer()->lyxvc().inUse())
854 lyx_view_->buffer()->lyxvc().toggleReadOnly();
856 lyx_view_->buffer()->setReadonly(
857 !lyx_view_->buffer()->isReadonly());
860 // --- Menus -----------------------------------------------
861 case LFUN_BUFFER_NEW:
862 menuNew(argument, false);
865 case LFUN_BUFFER_NEW_TEMPLATE:
866 menuNew(argument, true);
869 case LFUN_BUFFER_CLOSE:
874 case LFUN_BUFFER_WRITE:
875 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
876 if (!lyx_view_->buffer()->isUnnamed()) {
877 docstring const str = bformat(_("Saving document %1$s..."),
878 makeDisplayPath(lyx_view_->buffer()->fileName()));
879 lyx_view_->message(str);
880 menuWrite(lyx_view_->buffer());
881 lyx_view_->message(str + _(" done."));
883 writeAs(lyx_view_->buffer());
885 updateFlags = Update::None;
888 case LFUN_BUFFER_WRITE_AS:
889 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
890 writeAs(lyx_view_->buffer(), argument);
891 updateFlags = Update::None;
894 case LFUN_BUFFER_RELOAD: {
895 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
896 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
897 docstring text = bformat(_("Any changes will be lost. Are you sure "
898 "you want to revert to the saved version of the document %1$s?"), file);
899 int const ret = Alert::prompt(_("Revert to saved document?"),
900 text, 0, 1, _("&Revert"), _("&Cancel"));
907 case LFUN_BUFFER_UPDATE:
908 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
909 Exporter::Export(lyx_view_->buffer(), argument, true);
912 case LFUN_BUFFER_VIEW:
913 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
914 Exporter::preview(lyx_view_->buffer(), argument);
917 case LFUN_BUILD_PROGRAM:
918 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
919 Exporter::Export(lyx_view_->buffer(), "program", true);
922 case LFUN_BUFFER_CHKTEX:
923 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
924 lyx_view_->buffer()->runChktex();
927 case LFUN_BUFFER_EXPORT:
928 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
929 if (argument == "custom")
930 lyx_view_->getDialogs().show("sendto");
932 Exporter::Export(lyx_view_->buffer(), argument, false);
936 case LFUN_BUFFER_EXPORT_CUSTOM: {
937 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
939 string command = split(argument, format_name, ' ');
940 Format const * format = formats.getFormat(format_name);
942 lyxerr << "Format \"" << format_name
943 << "\" not recognized!"
948 Buffer * buffer = lyx_view_->buffer();
950 // The name of the file created by the conversion process
953 // Output to filename
954 if (format->name() == "lyx") {
955 string const latexname =
956 buffer->getLatexName(false);
957 filename = changeExtension(latexname,
958 format->extension());
959 filename = addName(buffer->temppath(), filename);
961 if (!buffer->writeFile(FileName(filename)))
965 Exporter::Export(buffer, format_name, true, filename);
968 // Substitute $$FName for filename
969 if (!contains(command, "$$FName"))
970 command = "( " + command + " ) < $$FName";
971 command = subst(command, "$$FName", filename);
973 // Execute the command in the background
975 call.startscript(Systemcall::DontWait, command);
979 case LFUN_BUFFER_PRINT: {
980 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
983 string command = split(split(argument, target, ' '),
987 || target_name.empty()
988 || command.empty()) {
989 lyxerr << "Unable to parse \""
990 << argument << '"' << std::endl;
993 if (target != "printer" && target != "file") {
994 lyxerr << "Unrecognized target \""
995 << target << '"' << std::endl;
999 Buffer * buffer = lyx_view_->buffer();
1001 if (!Exporter::Export(buffer, "dvi", true)) {
1002 showPrintError(buffer->fileName());
1006 // Push directory path.
1007 string const path(buffer->temppath());
1008 // Prevent the compiler from optimizing away p
1010 support::Path p(pp);
1012 // there are three cases here:
1013 // 1. we print to a file
1014 // 2. we print directly to a printer
1015 // 3. we print using a spool command (print to file first)
1018 string const dviname =
1019 changeExtension(buffer->getLatexName(true),
1022 if (target == "printer") {
1023 if (!lyxrc.print_spool_command.empty()) {
1024 // case 3: print using a spool
1025 string const psname =
1026 changeExtension(dviname,".ps");
1027 command += lyxrc.print_to_file
1030 + quoteName(dviname);
1033 lyxrc.print_spool_command +' ';
1034 if (target_name != "default") {
1035 command2 += lyxrc.print_spool_printerprefix
1039 command2 += quoteName(psname);
1041 // If successful, then spool command
1042 res = one.startscript(
1047 res = one.startscript(
1048 Systemcall::DontWait,
1051 // case 2: print directly to a printer
1052 res = one.startscript(
1053 Systemcall::DontWait,
1054 command + quoteName(dviname));
1058 // case 1: print to a file
1059 FileName const filename(makeAbsPath(target_name, path));
1060 if (fs::exists(filename.toFilesystemEncoding())) {
1061 docstring text = bformat(
1062 _("The file %1$s already exists.\n\n"
1063 "Do you want to over-write that file?"),
1064 makeDisplayPath(filename.absFilename()));
1065 if (Alert::prompt(_("Over-write file?"),
1066 text, 0, 1, _("&Over-write"), _("&Cancel")) != 0)
1069 command += lyxrc.print_to_file
1070 + quoteName(filename.toFilesystemEncoding())
1072 + quoteName(dviname);
1073 res = one.startscript(Systemcall::DontWait,
1078 showPrintError(buffer->fileName());
1082 case LFUN_BUFFER_IMPORT:
1087 // quitting is triggered by the gui code
1088 // (leaving the event loop).
1089 lyx_view_->message(from_utf8(N_("Exiting.")));
1090 if (theBufferList().quitWriteAll())
1091 theApp()->gui().closeAllViews();
1094 case LFUN_TOC_VIEW: {
1095 BOOST_ASSERT(lyx_view_);
1096 InsetCommandParams p("tableofcontents");
1097 string const data = InsetCommandMailer::params2string("toc", p);
1098 lyx_view_->getDialogs().show("toc", data, 0);
1102 case LFUN_BUFFER_AUTO_SAVE:
1106 case LFUN_RECONFIGURE:
1107 BOOST_ASSERT(lyx_view_);
1108 reconfigure(*lyx_view_);
1111 case LFUN_HELP_OPEN: {
1112 BOOST_ASSERT(lyx_view_);
1113 string const arg = argument;
1115 setErrorMessage(from_ascii(N_("Missing argument")));
1118 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1119 if (fname.empty()) {
1120 lyxerr << "LyX: unable to find documentation file `"
1121 << arg << "'. Bad installation?" << endl;
1124 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1125 makeDisplayPath(fname.absFilename())));
1126 lyx_view_->loadLyXFile(fname, false);
1130 // --- version control -------------------------------
1131 case LFUN_VC_REGISTER:
1132 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1133 if (!ensureBufferClean(view()))
1135 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1136 lyx_view_->buffer()->lyxvc().registrer();
1139 updateFlags = Update::Force;
1142 case LFUN_VC_CHECK_IN:
1143 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1144 if (!ensureBufferClean(view()))
1146 if (lyx_view_->buffer()->lyxvc().inUse()
1147 && !lyx_view_->buffer()->isReadonly()) {
1148 lyx_view_->buffer()->lyxvc().checkIn();
1153 case LFUN_VC_CHECK_OUT:
1154 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1155 if (!ensureBufferClean(view()))
1157 if (lyx_view_->buffer()->lyxvc().inUse()
1158 && lyx_view_->buffer()->isReadonly()) {
1159 lyx_view_->buffer()->lyxvc().checkOut();
1164 case LFUN_VC_REVERT:
1165 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1166 lyx_view_->buffer()->lyxvc().revert();
1170 case LFUN_VC_UNDO_LAST:
1171 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1172 lyx_view_->buffer()->lyxvc().undoLast();
1176 // --- buffers ----------------------------------------
1177 case LFUN_BUFFER_SWITCH:
1178 BOOST_ASSERT(lyx_view_);
1179 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1182 case LFUN_BUFFER_NEXT:
1183 BOOST_ASSERT(lyx_view_);
1184 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1187 case LFUN_BUFFER_PREVIOUS:
1188 BOOST_ASSERT(lyx_view_);
1189 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1193 BOOST_ASSERT(lyx_view_);
1194 newFile(view(), argument);
1197 case LFUN_FILE_OPEN:
1198 BOOST_ASSERT(lyx_view_);
1202 case LFUN_DROP_LAYOUTS_CHOICE:
1203 BOOST_ASSERT(lyx_view_);
1204 lyx_view_->getToolbars().openLayoutList();
1207 case LFUN_MENU_OPEN:
1208 BOOST_ASSERT(lyx_view_);
1209 lyx_view_->getMenubar().openByName(from_utf8(argument));
1212 // --- lyxserver commands ----------------------------
1213 case LFUN_SERVER_GET_NAME:
1214 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1215 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1216 LYXERR(Debug::INFO) << "FNAME["
1217 << lyx_view_->buffer()->fileName()
1221 case LFUN_SERVER_NOTIFY:
1222 dispatch_buffer = keyseq->print(false);
1223 theServer().notifyClient(to_utf8(dispatch_buffer));
1226 case LFUN_SERVER_GOTO_FILE_ROW: {
1227 BOOST_ASSERT(lyx_view_);
1230 istringstream is(argument);
1231 is >> file_name >> row;
1232 if (prefixIs(file_name, package().temp_dir().absFilename())) {
1233 // Needed by inverse dvi search. If it is a file
1234 // in tmpdir, call the apropriated function
1235 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1237 // Must replace extension of the file to be .lyx
1238 // and get full path
1239 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1240 // Either change buffer or load the file
1241 if (theBufferList().exists(s.absFilename())) {
1242 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1244 lyx_view_->loadLyXFile(s);
1248 view()->setCursorFromRow(row);
1250 updateFlags = Update::FitCursor;
1254 case LFUN_DIALOG_SHOW: {
1255 BOOST_ASSERT(lyx_view_);
1256 string const name = cmd.getArg(0);
1257 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1259 if (name == "character") {
1260 data = freefont2string();
1262 lyx_view_->getDialogs().show("character", data);
1263 } else if (name == "latexlog") {
1264 pair<Buffer::LogType, string> const logfile =
1265 lyx_view_->buffer()->getLogName();
1266 switch (logfile.first) {
1267 case Buffer::latexlog:
1270 case Buffer::buildlog:
1274 data += Lexer::quoteString(logfile.second);
1275 lyx_view_->getDialogs().show("log", data);
1276 } else if (name == "vclog") {
1277 string const data = "vc " +
1278 Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1279 lyx_view_->getDialogs().show("log", data);
1281 lyx_view_->getDialogs().show(name, data);
1285 case LFUN_DIALOG_SHOW_NEW_INSET: {
1286 BOOST_ASSERT(lyx_view_);
1287 string const name = cmd.getArg(0);
1288 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1289 if (name == "bibitem" ||
1293 name == "nomenclature" ||
1297 InsetCommandParams p(name);
1298 data = InsetCommandMailer::params2string(name, p);
1299 } else if (name == "include") {
1300 // data is the include type: one of "include",
1301 // "input", "verbatiminput" or "verbatiminput*"
1303 // default type is requested
1305 InsetCommandParams p(data);
1306 data = InsetIncludeMailer::params2string(p);
1307 } else if (name == "box") {
1308 // \c data == "Boxed" || "Frameless" etc
1309 InsetBoxParams p(data);
1310 data = InsetBoxMailer::params2string(p);
1311 } else if (name == "branch") {
1312 InsetBranchParams p;
1313 data = InsetBranchMailer::params2string(p);
1314 } else if (name == "citation") {
1315 InsetCommandParams p("cite");
1316 data = InsetCommandMailer::params2string(name, p);
1317 } else if (name == "ert") {
1318 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1319 } else if (name == "external") {
1320 InsetExternalParams p;
1321 Buffer const & buffer = *lyx_view_->buffer();
1322 data = InsetExternalMailer::params2string(p, buffer);
1323 } else if (name == "float") {
1325 data = InsetFloatMailer::params2string(p);
1326 } else if (name == "graphics") {
1327 InsetGraphicsParams p;
1328 Buffer const & buffer = *lyx_view_->buffer();
1329 data = InsetGraphicsMailer::params2string(p, buffer);
1330 } else if (name == "note") {
1332 data = InsetNoteMailer::params2string(p);
1333 } else if (name == "vspace") {
1335 data = InsetVSpaceMailer::params2string(space);
1336 } else if (name == "wrap") {
1338 data = InsetWrapMailer::params2string(p);
1340 lyx_view_->getDialogs().show(name, data, 0);
1344 case LFUN_DIALOG_UPDATE: {
1345 BOOST_ASSERT(lyx_view_);
1346 string const & name = argument;
1347 // Can only update a dialog connected to an existing inset
1348 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1350 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1351 inset->dispatch(view()->cursor(), fr);
1352 } else if (name == "paragraph") {
1353 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1354 } else if (name == "prefs") {
1355 lyx_view_->getDialogs().update(name, string());
1360 case LFUN_DIALOG_HIDE:
1361 Dialogs::hide(argument, 0);
1364 case LFUN_DIALOG_DISCONNECT_INSET:
1365 BOOST_ASSERT(lyx_view_);
1366 lyx_view_->getDialogs().disconnect(argument);
1370 case LFUN_CITATION_INSERT: {
1371 BOOST_ASSERT(lyx_view_);
1372 if (!argument.empty()) {
1373 // we can have one optional argument, delimited by '|'
1374 // citation-insert <key>|<text_before>
1375 // this should be enhanced to also support text_after
1376 // and citation style
1377 string arg = argument;
1379 if (contains(argument, "|")) {
1380 arg = token(argument, '|', 0);
1381 opt1 = token(argument, '|', 1);
1383 InsetCommandParams icp("cite");
1384 icp["key"] = from_utf8(arg);
1386 icp["before"] = from_utf8(opt1);
1387 string icstr = InsetCommandMailer::params2string("citation", icp);
1388 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1391 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1395 case LFUN_BUFFER_CHILD_OPEN: {
1396 BOOST_ASSERT(lyx_view_);
1397 FileName const filename =
1398 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1399 setMessage(bformat(_("Opening child document %1$s..."),
1400 makeDisplayPath(filename.absFilename())));
1401 view()->saveBookmark(false);
1402 string const parentfilename = lyx_view_->buffer()->fileName();
1403 if (theBufferList().exists(filename.absFilename()))
1404 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1406 lyx_view_->loadLyXFile(filename);
1407 // Set the parent name of the child document.
1408 // This makes insertion of citations and references in the child work,
1409 // when the target is in the parent or another child document.
1410 lyx_view_->buffer()->setParentName(parentfilename);
1414 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1415 BOOST_ASSERT(lyx_view_);
1416 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1419 case LFUN_KEYMAP_OFF:
1420 BOOST_ASSERT(lyx_view_);
1421 lyx_view_->view()->getIntl().keyMapOn(false);
1424 case LFUN_KEYMAP_PRIMARY:
1425 BOOST_ASSERT(lyx_view_);
1426 lyx_view_->view()->getIntl().keyMapPrim();
1429 case LFUN_KEYMAP_SECONDARY:
1430 BOOST_ASSERT(lyx_view_);
1431 lyx_view_->view()->getIntl().keyMapSec();
1434 case LFUN_KEYMAP_TOGGLE:
1435 BOOST_ASSERT(lyx_view_);
1436 lyx_view_->view()->getIntl().toggleKeyMap();
1442 string rest = split(argument, countstr, ' ');
1443 istringstream is(countstr);
1446 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1447 for (int i = 0; i < count; ++i)
1448 dispatch(lyxaction.lookupFunc(rest));
1452 case LFUN_COMMAND_SEQUENCE: {
1453 // argument contains ';'-terminated commands
1454 string arg = argument;
1455 while (!arg.empty()) {
1457 arg = split(arg, first, ';');
1458 FuncRequest func(lyxaction.lookupFunc(first));
1459 func.origin = cmd.origin;
1465 case LFUN_PREFERENCES_SAVE: {
1466 lyxrc.write(makeAbsPath("preferences",
1467 package().user_support().absFilename()),
1472 case LFUN_SCREEN_FONT_UPDATE:
1473 BOOST_ASSERT(lyx_view_);
1474 // handle the screen font changes.
1475 theFontLoader().update();
1476 /// FIXME: only the current view will be updated. the Gui
1477 /// class is able to furnish the list of views.
1478 updateFlags = Update::Force;
1481 case LFUN_SET_COLOR: {
1483 string const x11_name = split(argument, lyx_name, ' ');
1484 if (lyx_name.empty() || x11_name.empty()) {
1485 setErrorMessage(from_ascii(N_(
1486 "Syntax: set-color <lyx_name>"
1491 bool const graphicsbg_changed =
1492 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1493 x11_name != lcolor.getX11Name(Color::graphicsbg));
1495 if (!lcolor.setColor(lyx_name, x11_name)) {
1497 bformat(_("Set-color \"%1$s\" failed "
1498 "- color is undefined or "
1499 "may not be redefined"),
1500 from_utf8(lyx_name)));
1504 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1506 if (graphicsbg_changed) {
1507 #ifdef WITH_WARNINGS
1508 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1511 graphics::GCache::get().changeDisplay(true);
1518 BOOST_ASSERT(lyx_view_);
1519 lyx_view_->message(from_utf8(argument));
1522 case LFUN_EXTERNAL_EDIT: {
1523 BOOST_ASSERT(lyx_view_);
1524 FuncRequest fr(action, argument);
1525 InsetExternal().dispatch(view()->cursor(), fr);
1529 case LFUN_GRAPHICS_EDIT: {
1530 FuncRequest fr(action, argument);
1531 InsetGraphics().dispatch(view()->cursor(), fr);
1535 case LFUN_INSET_APPLY: {
1536 BOOST_ASSERT(lyx_view_);
1537 string const name = cmd.getArg(0);
1538 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1540 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1541 inset->dispatch(view()->cursor(), fr);
1543 FuncRequest fr(LFUN_INSET_INSERT, argument);
1546 // ideally, the update flag should be set by the insets,
1547 // but this is not possible currently
1548 updateFlags = Update::Force | Update::FitCursor;
1552 case LFUN_ALL_INSETS_TOGGLE: {
1553 BOOST_ASSERT(lyx_view_);
1555 string const name = split(argument, action, ' ');
1556 Inset::Code const inset_code =
1557 Inset::translate(name);
1559 Cursor & cur = view()->cursor();
1560 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1562 Inset & inset = lyx_view_->buffer()->inset();
1563 InsetIterator it = inset_iterator_begin(inset);
1564 InsetIterator const end = inset_iterator_end(inset);
1565 for (; it != end; ++it) {
1566 if (!it->asInsetMath()
1567 && (inset_code == Inset::NO_CODE
1568 || inset_code == it->lyxCode())) {
1569 Cursor tmpcur = cur;
1570 tmpcur.pushLeft(*it);
1571 it->dispatch(tmpcur, fr);
1574 updateFlags = Update::Force | Update::FitCursor;
1578 case LFUN_BUFFER_LANGUAGE: {
1579 BOOST_ASSERT(lyx_view_);
1580 Buffer & buffer = *lyx_view_->buffer();
1581 Language const * oldL = buffer.params().language;
1582 Language const * newL = languages.getLanguage(argument);
1583 if (!newL || oldL == newL)
1586 if (oldL->rightToLeft() == newL->rightToLeft()
1587 && !buffer.isMultiLingual())
1588 buffer.changeLanguage(oldL, newL);
1592 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1593 string const fname =
1594 addName(addPath(package().user_support().absFilename(), "templates/"),
1596 Buffer defaults(fname);
1598 istringstream ss(argument);
1601 int const unknown_tokens = defaults.readHeader(lex);
1603 if (unknown_tokens != 0) {
1604 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1605 << unknown_tokens << " unknown token"
1606 << (unknown_tokens == 1 ? "" : "s")
1610 if (defaults.writeFile(FileName(defaults.fileName())))
1611 setMessage(bformat(_("Document defaults saved in %1$s"),
1612 makeDisplayPath(fname)));
1614 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1618 case LFUN_BUFFER_PARAMS_APPLY: {
1619 BOOST_ASSERT(lyx_view_);
1620 biblio::CiteEngine const engine =
1621 lyx_view_->buffer()->params().getEngine();
1623 istringstream ss(argument);
1626 int const unknown_tokens =
1627 lyx_view_->buffer()->readHeader(lex);
1629 if (unknown_tokens != 0) {
1630 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1631 << unknown_tokens << " unknown token"
1632 << (unknown_tokens == 1 ? "" : "s")
1635 if (engine == lyx_view_->buffer()->params().getEngine())
1638 Cursor & cur = view()->cursor();
1639 FuncRequest fr(LFUN_INSET_REFRESH);
1641 Inset & inset = lyx_view_->buffer()->inset();
1642 InsetIterator it = inset_iterator_begin(inset);
1643 InsetIterator const end = inset_iterator_end(inset);
1644 for (; it != end; ++it)
1645 if (it->lyxCode() == Inset::CITE_CODE)
1646 it->dispatch(cur, fr);
1650 case LFUN_TEXTCLASS_APPLY: {
1651 BOOST_ASSERT(lyx_view_);
1652 Buffer * buffer = lyx_view_->buffer();
1654 textclass_type const old_class =
1655 buffer->params().textclass;
1657 loadTextclass(argument);
1659 std::pair<bool, textclass_type> const tc_pair =
1660 textclasslist.numberOfClass(argument);
1665 textclass_type const new_class = tc_pair.second;
1666 if (old_class == new_class)
1670 lyx_view_->message(_("Converting document to new document class..."));
1671 recordUndoFullDocument(view());
1672 buffer->params().textclass = new_class;
1673 StableDocIterator backcur(view()->cursor());
1674 ErrorList & el = buffer->errorList("Class Switch");
1675 cap::switchBetweenClasses(
1676 old_class, new_class,
1677 static_cast<InsetText &>(buffer->inset()), el);
1679 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1681 buffer->errors("Class Switch");
1682 updateLabels(*buffer);
1683 updateFlags = Update::Force | Update::FitCursor;
1687 case LFUN_TEXTCLASS_LOAD:
1688 loadTextclass(argument);
1691 case LFUN_LYXRC_APPLY: {
1692 LyXRC const lyxrc_orig = lyxrc;
1694 istringstream ss(argument);
1695 bool const success = lyxrc.read(ss) == 0;
1698 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1699 << "Unable to read lyxrc data"
1704 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1706 /// We force the redraw in any case because there might be
1707 /// some screen font changes.
1708 /// FIXME: only the current view will be updated. the Gui
1709 /// class is able to furnish the list of views.
1710 updateFlags = Update::Force;
1714 case LFUN_WINDOW_NEW:
1715 LyX::ref().newLyXView();
1718 case LFUN_WINDOW_CLOSE:
1719 BOOST_ASSERT(lyx_view_);
1720 BOOST_ASSERT(theApp());
1721 // update bookmark pit of the current buffer before window close
1722 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1723 gotoBookmark(i+1, false, false);
1724 // ask the user for saving changes or cancel quit
1725 if (!theBufferList().quitWriteAll())
1730 case LFUN_BOOKMARK_GOTO:
1731 // go to bookmark, open unopened file and switch to buffer if necessary
1732 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1735 case LFUN_BOOKMARK_CLEAR:
1736 LyX::ref().session().bookmarks().clear();
1739 case LFUN_TOOLBAR_TOGGLE_STATE:
1740 lyx_view_->toggleToolbarState(argument);
1744 BOOST_ASSERT(lyx_view_);
1745 view()->cursor().dispatch(cmd);
1746 updateFlags = view()->cursor().result().update();
1747 if (!view()->cursor().result().dispatched())
1748 updateFlags = view()->dispatch(cmd);
1753 if (lyx_view_ && view()->buffer()) {
1754 // BufferView::update() updates the ViewMetricsInfo and
1755 // also initializes the position cache for all insets in
1756 // (at least partially) visible top-level paragraphs.
1757 // We will redraw the screen only if needed.
1758 if (view()->update(updateFlags)) {
1759 // Buffer::changed() signals that a repaint is needed.
1760 // The frontend (WorkArea) knows which area to repaint
1761 // thanks to the ViewMetricsInfo updated above.
1762 view()->buffer()->changed();
1765 lyx_view_->updateStatusBar();
1767 // if we executed a mutating lfun, mark the buffer as dirty
1769 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1770 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1771 view()->buffer()->markDirty();
1773 if (view()->cursor().inTexted()) {
1774 lyx_view_->updateLayoutChoice();
1779 lyx_view_->updateMenubar();
1780 lyx_view_->updateToolbars();
1781 // Some messages may already be translated, so we cannot use _()
1782 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1787 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1789 const bool verbose = (cmd.origin == FuncRequest::MENU
1790 || cmd.origin == FuncRequest::TOOLBAR
1791 || cmd.origin == FuncRequest::COMMANDBUFFER);
1793 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1794 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1796 lyx_view_->message(msg);
1800 docstring dispatch_msg = msg;
1801 if (!dispatch_msg.empty())
1802 dispatch_msg += ' ';
1804 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1806 bool argsadded = false;
1808 if (!cmd.argument().empty()) {
1809 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1810 comname += ' ' + cmd.argument();
1815 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1817 if (!shortcuts.empty())
1818 comname += ": " + shortcuts;
1819 else if (!argsadded && !cmd.argument().empty())
1820 comname += ' ' + cmd.argument();
1822 if (!comname.empty()) {
1823 comname = rtrim(comname);
1824 dispatch_msg += '(' + rtrim(comname) + ')';
1827 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1828 << to_utf8(dispatch_msg) << endl;
1829 if (!dispatch_msg.empty())
1830 lyx_view_->message(dispatch_msg);
1834 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1836 // FIXME: initpath is not used. What to do?
1837 string initpath = lyxrc.document_path;
1838 string filename(name);
1840 if (view()->buffer()) {
1841 string const trypath = lyx_view_->buffer()->filePath();
1842 // If directory is writeable, use this as default.
1843 if (isDirWriteable(FileName(trypath)))
1847 static int newfile_number;
1849 if (filename.empty()) {
1850 filename = addName(lyxrc.document_path,
1851 "newfile" + convert<string>(++newfile_number) + ".lyx");
1852 while (theBufferList().exists(filename) ||
1853 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1855 filename = addName(lyxrc.document_path,
1856 "newfile" + convert<string>(newfile_number) +
1861 // The template stuff
1864 FileDialog fileDlg(_("Select template file"),
1865 LFUN_SELECT_FILE_SYNC,
1866 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1867 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1869 FileDialog::Result result =
1870 fileDlg.open(from_utf8(lyxrc.template_path),
1871 FileFilterList(_("LyX Documents (*.lyx)")),
1874 if (result.first == FileDialog::Later)
1876 if (result.second.empty())
1878 templname = to_utf8(result.second);
1881 Buffer * const b = newFile(filename, templname, !name.empty());
1884 lyx_view_->setBuffer(b);
1889 void LyXFunc::open(string const & fname)
1891 string initpath = lyxrc.document_path;
1893 if (view()->buffer()) {
1894 string const trypath = lyx_view_->buffer()->filePath();
1895 // If directory is writeable, use this as default.
1896 if (isDirWriteable(FileName(trypath)))
1902 if (fname.empty()) {
1903 FileDialog fileDlg(_("Select document to open"),
1905 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1906 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1908 FileDialog::Result result =
1909 fileDlg.open(from_utf8(initpath),
1910 FileFilterList(_("LyX Documents (*.lyx)")),
1913 if (result.first == FileDialog::Later)
1916 filename = to_utf8(result.second);
1918 // check selected filename
1919 if (filename.empty()) {
1920 lyx_view_->message(_("Canceled."));
1926 // get absolute path of file and add ".lyx" to the filename if
1928 FileName const fullname = fileSearch(string(), filename, "lyx");
1929 if (!fullname.empty())
1930 filename = fullname.absFilename();
1932 // if the file doesn't exist, let the user create one
1933 if (!fs::exists(fullname.toFilesystemEncoding())) {
1934 // the user specifically chose this name. Believe him.
1935 Buffer * const b = newFile(filename, string(), true);
1937 lyx_view_->setBuffer(b);
1941 docstring const disp_fn = makeDisplayPath(filename);
1942 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1945 if (lyx_view_->loadLyXFile(fullname)) {
1946 str2 = bformat(_("Document %1$s opened."), disp_fn);
1948 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1950 lyx_view_->message(str2);
1954 void LyXFunc::doImport(string const & argument)
1957 string filename = split(argument, format, ' ');
1959 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1960 << " file: " << filename << endl;
1962 // need user interaction
1963 if (filename.empty()) {
1964 string initpath = lyxrc.document_path;
1966 if (view()->buffer()) {
1967 string const trypath = lyx_view_->buffer()->filePath();
1968 // If directory is writeable, use this as default.
1969 if (isDirWriteable(FileName(trypath)))
1973 docstring const text = bformat(_("Select %1$s file to import"),
1974 formats.prettyName(format));
1976 FileDialog fileDlg(text,
1978 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1979 make_pair(_("Examples|#E#e"),
1980 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1982 docstring filter = formats.prettyName(format);
1985 filter += from_utf8(formats.extension(format));
1988 FileDialog::Result result =
1989 fileDlg.open(from_utf8(initpath),
1990 FileFilterList(filter),
1993 if (result.first == FileDialog::Later)
1996 filename = to_utf8(result.second);
1998 // check selected filename
1999 if (filename.empty())
2000 lyx_view_->message(_("Canceled."));
2003 if (filename.empty())
2006 // get absolute path of file
2007 FileName const fullname(makeAbsPath(filename));
2009 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2011 // Check if the document already is open
2012 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2013 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2014 lyx_view_->message(_("Canceled."));
2019 // if the file exists already, and we didn't do
2020 // -i lyx thefile.lyx, warn
2021 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2022 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2024 docstring text = bformat(_("The document %1$s already exists.\n\n"
2025 "Do you want to over-write that document?"), file);
2026 int const ret = Alert::prompt(_("Over-write document?"),
2027 text, 0, 1, _("&Over-write"), _("&Cancel"));
2030 lyx_view_->message(_("Canceled."));
2035 ErrorList errorList;
2036 Importer::Import(lyx_view_, fullname, format, errorList);
2037 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2041 void LyXFunc::closeBuffer()
2043 // goto bookmark to update bookmark pit.
2044 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2045 gotoBookmark(i+1, false, false);
2046 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2047 if (theBufferList().empty()) {
2048 // need this otherwise SEGV may occur while
2049 // trying to set variables that don't exist
2050 // since there's no current buffer
2051 lyx_view_->getDialogs().hideBufferDependent();
2053 lyx_view_->setBuffer(theBufferList().first());
2059 void LyXFunc::reloadBuffer()
2061 FileName filename(lyx_view_->buffer()->fileName());
2063 lyx_view_->loadLyXFile(filename);
2066 // Each "lyx_view_" should have it's own message method. lyxview and
2067 // the minibuffer would use the minibuffer, but lyxserver would
2068 // send an ERROR signal to its client. Alejandro 970603
2069 // This function is bit problematic when it comes to NLS, to make the
2070 // lyx servers client be language indepenent we must not translate
2071 // strings sent to this func.
2072 void LyXFunc::setErrorMessage(docstring const & m) const
2074 dispatch_buffer = m;
2079 void LyXFunc::setMessage(docstring const & m) const
2081 dispatch_buffer = m;
2085 docstring const LyXFunc::viewStatusMessage()
2087 // When meta-fake key is pressed, show the key sequence so far + "M-".
2089 return keyseq->print(true) + "M-";
2091 // Else, when a non-complete key sequence is pressed,
2092 // show the available options.
2093 if (keyseq->length() > 0 && !keyseq->deleted())
2094 return keyseq->printOptions(true);
2096 if (!view()->buffer())
2097 return _("Welcome to LyX!");
2099 return view()->cursor().currentState();
2103 BufferView * LyXFunc::view() const
2105 BOOST_ASSERT(lyx_view_);
2106 return lyx_view_->view();
2110 bool LyXFunc::wasMetaKey() const
2112 return (meta_fake_bit != key_modifier::none);
2118 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2120 // Why the switch you might ask. It is a trick to ensure that all
2121 // the elements in the LyXRCTags enum is handled. As you can see
2122 // there are no breaks at all. So it is just a huge fall-through.
2123 // The nice thing is that we will get a warning from the compiler
2124 // if we forget an element.
2125 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2127 case LyXRC::RC_ACCEPT_COMPOUND:
2128 case LyXRC::RC_ALT_LANG:
2129 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2130 case LyXRC::RC_PLAINTEXT_LINELEN:
2131 case LyXRC::RC_AUTOREGIONDELETE:
2132 case LyXRC::RC_AUTORESET_OPTIONS:
2133 case LyXRC::RC_AUTOSAVE:
2134 case LyXRC::RC_AUTO_NUMBER:
2135 case LyXRC::RC_BACKUPDIR_PATH:
2136 case LyXRC::RC_BIBTEX_COMMAND:
2137 case LyXRC::RC_BINDFILE:
2138 case LyXRC::RC_CHECKLASTFILES:
2139 case LyXRC::RC_USELASTFILEPOS:
2140 case LyXRC::RC_LOADSESSION:
2141 case LyXRC::RC_CHKTEX_COMMAND:
2142 case LyXRC::RC_CONVERTER:
2143 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2144 case LyXRC::RC_COPIER:
2145 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2146 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2147 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2148 case LyXRC::RC_DATE_INSERT_FORMAT:
2149 case LyXRC::RC_DEFAULT_LANGUAGE:
2150 case LyXRC::RC_DEFAULT_PAPERSIZE:
2151 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2152 case LyXRC::RC_DISPLAY_GRAPHICS:
2153 case LyXRC::RC_DOCUMENTPATH:
2154 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2155 string const encoded = FileName(
2156 lyxrc_new.document_path).toFilesystemEncoding();
2157 if (fs::exists(encoded) && fs::is_directory(encoded))
2158 support::package().document_dir() = FileName(lyxrc.document_path);
2160 case LyXRC::RC_ESC_CHARS:
2161 case LyXRC::RC_FONT_ENCODING:
2162 case LyXRC::RC_FORMAT:
2163 case LyXRC::RC_INDEX_COMMAND:
2164 case LyXRC::RC_INPUT:
2165 case LyXRC::RC_KBMAP:
2166 case LyXRC::RC_KBMAP_PRIMARY:
2167 case LyXRC::RC_KBMAP_SECONDARY:
2168 case LyXRC::RC_LABEL_INIT_LENGTH:
2169 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2170 case LyXRC::RC_LANGUAGE_AUTO_END:
2171 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2172 case LyXRC::RC_LANGUAGE_COMMAND_END:
2173 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2174 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2175 case LyXRC::RC_LANGUAGE_PACKAGE:
2176 case LyXRC::RC_LANGUAGE_USE_BABEL:
2177 case LyXRC::RC_MAKE_BACKUP:
2178 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2179 case LyXRC::RC_NUMLASTFILES:
2180 case LyXRC::RC_PATH_PREFIX:
2181 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2182 support::prependEnvPath("PATH", lyxrc.path_prefix);
2184 case LyXRC::RC_PERS_DICT:
2185 case LyXRC::RC_PREVIEW:
2186 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2187 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2188 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2189 case LyXRC::RC_PRINTCOPIESFLAG:
2190 case LyXRC::RC_PRINTER:
2191 case LyXRC::RC_PRINTEVENPAGEFLAG:
2192 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2193 case LyXRC::RC_PRINTFILEEXTENSION:
2194 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2195 case LyXRC::RC_PRINTODDPAGEFLAG:
2196 case LyXRC::RC_PRINTPAGERANGEFLAG:
2197 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2198 case LyXRC::RC_PRINTPAPERFLAG:
2199 case LyXRC::RC_PRINTREVERSEFLAG:
2200 case LyXRC::RC_PRINTSPOOL_COMMAND:
2201 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2202 case LyXRC::RC_PRINTTOFILE:
2203 case LyXRC::RC_PRINTTOPRINTER:
2204 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2205 case LyXRC::RC_PRINT_COMMAND:
2206 case LyXRC::RC_RTL_SUPPORT:
2207 case LyXRC::RC_SCREEN_DPI:
2208 case LyXRC::RC_SCREEN_FONT_ROMAN:
2209 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2210 case LyXRC::RC_SCREEN_FONT_SANS:
2211 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2212 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2213 case LyXRC::RC_SCREEN_FONT_SIZES:
2214 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2215 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2216 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2217 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2218 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2219 case LyXRC::RC_SCREEN_ZOOM:
2220 case LyXRC::RC_SERVERPIPE:
2221 case LyXRC::RC_SET_COLOR:
2222 case LyXRC::RC_SHOW_BANNER:
2223 case LyXRC::RC_SPELL_COMMAND:
2224 case LyXRC::RC_TEMPDIRPATH:
2225 case LyXRC::RC_TEMPLATEPATH:
2226 case LyXRC::RC_TEX_ALLOWS_SPACES:
2227 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2228 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2229 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2231 case LyXRC::RC_UIFILE:
2232 case LyXRC::RC_USER_EMAIL:
2233 case LyXRC::RC_USER_NAME:
2234 case LyXRC::RC_USETEMPDIR:
2235 case LyXRC::RC_USE_ALT_LANG:
2236 case LyXRC::RC_USE_CONVERTER_CACHE:
2237 case LyXRC::RC_USE_ESC_CHARS:
2238 case LyXRC::RC_USE_INP_ENC:
2239 case LyXRC::RC_USE_PERS_DICT:
2240 case LyXRC::RC_USE_SPELL_LIB:
2241 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2242 case LyXRC::RC_VIEWER:
2243 case LyXRC::RC_LAST: