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"
56 #include "lyxserver.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/LyXKeySym.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/forkedcontr.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(LCursor 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 //LCursor safe = *this;
160 for ( ; cursor.depth(); cursor.pop()) {
161 //lyxerr << "\nLCursor::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(kb_keymap * kb)
212 keyseq.reset(new kb_sequence(kb, kb));
213 cancel_meta_seq.reset(new kb_sequence(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.
268 boost::tie(new_pit, new_id) = view()->moveToPosition(bm.par_pit, bm.par_id, bm.par_pos);
269 // if par_id or pit has been changed, reset par_pit and par_id
270 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
271 if (bm.par_pit != new_pit || bm.par_id != new_id)
272 const_cast<BookmarksSection::Bookmark &>(bm).setPos(new_pit, new_id);
277 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
279 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
281 // Do nothing if we have nothing (JMarc)
282 if (!keysym->isOK()) {
283 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
288 if (keysym->isModifier()) {
289 lyxerr[Debug::KEY] << "isModifier true" << endl;
293 //Encoding const * encoding = view()->cursor().getEncoding();
294 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
295 // FIXME: encoded_last_key shadows the member variable of the same
296 // name. Is that intended?
297 char_type encoded_last_key = keysym->getUCSEncoded();
299 // Do a one-deep top-level lookup for
300 // cancel and meta-fake keys. RVDK_PATCH_5
301 cancel_meta_seq->reset();
303 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
304 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
305 << " action first set to [" << func.action << ']'
308 // When not cancel or meta-fake, do the normal lookup.
309 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
310 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
311 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
312 // remove Caps Lock and Mod2 as a modifiers
313 func = keyseq->addkey(keysym, (state | meta_fake_bit));
314 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
315 << "action now set to ["
316 << func.action << ']' << endl;
319 // Dont remove this unless you know what you are doing.
320 meta_fake_bit = key_modifier::none;
322 // Can this happen now ?
323 if (func.action == LFUN_NOACTION) {
324 func = FuncRequest(LFUN_COMMAND_PREFIX);
327 if (lyxerr.debugging(Debug::KEY)) {
328 lyxerr << BOOST_CURRENT_FUNCTION
330 << func.action << "]["
331 << to_utf8(keyseq->print(false)) << ']'
335 // already here we know if it any point in going further
336 // why not return already here if action == -1 and
337 // num_bytes == 0? (Lgb)
339 if (keyseq->length() > 1) {
340 lyx_view_->message(keyseq->print(true));
344 // Maybe user can only reach the key via holding down shift.
345 // Let's see. But only if shift is the only modifier
346 if (func.action == LFUN_UNKNOWN_ACTION &&
347 state == key_modifier::shift) {
348 lyxerr[Debug::KEY] << "Trying without shift" << endl;
349 func = keyseq->addkey(keysym, key_modifier::none);
350 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
353 if (func.action == LFUN_UNKNOWN_ACTION) {
354 // Hmm, we didn't match any of the keysequences. See
355 // if it's normal insertable text not already covered
357 if (keysym->isText() && keyseq->length() == 1) {
358 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
359 func = FuncRequest(LFUN_SELF_INSERT,
360 FuncRequest::KEYBOARD);
362 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
363 lyx_view_->message(_("Unknown function."));
368 if (func.action == LFUN_SELF_INSERT) {
369 if (encoded_last_key != 0) {
370 docstring const arg(1, encoded_last_key);
371 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
372 FuncRequest::KEYBOARD));
374 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
382 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
384 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
387 LCursor & cur = view()->cursor();
389 /* In LyX/Mac, when a dialog is open, the menus of the
390 application can still be accessed without giving focus to
391 the main window. In this case, we want to disable the menu
392 entries that are buffer-related.
394 Note that this code is not perfect, as bug 1941 attests:
395 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
397 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
398 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
401 if (cmd.action == LFUN_NOACTION) {
402 flag.message(from_utf8(N_("Nothing to do")));
407 switch (cmd.action) {
408 case LFUN_UNKNOWN_ACTION:
409 #ifndef HAVE_LIBAIKSAURUS
410 case LFUN_THESAURUS_ENTRY:
420 if (flag.unknown()) {
421 flag.message(from_utf8(N_("Unknown action")));
425 if (!flag.enabled()) {
426 if (flag.message().empty())
427 flag.message(from_utf8(N_("Command disabled")));
431 // Check whether we need a buffer
432 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
434 flag.message(from_utf8(N_("Command not allowed with"
435 "out any document open")));
440 // I would really like to avoid having this switch and rather try to
441 // encode this in the function itself.
442 // -- And I'd rather let an inset decide which LFUNs it is willing
443 // to handle (Andre')
445 switch (cmd.action) {
446 case LFUN_BUFFER_TOGGLE_READ_ONLY:
447 flag.setOnOff(buf->isReadonly());
450 case LFUN_BUFFER_SWITCH:
451 // toggle on the current buffer, but do not toggle off
452 // the other ones (is that a good idea?)
453 if (to_utf8(cmd.argument()) == buf->fileName())
457 case LFUN_BUFFER_EXPORT:
458 enable = cmd.argument() == "custom"
459 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
462 case LFUN_BUFFER_CHKTEX:
463 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
466 case LFUN_BUILD_PROGRAM:
467 enable = Exporter::isExportable(*buf, "program");
470 case LFUN_LAYOUT_TABULAR:
471 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
475 case LFUN_LAYOUT_PARAGRAPH:
476 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
479 case LFUN_VC_REGISTER:
480 enable = !buf->lyxvc().inUse();
482 case LFUN_VC_CHECK_IN:
483 enable = buf->lyxvc().inUse() && !buf->isReadonly();
485 case LFUN_VC_CHECK_OUT:
486 enable = buf->lyxvc().inUse() && buf->isReadonly();
489 case LFUN_VC_UNDO_LAST:
490 enable = buf->lyxvc().inUse();
492 case LFUN_BUFFER_RELOAD:
493 enable = !buf->isUnnamed() && !buf->isClean();
496 case LFUN_INSET_SETTINGS: {
500 InsetBase::Code code = cur.inset().lyxCode();
502 case InsetBase::TABULAR_CODE:
503 enable = cmd.argument() == "tabular";
505 case InsetBase::ERT_CODE:
506 enable = cmd.argument() == "ert";
508 case InsetBase::FLOAT_CODE:
509 enable = cmd.argument() == "float";
511 case InsetBase::WRAP_CODE:
512 enable = cmd.argument() == "wrap";
514 case InsetBase::NOTE_CODE:
515 enable = cmd.argument() == "note";
517 case InsetBase::BRANCH_CODE:
518 enable = cmd.argument() == "branch";
520 case InsetBase::BOX_CODE:
521 enable = cmd.argument() == "box";
529 case LFUN_INSET_APPLY: {
530 string const name = cmd.getArg(0);
531 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
533 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
535 if (!inset->getStatus(cur, fr, fs)) {
536 // Every inset is supposed to handle this
541 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
542 flag |= getStatus(fr);
544 enable = flag.enabled();
548 case LFUN_DIALOG_SHOW: {
549 string const name = cmd.getArg(0);
551 enable = name == "aboutlyx"
555 || name == "texinfo";
556 else if (name == "print")
557 enable = Exporter::isExportable(*buf, "dvi")
558 && lyxrc.print_command != "none";
559 else if (name == "character" || name == "mathpanel")
560 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
561 else if (name == "latexlog")
562 enable = isFileReadable(FileName(buf->getLogName().second));
563 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
564 else if (name == "spellchecker")
567 else if (name == "vclog")
568 enable = buf->lyxvc().inUse();
569 else if (name == "view-source")
574 case LFUN_DIALOG_SHOW_NEW_INSET:
575 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
576 if (cur.inset().lyxCode() == InsetBase::CAPTION_CODE) {
578 if (cur.inset().getStatus(cur, cmd, flag))
583 case LFUN_DIALOG_UPDATE: {
584 string const name = cmd.getArg(0);
586 enable = name == "prefs";
590 case LFUN_CITATION_INSERT: {
591 FuncRequest fr(LFUN_INSET_INSERT, "citation");
592 enable = getStatus(fr).enabled();
596 case LFUN_BUFFER_WRITE: {
597 enable = view()->buffer()->isUnnamed()
598 || !view()->buffer()->isClean();
602 case LFUN_BOOKMARK_GOTO: {
603 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
604 enable = LyX::ref().session().bookmarks().isValid(num);
608 case LFUN_BOOKMARK_CLEAR:
609 enable = LyX::ref().session().bookmarks().size() > 0;
612 case LFUN_TOOLBAR_TOGGLE_STATE: {
613 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
614 if (!(flags & ToolbarBackend::AUTO))
615 flag.setOnOff(flags & ToolbarBackend::ON);
619 // this one is difficult to get right. As a half-baked
620 // solution, we consider only the first action of the sequence
621 case LFUN_COMMAND_SEQUENCE: {
622 // argument contains ';'-terminated commands
623 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
624 FuncRequest func(lyxaction.lookupFunc(firstcmd));
625 func.origin = cmd.origin;
626 flag = getStatus(func);
629 case LFUN_BUFFER_NEW:
630 case LFUN_BUFFER_NEW_TEMPLATE:
631 case LFUN_WORD_FIND_FORWARD:
632 case LFUN_WORD_FIND_BACKWARD:
633 case LFUN_COMMAND_PREFIX:
634 case LFUN_COMMAND_EXECUTE:
636 case LFUN_META_PREFIX:
637 case LFUN_BUFFER_CLOSE:
638 case LFUN_BUFFER_WRITE_AS:
639 case LFUN_BUFFER_UPDATE:
640 case LFUN_BUFFER_VIEW:
641 case LFUN_BUFFER_IMPORT:
643 case LFUN_BUFFER_AUTO_SAVE:
644 case LFUN_RECONFIGURE:
648 case LFUN_DROP_LAYOUTS_CHOICE:
650 case LFUN_SERVER_GET_NAME:
651 case LFUN_SERVER_NOTIFY:
652 case LFUN_SERVER_GOTO_FILE_ROW:
653 case LFUN_DIALOG_HIDE:
654 case LFUN_DIALOG_DISCONNECT_INSET:
655 case LFUN_BUFFER_CHILD_OPEN:
656 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
657 case LFUN_KEYMAP_OFF:
658 case LFUN_KEYMAP_PRIMARY:
659 case LFUN_KEYMAP_SECONDARY:
660 case LFUN_KEYMAP_TOGGLE:
662 case LFUN_BUFFER_EXPORT_CUSTOM:
663 case LFUN_BUFFER_PRINT:
664 case LFUN_PREFERENCES_SAVE:
665 case LFUN_SCREEN_FONT_UPDATE:
668 case LFUN_EXTERNAL_EDIT:
669 case LFUN_GRAPHICS_EDIT:
670 case LFUN_ALL_INSETS_TOGGLE:
671 case LFUN_BUFFER_LANGUAGE:
672 case LFUN_TEXTCLASS_APPLY:
673 case LFUN_TEXTCLASS_LOAD:
674 case LFUN_BUFFER_SAVE_AS_DEFAULT:
675 case LFUN_BUFFER_PARAMS_APPLY:
676 case LFUN_LYXRC_APPLY:
677 case LFUN_BUFFER_NEXT:
678 case LFUN_BUFFER_PREVIOUS:
679 case LFUN_WINDOW_NEW:
680 case LFUN_WINDOW_CLOSE:
682 // these are handled in our dispatch()
686 if (!getLocalStatus(cur, cmd, flag))
687 flag = view()->getStatus(cmd);
693 // Can we use a readonly buffer?
694 if (buf && buf->isReadonly()
695 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
696 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
697 flag.message(from_utf8(N_("Document is read-only")));
701 // Are we in a DELETED change-tracking region?
702 if (buf && lookupChangeType(cur, true) == Change::DELETED
703 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
704 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
705 flag.message(from_utf8(N_("This portion of the document is deleted.")));
709 // the default error message if we disable the command
710 if (!flag.enabled() && flag.message().empty())
711 flag.message(from_utf8(N_("Command disabled")));
717 bool LyXFunc::ensureBufferClean(BufferView * bv)
719 Buffer & buf = *bv->buffer();
723 docstring const file = makeDisplayPath(buf.fileName(), 30);
724 docstring text = bformat(_("The document %1$s has unsaved "
725 "changes.\n\nDo you want to save "
726 "the document?"), file);
727 int const ret = Alert::prompt(_("Save changed document?"),
728 text, 0, 1, _("&Save"),
732 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
734 return buf.isClean();
740 void showPrintError(string const & name)
742 docstring str = bformat(_("Could not print the document %1$s.\n"
743 "Check that your printer is set up correctly."),
744 makeDisplayPath(name, 50));
745 Alert::error(_("Print document failed"), str);
749 void loadTextclass(string const & name)
751 std::pair<bool, textclass_type> const tc_pair =
752 textclasslist.numberOfClass(name);
754 if (!tc_pair.first) {
755 lyxerr << "Document class \"" << name
756 << "\" does not exist."
761 textclass_type const tc = tc_pair.second;
763 if (!textclasslist[tc].load()) {
764 docstring s = bformat(_("The document could not be converted\n"
765 "into the document class %1$s."),
766 from_utf8(textclasslist[tc].name()));
767 Alert::error(_("Could not change class"), s);
772 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
777 void LyXFunc::dispatch(FuncRequest const & cmd)
779 string const argument = to_utf8(cmd.argument());
780 kb_action const action = cmd.action;
782 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
783 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
785 // we have not done anything wrong yet.
787 dispatch_buffer.erase();
789 // redraw the screen at the end (first of the two drawing steps).
790 //This is done unless explicitely requested otherwise
791 Update::flags updateFlags = Update::FitCursor;
793 FuncStatus const flag = getStatus(cmd);
794 if (!flag.enabled()) {
795 // We cannot use this function here
796 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
797 << lyxaction.getActionName(action)
798 << " [" << action << "] is disabled at this location"
800 setErrorMessage(flag.message());
804 case LFUN_WORD_FIND_FORWARD:
805 case LFUN_WORD_FIND_BACKWARD: {
806 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
807 static docstring last_search;
808 docstring searched_string;
810 if (!cmd.argument().empty()) {
811 last_search = cmd.argument();
812 searched_string = cmd.argument();
814 searched_string = last_search;
817 if (searched_string.empty())
820 bool const fw = action == LFUN_WORD_FIND_FORWARD;
821 docstring const data =
822 find2string(searched_string, true, false, fw);
823 find(view(), FuncRequest(LFUN_WORD_FIND, data));
827 case LFUN_COMMAND_PREFIX:
828 BOOST_ASSERT(lyx_view_);
829 lyx_view_->message(keyseq->printOptions(true));
832 case LFUN_COMMAND_EXECUTE:
833 BOOST_ASSERT(lyx_view_);
834 lyx_view_->getToolbars().display("minibuffer", true);
835 lyx_view_->focus_command_buffer();
839 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
841 meta_fake_bit = key_modifier::none;
842 if (view()->buffer())
843 // cancel any selection
844 dispatch(FuncRequest(LFUN_MARK_OFF));
845 setMessage(from_ascii(N_("Cancel")));
848 case LFUN_META_PREFIX:
849 meta_fake_bit = key_modifier::alt;
850 setMessage(keyseq->print(true));
853 case LFUN_BUFFER_TOGGLE_READ_ONLY:
854 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
855 if (lyx_view_->buffer()->lyxvc().inUse())
856 lyx_view_->buffer()->lyxvc().toggleReadOnly();
858 lyx_view_->buffer()->setReadonly(
859 !lyx_view_->buffer()->isReadonly());
862 // --- Menus -----------------------------------------------
863 case LFUN_BUFFER_NEW:
864 menuNew(argument, false);
867 case LFUN_BUFFER_NEW_TEMPLATE:
868 menuNew(argument, true);
871 case LFUN_BUFFER_CLOSE:
876 case LFUN_BUFFER_WRITE:
877 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
878 if (!lyx_view_->buffer()->isUnnamed()) {
879 docstring const str = bformat(_("Saving document %1$s..."),
880 makeDisplayPath(lyx_view_->buffer()->fileName()));
881 lyx_view_->message(str);
882 menuWrite(lyx_view_->buffer());
883 lyx_view_->message(str + _(" done."));
885 writeAs(lyx_view_->buffer());
887 updateFlags = Update::None;
890 case LFUN_BUFFER_WRITE_AS:
891 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
892 writeAs(lyx_view_->buffer(), argument);
893 updateFlags = Update::None;
896 case LFUN_BUFFER_RELOAD: {
897 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
898 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
899 docstring text = bformat(_("Any changes will be lost. Are you sure "
900 "you want to revert to the saved version of the document %1$s?"), file);
901 int const ret = Alert::prompt(_("Revert to saved document?"),
902 text, 0, 1, _("&Revert"), _("&Cancel"));
909 case LFUN_BUFFER_UPDATE:
910 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
911 Exporter::Export(lyx_view_->buffer(), argument, true);
914 case LFUN_BUFFER_VIEW:
915 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
916 Exporter::preview(lyx_view_->buffer(), argument);
919 case LFUN_BUILD_PROGRAM:
920 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
921 Exporter::Export(lyx_view_->buffer(), "program", true);
924 case LFUN_BUFFER_CHKTEX:
925 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
926 lyx_view_->buffer()->runChktex();
929 case LFUN_BUFFER_EXPORT:
930 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
931 if (argument == "custom")
932 lyx_view_->getDialogs().show("sendto");
934 Exporter::Export(lyx_view_->buffer(), argument, false);
938 case LFUN_BUFFER_EXPORT_CUSTOM: {
939 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
941 string command = split(argument, format_name, ' ');
942 Format const * format = formats.getFormat(format_name);
944 lyxerr << "Format \"" << format_name
945 << "\" not recognized!"
950 Buffer * buffer = lyx_view_->buffer();
952 // The name of the file created by the conversion process
955 // Output to filename
956 if (format->name() == "lyx") {
957 string const latexname =
958 buffer->getLatexName(false);
959 filename = changeExtension(latexname,
960 format->extension());
961 filename = addName(buffer->temppath(), filename);
963 if (!buffer->writeFile(FileName(filename)))
967 Exporter::Export(buffer, format_name, true, filename);
970 // Substitute $$FName for filename
971 if (!contains(command, "$$FName"))
972 command = "( " + command + " ) < $$FName";
973 command = subst(command, "$$FName", filename);
975 // Execute the command in the background
977 call.startscript(Systemcall::DontWait, command);
981 case LFUN_BUFFER_PRINT: {
982 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
985 string command = split(split(argument, target, ' '),
989 || target_name.empty()
990 || command.empty()) {
991 lyxerr << "Unable to parse \""
992 << argument << '"' << std::endl;
995 if (target != "printer" && target != "file") {
996 lyxerr << "Unrecognized target \""
997 << target << '"' << std::endl;
1001 Buffer * buffer = lyx_view_->buffer();
1003 if (!Exporter::Export(buffer, "dvi", true)) {
1004 showPrintError(buffer->fileName());
1008 // Push directory path.
1009 string const path = buffer->temppath();
1010 support::Path p(path);
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 theLyXServer().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())) {
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 += LyXLex::quoteString(logfile.second);
1275 lyx_view_->getDialogs().show("log", data);
1276 } else if (name == "vclog") {
1277 string const data = "vc " +
1278 LyXLex::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 InsetBase * 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 std::ostringstream os;
1384 os << "citation LatexCommand\n"
1385 << "\\cite" << opt1 << "{" << arg << "}\n"
1387 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1390 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1394 case LFUN_BUFFER_CHILD_OPEN: {
1395 BOOST_ASSERT(lyx_view_);
1396 FileName const filename =
1397 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1398 setMessage(bformat(_("Opening child document %1$s..."),
1399 makeDisplayPath(filename.absFilename())));
1400 view()->saveBookmark(false);
1401 string const parentfilename = lyx_view_->buffer()->fileName();
1402 if (theBufferList().exists(filename.absFilename()))
1403 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1405 lyx_view_->loadLyXFile(filename);
1406 // Set the parent name of the child document.
1407 // This makes insertion of citations and references in the child work,
1408 // when the target is in the parent or another child document.
1409 lyx_view_->buffer()->setParentName(parentfilename);
1413 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1414 BOOST_ASSERT(lyx_view_);
1415 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1418 case LFUN_KEYMAP_OFF:
1419 BOOST_ASSERT(lyx_view_);
1420 lyx_view_->view()->getIntl().keyMapOn(false);
1423 case LFUN_KEYMAP_PRIMARY:
1424 BOOST_ASSERT(lyx_view_);
1425 lyx_view_->view()->getIntl().keyMapPrim();
1428 case LFUN_KEYMAP_SECONDARY:
1429 BOOST_ASSERT(lyx_view_);
1430 lyx_view_->view()->getIntl().keyMapSec();
1433 case LFUN_KEYMAP_TOGGLE:
1434 BOOST_ASSERT(lyx_view_);
1435 lyx_view_->view()->getIntl().toggleKeyMap();
1441 string rest = split(argument, countstr, ' ');
1442 istringstream is(countstr);
1445 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1446 for (int i = 0; i < count; ++i)
1447 dispatch(lyxaction.lookupFunc(rest));
1451 case LFUN_COMMAND_SEQUENCE: {
1452 // argument contains ';'-terminated commands
1453 string arg = argument;
1454 while (!arg.empty()) {
1456 arg = split(arg, first, ';');
1457 FuncRequest func(lyxaction.lookupFunc(first));
1458 func.origin = cmd.origin;
1464 case LFUN_PREFERENCES_SAVE: {
1465 lyxrc.write(makeAbsPath("preferences",
1466 package().user_support()),
1471 case LFUN_SCREEN_FONT_UPDATE:
1472 BOOST_ASSERT(lyx_view_);
1473 // handle the screen font changes.
1474 theFontLoader().update();
1475 /// FIXME: only the current view will be updated. the Gui
1476 /// class is able to furnish the list of views.
1477 updateFlags = Update::Force;
1480 case LFUN_SET_COLOR: {
1482 string const x11_name = split(argument, lyx_name, ' ');
1483 if (lyx_name.empty() || x11_name.empty()) {
1484 setErrorMessage(from_ascii(N_(
1485 "Syntax: set-color <lyx_name>"
1490 bool const graphicsbg_changed =
1491 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1492 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1494 if (!lcolor.setColor(lyx_name, x11_name)) {
1496 bformat(_("Set-color \"%1$s\" failed "
1497 "- color is undefined or "
1498 "may not be redefined"),
1499 from_utf8(lyx_name)));
1503 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1505 if (graphicsbg_changed) {
1506 #ifdef WITH_WARNINGS
1507 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1510 graphics::GCache::get().changeDisplay(true);
1517 BOOST_ASSERT(lyx_view_);
1518 lyx_view_->message(from_utf8(argument));
1521 case LFUN_EXTERNAL_EDIT: {
1522 BOOST_ASSERT(lyx_view_);
1523 FuncRequest fr(action, argument);
1524 InsetExternal().dispatch(view()->cursor(), fr);
1528 case LFUN_GRAPHICS_EDIT: {
1529 FuncRequest fr(action, argument);
1530 InsetGraphics().dispatch(view()->cursor(), fr);
1534 case LFUN_INSET_APPLY: {
1535 BOOST_ASSERT(lyx_view_);
1536 string const name = cmd.getArg(0);
1537 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1539 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1540 inset->dispatch(view()->cursor(), fr);
1542 FuncRequest fr(LFUN_INSET_INSERT, argument);
1545 // ideally, the update flag should be set by the insets,
1546 // but this is not possible currently
1547 updateFlags = Update::Force | Update::FitCursor;
1551 case LFUN_ALL_INSETS_TOGGLE: {
1552 BOOST_ASSERT(lyx_view_);
1554 string const name = split(argument, action, ' ');
1555 InsetBase::Code const inset_code =
1556 InsetBase::translate(name);
1558 LCursor & cur = view()->cursor();
1559 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1561 InsetBase & inset = lyx_view_->buffer()->inset();
1562 InsetIterator it = inset_iterator_begin(inset);
1563 InsetIterator const end = inset_iterator_end(inset);
1564 for (; it != end; ++it) {
1565 if (inset_code == InsetBase::NO_CODE
1566 || inset_code == it->lyxCode()) {
1567 LCursor tmpcur = cur;
1568 tmpcur.pushLeft(*it);
1569 it->dispatch(tmpcur, fr);
1572 updateFlags = Update::Force | Update::FitCursor;
1576 case LFUN_BUFFER_LANGUAGE: {
1577 BOOST_ASSERT(lyx_view_);
1578 Buffer & buffer = *lyx_view_->buffer();
1579 Language const * oldL = buffer.params().language;
1580 Language const * newL = languages.getLanguage(argument);
1581 if (!newL || oldL == newL)
1584 if (oldL->rightToLeft() == newL->rightToLeft()
1585 && !buffer.isMultiLingual())
1586 buffer.changeLanguage(oldL, newL);
1588 buffer.updateDocLang(newL);
1592 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1593 string const fname =
1594 addName(addPath(package().user_support(), "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 LCursor & cur = view()->cursor();
1639 FuncRequest fr(LFUN_INSET_REFRESH);
1641 InsetBase & 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() == InsetBase::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(), "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(), "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 // save current cursor position
2044 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2045 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2046 // goto bookmark to update bookmark pit.
2047 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2048 gotoBookmark(i+1, false, false);
2049 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2050 if (theBufferList().empty()) {
2051 // need this otherwise SEGV may occur while
2052 // trying to set variables that don't exist
2053 // since there's no current buffer
2054 lyx_view_->getDialogs().hideBufferDependent();
2056 lyx_view_->setBuffer(theBufferList().first());
2062 void LyXFunc::reloadBuffer()
2064 FileName filename(lyx_view_->buffer()->fileName());
2066 lyx_view_->loadLyXFile(filename);
2069 // Each "lyx_view_" should have it's own message method. lyxview and
2070 // the minibuffer would use the minibuffer, but lyxserver would
2071 // send an ERROR signal to its client. Alejandro 970603
2072 // This function is bit problematic when it comes to NLS, to make the
2073 // lyx servers client be language indepenent we must not translate
2074 // strings sent to this func.
2075 void LyXFunc::setErrorMessage(docstring const & m) const
2077 dispatch_buffer = m;
2082 void LyXFunc::setMessage(docstring const & m) const
2084 dispatch_buffer = m;
2088 docstring const LyXFunc::viewStatusMessage()
2090 // When meta-fake key is pressed, show the key sequence so far + "M-".
2092 return keyseq->print(true) + "M-";
2094 // Else, when a non-complete key sequence is pressed,
2095 // show the available options.
2096 if (keyseq->length() > 0 && !keyseq->deleted())
2097 return keyseq->printOptions(true);
2099 if (!view()->buffer())
2100 return _("Welcome to LyX!");
2102 return view()->cursor().currentState();
2106 BufferView * LyXFunc::view() const
2108 BOOST_ASSERT(lyx_view_);
2109 return lyx_view_->view();
2113 bool LyXFunc::wasMetaKey() const
2115 return (meta_fake_bit != key_modifier::none);
2121 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2123 // Why the switch you might ask. It is a trick to ensure that all
2124 // the elements in the LyXRCTags enum is handled. As you can see
2125 // there are no breaks at all. So it is just a huge fall-through.
2126 // The nice thing is that we will get a warning from the compiler
2127 // if we forget an element.
2128 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2130 case LyXRC::RC_ACCEPT_COMPOUND:
2131 case LyXRC::RC_ALT_LANG:
2132 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2133 case LyXRC::RC_PLAINTEXT_LINELEN:
2134 case LyXRC::RC_AUTOREGIONDELETE:
2135 case LyXRC::RC_AUTORESET_OPTIONS:
2136 case LyXRC::RC_AUTOSAVE:
2137 case LyXRC::RC_AUTO_NUMBER:
2138 case LyXRC::RC_BACKUPDIR_PATH:
2139 case LyXRC::RC_BIBTEX_COMMAND:
2140 case LyXRC::RC_BINDFILE:
2141 case LyXRC::RC_CHECKLASTFILES:
2142 case LyXRC::RC_USELASTFILEPOS:
2143 case LyXRC::RC_LOADSESSION:
2144 case LyXRC::RC_CHKTEX_COMMAND:
2145 case LyXRC::RC_CONVERTER:
2146 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2147 case LyXRC::RC_COPIER:
2148 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2149 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2150 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2151 case LyXRC::RC_DATE_INSERT_FORMAT:
2152 case LyXRC::RC_DEFAULT_LANGUAGE:
2153 case LyXRC::RC_DEFAULT_PAPERSIZE:
2154 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2155 case LyXRC::RC_DISPLAY_GRAPHICS:
2156 case LyXRC::RC_DOCUMENTPATH:
2157 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2158 string const encoded = FileName(
2159 lyxrc_new.document_path).toFilesystemEncoding();
2160 if (fs::exists(encoded) && fs::is_directory(encoded))
2161 support::package().document_dir() = lyxrc.document_path;
2163 case LyXRC::RC_ESC_CHARS:
2164 case LyXRC::RC_FONT_ENCODING:
2165 case LyXRC::RC_FORMAT:
2166 case LyXRC::RC_INDEX_COMMAND:
2167 case LyXRC::RC_INPUT:
2168 case LyXRC::RC_KBMAP:
2169 case LyXRC::RC_KBMAP_PRIMARY:
2170 case LyXRC::RC_KBMAP_SECONDARY:
2171 case LyXRC::RC_LABEL_INIT_LENGTH:
2172 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2173 case LyXRC::RC_LANGUAGE_AUTO_END:
2174 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2175 case LyXRC::RC_LANGUAGE_COMMAND_END:
2176 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2177 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2178 case LyXRC::RC_LANGUAGE_PACKAGE:
2179 case LyXRC::RC_LANGUAGE_USE_BABEL:
2180 case LyXRC::RC_MAKE_BACKUP:
2181 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2182 case LyXRC::RC_NUMLASTFILES:
2183 case LyXRC::RC_PATH_PREFIX:
2184 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2185 support::prependEnvPath("PATH", lyxrc.path_prefix);
2187 case LyXRC::RC_PERS_DICT:
2188 case LyXRC::RC_PREVIEW:
2189 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2190 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2191 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2192 case LyXRC::RC_PRINTCOPIESFLAG:
2193 case LyXRC::RC_PRINTER:
2194 case LyXRC::RC_PRINTEVENPAGEFLAG:
2195 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2196 case LyXRC::RC_PRINTFILEEXTENSION:
2197 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2198 case LyXRC::RC_PRINTODDPAGEFLAG:
2199 case LyXRC::RC_PRINTPAGERANGEFLAG:
2200 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2201 case LyXRC::RC_PRINTPAPERFLAG:
2202 case LyXRC::RC_PRINTREVERSEFLAG:
2203 case LyXRC::RC_PRINTSPOOL_COMMAND:
2204 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2205 case LyXRC::RC_PRINTTOFILE:
2206 case LyXRC::RC_PRINTTOPRINTER:
2207 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2208 case LyXRC::RC_PRINT_COMMAND:
2209 case LyXRC::RC_RTL_SUPPORT:
2210 case LyXRC::RC_SCREEN_DPI:
2211 case LyXRC::RC_SCREEN_FONT_ROMAN:
2212 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2213 case LyXRC::RC_SCREEN_FONT_SANS:
2214 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2215 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2216 case LyXRC::RC_SCREEN_FONT_SIZES:
2217 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2218 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2219 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2220 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2221 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2222 case LyXRC::RC_SCREEN_ZOOM:
2223 case LyXRC::RC_SERVERPIPE:
2224 case LyXRC::RC_SET_COLOR:
2225 case LyXRC::RC_SHOW_BANNER:
2226 case LyXRC::RC_SPELL_COMMAND:
2227 case LyXRC::RC_TEMPDIRPATH:
2228 case LyXRC::RC_TEMPLATEPATH:
2229 case LyXRC::RC_TEX_ALLOWS_SPACES:
2230 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2231 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2232 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2234 case LyXRC::RC_UIFILE:
2235 case LyXRC::RC_USER_EMAIL:
2236 case LyXRC::RC_USER_NAME:
2237 case LyXRC::RC_USETEMPDIR:
2238 case LyXRC::RC_USE_ALT_LANG:
2239 case LyXRC::RC_USE_CONVERTER_CACHE:
2240 case LyXRC::RC_USE_ESC_CHARS:
2241 case LyXRC::RC_USE_INP_ENC:
2242 case LyXRC::RC_USE_PERS_DICT:
2243 case LyXRC::RC_USE_SPELL_LIB:
2244 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2245 case LyXRC::RC_VIEWER:
2246 case LyXRC::RC_LAST: