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 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
329 << func.action << "]["
330 << to_utf8(keyseq->print(false)) << ']'
333 // already here we know if it any point in going further
334 // why not return already here if action == -1 and
335 // num_bytes == 0? (Lgb)
337 if (keyseq->length() > 1) {
338 lyx_view_->message(keyseq->print(true));
342 // Maybe user can only reach the key via holding down shift.
343 // Let's see. But only if shift is the only modifier
344 if (func.action == LFUN_UNKNOWN_ACTION &&
345 state == key_modifier::shift) {
346 LYXERR(Debug::KEY) << "Trying without shift" << endl;
347 func = keyseq->addkey(keysym, key_modifier::none);
348 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
351 if (func.action == LFUN_UNKNOWN_ACTION) {
352 // Hmm, we didn't match any of the keysequences. See
353 // if it's normal insertable text not already covered
355 if (keysym->isText() && keyseq->length() == 1) {
356 LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
357 func = FuncRequest(LFUN_SELF_INSERT,
358 FuncRequest::KEYBOARD);
360 LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
361 lyx_view_->message(_("Unknown function."));
366 if (func.action == LFUN_SELF_INSERT) {
367 if (encoded_last_key != 0) {
368 docstring const arg(1, encoded_last_key);
369 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
370 FuncRequest::KEYBOARD));
372 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
380 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
382 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
385 LCursor & cur = view()->cursor();
387 /* In LyX/Mac, when a dialog is open, the menus of the
388 application can still be accessed without giving focus to
389 the main window. In this case, we want to disable the menu
390 entries that are buffer-related.
392 Note that this code is not perfect, as bug 1941 attests:
393 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
395 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
396 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
399 if (cmd.action == LFUN_NOACTION) {
400 flag.message(from_utf8(N_("Nothing to do")));
405 switch (cmd.action) {
406 case LFUN_UNKNOWN_ACTION:
407 #ifndef HAVE_LIBAIKSAURUS
408 case LFUN_THESAURUS_ENTRY:
418 if (flag.unknown()) {
419 flag.message(from_utf8(N_("Unknown action")));
423 if (!flag.enabled()) {
424 if (flag.message().empty())
425 flag.message(from_utf8(N_("Command disabled")));
429 // Check whether we need a buffer
430 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
432 flag.message(from_utf8(N_("Command not allowed with"
433 "out any document open")));
438 // I would really like to avoid having this switch and rather try to
439 // encode this in the function itself.
440 // -- And I'd rather let an inset decide which LFUNs it is willing
441 // to handle (Andre')
443 switch (cmd.action) {
444 case LFUN_BUFFER_TOGGLE_READ_ONLY:
445 flag.setOnOff(buf->isReadonly());
448 case LFUN_BUFFER_SWITCH:
449 // toggle on the current buffer, but do not toggle off
450 // the other ones (is that a good idea?)
451 if (to_utf8(cmd.argument()) == buf->fileName())
455 case LFUN_BUFFER_EXPORT:
456 enable = cmd.argument() == "custom"
457 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
460 case LFUN_BUFFER_CHKTEX:
461 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
464 case LFUN_BUILD_PROGRAM:
465 enable = Exporter::isExportable(*buf, "program");
468 case LFUN_LAYOUT_TABULAR:
469 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
473 case LFUN_LAYOUT_PARAGRAPH:
474 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
477 case LFUN_VC_REGISTER:
478 enable = !buf->lyxvc().inUse();
480 case LFUN_VC_CHECK_IN:
481 enable = buf->lyxvc().inUse() && !buf->isReadonly();
483 case LFUN_VC_CHECK_OUT:
484 enable = buf->lyxvc().inUse() && buf->isReadonly();
487 case LFUN_VC_UNDO_LAST:
488 enable = buf->lyxvc().inUse();
490 case LFUN_BUFFER_RELOAD:
491 enable = !buf->isUnnamed() && !buf->isClean();
494 case LFUN_INSET_SETTINGS: {
498 InsetBase::Code code = cur.inset().lyxCode();
500 case InsetBase::TABULAR_CODE:
501 enable = cmd.argument() == "tabular";
503 case InsetBase::ERT_CODE:
504 enable = cmd.argument() == "ert";
506 case InsetBase::FLOAT_CODE:
507 enable = cmd.argument() == "float";
509 case InsetBase::WRAP_CODE:
510 enable = cmd.argument() == "wrap";
512 case InsetBase::NOTE_CODE:
513 enable = cmd.argument() == "note";
515 case InsetBase::BRANCH_CODE:
516 enable = cmd.argument() == "branch";
518 case InsetBase::BOX_CODE:
519 enable = cmd.argument() == "box";
527 case LFUN_INSET_APPLY: {
528 string const name = cmd.getArg(0);
529 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
531 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
533 if (!inset->getStatus(cur, fr, fs)) {
534 // Every inset is supposed to handle this
539 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
540 flag |= getStatus(fr);
542 enable = flag.enabled();
546 case LFUN_DIALOG_SHOW: {
547 string const name = cmd.getArg(0);
549 enable = name == "aboutlyx"
553 || name == "texinfo";
554 else if (name == "print")
555 enable = Exporter::isExportable(*buf, "dvi")
556 && lyxrc.print_command != "none";
557 else if (name == "character" || name == "mathpanel")
558 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
559 else if (name == "latexlog")
560 enable = isFileReadable(FileName(buf->getLogName().second));
561 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
562 else if (name == "spellchecker")
565 else if (name == "vclog")
566 enable = buf->lyxvc().inUse();
567 else if (name == "view-source")
572 case LFUN_DIALOG_SHOW_NEW_INSET:
573 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
574 if (cur.inset().lyxCode() == InsetBase::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 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
612 if (!(flags & ToolbarBackend::AUTO))
613 flag.setOnOff(flags & ToolbarBackend::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 support::Path p(path);
1010 // there are three cases here:
1011 // 1. we print to a file
1012 // 2. we print directly to a printer
1013 // 3. we print using a spool command (print to file first)
1016 string const dviname =
1017 changeExtension(buffer->getLatexName(true),
1020 if (target == "printer") {
1021 if (!lyxrc.print_spool_command.empty()) {
1022 // case 3: print using a spool
1023 string const psname =
1024 changeExtension(dviname,".ps");
1025 command += lyxrc.print_to_file
1028 + quoteName(dviname);
1031 lyxrc.print_spool_command +' ';
1032 if (target_name != "default") {
1033 command2 += lyxrc.print_spool_printerprefix
1037 command2 += quoteName(psname);
1039 // If successful, then spool command
1040 res = one.startscript(
1045 res = one.startscript(
1046 Systemcall::DontWait,
1049 // case 2: print directly to a printer
1050 res = one.startscript(
1051 Systemcall::DontWait,
1052 command + quoteName(dviname));
1056 // case 1: print to a file
1057 FileName const filename(makeAbsPath(target_name, path));
1058 if (fs::exists(filename.toFilesystemEncoding())) {
1059 docstring text = bformat(
1060 _("The file %1$s already exists.\n\n"
1061 "Do you want to over-write that file?"),
1062 makeDisplayPath(filename.absFilename()));
1063 if (Alert::prompt(_("Over-write file?"),
1064 text, 0, 1, _("&Over-write"), _("&Cancel")) != 0)
1067 command += lyxrc.print_to_file
1068 + quoteName(filename.toFilesystemEncoding())
1070 + quoteName(dviname);
1071 res = one.startscript(Systemcall::DontWait,
1076 showPrintError(buffer->fileName());
1080 case LFUN_BUFFER_IMPORT:
1085 // quitting is triggered by the gui code
1086 // (leaving the event loop).
1087 lyx_view_->message(from_utf8(N_("Exiting.")));
1088 if (theBufferList().quitWriteAll())
1089 theApp()->gui().closeAllViews();
1092 case LFUN_TOC_VIEW: {
1093 BOOST_ASSERT(lyx_view_);
1094 InsetCommandParams p("tableofcontents");
1095 string const data = InsetCommandMailer::params2string("toc", p);
1096 lyx_view_->getDialogs().show("toc", data, 0);
1100 case LFUN_BUFFER_AUTO_SAVE:
1104 case LFUN_RECONFIGURE:
1105 BOOST_ASSERT(lyx_view_);
1106 reconfigure(*lyx_view_);
1109 case LFUN_HELP_OPEN: {
1110 BOOST_ASSERT(lyx_view_);
1111 string const arg = argument;
1113 setErrorMessage(from_ascii(N_("Missing argument")));
1116 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1117 if (fname.empty()) {
1118 lyxerr << "LyX: unable to find documentation file `"
1119 << arg << "'. Bad installation?" << endl;
1122 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1123 makeDisplayPath(fname.absFilename())));
1124 lyx_view_->loadLyXFile(fname, false);
1128 // --- version control -------------------------------
1129 case LFUN_VC_REGISTER:
1130 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1131 if (!ensureBufferClean(view()))
1133 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1134 lyx_view_->buffer()->lyxvc().registrer();
1137 updateFlags = Update::Force;
1140 case LFUN_VC_CHECK_IN:
1141 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1142 if (!ensureBufferClean(view()))
1144 if (lyx_view_->buffer()->lyxvc().inUse()
1145 && !lyx_view_->buffer()->isReadonly()) {
1146 lyx_view_->buffer()->lyxvc().checkIn();
1151 case LFUN_VC_CHECK_OUT:
1152 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1153 if (!ensureBufferClean(view()))
1155 if (lyx_view_->buffer()->lyxvc().inUse()
1156 && lyx_view_->buffer()->isReadonly()) {
1157 lyx_view_->buffer()->lyxvc().checkOut();
1162 case LFUN_VC_REVERT:
1163 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1164 lyx_view_->buffer()->lyxvc().revert();
1168 case LFUN_VC_UNDO_LAST:
1169 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1170 lyx_view_->buffer()->lyxvc().undoLast();
1174 // --- buffers ----------------------------------------
1175 case LFUN_BUFFER_SWITCH:
1176 BOOST_ASSERT(lyx_view_);
1177 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1180 case LFUN_BUFFER_NEXT:
1181 BOOST_ASSERT(lyx_view_);
1182 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1185 case LFUN_BUFFER_PREVIOUS:
1186 BOOST_ASSERT(lyx_view_);
1187 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1191 BOOST_ASSERT(lyx_view_);
1192 newFile(view(), argument);
1195 case LFUN_FILE_OPEN:
1196 BOOST_ASSERT(lyx_view_);
1200 case LFUN_DROP_LAYOUTS_CHOICE:
1201 BOOST_ASSERT(lyx_view_);
1202 lyx_view_->getToolbars().openLayoutList();
1205 case LFUN_MENU_OPEN:
1206 BOOST_ASSERT(lyx_view_);
1207 lyx_view_->getMenubar().openByName(from_utf8(argument));
1210 // --- lyxserver commands ----------------------------
1211 case LFUN_SERVER_GET_NAME:
1212 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1213 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1214 LYXERR(Debug::INFO) << "FNAME["
1215 << lyx_view_->buffer()->fileName()
1219 case LFUN_SERVER_NOTIFY:
1220 dispatch_buffer = keyseq->print(false);
1221 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1224 case LFUN_SERVER_GOTO_FILE_ROW: {
1225 BOOST_ASSERT(lyx_view_);
1228 istringstream is(argument);
1229 is >> file_name >> row;
1230 if (prefixIs(file_name, package().temp_dir())) {
1231 // Needed by inverse dvi search. If it is a file
1232 // in tmpdir, call the apropriated function
1233 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1235 // Must replace extension of the file to be .lyx
1236 // and get full path
1237 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1238 // Either change buffer or load the file
1239 if (theBufferList().exists(s.absFilename())) {
1240 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1242 lyx_view_->loadLyXFile(s);
1246 view()->setCursorFromRow(row);
1248 updateFlags = Update::FitCursor;
1252 case LFUN_DIALOG_SHOW: {
1253 BOOST_ASSERT(lyx_view_);
1254 string const name = cmd.getArg(0);
1255 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1257 if (name == "character") {
1258 data = freefont2string();
1260 lyx_view_->getDialogs().show("character", data);
1261 } else if (name == "latexlog") {
1262 pair<Buffer::LogType, string> const logfile =
1263 lyx_view_->buffer()->getLogName();
1264 switch (logfile.first) {
1265 case Buffer::latexlog:
1268 case Buffer::buildlog:
1272 data += LyXLex::quoteString(logfile.second);
1273 lyx_view_->getDialogs().show("log", data);
1274 } else if (name == "vclog") {
1275 string const data = "vc " +
1276 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1277 lyx_view_->getDialogs().show("log", data);
1279 lyx_view_->getDialogs().show(name, data);
1283 case LFUN_DIALOG_SHOW_NEW_INSET: {
1284 BOOST_ASSERT(lyx_view_);
1285 string const name = cmd.getArg(0);
1286 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1287 if (name == "bibitem" ||
1291 name == "nomenclature" ||
1295 InsetCommandParams p(name);
1296 data = InsetCommandMailer::params2string(name, p);
1297 } else if (name == "include") {
1298 // data is the include type: one of "include",
1299 // "input", "verbatiminput" or "verbatiminput*"
1301 // default type is requested
1303 InsetCommandParams p(data);
1304 data = InsetIncludeMailer::params2string(p);
1305 } else if (name == "box") {
1306 // \c data == "Boxed" || "Frameless" etc
1307 InsetBoxParams p(data);
1308 data = InsetBoxMailer::params2string(p);
1309 } else if (name == "branch") {
1310 InsetBranchParams p;
1311 data = InsetBranchMailer::params2string(p);
1312 } else if (name == "citation") {
1313 InsetCommandParams p("cite");
1314 data = InsetCommandMailer::params2string(name, p);
1315 } else if (name == "ert") {
1316 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1317 } else if (name == "external") {
1318 InsetExternalParams p;
1319 Buffer const & buffer = *lyx_view_->buffer();
1320 data = InsetExternalMailer::params2string(p, buffer);
1321 } else if (name == "float") {
1323 data = InsetFloatMailer::params2string(p);
1324 } else if (name == "graphics") {
1325 InsetGraphicsParams p;
1326 Buffer const & buffer = *lyx_view_->buffer();
1327 data = InsetGraphicsMailer::params2string(p, buffer);
1328 } else if (name == "note") {
1330 data = InsetNoteMailer::params2string(p);
1331 } else if (name == "vspace") {
1333 data = InsetVSpaceMailer::params2string(space);
1334 } else if (name == "wrap") {
1336 data = InsetWrapMailer::params2string(p);
1338 lyx_view_->getDialogs().show(name, data, 0);
1342 case LFUN_DIALOG_UPDATE: {
1343 BOOST_ASSERT(lyx_view_);
1344 string const & name = argument;
1345 // Can only update a dialog connected to an existing inset
1346 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1348 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1349 inset->dispatch(view()->cursor(), fr);
1350 } else if (name == "paragraph") {
1351 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1352 } else if (name == "prefs") {
1353 lyx_view_->getDialogs().update(name, string());
1358 case LFUN_DIALOG_HIDE:
1359 Dialogs::hide(argument, 0);
1362 case LFUN_DIALOG_DISCONNECT_INSET:
1363 BOOST_ASSERT(lyx_view_);
1364 lyx_view_->getDialogs().disconnect(argument);
1368 case LFUN_CITATION_INSERT: {
1369 BOOST_ASSERT(lyx_view_);
1370 if (!argument.empty()) {
1371 // we can have one optional argument, delimited by '|'
1372 // citation-insert <key>|<text_before>
1373 // this should be enhanced to also support text_after
1374 // and citation style
1375 string arg = argument;
1377 if (contains(argument, "|")) {
1378 arg = token(argument, '|', 0);
1379 opt1 = '[' + token(argument, '|', 1) + ']';
1381 std::ostringstream os;
1382 os << "citation LatexCommand\n"
1383 << "\\cite" << opt1 << "{" << arg << "}\n"
1385 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1388 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1392 case LFUN_BUFFER_CHILD_OPEN: {
1393 BOOST_ASSERT(lyx_view_);
1394 FileName const filename =
1395 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1396 setMessage(bformat(_("Opening child document %1$s..."),
1397 makeDisplayPath(filename.absFilename())));
1398 view()->saveBookmark(false);
1399 string const parentfilename = lyx_view_->buffer()->fileName();
1400 if (theBufferList().exists(filename.absFilename()))
1401 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1403 lyx_view_->loadLyXFile(filename);
1404 // Set the parent name of the child document.
1405 // This makes insertion of citations and references in the child work,
1406 // when the target is in the parent or another child document.
1407 lyx_view_->buffer()->setParentName(parentfilename);
1411 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1412 BOOST_ASSERT(lyx_view_);
1413 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1416 case LFUN_KEYMAP_OFF:
1417 BOOST_ASSERT(lyx_view_);
1418 lyx_view_->view()->getIntl().keyMapOn(false);
1421 case LFUN_KEYMAP_PRIMARY:
1422 BOOST_ASSERT(lyx_view_);
1423 lyx_view_->view()->getIntl().keyMapPrim();
1426 case LFUN_KEYMAP_SECONDARY:
1427 BOOST_ASSERT(lyx_view_);
1428 lyx_view_->view()->getIntl().keyMapSec();
1431 case LFUN_KEYMAP_TOGGLE:
1432 BOOST_ASSERT(lyx_view_);
1433 lyx_view_->view()->getIntl().toggleKeyMap();
1439 string rest = split(argument, countstr, ' ');
1440 istringstream is(countstr);
1443 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1444 for (int i = 0; i < count; ++i)
1445 dispatch(lyxaction.lookupFunc(rest));
1449 case LFUN_COMMAND_SEQUENCE: {
1450 // argument contains ';'-terminated commands
1451 string arg = argument;
1452 while (!arg.empty()) {
1454 arg = split(arg, first, ';');
1455 FuncRequest func(lyxaction.lookupFunc(first));
1456 func.origin = cmd.origin;
1462 case LFUN_PREFERENCES_SAVE: {
1463 lyxrc.write(makeAbsPath("preferences",
1464 package().user_support()),
1469 case LFUN_SCREEN_FONT_UPDATE:
1470 BOOST_ASSERT(lyx_view_);
1471 // handle the screen font changes.
1472 theFontLoader().update();
1473 /// FIXME: only the current view will be updated. the Gui
1474 /// class is able to furnish the list of views.
1475 updateFlags = Update::Force;
1478 case LFUN_SET_COLOR: {
1480 string const x11_name = split(argument, lyx_name, ' ');
1481 if (lyx_name.empty() || x11_name.empty()) {
1482 setErrorMessage(from_ascii(N_(
1483 "Syntax: set-color <lyx_name>"
1488 bool const graphicsbg_changed =
1489 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1490 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1492 if (!lcolor.setColor(lyx_name, x11_name)) {
1494 bformat(_("Set-color \"%1$s\" failed "
1495 "- color is undefined or "
1496 "may not be redefined"),
1497 from_utf8(lyx_name)));
1501 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1503 if (graphicsbg_changed) {
1504 #ifdef WITH_WARNINGS
1505 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1508 graphics::GCache::get().changeDisplay(true);
1515 BOOST_ASSERT(lyx_view_);
1516 lyx_view_->message(from_utf8(argument));
1519 case LFUN_EXTERNAL_EDIT: {
1520 BOOST_ASSERT(lyx_view_);
1521 FuncRequest fr(action, argument);
1522 InsetExternal().dispatch(view()->cursor(), fr);
1526 case LFUN_GRAPHICS_EDIT: {
1527 FuncRequest fr(action, argument);
1528 InsetGraphics().dispatch(view()->cursor(), fr);
1532 case LFUN_INSET_APPLY: {
1533 BOOST_ASSERT(lyx_view_);
1534 string const name = cmd.getArg(0);
1535 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1537 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1538 inset->dispatch(view()->cursor(), fr);
1540 FuncRequest fr(LFUN_INSET_INSERT, argument);
1543 // ideally, the update flag should be set by the insets,
1544 // but this is not possible currently
1545 updateFlags = Update::Force | Update::FitCursor;
1549 case LFUN_ALL_INSETS_TOGGLE: {
1550 BOOST_ASSERT(lyx_view_);
1552 string const name = split(argument, action, ' ');
1553 InsetBase::Code const inset_code =
1554 InsetBase::translate(name);
1556 LCursor & cur = view()->cursor();
1557 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1559 InsetBase & inset = lyx_view_->buffer()->inset();
1560 InsetIterator it = inset_iterator_begin(inset);
1561 InsetIterator const end = inset_iterator_end(inset);
1562 for (; it != end; ++it) {
1563 if (inset_code == InsetBase::NO_CODE
1564 || inset_code == it->lyxCode()) {
1565 LCursor tmpcur = cur;
1566 tmpcur.pushLeft(*it);
1567 it->dispatch(tmpcur, fr);
1570 updateFlags = Update::Force | Update::FitCursor;
1574 case LFUN_BUFFER_LANGUAGE: {
1575 BOOST_ASSERT(lyx_view_);
1576 Buffer & buffer = *lyx_view_->buffer();
1577 Language const * oldL = buffer.params().language;
1578 Language const * newL = languages.getLanguage(argument);
1579 if (!newL || oldL == newL)
1582 if (oldL->rightToLeft() == newL->rightToLeft()
1583 && !buffer.isMultiLingual())
1584 buffer.changeLanguage(oldL, newL);
1586 buffer.updateDocLang(newL);
1590 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1591 string const fname =
1592 addName(addPath(package().user_support(), "templates/"),
1594 Buffer defaults(fname);
1596 istringstream ss(argument);
1599 int const unknown_tokens = defaults.readHeader(lex);
1601 if (unknown_tokens != 0) {
1602 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1603 << unknown_tokens << " unknown token"
1604 << (unknown_tokens == 1 ? "" : "s")
1608 if (defaults.writeFile(FileName(defaults.fileName())))
1609 setMessage(bformat(_("Document defaults saved in %1$s"),
1610 makeDisplayPath(fname)));
1612 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1616 case LFUN_BUFFER_PARAMS_APPLY: {
1617 BOOST_ASSERT(lyx_view_);
1618 biblio::CiteEngine const engine =
1619 lyx_view_->buffer()->params().getEngine();
1621 istringstream ss(argument);
1624 int const unknown_tokens =
1625 lyx_view_->buffer()->readHeader(lex);
1627 if (unknown_tokens != 0) {
1628 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1629 << unknown_tokens << " unknown token"
1630 << (unknown_tokens == 1 ? "" : "s")
1633 if (engine == lyx_view_->buffer()->params().getEngine())
1636 LCursor & cur = view()->cursor();
1637 FuncRequest fr(LFUN_INSET_REFRESH);
1639 InsetBase & inset = lyx_view_->buffer()->inset();
1640 InsetIterator it = inset_iterator_begin(inset);
1641 InsetIterator const end = inset_iterator_end(inset);
1642 for (; it != end; ++it)
1643 if (it->lyxCode() == InsetBase::CITE_CODE)
1644 it->dispatch(cur, fr);
1648 case LFUN_TEXTCLASS_APPLY: {
1649 BOOST_ASSERT(lyx_view_);
1650 Buffer * buffer = lyx_view_->buffer();
1652 textclass_type const old_class =
1653 buffer->params().textclass;
1655 loadTextclass(argument);
1657 std::pair<bool, textclass_type> const tc_pair =
1658 textclasslist.numberOfClass(argument);
1663 textclass_type const new_class = tc_pair.second;
1664 if (old_class == new_class)
1668 lyx_view_->message(_("Converting document to new document class..."));
1669 recordUndoFullDocument(view());
1670 buffer->params().textclass = new_class;
1671 StableDocIterator backcur(view()->cursor());
1672 ErrorList & el = buffer->errorList("Class Switch");
1673 cap::switchBetweenClasses(
1674 old_class, new_class,
1675 static_cast<InsetText &>(buffer->inset()), el);
1677 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1679 buffer->errors("Class Switch");
1680 updateLabels(*buffer);
1681 updateFlags = Update::Force | Update::FitCursor;
1685 case LFUN_TEXTCLASS_LOAD:
1686 loadTextclass(argument);
1689 case LFUN_LYXRC_APPLY: {
1690 LyXRC const lyxrc_orig = lyxrc;
1692 istringstream ss(argument);
1693 bool const success = lyxrc.read(ss) == 0;
1696 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1697 << "Unable to read lyxrc data"
1702 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1704 /// We force the redraw in any case because there might be
1705 /// some screen font changes.
1706 /// FIXME: only the current view will be updated. the Gui
1707 /// class is able to furnish the list of views.
1708 updateFlags = Update::Force;
1712 case LFUN_WINDOW_NEW:
1713 LyX::ref().newLyXView();
1716 case LFUN_WINDOW_CLOSE:
1717 BOOST_ASSERT(lyx_view_);
1718 BOOST_ASSERT(theApp());
1719 // update bookmark pit of the current buffer before window close
1720 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1721 gotoBookmark(i+1, false, false);
1722 // ask the user for saving changes or cancel quit
1723 if (!theBufferList().quitWriteAll())
1728 case LFUN_BOOKMARK_GOTO:
1729 // go to bookmark, open unopened file and switch to buffer if necessary
1730 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1733 case LFUN_BOOKMARK_CLEAR:
1734 LyX::ref().session().bookmarks().clear();
1737 case LFUN_TOOLBAR_TOGGLE_STATE:
1738 lyx_view_->toggleToolbarState(argument);
1742 BOOST_ASSERT(lyx_view_);
1743 view()->cursor().dispatch(cmd);
1744 updateFlags = view()->cursor().result().update();
1745 if (!view()->cursor().result().dispatched())
1746 updateFlags = view()->dispatch(cmd);
1751 if (lyx_view_ && view()->buffer()) {
1752 // BufferView::update() updates the ViewMetricsInfo and
1753 // also initializes the position cache for all insets in
1754 // (at least partially) visible top-level paragraphs.
1755 // We will redraw the screen only if needed.
1756 if (view()->update(updateFlags)) {
1757 // Buffer::changed() signals that a repaint is needed.
1758 // The frontend (WorkArea) knows which area to repaint
1759 // thanks to the ViewMetricsInfo updated above.
1760 view()->buffer()->changed();
1763 lyx_view_->updateStatusBar();
1765 // if we executed a mutating lfun, mark the buffer as dirty
1767 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1768 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1769 view()->buffer()->markDirty();
1771 if (view()->cursor().inTexted()) {
1772 lyx_view_->updateLayoutChoice();
1777 lyx_view_->updateMenubar();
1778 lyx_view_->updateToolbars();
1779 // Some messages may already be translated, so we cannot use _()
1780 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1785 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1787 const bool verbose = (cmd.origin == FuncRequest::MENU
1788 || cmd.origin == FuncRequest::TOOLBAR
1789 || cmd.origin == FuncRequest::COMMANDBUFFER);
1791 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1792 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1794 lyx_view_->message(msg);
1798 docstring dispatch_msg = msg;
1799 if (!dispatch_msg.empty())
1800 dispatch_msg += ' ';
1802 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1804 bool argsadded = false;
1806 if (!cmd.argument().empty()) {
1807 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1808 comname += ' ' + cmd.argument();
1813 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1815 if (!shortcuts.empty())
1816 comname += ": " + shortcuts;
1817 else if (!argsadded && !cmd.argument().empty())
1818 comname += ' ' + cmd.argument();
1820 if (!comname.empty()) {
1821 comname = rtrim(comname);
1822 dispatch_msg += '(' + rtrim(comname) + ')';
1825 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1826 << to_utf8(dispatch_msg) << endl;
1827 if (!dispatch_msg.empty())
1828 lyx_view_->message(dispatch_msg);
1832 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1834 // FIXME: initpath is not used. What to do?
1835 string initpath = lyxrc.document_path;
1836 string filename(name);
1838 if (view()->buffer()) {
1839 string const trypath = lyx_view_->buffer()->filePath();
1840 // If directory is writeable, use this as default.
1841 if (isDirWriteable(FileName(trypath)))
1845 static int newfile_number;
1847 if (filename.empty()) {
1848 filename = addName(lyxrc.document_path,
1849 "newfile" + convert<string>(++newfile_number) + ".lyx");
1850 while (theBufferList().exists(filename) ||
1851 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1853 filename = addName(lyxrc.document_path,
1854 "newfile" + convert<string>(newfile_number) +
1859 // The template stuff
1862 FileDialog fileDlg(_("Select template file"),
1863 LFUN_SELECT_FILE_SYNC,
1864 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1865 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1867 FileDialog::Result result =
1868 fileDlg.open(from_utf8(lyxrc.template_path),
1869 FileFilterList(_("LyX Documents (*.lyx)")),
1872 if (result.first == FileDialog::Later)
1874 if (result.second.empty())
1876 templname = to_utf8(result.second);
1879 Buffer * const b = newFile(filename, templname, !name.empty());
1882 lyx_view_->setBuffer(b);
1887 void LyXFunc::open(string const & fname)
1889 string initpath = lyxrc.document_path;
1891 if (view()->buffer()) {
1892 string const trypath = lyx_view_->buffer()->filePath();
1893 // If directory is writeable, use this as default.
1894 if (isDirWriteable(FileName(trypath)))
1900 if (fname.empty()) {
1901 FileDialog fileDlg(_("Select document to open"),
1903 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1904 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1906 FileDialog::Result result =
1907 fileDlg.open(from_utf8(initpath),
1908 FileFilterList(_("LyX Documents (*.lyx)")),
1911 if (result.first == FileDialog::Later)
1914 filename = to_utf8(result.second);
1916 // check selected filename
1917 if (filename.empty()) {
1918 lyx_view_->message(_("Canceled."));
1924 // get absolute path of file and add ".lyx" to the filename if
1926 FileName const fullname = fileSearch(string(), filename, "lyx");
1927 if (!fullname.empty())
1928 filename = fullname.absFilename();
1930 // if the file doesn't exist, let the user create one
1931 if (!fs::exists(fullname.toFilesystemEncoding())) {
1932 // the user specifically chose this name. Believe him.
1933 Buffer * const b = newFile(filename, string(), true);
1935 lyx_view_->setBuffer(b);
1939 docstring const disp_fn = makeDisplayPath(filename);
1940 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1943 if (lyx_view_->loadLyXFile(fullname)) {
1944 str2 = bformat(_("Document %1$s opened."), disp_fn);
1946 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1948 lyx_view_->message(str2);
1952 void LyXFunc::doImport(string const & argument)
1955 string filename = split(argument, format, ' ');
1957 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1958 << " file: " << filename << endl;
1960 // need user interaction
1961 if (filename.empty()) {
1962 string initpath = lyxrc.document_path;
1964 if (view()->buffer()) {
1965 string const trypath = lyx_view_->buffer()->filePath();
1966 // If directory is writeable, use this as default.
1967 if (isDirWriteable(FileName(trypath)))
1971 docstring const text = bformat(_("Select %1$s file to import"),
1972 formats.prettyName(format));
1974 FileDialog fileDlg(text,
1976 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1977 make_pair(_("Examples|#E#e"),
1978 from_utf8(addPath(package().system_support(), "examples"))));
1980 docstring filter = formats.prettyName(format);
1983 filter += from_utf8(formats.extension(format));
1986 FileDialog::Result result =
1987 fileDlg.open(from_utf8(initpath),
1988 FileFilterList(filter),
1991 if (result.first == FileDialog::Later)
1994 filename = to_utf8(result.second);
1996 // check selected filename
1997 if (filename.empty())
1998 lyx_view_->message(_("Canceled."));
2001 if (filename.empty())
2004 // get absolute path of file
2005 FileName const fullname(makeAbsPath(filename));
2007 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2009 // Check if the document already is open
2010 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2011 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2012 lyx_view_->message(_("Canceled."));
2017 // if the file exists already, and we didn't do
2018 // -i lyx thefile.lyx, warn
2019 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2020 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2022 docstring text = bformat(_("The document %1$s already exists.\n\n"
2023 "Do you want to over-write that document?"), file);
2024 int const ret = Alert::prompt(_("Over-write document?"),
2025 text, 0, 1, _("&Over-write"), _("&Cancel"));
2028 lyx_view_->message(_("Canceled."));
2033 ErrorList errorList;
2034 Importer::Import(lyx_view_, fullname, format, errorList);
2035 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2039 void LyXFunc::closeBuffer()
2041 // save current cursor position
2042 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2043 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2044 // goto bookmark to update bookmark pit.
2045 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2046 gotoBookmark(i+1, false, false);
2047 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2048 if (theBufferList().empty()) {
2049 // need this otherwise SEGV may occur while
2050 // trying to set variables that don't exist
2051 // since there's no current buffer
2052 lyx_view_->getDialogs().hideBufferDependent();
2054 lyx_view_->setBuffer(theBufferList().first());
2060 void LyXFunc::reloadBuffer()
2062 FileName filename(lyx_view_->buffer()->fileName());
2064 lyx_view_->loadLyXFile(filename);
2067 // Each "lyx_view_" should have it's own message method. lyxview and
2068 // the minibuffer would use the minibuffer, but lyxserver would
2069 // send an ERROR signal to its client. Alejandro 970603
2070 // This function is bit problematic when it comes to NLS, to make the
2071 // lyx servers client be language indepenent we must not translate
2072 // strings sent to this func.
2073 void LyXFunc::setErrorMessage(docstring const & m) const
2075 dispatch_buffer = m;
2080 void LyXFunc::setMessage(docstring const & m) const
2082 dispatch_buffer = m;
2086 docstring const LyXFunc::viewStatusMessage()
2088 // When meta-fake key is pressed, show the key sequence so far + "M-".
2090 return keyseq->print(true) + "M-";
2092 // Else, when a non-complete key sequence is pressed,
2093 // show the available options.
2094 if (keyseq->length() > 0 && !keyseq->deleted())
2095 return keyseq->printOptions(true);
2097 if (!view()->buffer())
2098 return _("Welcome to LyX!");
2100 return view()->cursor().currentState();
2104 BufferView * LyXFunc::view() const
2106 BOOST_ASSERT(lyx_view_);
2107 return lyx_view_->view();
2111 bool LyXFunc::wasMetaKey() const
2113 return (meta_fake_bit != key_modifier::none);
2119 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2121 // Why the switch you might ask. It is a trick to ensure that all
2122 // the elements in the LyXRCTags enum is handled. As you can see
2123 // there are no breaks at all. So it is just a huge fall-through.
2124 // The nice thing is that we will get a warning from the compiler
2125 // if we forget an element.
2126 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2128 case LyXRC::RC_ACCEPT_COMPOUND:
2129 case LyXRC::RC_ALT_LANG:
2130 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2131 case LyXRC::RC_PLAINTEXT_LINELEN:
2132 case LyXRC::RC_AUTOREGIONDELETE:
2133 case LyXRC::RC_AUTORESET_OPTIONS:
2134 case LyXRC::RC_AUTOSAVE:
2135 case LyXRC::RC_AUTO_NUMBER:
2136 case LyXRC::RC_BACKUPDIR_PATH:
2137 case LyXRC::RC_BIBTEX_COMMAND:
2138 case LyXRC::RC_BINDFILE:
2139 case LyXRC::RC_CHECKLASTFILES:
2140 case LyXRC::RC_USELASTFILEPOS:
2141 case LyXRC::RC_LOADSESSION:
2142 case LyXRC::RC_CHKTEX_COMMAND:
2143 case LyXRC::RC_CONVERTER:
2144 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2145 case LyXRC::RC_COPIER:
2146 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2147 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2148 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2149 case LyXRC::RC_DATE_INSERT_FORMAT:
2150 case LyXRC::RC_DEFAULT_LANGUAGE:
2151 case LyXRC::RC_DEFAULT_PAPERSIZE:
2152 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2153 case LyXRC::RC_DISPLAY_GRAPHICS:
2154 case LyXRC::RC_DOCUMENTPATH:
2155 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2156 string const encoded = FileName(
2157 lyxrc_new.document_path).toFilesystemEncoding();
2158 if (fs::exists(encoded) && fs::is_directory(encoded))
2159 support::package().document_dir() = lyxrc.document_path;
2161 case LyXRC::RC_ESC_CHARS:
2162 case LyXRC::RC_FONT_ENCODING:
2163 case LyXRC::RC_FORMAT:
2164 case LyXRC::RC_INDEX_COMMAND:
2165 case LyXRC::RC_INPUT:
2166 case LyXRC::RC_KBMAP:
2167 case LyXRC::RC_KBMAP_PRIMARY:
2168 case LyXRC::RC_KBMAP_SECONDARY:
2169 case LyXRC::RC_LABEL_INIT_LENGTH:
2170 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2171 case LyXRC::RC_LANGUAGE_AUTO_END:
2172 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2173 case LyXRC::RC_LANGUAGE_COMMAND_END:
2174 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2175 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2176 case LyXRC::RC_LANGUAGE_PACKAGE:
2177 case LyXRC::RC_LANGUAGE_USE_BABEL:
2178 case LyXRC::RC_MAKE_BACKUP:
2179 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2180 case LyXRC::RC_NUMLASTFILES:
2181 case LyXRC::RC_PATH_PREFIX:
2182 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2183 support::prependEnvPath("PATH", lyxrc.path_prefix);
2185 case LyXRC::RC_PERS_DICT:
2186 case LyXRC::RC_PREVIEW:
2187 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2188 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2189 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2190 case LyXRC::RC_PRINTCOPIESFLAG:
2191 case LyXRC::RC_PRINTER:
2192 case LyXRC::RC_PRINTEVENPAGEFLAG:
2193 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2194 case LyXRC::RC_PRINTFILEEXTENSION:
2195 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2196 case LyXRC::RC_PRINTODDPAGEFLAG:
2197 case LyXRC::RC_PRINTPAGERANGEFLAG:
2198 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2199 case LyXRC::RC_PRINTPAPERFLAG:
2200 case LyXRC::RC_PRINTREVERSEFLAG:
2201 case LyXRC::RC_PRINTSPOOL_COMMAND:
2202 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2203 case LyXRC::RC_PRINTTOFILE:
2204 case LyXRC::RC_PRINTTOPRINTER:
2205 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2206 case LyXRC::RC_PRINT_COMMAND:
2207 case LyXRC::RC_RTL_SUPPORT:
2208 case LyXRC::RC_SCREEN_DPI:
2209 case LyXRC::RC_SCREEN_FONT_ROMAN:
2210 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2211 case LyXRC::RC_SCREEN_FONT_SANS:
2212 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2213 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2214 case LyXRC::RC_SCREEN_FONT_SIZES:
2215 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2216 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2217 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2218 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2219 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2220 case LyXRC::RC_SCREEN_ZOOM:
2221 case LyXRC::RC_SERVERPIPE:
2222 case LyXRC::RC_SET_COLOR:
2223 case LyXRC::RC_SHOW_BANNER:
2224 case LyXRC::RC_SPELL_COMMAND:
2225 case LyXRC::RC_TEMPDIRPATH:
2226 case LyXRC::RC_TEMPLATEPATH:
2227 case LyXRC::RC_TEX_ALLOWS_SPACES:
2228 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2229 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2230 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2232 case LyXRC::RC_UIFILE:
2233 case LyXRC::RC_USER_EMAIL:
2234 case LyXRC::RC_USER_NAME:
2235 case LyXRC::RC_USETEMPDIR:
2236 case LyXRC::RC_USE_ALT_LANG:
2237 case LyXRC::RC_USE_CONVERTER_CACHE:
2238 case LyXRC::RC_USE_ESC_CHARS:
2239 case LyXRC::RC_USE_INP_ENC:
2240 case LyXRC::RC_USE_PERS_DICT:
2241 case LyXRC::RC_USE_SPELL_LIB:
2242 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2243 case LyXRC::RC_VIEWER:
2244 case LyXRC::RC_LAST: