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;
148 extern tex_accent_struct get_accent(kb_action action);
153 bool getLocalStatus(LCursor cursor,
154 FuncRequest const & cmd, FuncStatus & status)
156 // Try to fix cursor in case it is broken.
157 cursor.fixIfBroken();
159 // This is, of course, a mess. Better create a new doc iterator and use
160 // this in Inset::getStatus. This might require an additional
161 // BufferView * arg, though (which should be avoided)
162 //LCursor safe = *this;
164 for ( ; cursor.depth(); cursor.pop()) {
165 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
166 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
167 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
168 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
170 // The inset's getStatus() will return 'true' if it made
171 // a definitive decision on whether it want to handle the
172 // request or not. The result of this decision is put into
173 // the 'status' parameter.
174 if (cursor.inset().getStatus(cursor, cmd, status)) {
183 /** Return the change status at cursor position, taking in account the
184 * status at each level of the document iterator (a table in a deleted
185 * footnote is deleted).
186 * When \param outer is true, the top slice is not looked at.
188 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
190 size_t const depth = dit.depth() - (outer ? 1 : 0);
192 for (size_t i = 0 ; i < depth ; ++i) {
193 CursorSlice const & slice = dit[i];
194 if (!slice.inset().inMathed()
195 && slice.pos() < slice.paragraph().size()) {
196 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
197 if (ch != Change::UNCHANGED)
201 return Change::UNCHANGED;
209 meta_fake_bit(key_modifier::none)
214 void LyXFunc::initKeySequences(kb_keymap * kb)
216 keyseq.reset(new kb_sequence(kb, kb));
217 cancel_meta_seq.reset(new kb_sequence(kb, kb));
221 void LyXFunc::setLyXView(LyXView * lv)
227 void LyXFunc::handleKeyFunc(kb_action action)
229 char_type c = encoded_last_key;
231 if (keyseq->length())
234 lyx_view_->view()->getIntl().getTransManager().deadkey(
235 c, get_accent(action).accent, view()->getLyXText(), view()->cursor());
236 // Need to clear, in case the minibuffer calls these
239 // copied verbatim from do_accent_char
240 view()->cursor().resetAnchor();
245 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
247 BOOST_ASSERT(lyx_view_);
248 if (!LyX::ref().session().bookmarks().isValid(idx))
250 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
251 BOOST_ASSERT(!bm.filename.empty());
252 string const file = bm.filename.absFilename();
253 // if the file is not opened, open it.
254 if (!theBufferList().exists(file)) {
256 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
260 // open may fail, so we need to test it again
261 if (theBufferList().exists(file)) {
262 // if the current buffer is not that one, switch to it.
263 if (lyx_view_->buffer()->fileName() != file) {
265 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
269 // moveToPosition use par_id, and par_pit and return new par_id.
272 boost::tie(new_pit, new_id) = view()->moveToPosition(bm.par_pit, bm.par_id, bm.par_pos);
273 // if par_id or pit has been changed, reset par_pit and par_id
274 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
275 if (bm.par_pit != new_pit || bm.par_id != new_id)
276 const_cast<BookmarksSection::Bookmark &>(bm).setPos(new_pit, new_id);
281 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
283 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
285 // Do nothing if we have nothing (JMarc)
286 if (!keysym->isOK()) {
287 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
292 if (keysym->isModifier()) {
293 lyxerr[Debug::KEY] << "isModifier true" << endl;
297 //Encoding const * encoding = view()->cursor().getEncoding();
298 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
299 // FIXME: encoded_last_key shadows the member variable of the same
300 // name. Is that intended?
301 char_type encoded_last_key = keysym->getUCSEncoded();
303 // Do a one-deep top-level lookup for
304 // cancel and meta-fake keys. RVDK_PATCH_5
305 cancel_meta_seq->reset();
307 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
308 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
309 << " action first set to [" << func.action << ']'
312 // When not cancel or meta-fake, do the normal lookup.
313 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
314 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
315 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
316 // remove Caps Lock and Mod2 as a modifiers
317 func = keyseq->addkey(keysym, (state | meta_fake_bit));
318 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
319 << "action now set to ["
320 << func.action << ']' << endl;
323 // Dont remove this unless you know what you are doing.
324 meta_fake_bit = key_modifier::none;
326 // Can this happen now ?
327 if (func.action == LFUN_NOACTION) {
328 func = FuncRequest(LFUN_COMMAND_PREFIX);
331 if (lyxerr.debugging(Debug::KEY)) {
332 lyxerr << BOOST_CURRENT_FUNCTION
334 << func.action << "]["
335 << to_utf8(keyseq->print(false)) << ']'
339 // already here we know if it any point in going further
340 // why not return already here if action == -1 and
341 // num_bytes == 0? (Lgb)
343 if (keyseq->length() > 1) {
344 lyx_view_->message(keyseq->print(true));
348 // Maybe user can only reach the key via holding down shift.
349 // Let's see. But only if shift is the only modifier
350 if (func.action == LFUN_UNKNOWN_ACTION &&
351 state == key_modifier::shift) {
352 lyxerr[Debug::KEY] << "Trying without shift" << endl;
353 func = keyseq->addkey(keysym, key_modifier::none);
354 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
357 if (func.action == LFUN_UNKNOWN_ACTION) {
358 // Hmm, we didn't match any of the keysequences. See
359 // if it's normal insertable text not already covered
361 if (keysym->isText() && keyseq->length() == 1) {
362 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
363 func = FuncRequest(LFUN_SELF_INSERT,
364 FuncRequest::KEYBOARD);
366 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
367 lyx_view_->message(_("Unknown function."));
372 if (func.action == LFUN_SELF_INSERT) {
373 if (encoded_last_key != 0) {
374 docstring const arg(1, encoded_last_key);
375 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
376 FuncRequest::KEYBOARD));
378 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
386 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
388 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
391 LCursor & cur = view()->cursor();
393 /* In LyX/Mac, when a dialog is open, the menus of the
394 application can still be accessed without giving focus to
395 the main window. In this case, we want to disable the menu
396 entries that are buffer-related.
398 Note that this code is not perfect, as bug 1941 attests:
399 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
401 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
402 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
405 if (cmd.action == LFUN_NOACTION) {
406 flag.message(from_utf8(N_("Nothing to do")));
411 switch (cmd.action) {
412 case LFUN_UNKNOWN_ACTION:
413 #ifndef HAVE_LIBAIKSAURUS
414 case LFUN_THESAURUS_ENTRY:
424 if (flag.unknown()) {
425 flag.message(from_utf8(N_("Unknown action")));
429 if (!flag.enabled()) {
430 if (flag.message().empty())
431 flag.message(from_utf8(N_("Command disabled")));
435 // Check whether we need a buffer
436 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
438 flag.message(from_utf8(N_("Command not allowed with"
439 "out any document open")));
444 // I would really like to avoid having this switch and rather try to
445 // encode this in the function itself.
446 // -- And I'd rather let an inset decide which LFUNs it is willing
447 // to handle (Andre')
449 switch (cmd.action) {
450 case LFUN_BUFFER_TOGGLE_READ_ONLY:
451 flag.setOnOff(buf->isReadonly());
454 case LFUN_BUFFER_SWITCH:
455 // toggle on the current buffer, but do not toggle off
456 // the other ones (is that a good idea?)
457 if (to_utf8(cmd.argument()) == buf->fileName())
461 case LFUN_BUFFER_EXPORT:
462 enable = cmd.argument() == "custom"
463 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
466 case LFUN_BUFFER_CHKTEX:
467 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
470 case LFUN_BUILD_PROGRAM:
471 enable = Exporter::isExportable(*buf, "program");
474 case LFUN_LAYOUT_TABULAR:
475 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
479 case LFUN_LAYOUT_PARAGRAPH:
480 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
483 case LFUN_VC_REGISTER:
484 enable = !buf->lyxvc().inUse();
486 case LFUN_VC_CHECK_IN:
487 enable = buf->lyxvc().inUse() && !buf->isReadonly();
489 case LFUN_VC_CHECK_OUT:
490 enable = buf->lyxvc().inUse() && buf->isReadonly();
493 case LFUN_VC_UNDO_LAST:
494 enable = buf->lyxvc().inUse();
496 case LFUN_BUFFER_RELOAD:
497 enable = !buf->isUnnamed() && !buf->isClean();
500 case LFUN_INSET_SETTINGS: {
504 InsetBase::Code code = cur.inset().lyxCode();
506 case InsetBase::TABULAR_CODE:
507 enable = cmd.argument() == "tabular";
509 case InsetBase::ERT_CODE:
510 enable = cmd.argument() == "ert";
512 case InsetBase::FLOAT_CODE:
513 enable = cmd.argument() == "float";
515 case InsetBase::WRAP_CODE:
516 enable = cmd.argument() == "wrap";
518 case InsetBase::NOTE_CODE:
519 enable = cmd.argument() == "note";
521 case InsetBase::BRANCH_CODE:
522 enable = cmd.argument() == "branch";
524 case InsetBase::BOX_CODE:
525 enable = cmd.argument() == "box";
533 case LFUN_INSET_APPLY: {
534 string const name = cmd.getArg(0);
535 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
537 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
539 if (!inset->getStatus(cur, fr, fs)) {
540 // Every inset is supposed to handle this
545 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
546 flag |= getStatus(fr);
548 enable = flag.enabled();
552 case LFUN_DIALOG_SHOW: {
553 string const name = cmd.getArg(0);
555 enable = name == "aboutlyx"
559 || name == "texinfo";
560 else if (name == "print")
561 enable = Exporter::isExportable(*buf, "dvi")
562 && lyxrc.print_command != "none";
563 else if (name == "character" || name == "mathpanel")
564 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
565 else if (name == "latexlog")
566 enable = isFileReadable(FileName(buf->getLogName().second));
567 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
568 else if (name == "spellchecker")
571 else if (name == "vclog")
572 enable = buf->lyxvc().inUse();
573 else if (name == "view-source")
578 case LFUN_DIALOG_SHOW_NEW_INSET:
579 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE
580 && cur.inset().lyxCode() != InsetBase::CAPTION_CODE;
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(_("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) {
1067 showPrintError(buffer->fileName());
1071 command += lyxrc.print_to_file
1072 + quoteName(filename.toFilesystemEncoding())
1074 + quoteName(dviname);
1075 res = one.startscript(Systemcall::DontWait,
1080 showPrintError(buffer->fileName());
1084 case LFUN_BUFFER_IMPORT:
1089 // quitting is triggered by the gui code
1090 // (leaving the event loop).
1091 lyx_view_->message(from_utf8(N_("Exiting.")));
1092 if (theBufferList().quitWriteAll())
1093 theApp()->gui().closeAllViews();
1096 case LFUN_TOC_VIEW: {
1097 BOOST_ASSERT(lyx_view_);
1098 InsetCommandParams p("tableofcontents");
1099 string const data = InsetCommandMailer::params2string("toc", p);
1100 lyx_view_->getDialogs().show("toc", data, 0);
1104 case LFUN_BUFFER_AUTO_SAVE:
1108 case LFUN_RECONFIGURE:
1109 BOOST_ASSERT(lyx_view_);
1110 reconfigure(*lyx_view_);
1113 case LFUN_HELP_OPEN: {
1114 BOOST_ASSERT(lyx_view_);
1115 string const arg = argument;
1117 setErrorMessage(_("Missing argument"));
1120 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1121 if (fname.empty()) {
1122 lyxerr << "LyX: unable to find documentation file `"
1123 << arg << "'. Bad installation?" << endl;
1126 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1127 makeDisplayPath(fname.absFilename())));
1128 lyx_view_->loadLyXFile(fname, false);
1132 // --- version control -------------------------------
1133 case LFUN_VC_REGISTER:
1134 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1135 if (!ensureBufferClean(view()))
1137 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1138 lyx_view_->buffer()->lyxvc().registrer();
1141 updateFlags = Update::Force;
1144 case LFUN_VC_CHECK_IN:
1145 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1146 if (!ensureBufferClean(view()))
1148 if (lyx_view_->buffer()->lyxvc().inUse()
1149 && !lyx_view_->buffer()->isReadonly()) {
1150 lyx_view_->buffer()->lyxvc().checkIn();
1155 case LFUN_VC_CHECK_OUT:
1156 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1157 if (!ensureBufferClean(view()))
1159 if (lyx_view_->buffer()->lyxvc().inUse()
1160 && lyx_view_->buffer()->isReadonly()) {
1161 lyx_view_->buffer()->lyxvc().checkOut();
1166 case LFUN_VC_REVERT:
1167 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1168 lyx_view_->buffer()->lyxvc().revert();
1172 case LFUN_VC_UNDO_LAST:
1173 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1174 lyx_view_->buffer()->lyxvc().undoLast();
1178 // --- buffers ----------------------------------------
1179 case LFUN_BUFFER_SWITCH:
1180 BOOST_ASSERT(lyx_view_);
1181 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1184 case LFUN_BUFFER_NEXT:
1185 BOOST_ASSERT(lyx_view_);
1186 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1189 case LFUN_BUFFER_PREVIOUS:
1190 BOOST_ASSERT(lyx_view_);
1191 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1195 BOOST_ASSERT(lyx_view_);
1196 newFile(view(), argument);
1199 case LFUN_FILE_OPEN:
1200 BOOST_ASSERT(lyx_view_);
1204 case LFUN_DROP_LAYOUTS_CHOICE:
1205 BOOST_ASSERT(lyx_view_);
1206 lyx_view_->getToolbars().openLayoutList();
1209 case LFUN_MENU_OPEN:
1210 BOOST_ASSERT(lyx_view_);
1211 lyx_view_->getMenubar().openByName(from_utf8(argument));
1214 // --- lyxserver commands ----------------------------
1215 case LFUN_SERVER_GET_NAME:
1216 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1217 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1218 lyxerr[Debug::INFO] << "FNAME["
1219 << lyx_view_->buffer()->fileName()
1223 case LFUN_SERVER_NOTIFY:
1224 dispatch_buffer = keyseq->print(false);
1225 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1228 case LFUN_SERVER_GOTO_FILE_ROW: {
1229 BOOST_ASSERT(lyx_view_);
1232 istringstream is(argument);
1233 is >> file_name >> row;
1234 if (prefixIs(file_name, package().temp_dir())) {
1235 // Needed by inverse dvi search. If it is a file
1236 // in tmpdir, call the apropriated function
1237 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1239 // Must replace extension of the file to be .lyx
1240 // and get full path
1241 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1242 // Either change buffer or load the file
1243 if (theBufferList().exists(s.absFilename())) {
1244 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1246 lyx_view_->loadLyXFile(s);
1250 view()->setCursorFromRow(row);
1252 updateFlags = Update::FitCursor;
1256 case LFUN_DIALOG_SHOW: {
1257 BOOST_ASSERT(lyx_view_);
1258 string const name = cmd.getArg(0);
1259 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1261 if (name == "character") {
1262 data = freefont2string();
1264 lyx_view_->getDialogs().show("character", data);
1265 } else if (name == "latexlog") {
1266 pair<Buffer::LogType, string> const logfile =
1267 lyx_view_->buffer()->getLogName();
1268 switch (logfile.first) {
1269 case Buffer::latexlog:
1272 case Buffer::buildlog:
1276 data += LyXLex::quoteString(logfile.second);
1277 lyx_view_->getDialogs().show("log", data);
1278 } else if (name == "vclog") {
1279 string const data = "vc " +
1280 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1281 lyx_view_->getDialogs().show("log", data);
1283 lyx_view_->getDialogs().show(name, data);
1287 case LFUN_DIALOG_SHOW_NEW_INSET: {
1288 BOOST_ASSERT(lyx_view_);
1289 string const name = cmd.getArg(0);
1290 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1291 if (name == "bibitem" ||
1295 name == "nomenclature" ||
1299 InsetCommandParams p(name);
1300 data = InsetCommandMailer::params2string(name, p);
1301 } else if (name == "include") {
1302 // data is the include type: one of "include",
1303 // "input", "verbatiminput" or "verbatiminput*"
1305 // default type is requested
1307 InsetCommandParams p(data);
1308 data = InsetIncludeMailer::params2string(p);
1309 } else if (name == "box") {
1310 // \c data == "Boxed" || "Frameless" etc
1311 InsetBoxParams p(data);
1312 data = InsetBoxMailer::params2string(p);
1313 } else if (name == "branch") {
1314 InsetBranchParams p;
1315 data = InsetBranchMailer::params2string(p);
1316 } else if (name == "citation") {
1317 InsetCommandParams p("cite");
1318 data = InsetCommandMailer::params2string(name, p);
1319 } else if (name == "ert") {
1320 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1321 } else if (name == "external") {
1322 InsetExternalParams p;
1323 Buffer const & buffer = *lyx_view_->buffer();
1324 data = InsetExternalMailer::params2string(p, buffer);
1325 } else if (name == "float") {
1327 data = InsetFloatMailer::params2string(p);
1328 } else if (name == "graphics") {
1329 InsetGraphicsParams p;
1330 Buffer const & buffer = *lyx_view_->buffer();
1331 data = InsetGraphicsMailer::params2string(p, buffer);
1332 } else if (name == "note") {
1334 data = InsetNoteMailer::params2string(p);
1335 } else if (name == "vspace") {
1337 data = InsetVSpaceMailer::params2string(space);
1338 } else if (name == "wrap") {
1340 data = InsetWrapMailer::params2string(p);
1342 lyx_view_->getDialogs().show(name, data, 0);
1346 case LFUN_DIALOG_UPDATE: {
1347 BOOST_ASSERT(lyx_view_);
1348 string const & name = argument;
1349 // Can only update a dialog connected to an existing inset
1350 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1352 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1353 inset->dispatch(view()->cursor(), fr);
1354 } else if (name == "paragraph") {
1355 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1356 } else if (name == "prefs") {
1357 lyx_view_->getDialogs().update(name, string());
1362 case LFUN_DIALOG_HIDE:
1363 Dialogs::hide(argument, 0);
1366 case LFUN_DIALOG_DISCONNECT_INSET:
1367 BOOST_ASSERT(lyx_view_);
1368 lyx_view_->getDialogs().disconnect(argument);
1372 case LFUN_CITATION_INSERT: {
1373 BOOST_ASSERT(lyx_view_);
1374 if (!argument.empty()) {
1375 // we can have one optional argument, delimited by '|'
1376 // citation-insert <key>|<text_before>
1377 // this should be enhanced to also support text_after
1378 // and citation style
1379 string arg = argument;
1381 if (contains(argument, "|")) {
1382 arg = token(argument, '|', 0);
1383 opt1 = '[' + token(argument, '|', 1) + ']';
1385 std::ostringstream os;
1386 os << "citation LatexCommand\n"
1387 << "\\cite" << opt1 << "{" << arg << "}\n"
1389 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1392 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1396 case LFUN_BUFFER_CHILD_OPEN: {
1397 BOOST_ASSERT(lyx_view_);
1398 FileName const filename =
1399 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1400 // FIXME Should use bformat
1401 setMessage(_("Opening child document ") +
1402 makeDisplayPath(filename.absFilename()) + "...");
1403 view()->saveBookmark(false);
1404 string const parentfilename = lyx_view_->buffer()->fileName();
1405 if (theBufferList().exists(filename.absFilename()))
1406 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1408 lyx_view_->loadLyXFile(filename);
1409 // Set the parent name of the child document.
1410 // This makes insertion of citations and references in the child work,
1411 // when the target is in the parent or another child document.
1412 lyx_view_->buffer()->setParentName(parentfilename);
1416 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1417 BOOST_ASSERT(lyx_view_);
1418 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1421 case LFUN_KEYMAP_OFF:
1422 BOOST_ASSERT(lyx_view_);
1423 lyx_view_->view()->getIntl().keyMapOn(false);
1426 case LFUN_KEYMAP_PRIMARY:
1427 BOOST_ASSERT(lyx_view_);
1428 lyx_view_->view()->getIntl().keyMapPrim();
1431 case LFUN_KEYMAP_SECONDARY:
1432 BOOST_ASSERT(lyx_view_);
1433 lyx_view_->view()->getIntl().keyMapSec();
1436 case LFUN_KEYMAP_TOGGLE:
1437 BOOST_ASSERT(lyx_view_);
1438 lyx_view_->view()->getIntl().toggleKeyMap();
1444 string rest = split(argument, countstr, ' ');
1445 istringstream is(countstr);
1448 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1449 for (int i = 0; i < count; ++i)
1450 dispatch(lyxaction.lookupFunc(rest));
1454 case LFUN_COMMAND_SEQUENCE: {
1455 // argument contains ';'-terminated commands
1456 string arg = argument;
1457 while (!arg.empty()) {
1459 arg = split(arg, first, ';');
1460 FuncRequest func(lyxaction.lookupFunc(first));
1461 func.origin = cmd.origin;
1467 case LFUN_PREFERENCES_SAVE: {
1468 lyxrc.write(makeAbsPath("preferences",
1469 package().user_support()),
1474 case LFUN_SCREEN_FONT_UPDATE:
1475 BOOST_ASSERT(lyx_view_);
1476 // handle the screen font changes.
1477 lyxrc.set_font_norm_type();
1478 theFontLoader().update();
1479 /// FIXME: only the current view will be updated. the Gui
1480 /// class is able to furnish the list of views.
1481 updateFlags = Update::Force;
1484 case LFUN_SET_COLOR: {
1486 string const x11_name = split(argument, lyx_name, ' ');
1487 if (lyx_name.empty() || x11_name.empty()) {
1488 setErrorMessage(_("Syntax: set-color <lyx_name>"
1493 bool const graphicsbg_changed =
1494 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1495 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1497 if (!lcolor.setColor(lyx_name, x11_name)) {
1499 bformat(_("Set-color \"%1$s\" failed "
1500 "- color is undefined or "
1501 "may not be redefined"),
1502 from_utf8(lyx_name)));
1506 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1508 if (graphicsbg_changed) {
1509 #ifdef WITH_WARNINGS
1510 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1513 graphics::GCache::get().changeDisplay(true);
1520 BOOST_ASSERT(lyx_view_);
1521 lyx_view_->message(from_utf8(argument));
1524 case LFUN_EXTERNAL_EDIT: {
1525 BOOST_ASSERT(lyx_view_);
1526 FuncRequest fr(action, argument);
1527 InsetExternal().dispatch(view()->cursor(), fr);
1531 case LFUN_GRAPHICS_EDIT: {
1532 FuncRequest fr(action, argument);
1533 InsetGraphics().dispatch(view()->cursor(), fr);
1537 case LFUN_INSET_APPLY: {
1538 BOOST_ASSERT(lyx_view_);
1539 string const name = cmd.getArg(0);
1540 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1542 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1543 inset->dispatch(view()->cursor(), fr);
1545 FuncRequest fr(LFUN_INSET_INSERT, argument);
1548 // ideally, the update flag should be set by the insets,
1549 // but this is not possible currently
1550 updateFlags = Update::Force | Update::FitCursor;
1554 case LFUN_ALL_INSETS_TOGGLE: {
1555 BOOST_ASSERT(lyx_view_);
1557 string const name = split(argument, action, ' ');
1558 InsetBase::Code const inset_code =
1559 InsetBase::translate(name);
1561 LCursor & cur = view()->cursor();
1562 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1564 InsetBase & inset = lyx_view_->buffer()->inset();
1565 InsetIterator it = inset_iterator_begin(inset);
1566 InsetIterator const end = inset_iterator_end(inset);
1567 for (; it != end; ++it) {
1568 if (inset_code == InsetBase::NO_CODE
1569 || inset_code == it->lyxCode()) {
1570 LCursor tmpcur = cur;
1571 tmpcur.pushLeft(*it);
1572 it->dispatch(tmpcur, fr);
1575 updateFlags = Update::Force | Update::FitCursor;
1579 case LFUN_BUFFER_LANGUAGE: {
1580 BOOST_ASSERT(lyx_view_);
1581 Buffer & buffer = *lyx_view_->buffer();
1582 Language const * oldL = buffer.params().language;
1583 Language const * newL = languages.getLanguage(argument);
1584 if (!newL || oldL == newL)
1587 if (oldL->rightToLeft() == newL->rightToLeft()
1588 && !buffer.isMultiLingual())
1589 buffer.changeLanguage(oldL, newL);
1591 buffer.updateDocLang(newL);
1595 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1596 string const fname =
1597 addName(addPath(package().user_support(), "templates/"),
1599 Buffer defaults(fname);
1601 istringstream ss(argument);
1604 int const unknown_tokens = defaults.readHeader(lex);
1606 if (unknown_tokens != 0) {
1607 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1608 << unknown_tokens << " unknown token"
1609 << (unknown_tokens == 1 ? "" : "s")
1613 if (defaults.writeFile(FileName(defaults.fileName())))
1614 // FIXME Should use bformat
1615 setMessage(_("Document defaults saved in ")
1616 + makeDisplayPath(fname));
1618 setErrorMessage(_("Unable to save document defaults"));
1622 case LFUN_BUFFER_PARAMS_APPLY: {
1623 BOOST_ASSERT(lyx_view_);
1624 biblio::CiteEngine const engine =
1625 lyx_view_->buffer()->params().cite_engine;
1627 istringstream ss(argument);
1630 int const unknown_tokens =
1631 lyx_view_->buffer()->readHeader(lex);
1633 if (unknown_tokens != 0) {
1634 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1635 << unknown_tokens << " unknown token"
1636 << (unknown_tokens == 1 ? "" : "s")
1639 if (engine == lyx_view_->buffer()->params().cite_engine)
1642 LCursor & cur = view()->cursor();
1643 FuncRequest fr(LFUN_INSET_REFRESH);
1645 InsetBase & inset = lyx_view_->buffer()->inset();
1646 InsetIterator it = inset_iterator_begin(inset);
1647 InsetIterator const end = inset_iterator_end(inset);
1648 for (; it != end; ++it)
1649 if (it->lyxCode() == InsetBase::CITE_CODE)
1650 it->dispatch(cur, fr);
1654 case LFUN_TEXTCLASS_APPLY: {
1655 BOOST_ASSERT(lyx_view_);
1656 Buffer * buffer = lyx_view_->buffer();
1658 textclass_type const old_class =
1659 buffer->params().textclass;
1661 loadTextclass(argument);
1663 std::pair<bool, textclass_type> const tc_pair =
1664 textclasslist.numberOfClass(argument);
1669 textclass_type const new_class = tc_pair.second;
1670 if (old_class == new_class)
1674 lyx_view_->message(_("Converting document to new document class..."));
1675 recordUndoFullDocument(view());
1676 buffer->params().textclass = new_class;
1677 StableDocIterator backcur(view()->cursor());
1678 ErrorList & el = buffer->errorList("Class Switch");
1679 cap::switchBetweenClasses(
1680 old_class, new_class,
1681 static_cast<InsetText &>(buffer->inset()), el);
1683 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1685 buffer->errors("Class Switch");
1686 updateLabels(*buffer);
1687 updateFlags = Update::Force | Update::FitCursor;
1691 case LFUN_TEXTCLASS_LOAD:
1692 loadTextclass(argument);
1695 case LFUN_LYXRC_APPLY: {
1696 LyXRC const lyxrc_orig = lyxrc;
1698 istringstream ss(argument);
1699 bool const success = lyxrc.read(ss) == 0;
1702 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1703 << "Unable to read lyxrc data"
1708 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1710 /// We force the redraw in any case because there might be
1711 /// some screen font changes.
1712 /// FIXME: only the current view will be updated. the Gui
1713 /// class is able to furnish the list of views.
1714 updateFlags = Update::Force;
1718 case LFUN_WINDOW_NEW:
1719 LyX::ref().newLyXView();
1722 case LFUN_WINDOW_CLOSE:
1723 BOOST_ASSERT(lyx_view_);
1724 BOOST_ASSERT(theApp());
1725 // update bookmark pit of the current buffer before window close
1726 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1727 gotoBookmark(i+1, false, false);
1728 // ask the user for saving changes or cancel quit
1729 if (!theBufferList().quitWriteAll())
1734 case LFUN_BOOKMARK_GOTO:
1735 // go to bookmark, open unopened file and switch to buffer if necessary
1736 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1739 case LFUN_BOOKMARK_CLEAR:
1740 LyX::ref().session().bookmarks().clear();
1743 case LFUN_TOOLBAR_TOGGLE_STATE:
1744 lyx_view_->toggleToolbarState(argument);
1748 BOOST_ASSERT(lyx_view_);
1749 view()->cursor().dispatch(cmd);
1750 updateFlags = view()->cursor().result().update();
1751 if (!view()->cursor().result().dispatched())
1752 updateFlags = view()->dispatch(cmd);
1757 if (lyx_view_ && view()->buffer()) {
1758 // BufferView::update() updates the ViewMetricsInfo and
1759 // also initializes the position cache for all insets in
1760 // (at least partially) visible top-level paragraphs.
1761 // We will redraw the screen only if needed.
1762 if (view()->update(updateFlags)) {
1763 // Buffer::changed() signals that a repaint is needed.
1764 // The frontend (WorkArea) knows which area to repaint
1765 // thanks to the ViewMetricsInfo updated above.
1766 view()->buffer()->changed();
1769 lyx_view_->updateStatusBar();
1771 // if we executed a mutating lfun, mark the buffer as dirty
1773 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1774 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1775 view()->buffer()->markDirty();
1777 if (view()->cursor().inTexted()) {
1778 lyx_view_->updateLayoutChoice();
1783 lyx_view_->updateMenubar();
1784 lyx_view_->updateToolbars();
1785 sendDispatchMessage(getMessage(), cmd);
1790 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1792 const bool verbose = (cmd.origin == FuncRequest::MENU
1793 || cmd.origin == FuncRequest::TOOLBAR
1794 || cmd.origin == FuncRequest::COMMANDBUFFER);
1796 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1797 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1799 lyx_view_->message(msg);
1803 docstring dispatch_msg = msg;
1804 if (!dispatch_msg.empty())
1805 dispatch_msg += ' ';
1807 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1809 bool argsadded = false;
1811 if (!cmd.argument().empty()) {
1812 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1813 comname += ' ' + cmd.argument();
1818 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1820 if (!shortcuts.empty())
1821 comname += ": " + shortcuts;
1822 else if (!argsadded && !cmd.argument().empty())
1823 comname += ' ' + cmd.argument();
1825 if (!comname.empty()) {
1826 comname = rtrim(comname);
1827 dispatch_msg += '(' + rtrim(comname) + ')';
1830 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1831 << to_utf8(dispatch_msg) << endl;
1832 if (!dispatch_msg.empty())
1833 lyx_view_->message(dispatch_msg);
1837 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1839 // FIXME: initpath is not used. What to do?
1840 string initpath = lyxrc.document_path;
1841 string filename(name);
1843 if (view()->buffer()) {
1844 string const trypath = lyx_view_->buffer()->filePath();
1845 // If directory is writeable, use this as default.
1846 if (isDirWriteable(FileName(trypath)))
1850 static int newfile_number;
1852 if (filename.empty()) {
1853 filename = addName(lyxrc.document_path,
1854 "newfile" + convert<string>(++newfile_number) + ".lyx");
1855 while (theBufferList().exists(filename) ||
1856 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1858 filename = addName(lyxrc.document_path,
1859 "newfile" + convert<string>(newfile_number) +
1864 // The template stuff
1867 FileDialog fileDlg(_("Select template file"),
1868 LFUN_SELECT_FILE_SYNC,
1869 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1870 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1872 FileDialog::Result result =
1873 fileDlg.open(from_utf8(lyxrc.template_path),
1874 FileFilterList(_("LyX Documents (*.lyx)")),
1877 if (result.first == FileDialog::Later)
1879 if (result.second.empty())
1881 templname = to_utf8(result.second);
1884 Buffer * const b = newFile(filename, templname, !name.empty());
1886 lyx_view_->setBuffer(b);
1890 void LyXFunc::open(string const & fname)
1892 string initpath = lyxrc.document_path;
1894 if (view()->buffer()) {
1895 string const trypath = lyx_view_->buffer()->filePath();
1896 // If directory is writeable, use this as default.
1897 if (isDirWriteable(FileName(trypath)))
1903 if (fname.empty()) {
1904 FileDialog fileDlg(_("Select document to open"),
1906 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1907 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1909 FileDialog::Result result =
1910 fileDlg.open(from_utf8(initpath),
1911 FileFilterList(_("LyX Documents (*.lyx)")),
1914 if (result.first == FileDialog::Later)
1917 filename = to_utf8(result.second);
1919 // check selected filename
1920 if (filename.empty()) {
1921 lyx_view_->message(_("Canceled."));
1927 // get absolute path of file and add ".lyx" to the filename if
1929 FileName const fullname = fileSearch(string(), filename, "lyx");
1930 if (!fullname.empty())
1931 filename = fullname.absFilename();
1933 // if the file doesn't exist, let the user create one
1934 if (!fs::exists(fullname.toFilesystemEncoding())) {
1935 // the user specifically chose this name. Believe him.
1936 Buffer * const b = newFile(filename, string(), true);
1938 lyx_view_->setBuffer(b);
1942 docstring const disp_fn = makeDisplayPath(filename);
1943 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1946 if (lyx_view_->loadLyXFile(fullname)) {
1947 str2 = bformat(_("Document %1$s opened."), disp_fn);
1949 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1951 lyx_view_->message(str2);
1955 void LyXFunc::doImport(string const & argument)
1958 string filename = split(argument, format, ' ');
1960 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1961 << " file: " << filename << endl;
1963 // need user interaction
1964 if (filename.empty()) {
1965 string initpath = lyxrc.document_path;
1967 if (view()->buffer()) {
1968 string const trypath = lyx_view_->buffer()->filePath();
1969 // If directory is writeable, use this as default.
1970 if (isDirWriteable(FileName(trypath)))
1974 docstring const text = bformat(_("Select %1$s file to import"),
1975 formats.prettyName(format));
1977 FileDialog fileDlg(text,
1979 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1980 make_pair(_("Examples|#E#e"),
1981 from_utf8(addPath(package().system_support(), "examples"))));
1983 docstring filter = formats.prettyName(format);
1986 filter += from_utf8(formats.extension(format));
1989 FileDialog::Result result =
1990 fileDlg.open(from_utf8(initpath),
1991 FileFilterList(filter),
1994 if (result.first == FileDialog::Later)
1997 filename = to_utf8(result.second);
1999 // check selected filename
2000 if (filename.empty())
2001 lyx_view_->message(_("Canceled."));
2004 if (filename.empty())
2007 // get absolute path of file
2008 FileName const fullname(makeAbsPath(filename));
2010 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2012 // Check if the document already is open
2013 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2014 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2015 lyx_view_->message(_("Canceled."));
2020 // if the file exists already, and we didn't do
2021 // -i lyx thefile.lyx, warn
2022 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2023 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2025 docstring text = bformat(_("The document %1$s already exists.\n\n"
2026 "Do you want to over-write that document?"), file);
2027 int const ret = Alert::prompt(_("Over-write document?"),
2028 text, 0, 1, _("&Over-write"), _("&Cancel"));
2031 lyx_view_->message(_("Canceled."));
2036 ErrorList errorList;
2037 Importer::Import(lyx_view_, fullname, format, errorList);
2038 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2042 void LyXFunc::closeBuffer()
2044 // save current cursor position
2045 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2046 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2047 // goto bookmark to update bookmark pit.
2048 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2049 gotoBookmark(i+1, false, false);
2050 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2051 if (theBufferList().empty()) {
2052 // need this otherwise SEGV may occur while
2053 // trying to set variables that don't exist
2054 // since there's no current buffer
2055 lyx_view_->getDialogs().hideBufferDependent();
2057 lyx_view_->setBuffer(theBufferList().first());
2063 void LyXFunc::reloadBuffer()
2065 FileName filename(lyx_view_->buffer()->fileName());
2067 lyx_view_->loadLyXFile(filename);
2070 // Each "lyx_view_" should have it's own message method. lyxview and
2071 // the minibuffer would use the minibuffer, but lyxserver would
2072 // send an ERROR signal to its client. Alejandro 970603
2073 // This function is bit problematic when it comes to NLS, to make the
2074 // lyx servers client be language indepenent we must not translate
2075 // strings sent to this func.
2076 void LyXFunc::setErrorMessage(docstring const & m) const
2078 dispatch_buffer = m;
2083 void LyXFunc::setMessage(docstring const & m) const
2085 dispatch_buffer = m;
2089 docstring const LyXFunc::viewStatusMessage()
2091 // When meta-fake key is pressed, show the key sequence so far + "M-".
2093 return keyseq->print(true) + "M-";
2095 // Else, when a non-complete key sequence is pressed,
2096 // show the available options.
2097 if (keyseq->length() > 0 && !keyseq->deleted())
2098 return keyseq->printOptions(true);
2100 if (!view()->buffer())
2101 return _("Welcome to LyX!");
2103 return view()->cursor().currentState();
2107 BufferView * LyXFunc::view() const
2109 BOOST_ASSERT(lyx_view_);
2110 return lyx_view_->view();
2114 bool LyXFunc::wasMetaKey() const
2116 return (meta_fake_bit != key_modifier::none);
2122 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2124 // Why the switch you might ask. It is a trick to ensure that all
2125 // the elements in the LyXRCTags enum is handled. As you can see
2126 // there are no breaks at all. So it is just a huge fall-through.
2127 // The nice thing is that we will get a warning from the compiler
2128 // if we forget an element.
2129 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2131 case LyXRC::RC_ACCEPT_COMPOUND:
2132 case LyXRC::RC_ALT_LANG:
2133 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2134 case LyXRC::RC_PLAINTEXT_LINELEN:
2135 case LyXRC::RC_AUTOREGIONDELETE:
2136 case LyXRC::RC_AUTORESET_OPTIONS:
2137 case LyXRC::RC_AUTOSAVE:
2138 case LyXRC::RC_AUTO_NUMBER:
2139 case LyXRC::RC_BACKUPDIR_PATH:
2140 case LyXRC::RC_BIBTEX_COMMAND:
2141 case LyXRC::RC_BINDFILE:
2142 case LyXRC::RC_CHECKLASTFILES:
2143 case LyXRC::RC_USELASTFILEPOS:
2144 case LyXRC::RC_LOADSESSION:
2145 case LyXRC::RC_CHKTEX_COMMAND:
2146 case LyXRC::RC_CONVERTER:
2147 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2148 case LyXRC::RC_COPIER:
2149 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2150 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2151 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2152 case LyXRC::RC_DATE_INSERT_FORMAT:
2153 case LyXRC::RC_DEFAULT_LANGUAGE:
2154 case LyXRC::RC_DEFAULT_PAPERSIZE:
2155 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2156 case LyXRC::RC_DISPLAY_GRAPHICS:
2157 case LyXRC::RC_DOCUMENTPATH:
2158 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2159 string const encoded = FileName(
2160 lyxrc_new.document_path).toFilesystemEncoding();
2161 if (fs::exists(encoded) && fs::is_directory(encoded))
2162 support::package().document_dir() = lyxrc.document_path;
2164 case LyXRC::RC_ESC_CHARS:
2165 case LyXRC::RC_FONT_ENCODING:
2166 case LyXRC::RC_FORMAT:
2167 case LyXRC::RC_INDEX_COMMAND:
2168 case LyXRC::RC_INPUT:
2169 case LyXRC::RC_KBMAP:
2170 case LyXRC::RC_KBMAP_PRIMARY:
2171 case LyXRC::RC_KBMAP_SECONDARY:
2172 case LyXRC::RC_LABEL_INIT_LENGTH:
2173 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2174 case LyXRC::RC_LANGUAGE_AUTO_END:
2175 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2176 case LyXRC::RC_LANGUAGE_COMMAND_END:
2177 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2178 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2179 case LyXRC::RC_LANGUAGE_PACKAGE:
2180 case LyXRC::RC_LANGUAGE_USE_BABEL:
2181 case LyXRC::RC_MAKE_BACKUP:
2182 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2183 case LyXRC::RC_NUMLASTFILES:
2184 case LyXRC::RC_PATH_PREFIX:
2185 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2186 support::prependEnvPath("PATH", lyxrc.path_prefix);
2188 case LyXRC::RC_PERS_DICT:
2189 case LyXRC::RC_POPUP_BOLD_FONT:
2190 case LyXRC::RC_POPUP_FONT_ENCODING:
2191 case LyXRC::RC_POPUP_NORMAL_FONT:
2192 case LyXRC::RC_PREVIEW:
2193 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2194 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2195 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2196 case LyXRC::RC_PRINTCOPIESFLAG:
2197 case LyXRC::RC_PRINTER:
2198 case LyXRC::RC_PRINTEVENPAGEFLAG:
2199 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2200 case LyXRC::RC_PRINTFILEEXTENSION:
2201 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2202 case LyXRC::RC_PRINTODDPAGEFLAG:
2203 case LyXRC::RC_PRINTPAGERANGEFLAG:
2204 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2205 case LyXRC::RC_PRINTPAPERFLAG:
2206 case LyXRC::RC_PRINTREVERSEFLAG:
2207 case LyXRC::RC_PRINTSPOOL_COMMAND:
2208 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2209 case LyXRC::RC_PRINTTOFILE:
2210 case LyXRC::RC_PRINTTOPRINTER:
2211 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2212 case LyXRC::RC_PRINT_COMMAND:
2213 case LyXRC::RC_RTL_SUPPORT:
2214 case LyXRC::RC_SCREEN_DPI:
2215 case LyXRC::RC_SCREEN_FONT_ENCODING:
2216 case LyXRC::RC_SCREEN_FONT_ROMAN:
2217 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2218 case LyXRC::RC_SCREEN_FONT_SANS:
2219 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2220 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2221 case LyXRC::RC_SCREEN_FONT_SIZES:
2222 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2223 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2224 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2225 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2226 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2227 case LyXRC::RC_SCREEN_ZOOM:
2228 case LyXRC::RC_SERVERPIPE:
2229 case LyXRC::RC_SET_COLOR:
2230 case LyXRC::RC_SHOW_BANNER:
2231 case LyXRC::RC_SPELL_COMMAND:
2232 case LyXRC::RC_TEMPDIRPATH:
2233 case LyXRC::RC_TEMPLATEPATH:
2234 case LyXRC::RC_TEX_ALLOWS_SPACES:
2235 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2236 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2237 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2239 case LyXRC::RC_UIFILE:
2240 case LyXRC::RC_USER_EMAIL:
2241 case LyXRC::RC_USER_NAME:
2242 case LyXRC::RC_USETEMPDIR:
2243 case LyXRC::RC_USE_ALT_LANG:
2244 case LyXRC::RC_USE_CONVERTER_CACHE:
2245 case LyXRC::RC_USE_ESC_CHARS:
2246 case LyXRC::RC_USE_INP_ENC:
2247 case LyXRC::RC_USE_PERS_DICT:
2248 case LyXRC::RC_USE_SPELL_LIB:
2249 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2250 case LyXRC::RC_VIEWER:
2251 case LyXRC::RC_LAST: