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;
582 case LFUN_DIALOG_UPDATE: {
583 string const name = cmd.getArg(0);
585 enable = name == "prefs";
589 case LFUN_CITATION_INSERT: {
590 FuncRequest fr(LFUN_INSET_INSERT, "citation");
591 enable = getStatus(fr).enabled();
595 case LFUN_BUFFER_WRITE: {
596 enable = view()->buffer()->isUnnamed()
597 || !view()->buffer()->isClean();
601 case LFUN_BOOKMARK_GOTO: {
602 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
603 enable = LyX::ref().session().bookmarks().isValid(num);
607 case LFUN_BOOKMARK_CLEAR:
608 enable = LyX::ref().session().bookmarks().size() > 0;
611 case LFUN_TOOLBAR_TOGGLE_STATE: {
612 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
613 if (!(flags & ToolbarBackend::AUTO))
614 flag.setOnOff(flags & ToolbarBackend::ON);
618 // this one is difficult to get right. As a half-baked
619 // solution, we consider only the first action of the sequence
620 case LFUN_COMMAND_SEQUENCE: {
621 // argument contains ';'-terminated commands
622 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
623 FuncRequest func(lyxaction.lookupFunc(firstcmd));
624 func.origin = cmd.origin;
625 flag = getStatus(func);
628 case LFUN_BUFFER_NEW:
629 case LFUN_BUFFER_NEW_TEMPLATE:
630 case LFUN_WORD_FIND_FORWARD:
631 case LFUN_WORD_FIND_BACKWARD:
632 case LFUN_COMMAND_PREFIX:
633 case LFUN_COMMAND_EXECUTE:
635 case LFUN_META_PREFIX:
636 case LFUN_BUFFER_CLOSE:
637 case LFUN_BUFFER_WRITE_AS:
638 case LFUN_BUFFER_UPDATE:
639 case LFUN_BUFFER_VIEW:
640 case LFUN_BUFFER_IMPORT:
642 case LFUN_BUFFER_AUTO_SAVE:
643 case LFUN_RECONFIGURE:
647 case LFUN_DROP_LAYOUTS_CHOICE:
649 case LFUN_SERVER_GET_NAME:
650 case LFUN_SERVER_NOTIFY:
651 case LFUN_SERVER_GOTO_FILE_ROW:
652 case LFUN_DIALOG_HIDE:
653 case LFUN_DIALOG_DISCONNECT_INSET:
654 case LFUN_BUFFER_CHILD_OPEN:
655 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
656 case LFUN_KEYMAP_OFF:
657 case LFUN_KEYMAP_PRIMARY:
658 case LFUN_KEYMAP_SECONDARY:
659 case LFUN_KEYMAP_TOGGLE:
661 case LFUN_BUFFER_EXPORT_CUSTOM:
662 case LFUN_BUFFER_PRINT:
663 case LFUN_PREFERENCES_SAVE:
664 case LFUN_SCREEN_FONT_UPDATE:
667 case LFUN_EXTERNAL_EDIT:
668 case LFUN_GRAPHICS_EDIT:
669 case LFUN_ALL_INSETS_TOGGLE:
670 case LFUN_BUFFER_LANGUAGE:
671 case LFUN_TEXTCLASS_APPLY:
672 case LFUN_TEXTCLASS_LOAD:
673 case LFUN_BUFFER_SAVE_AS_DEFAULT:
674 case LFUN_BUFFER_PARAMS_APPLY:
675 case LFUN_LYXRC_APPLY:
676 case LFUN_BUFFER_NEXT:
677 case LFUN_BUFFER_PREVIOUS:
678 case LFUN_WINDOW_NEW:
679 case LFUN_WINDOW_CLOSE:
681 // these are handled in our dispatch()
685 if (!getLocalStatus(cur, cmd, flag))
686 flag = view()->getStatus(cmd);
692 // Can we use a readonly buffer?
693 if (buf && buf->isReadonly()
694 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
695 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
696 flag.message(from_utf8(N_("Document is read-only")));
700 // Are we in a DELETED change-tracking region?
701 if (buf && lookupChangeType(cur, true) == Change::DELETED
702 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
703 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
704 flag.message(from_utf8(N_("This portion of the document is deleted.")));
708 // the default error message if we disable the command
709 if (!flag.enabled() && flag.message().empty())
710 flag.message(from_utf8(N_("Command disabled")));
716 bool LyXFunc::ensureBufferClean(BufferView * bv)
718 Buffer & buf = *bv->buffer();
722 docstring const file = makeDisplayPath(buf.fileName(), 30);
723 docstring text = bformat(_("The document %1$s has unsaved "
724 "changes.\n\nDo you want to save "
725 "the document?"), file);
726 int const ret = Alert::prompt(_("Save changed document?"),
727 text, 0, 1, _("&Save"),
731 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
733 return buf.isClean();
739 void showPrintError(string const & name)
741 docstring str = bformat(_("Could not print the document %1$s.\n"
742 "Check that your printer is set up correctly."),
743 makeDisplayPath(name, 50));
744 Alert::error(_("Print document failed"), str);
748 void loadTextclass(string const & name)
750 std::pair<bool, textclass_type> const tc_pair =
751 textclasslist.numberOfClass(name);
753 if (!tc_pair.first) {
754 lyxerr << "Document class \"" << name
755 << "\" does not exist."
760 textclass_type const tc = tc_pair.second;
762 if (!textclasslist[tc].load()) {
763 docstring s = bformat(_("The document could not be converted\n"
764 "into the document class %1$s."),
765 from_utf8(textclasslist[tc].name()));
766 Alert::error(_("Could not change class"), s);
771 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
776 void LyXFunc::dispatch(FuncRequest const & cmd)
778 string const argument = to_utf8(cmd.argument());
779 kb_action const action = cmd.action;
781 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
782 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
784 // we have not done anything wrong yet.
786 dispatch_buffer.erase();
788 // redraw the screen at the end (first of the two drawing steps).
789 //This is done unless explicitely requested otherwise
790 Update::flags updateFlags = Update::FitCursor;
792 FuncStatus const flag = getStatus(cmd);
793 if (!flag.enabled()) {
794 // We cannot use this function here
795 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
796 << lyxaction.getActionName(action)
797 << " [" << action << "] is disabled at this location"
799 setErrorMessage(flag.message());
803 case LFUN_WORD_FIND_FORWARD:
804 case LFUN_WORD_FIND_BACKWARD: {
805 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
806 static docstring last_search;
807 docstring searched_string;
809 if (!cmd.argument().empty()) {
810 last_search = cmd.argument();
811 searched_string = cmd.argument();
813 searched_string = last_search;
816 if (searched_string.empty())
819 bool const fw = action == LFUN_WORD_FIND_FORWARD;
820 docstring const data =
821 find2string(searched_string, true, false, fw);
822 find(view(), FuncRequest(LFUN_WORD_FIND, data));
826 case LFUN_COMMAND_PREFIX:
827 BOOST_ASSERT(lyx_view_);
828 lyx_view_->message(keyseq->printOptions(true));
831 case LFUN_COMMAND_EXECUTE:
832 BOOST_ASSERT(lyx_view_);
833 lyx_view_->getToolbars().display("minibuffer", true);
834 lyx_view_->focus_command_buffer();
838 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
840 meta_fake_bit = key_modifier::none;
841 if (view()->buffer())
842 // cancel any selection
843 dispatch(FuncRequest(LFUN_MARK_OFF));
844 setMessage(_("Cancel"));
847 case LFUN_META_PREFIX:
848 meta_fake_bit = key_modifier::alt;
849 setMessage(keyseq->print(true));
852 case LFUN_BUFFER_TOGGLE_READ_ONLY:
853 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
854 if (lyx_view_->buffer()->lyxvc().inUse())
855 lyx_view_->buffer()->lyxvc().toggleReadOnly();
857 lyx_view_->buffer()->setReadonly(
858 !lyx_view_->buffer()->isReadonly());
861 // --- Menus -----------------------------------------------
862 case LFUN_BUFFER_NEW:
863 menuNew(argument, false);
866 case LFUN_BUFFER_NEW_TEMPLATE:
867 menuNew(argument, true);
870 case LFUN_BUFFER_CLOSE:
875 case LFUN_BUFFER_WRITE:
876 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
877 if (!lyx_view_->buffer()->isUnnamed()) {
878 docstring const str = bformat(_("Saving document %1$s..."),
879 makeDisplayPath(lyx_view_->buffer()->fileName()));
880 lyx_view_->message(str);
881 menuWrite(lyx_view_->buffer());
882 lyx_view_->message(str + _(" done."));
884 writeAs(lyx_view_->buffer());
886 updateFlags = Update::None;
889 case LFUN_BUFFER_WRITE_AS:
890 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
891 writeAs(lyx_view_->buffer(), argument);
892 updateFlags = Update::None;
895 case LFUN_BUFFER_RELOAD: {
896 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
897 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
898 docstring text = bformat(_("Any changes will be lost. Are you sure "
899 "you want to revert to the saved version of the document %1$s?"), file);
900 int const ret = Alert::prompt(_("Revert to saved document?"),
901 text, 0, 1, _("&Revert"), _("&Cancel"));
908 case LFUN_BUFFER_UPDATE:
909 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
910 Exporter::Export(lyx_view_->buffer(), argument, true);
913 case LFUN_BUFFER_VIEW:
914 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
915 Exporter::preview(lyx_view_->buffer(), argument);
918 case LFUN_BUILD_PROGRAM:
919 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
920 Exporter::Export(lyx_view_->buffer(), "program", true);
923 case LFUN_BUFFER_CHKTEX:
924 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
925 lyx_view_->buffer()->runChktex();
928 case LFUN_BUFFER_EXPORT:
929 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
930 if (argument == "custom")
931 lyx_view_->getDialogs().show("sendto");
933 Exporter::Export(lyx_view_->buffer(), argument, false);
937 case LFUN_BUFFER_EXPORT_CUSTOM: {
938 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
940 string command = split(argument, format_name, ' ');
941 Format const * format = formats.getFormat(format_name);
943 lyxerr << "Format \"" << format_name
944 << "\" not recognized!"
949 Buffer * buffer = lyx_view_->buffer();
951 // The name of the file created by the conversion process
954 // Output to filename
955 if (format->name() == "lyx") {
956 string const latexname =
957 buffer->getLatexName(false);
958 filename = changeExtension(latexname,
959 format->extension());
960 filename = addName(buffer->temppath(), filename);
962 if (!buffer->writeFile(FileName(filename)))
966 Exporter::Export(buffer, format_name, true, filename);
969 // Substitute $$FName for filename
970 if (!contains(command, "$$FName"))
971 command = "( " + command + " ) < $$FName";
972 command = subst(command, "$$FName", filename);
974 // Execute the command in the background
976 call.startscript(Systemcall::DontWait, command);
980 case LFUN_BUFFER_PRINT: {
981 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
984 string command = split(split(argument, target, ' '),
988 || target_name.empty()
989 || command.empty()) {
990 lyxerr << "Unable to parse \""
991 << argument << '"' << std::endl;
994 if (target != "printer" && target != "file") {
995 lyxerr << "Unrecognized target \""
996 << target << '"' << std::endl;
1000 Buffer * buffer = lyx_view_->buffer();
1002 if (!Exporter::Export(buffer, "dvi", true)) {
1003 showPrintError(buffer->fileName());
1007 // Push directory path.
1008 string const path = buffer->temppath();
1009 support::Path p(path);
1011 // there are three cases here:
1012 // 1. we print to a file
1013 // 2. we print directly to a printer
1014 // 3. we print using a spool command (print to file first)
1017 string const dviname =
1018 changeExtension(buffer->getLatexName(true),
1021 if (target == "printer") {
1022 if (!lyxrc.print_spool_command.empty()) {
1023 // case 3: print using a spool
1024 string const psname =
1025 changeExtension(dviname,".ps");
1026 command += lyxrc.print_to_file
1029 + quoteName(dviname);
1032 lyxrc.print_spool_command +' ';
1033 if (target_name != "default") {
1034 command2 += lyxrc.print_spool_printerprefix
1038 command2 += quoteName(psname);
1040 // If successful, then spool command
1041 res = one.startscript(
1046 res = one.startscript(
1047 Systemcall::DontWait,
1050 // case 2: print directly to a printer
1051 res = one.startscript(
1052 Systemcall::DontWait,
1053 command + quoteName(dviname));
1057 // case 1: print to a file
1058 FileName const filename(makeAbsPath(target_name, path));
1059 if (fs::exists(filename.toFilesystemEncoding())) {
1060 docstring text = bformat(
1061 _("The file %1$s already exists.\n\n"
1062 "Do you want to over-write that file?"),
1063 makeDisplayPath(filename.absFilename()));
1064 if (Alert::prompt(_("Over-write file?"),
1065 text, 0, 1, _("&Over-write"), _("&Cancel")) != 0) {
1066 showPrintError(buffer->fileName());
1070 command += lyxrc.print_to_file
1071 + quoteName(filename.toFilesystemEncoding())
1073 + quoteName(dviname);
1074 res = one.startscript(Systemcall::DontWait,
1079 showPrintError(buffer->fileName());
1083 case LFUN_BUFFER_IMPORT:
1088 // quitting is triggered by the gui code
1089 // (leaving the event loop).
1090 lyx_view_->message(from_utf8(N_("Exiting.")));
1091 if (theBufferList().quitWriteAll())
1092 theApp()->gui().closeAllViews();
1095 case LFUN_TOC_VIEW: {
1096 BOOST_ASSERT(lyx_view_);
1097 InsetCommandParams p("tableofcontents");
1098 string const data = InsetCommandMailer::params2string("toc", p);
1099 lyx_view_->getDialogs().show("toc", data, 0);
1103 case LFUN_BUFFER_AUTO_SAVE:
1107 case LFUN_RECONFIGURE:
1108 BOOST_ASSERT(lyx_view_);
1109 reconfigure(*lyx_view_);
1112 case LFUN_HELP_OPEN: {
1113 BOOST_ASSERT(lyx_view_);
1114 string const arg = argument;
1116 setErrorMessage(_("Missing argument"));
1119 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1120 if (fname.empty()) {
1121 lyxerr << "LyX: unable to find documentation file `"
1122 << arg << "'. Bad installation?" << endl;
1125 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1126 makeDisplayPath(fname.absFilename())));
1127 lyx_view_->loadLyXFile(fname, false);
1131 // --- version control -------------------------------
1132 case LFUN_VC_REGISTER:
1133 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1134 if (!ensureBufferClean(view()))
1136 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1137 lyx_view_->buffer()->lyxvc().registrer();
1140 updateFlags = Update::Force;
1143 case LFUN_VC_CHECK_IN:
1144 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1145 if (!ensureBufferClean(view()))
1147 if (lyx_view_->buffer()->lyxvc().inUse()
1148 && !lyx_view_->buffer()->isReadonly()) {
1149 lyx_view_->buffer()->lyxvc().checkIn();
1154 case LFUN_VC_CHECK_OUT:
1155 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1156 if (!ensureBufferClean(view()))
1158 if (lyx_view_->buffer()->lyxvc().inUse()
1159 && lyx_view_->buffer()->isReadonly()) {
1160 lyx_view_->buffer()->lyxvc().checkOut();
1165 case LFUN_VC_REVERT:
1166 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1167 lyx_view_->buffer()->lyxvc().revert();
1171 case LFUN_VC_UNDO_LAST:
1172 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1173 lyx_view_->buffer()->lyxvc().undoLast();
1177 // --- buffers ----------------------------------------
1178 case LFUN_BUFFER_SWITCH:
1179 BOOST_ASSERT(lyx_view_);
1180 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1183 case LFUN_BUFFER_NEXT:
1184 BOOST_ASSERT(lyx_view_);
1185 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1188 case LFUN_BUFFER_PREVIOUS:
1189 BOOST_ASSERT(lyx_view_);
1190 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1194 BOOST_ASSERT(lyx_view_);
1195 newFile(view(), argument);
1198 case LFUN_FILE_OPEN:
1199 BOOST_ASSERT(lyx_view_);
1203 case LFUN_DROP_LAYOUTS_CHOICE:
1204 BOOST_ASSERT(lyx_view_);
1205 lyx_view_->getToolbars().openLayoutList();
1208 case LFUN_MENU_OPEN:
1209 BOOST_ASSERT(lyx_view_);
1210 lyx_view_->getMenubar().openByName(from_utf8(argument));
1213 // --- lyxserver commands ----------------------------
1214 case LFUN_SERVER_GET_NAME:
1215 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1216 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1217 lyxerr[Debug::INFO] << "FNAME["
1218 << lyx_view_->buffer()->fileName()
1222 case LFUN_SERVER_NOTIFY:
1223 dispatch_buffer = keyseq->print(false);
1224 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1227 case LFUN_SERVER_GOTO_FILE_ROW: {
1228 BOOST_ASSERT(lyx_view_);
1231 istringstream is(argument);
1232 is >> file_name >> row;
1233 if (prefixIs(file_name, package().temp_dir())) {
1234 // Needed by inverse dvi search. If it is a file
1235 // in tmpdir, call the apropriated function
1236 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1238 // Must replace extension of the file to be .lyx
1239 // and get full path
1240 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1241 // Either change buffer or load the file
1242 if (theBufferList().exists(s.absFilename())) {
1243 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1245 lyx_view_->loadLyXFile(s);
1249 view()->setCursorFromRow(row);
1251 updateFlags = Update::FitCursor;
1255 case LFUN_DIALOG_SHOW: {
1256 BOOST_ASSERT(lyx_view_);
1257 string const name = cmd.getArg(0);
1258 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1260 if (name == "character") {
1261 data = freefont2string();
1263 lyx_view_->getDialogs().show("character", data);
1264 } else if (name == "latexlog") {
1265 pair<Buffer::LogType, string> const logfile =
1266 lyx_view_->buffer()->getLogName();
1267 switch (logfile.first) {
1268 case Buffer::latexlog:
1271 case Buffer::buildlog:
1275 data += LyXLex::quoteString(logfile.second);
1276 lyx_view_->getDialogs().show("log", data);
1277 } else if (name == "vclog") {
1278 string const data = "vc " +
1279 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1280 lyx_view_->getDialogs().show("log", data);
1282 lyx_view_->getDialogs().show(name, data);
1286 case LFUN_DIALOG_SHOW_NEW_INSET: {
1287 BOOST_ASSERT(lyx_view_);
1288 string const name = cmd.getArg(0);
1289 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1290 if (name == "bibitem" ||
1294 name == "nomenclature" ||
1298 InsetCommandParams p(name);
1299 data = InsetCommandMailer::params2string(name, p);
1300 } else if (name == "include") {
1301 // data is the include type: one of "include",
1302 // "input", "verbatiminput" or "verbatiminput*"
1304 // default type is requested
1306 InsetCommandParams p(data);
1307 data = InsetIncludeMailer::params2string(p);
1308 } else if (name == "box") {
1309 // \c data == "Boxed" || "Frameless" etc
1310 InsetBoxParams p(data);
1311 data = InsetBoxMailer::params2string(p);
1312 } else if (name == "branch") {
1313 InsetBranchParams p;
1314 data = InsetBranchMailer::params2string(p);
1315 } else if (name == "citation") {
1316 InsetCommandParams p("cite");
1317 data = InsetCommandMailer::params2string(name, p);
1318 } else if (name == "ert") {
1319 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1320 } else if (name == "external") {
1321 InsetExternalParams p;
1322 Buffer const & buffer = *lyx_view_->buffer();
1323 data = InsetExternalMailer::params2string(p, buffer);
1324 } else if (name == "float") {
1326 data = InsetFloatMailer::params2string(p);
1327 } else if (name == "graphics") {
1328 InsetGraphicsParams p;
1329 Buffer const & buffer = *lyx_view_->buffer();
1330 data = InsetGraphicsMailer::params2string(p, buffer);
1331 } else if (name == "note") {
1333 data = InsetNoteMailer::params2string(p);
1334 } else if (name == "vspace") {
1336 data = InsetVSpaceMailer::params2string(space);
1337 } else if (name == "wrap") {
1339 data = InsetWrapMailer::params2string(p);
1341 lyx_view_->getDialogs().show(name, data, 0);
1345 case LFUN_DIALOG_UPDATE: {
1346 BOOST_ASSERT(lyx_view_);
1347 string const & name = argument;
1348 // Can only update a dialog connected to an existing inset
1349 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1351 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1352 inset->dispatch(view()->cursor(), fr);
1353 } else if (name == "paragraph") {
1354 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1355 } else if (name == "prefs") {
1356 lyx_view_->getDialogs().update(name, string());
1361 case LFUN_DIALOG_HIDE:
1362 Dialogs::hide(argument, 0);
1365 case LFUN_DIALOG_DISCONNECT_INSET:
1366 BOOST_ASSERT(lyx_view_);
1367 lyx_view_->getDialogs().disconnect(argument);
1371 case LFUN_CITATION_INSERT: {
1372 BOOST_ASSERT(lyx_view_);
1373 if (!argument.empty()) {
1374 // we can have one optional argument, delimited by '|'
1375 // citation-insert <key>|<text_before>
1376 // this should be enhanced to also support text_after
1377 // and citation style
1378 string arg = argument;
1380 if (contains(argument, "|")) {
1381 arg = token(argument, '|', 0);
1382 opt1 = '[' + token(argument, '|', 1) + ']';
1384 std::ostringstream os;
1385 os << "citation LatexCommand\n"
1386 << "\\cite" << opt1 << "{" << arg << "}\n"
1388 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1391 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1395 case LFUN_BUFFER_CHILD_OPEN: {
1396 BOOST_ASSERT(lyx_view_);
1397 FileName const filename =
1398 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1399 // FIXME Should use bformat
1400 setMessage(_("Opening child document ") +
1401 makeDisplayPath(filename.absFilename()) + "...");
1402 view()->saveBookmark(false);
1403 string const parentfilename = lyx_view_->buffer()->fileName();
1404 if (theBufferList().exists(filename.absFilename()))
1405 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1407 lyx_view_->loadLyXFile(filename);
1408 // Set the parent name of the child document.
1409 // This makes insertion of citations and references in the child work,
1410 // when the target is in the parent or another child document.
1411 lyx_view_->buffer()->setParentName(parentfilename);
1415 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1416 BOOST_ASSERT(lyx_view_);
1417 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1420 case LFUN_KEYMAP_OFF:
1421 BOOST_ASSERT(lyx_view_);
1422 lyx_view_->view()->getIntl().keyMapOn(false);
1425 case LFUN_KEYMAP_PRIMARY:
1426 BOOST_ASSERT(lyx_view_);
1427 lyx_view_->view()->getIntl().keyMapPrim();
1430 case LFUN_KEYMAP_SECONDARY:
1431 BOOST_ASSERT(lyx_view_);
1432 lyx_view_->view()->getIntl().keyMapSec();
1435 case LFUN_KEYMAP_TOGGLE:
1436 BOOST_ASSERT(lyx_view_);
1437 lyx_view_->view()->getIntl().toggleKeyMap();
1443 string rest = split(argument, countstr, ' ');
1444 istringstream is(countstr);
1447 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1448 for (int i = 0; i < count; ++i)
1449 dispatch(lyxaction.lookupFunc(rest));
1453 case LFUN_COMMAND_SEQUENCE: {
1454 // argument contains ';'-terminated commands
1455 string arg = argument;
1456 while (!arg.empty()) {
1458 arg = split(arg, first, ';');
1459 FuncRequest func(lyxaction.lookupFunc(first));
1460 func.origin = cmd.origin;
1466 case LFUN_PREFERENCES_SAVE: {
1467 lyxrc.write(makeAbsPath("preferences",
1468 package().user_support()),
1473 case LFUN_SCREEN_FONT_UPDATE:
1474 BOOST_ASSERT(lyx_view_);
1475 // handle the screen font changes.
1476 lyxrc.set_font_norm_type();
1477 theFontLoader().update();
1478 /// FIXME: only the current view will be updated. the Gui
1479 /// class is able to furnish the list of views.
1480 updateFlags = Update::Force;
1483 case LFUN_SET_COLOR: {
1485 string const x11_name = split(argument, lyx_name, ' ');
1486 if (lyx_name.empty() || x11_name.empty()) {
1487 setErrorMessage(_("Syntax: set-color <lyx_name>"
1492 bool const graphicsbg_changed =
1493 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1494 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1496 if (!lcolor.setColor(lyx_name, x11_name)) {
1498 bformat(_("Set-color \"%1$s\" failed "
1499 "- color is undefined or "
1500 "may not be redefined"),
1501 from_utf8(lyx_name)));
1505 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1507 if (graphicsbg_changed) {
1508 #ifdef WITH_WARNINGS
1509 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1512 graphics::GCache::get().changeDisplay(true);
1519 BOOST_ASSERT(lyx_view_);
1520 lyx_view_->message(from_utf8(argument));
1523 case LFUN_EXTERNAL_EDIT: {
1524 BOOST_ASSERT(lyx_view_);
1525 FuncRequest fr(action, argument);
1526 InsetExternal().dispatch(view()->cursor(), fr);
1530 case LFUN_GRAPHICS_EDIT: {
1531 FuncRequest fr(action, argument);
1532 InsetGraphics().dispatch(view()->cursor(), fr);
1536 case LFUN_INSET_APPLY: {
1537 BOOST_ASSERT(lyx_view_);
1538 string const name = cmd.getArg(0);
1539 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1541 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1542 inset->dispatch(view()->cursor(), fr);
1544 FuncRequest fr(LFUN_INSET_INSERT, argument);
1547 // ideally, the update flag should be set by the insets,
1548 // but this is not possible currently
1549 updateFlags = Update::Force | Update::FitCursor;
1553 case LFUN_ALL_INSETS_TOGGLE: {
1554 BOOST_ASSERT(lyx_view_);
1556 string const name = split(argument, action, ' ');
1557 InsetBase::Code const inset_code =
1558 InsetBase::translate(name);
1560 LCursor & cur = view()->cursor();
1561 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1563 InsetBase & inset = lyx_view_->buffer()->inset();
1564 InsetIterator it = inset_iterator_begin(inset);
1565 InsetIterator const end = inset_iterator_end(inset);
1566 for (; it != end; ++it) {
1567 if (inset_code == InsetBase::NO_CODE
1568 || inset_code == it->lyxCode()) {
1569 LCursor tmpcur = cur;
1570 tmpcur.pushLeft(*it);
1571 it->dispatch(tmpcur, fr);
1574 updateFlags = Update::Force | Update::FitCursor;
1578 case LFUN_BUFFER_LANGUAGE: {
1579 BOOST_ASSERT(lyx_view_);
1580 Buffer & buffer = *lyx_view_->buffer();
1581 Language const * oldL = buffer.params().language;
1582 Language const * newL = languages.getLanguage(argument);
1583 if (!newL || oldL == newL)
1586 if (oldL->rightToLeft() == newL->rightToLeft()
1587 && !buffer.isMultiLingual())
1588 buffer.changeLanguage(oldL, newL);
1590 buffer.updateDocLang(newL);
1594 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1595 string const fname =
1596 addName(addPath(package().user_support(), "templates/"),
1598 Buffer defaults(fname);
1600 istringstream ss(argument);
1603 int const unknown_tokens = defaults.readHeader(lex);
1605 if (unknown_tokens != 0) {
1606 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1607 << unknown_tokens << " unknown token"
1608 << (unknown_tokens == 1 ? "" : "s")
1612 if (defaults.writeFile(FileName(defaults.fileName())))
1613 // FIXME Should use bformat
1614 setMessage(_("Document defaults saved in ")
1615 + makeDisplayPath(fname));
1617 setErrorMessage(_("Unable to save document defaults"));
1621 case LFUN_BUFFER_PARAMS_APPLY: {
1622 BOOST_ASSERT(lyx_view_);
1623 biblio::CiteEngine const engine =
1624 lyx_view_->buffer()->params().cite_engine;
1626 istringstream ss(argument);
1629 int const unknown_tokens =
1630 lyx_view_->buffer()->readHeader(lex);
1632 if (unknown_tokens != 0) {
1633 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1634 << unknown_tokens << " unknown token"
1635 << (unknown_tokens == 1 ? "" : "s")
1638 if (engine == lyx_view_->buffer()->params().cite_engine)
1641 LCursor & cur = view()->cursor();
1642 FuncRequest fr(LFUN_INSET_REFRESH);
1644 InsetBase & inset = lyx_view_->buffer()->inset();
1645 InsetIterator it = inset_iterator_begin(inset);
1646 InsetIterator const end = inset_iterator_end(inset);
1647 for (; it != end; ++it)
1648 if (it->lyxCode() == InsetBase::CITE_CODE)
1649 it->dispatch(cur, fr);
1653 case LFUN_TEXTCLASS_APPLY: {
1654 BOOST_ASSERT(lyx_view_);
1655 Buffer * buffer = lyx_view_->buffer();
1657 textclass_type const old_class =
1658 buffer->params().textclass;
1660 loadTextclass(argument);
1662 std::pair<bool, textclass_type> const tc_pair =
1663 textclasslist.numberOfClass(argument);
1668 textclass_type const new_class = tc_pair.second;
1669 if (old_class == new_class)
1673 lyx_view_->message(_("Converting document to new document class..."));
1674 recordUndoFullDocument(view());
1675 buffer->params().textclass = new_class;
1676 StableDocIterator backcur(view()->cursor());
1677 ErrorList & el = buffer->errorList("Class Switch");
1678 cap::switchBetweenClasses(
1679 old_class, new_class,
1680 static_cast<InsetText &>(buffer->inset()), el);
1682 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1684 buffer->errors("Class Switch");
1685 updateLabels(*buffer);
1686 updateFlags = Update::Force | Update::FitCursor;
1690 case LFUN_TEXTCLASS_LOAD:
1691 loadTextclass(argument);
1694 case LFUN_LYXRC_APPLY: {
1695 LyXRC const lyxrc_orig = lyxrc;
1697 istringstream ss(argument);
1698 bool const success = lyxrc.read(ss) == 0;
1701 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1702 << "Unable to read lyxrc data"
1707 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1709 /// We force the redraw in any case because there might be
1710 /// some screen font changes.
1711 /// FIXME: only the current view will be updated. the Gui
1712 /// class is able to furnish the list of views.
1713 updateFlags = Update::Force;
1717 case LFUN_WINDOW_NEW:
1718 LyX::ref().newLyXView();
1721 case LFUN_WINDOW_CLOSE:
1722 BOOST_ASSERT(lyx_view_);
1723 BOOST_ASSERT(theApp());
1724 // update bookmark pit of the current buffer before window close
1725 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1726 gotoBookmark(i+1, false, false);
1727 // ask the user for saving changes or cancel quit
1728 if (!theBufferList().quitWriteAll())
1733 case LFUN_BOOKMARK_GOTO:
1734 // go to bookmark, open unopened file and switch to buffer if necessary
1735 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1738 case LFUN_BOOKMARK_CLEAR:
1739 LyX::ref().session().bookmarks().clear();
1742 case LFUN_TOOLBAR_TOGGLE_STATE:
1743 lyx_view_->toggleToolbarState(argument);
1747 BOOST_ASSERT(lyx_view_);
1748 view()->cursor().dispatch(cmd);
1749 updateFlags = view()->cursor().result().update();
1750 if (!view()->cursor().result().dispatched())
1751 updateFlags = view()->dispatch(cmd);
1756 if (lyx_view_ && view()->buffer()) {
1757 // BufferView::update() updates the ViewMetricsInfo and
1758 // also initializes the position cache for all insets in
1759 // (at least partially) visible top-level paragraphs.
1760 // We will redraw the screen only if needed.
1761 if (view()->update(updateFlags)) {
1762 // Buffer::changed() signals that a repaint is needed.
1763 // The frontend (WorkArea) knows which area to repaint
1764 // thanks to the ViewMetricsInfo updated above.
1765 view()->buffer()->changed();
1768 lyx_view_->updateStatusBar();
1770 // if we executed a mutating lfun, mark the buffer as dirty
1772 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1773 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1774 view()->buffer()->markDirty();
1776 if (view()->cursor().inTexted()) {
1777 lyx_view_->updateLayoutChoice();
1782 lyx_view_->updateMenubar();
1783 lyx_view_->updateToolbars();
1784 sendDispatchMessage(getMessage(), cmd);
1789 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1791 const bool verbose = (cmd.origin == FuncRequest::MENU
1792 || cmd.origin == FuncRequest::TOOLBAR
1793 || cmd.origin == FuncRequest::COMMANDBUFFER);
1795 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1796 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1798 lyx_view_->message(msg);
1802 docstring dispatch_msg = msg;
1803 if (!dispatch_msg.empty())
1804 dispatch_msg += ' ';
1806 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1808 bool argsadded = false;
1810 if (!cmd.argument().empty()) {
1811 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1812 comname += ' ' + cmd.argument();
1817 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1819 if (!shortcuts.empty())
1820 comname += ": " + shortcuts;
1821 else if (!argsadded && !cmd.argument().empty())
1822 comname += ' ' + cmd.argument();
1824 if (!comname.empty()) {
1825 comname = rtrim(comname);
1826 dispatch_msg += '(' + rtrim(comname) + ')';
1829 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1830 << to_utf8(dispatch_msg) << endl;
1831 if (!dispatch_msg.empty())
1832 lyx_view_->message(dispatch_msg);
1836 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1838 // FIXME: initpath is not used. What to do?
1839 string initpath = lyxrc.document_path;
1840 string filename(name);
1842 if (view()->buffer()) {
1843 string const trypath = lyx_view_->buffer()->filePath();
1844 // If directory is writeable, use this as default.
1845 if (isDirWriteable(FileName(trypath)))
1849 static int newfile_number;
1851 if (filename.empty()) {
1852 filename = addName(lyxrc.document_path,
1853 "newfile" + convert<string>(++newfile_number) + ".lyx");
1854 while (theBufferList().exists(filename) ||
1855 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1857 filename = addName(lyxrc.document_path,
1858 "newfile" + convert<string>(newfile_number) +
1863 // The template stuff
1866 FileDialog fileDlg(_("Select template file"),
1867 LFUN_SELECT_FILE_SYNC,
1868 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1869 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1871 FileDialog::Result result =
1872 fileDlg.open(from_utf8(lyxrc.template_path),
1873 FileFilterList(_("LyX Documents (*.lyx)")),
1876 if (result.first == FileDialog::Later)
1878 if (result.second.empty())
1880 templname = to_utf8(result.second);
1883 Buffer * const b = newFile(filename, templname, !name.empty());
1885 lyx_view_->setBuffer(b);
1889 void LyXFunc::open(string const & fname)
1891 string initpath = lyxrc.document_path;
1893 if (view()->buffer()) {
1894 string const trypath = lyx_view_->buffer()->filePath();
1895 // If directory is writeable, use this as default.
1896 if (isDirWriteable(FileName(trypath)))
1902 if (fname.empty()) {
1903 FileDialog fileDlg(_("Select document to open"),
1905 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1906 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1908 FileDialog::Result result =
1909 fileDlg.open(from_utf8(initpath),
1910 FileFilterList(_("LyX Documents (*.lyx)")),
1913 if (result.first == FileDialog::Later)
1916 filename = to_utf8(result.second);
1918 // check selected filename
1919 if (filename.empty()) {
1920 lyx_view_->message(_("Canceled."));
1926 // get absolute path of file and add ".lyx" to the filename if
1928 FileName const fullname = fileSearch(string(), filename, "lyx");
1929 if (!fullname.empty())
1930 filename = fullname.absFilename();
1932 // if the file doesn't exist, let the user create one
1933 if (!fs::exists(fullname.toFilesystemEncoding())) {
1934 // the user specifically chose this name. Believe him.
1935 Buffer * const b = newFile(filename, string(), true);
1937 lyx_view_->setBuffer(b);
1941 docstring const disp_fn = makeDisplayPath(filename);
1942 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1945 if (lyx_view_->loadLyXFile(fullname)) {
1946 str2 = bformat(_("Document %1$s opened."), disp_fn);
1948 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1950 lyx_view_->message(str2);
1954 void LyXFunc::doImport(string const & argument)
1957 string filename = split(argument, format, ' ');
1959 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1960 << " file: " << filename << endl;
1962 // need user interaction
1963 if (filename.empty()) {
1964 string initpath = lyxrc.document_path;
1966 if (view()->buffer()) {
1967 string const trypath = lyx_view_->buffer()->filePath();
1968 // If directory is writeable, use this as default.
1969 if (isDirWriteable(FileName(trypath)))
1973 docstring const text = bformat(_("Select %1$s file to import"),
1974 formats.prettyName(format));
1976 FileDialog fileDlg(text,
1978 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1979 make_pair(_("Examples|#E#e"),
1980 from_utf8(addPath(package().system_support(), "examples"))));
1982 docstring filter = formats.prettyName(format);
1985 filter += from_utf8(formats.extension(format));
1988 FileDialog::Result result =
1989 fileDlg.open(from_utf8(initpath),
1990 FileFilterList(filter),
1993 if (result.first == FileDialog::Later)
1996 filename = to_utf8(result.second);
1998 // check selected filename
1999 if (filename.empty())
2000 lyx_view_->message(_("Canceled."));
2003 if (filename.empty())
2006 // get absolute path of file
2007 FileName const fullname(makeAbsPath(filename));
2009 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2011 // Check if the document already is open
2012 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2013 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2014 lyx_view_->message(_("Canceled."));
2019 // if the file exists already, and we didn't do
2020 // -i lyx thefile.lyx, warn
2021 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2022 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2024 docstring text = bformat(_("The document %1$s already exists.\n\n"
2025 "Do you want to over-write that document?"), file);
2026 int const ret = Alert::prompt(_("Over-write document?"),
2027 text, 0, 1, _("&Over-write"), _("&Cancel"));
2030 lyx_view_->message(_("Canceled."));
2035 ErrorList errorList;
2036 Importer::Import(lyx_view_, fullname, format, errorList);
2037 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2041 void LyXFunc::closeBuffer()
2043 // save current cursor position
2044 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2045 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2046 // goto bookmark to update bookmark pit.
2047 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2048 gotoBookmark(i+1, false, false);
2049 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2050 if (theBufferList().empty()) {
2051 // need this otherwise SEGV may occur while
2052 // trying to set variables that don't exist
2053 // since there's no current buffer
2054 lyx_view_->getDialogs().hideBufferDependent();
2056 lyx_view_->setBuffer(theBufferList().first());
2062 void LyXFunc::reloadBuffer()
2064 FileName filename(lyx_view_->buffer()->fileName());
2066 lyx_view_->loadLyXFile(filename);
2069 // Each "lyx_view_" should have it's own message method. lyxview and
2070 // the minibuffer would use the minibuffer, but lyxserver would
2071 // send an ERROR signal to its client. Alejandro 970603
2072 // This function is bit problematic when it comes to NLS, to make the
2073 // lyx servers client be language indepenent we must not translate
2074 // strings sent to this func.
2075 void LyXFunc::setErrorMessage(docstring const & m) const
2077 dispatch_buffer = m;
2082 void LyXFunc::setMessage(docstring const & m) const
2084 dispatch_buffer = m;
2088 docstring const LyXFunc::viewStatusMessage()
2090 // When meta-fake key is pressed, show the key sequence so far + "M-".
2092 return keyseq->print(true) + "M-";
2094 // Else, when a non-complete key sequence is pressed,
2095 // show the available options.
2096 if (keyseq->length() > 0 && !keyseq->deleted())
2097 return keyseq->printOptions(true);
2099 if (!view()->buffer())
2100 return _("Welcome to LyX!");
2102 return view()->cursor().currentState();
2106 BufferView * LyXFunc::view() const
2108 BOOST_ASSERT(lyx_view_);
2109 return lyx_view_->view();
2113 bool LyXFunc::wasMetaKey() const
2115 return (meta_fake_bit != key_modifier::none);
2121 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2123 // Why the switch you might ask. It is a trick to ensure that all
2124 // the elements in the LyXRCTags enum is handled. As you can see
2125 // there are no breaks at all. So it is just a huge fall-through.
2126 // The nice thing is that we will get a warning from the compiler
2127 // if we forget an element.
2128 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2130 case LyXRC::RC_ACCEPT_COMPOUND:
2131 case LyXRC::RC_ALT_LANG:
2132 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2133 case LyXRC::RC_PLAINTEXT_LINELEN:
2134 case LyXRC::RC_AUTOREGIONDELETE:
2135 case LyXRC::RC_AUTORESET_OPTIONS:
2136 case LyXRC::RC_AUTOSAVE:
2137 case LyXRC::RC_AUTO_NUMBER:
2138 case LyXRC::RC_BACKUPDIR_PATH:
2139 case LyXRC::RC_BIBTEX_COMMAND:
2140 case LyXRC::RC_BINDFILE:
2141 case LyXRC::RC_CHECKLASTFILES:
2142 case LyXRC::RC_USELASTFILEPOS:
2143 case LyXRC::RC_LOADSESSION:
2144 case LyXRC::RC_CHKTEX_COMMAND:
2145 case LyXRC::RC_CONVERTER:
2146 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2147 case LyXRC::RC_COPIER:
2148 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2149 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2150 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2151 case LyXRC::RC_DATE_INSERT_FORMAT:
2152 case LyXRC::RC_DEFAULT_LANGUAGE:
2153 case LyXRC::RC_DEFAULT_PAPERSIZE:
2154 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2155 case LyXRC::RC_DISPLAY_GRAPHICS:
2156 case LyXRC::RC_DOCUMENTPATH:
2157 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2158 string const encoded = FileName(
2159 lyxrc_new.document_path).toFilesystemEncoding();
2160 if (fs::exists(encoded) && fs::is_directory(encoded))
2161 support::package().document_dir() = lyxrc.document_path;
2163 case LyXRC::RC_ESC_CHARS:
2164 case LyXRC::RC_FONT_ENCODING:
2165 case LyXRC::RC_FORMAT:
2166 case LyXRC::RC_INDEX_COMMAND:
2167 case LyXRC::RC_INPUT:
2168 case LyXRC::RC_KBMAP:
2169 case LyXRC::RC_KBMAP_PRIMARY:
2170 case LyXRC::RC_KBMAP_SECONDARY:
2171 case LyXRC::RC_LABEL_INIT_LENGTH:
2172 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2173 case LyXRC::RC_LANGUAGE_AUTO_END:
2174 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2175 case LyXRC::RC_LANGUAGE_COMMAND_END:
2176 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2177 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2178 case LyXRC::RC_LANGUAGE_PACKAGE:
2179 case LyXRC::RC_LANGUAGE_USE_BABEL:
2180 case LyXRC::RC_MAKE_BACKUP:
2181 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2182 case LyXRC::RC_NUMLASTFILES:
2183 case LyXRC::RC_PATH_PREFIX:
2184 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2185 support::prependEnvPath("PATH", lyxrc.path_prefix);
2187 case LyXRC::RC_PERS_DICT:
2188 case LyXRC::RC_POPUP_BOLD_FONT:
2189 case LyXRC::RC_POPUP_FONT_ENCODING:
2190 case LyXRC::RC_POPUP_NORMAL_FONT:
2191 case LyXRC::RC_PREVIEW:
2192 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2193 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2194 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2195 case LyXRC::RC_PRINTCOPIESFLAG:
2196 case LyXRC::RC_PRINTER:
2197 case LyXRC::RC_PRINTEVENPAGEFLAG:
2198 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2199 case LyXRC::RC_PRINTFILEEXTENSION:
2200 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2201 case LyXRC::RC_PRINTODDPAGEFLAG:
2202 case LyXRC::RC_PRINTPAGERANGEFLAG:
2203 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2204 case LyXRC::RC_PRINTPAPERFLAG:
2205 case LyXRC::RC_PRINTREVERSEFLAG:
2206 case LyXRC::RC_PRINTSPOOL_COMMAND:
2207 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2208 case LyXRC::RC_PRINTTOFILE:
2209 case LyXRC::RC_PRINTTOPRINTER:
2210 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2211 case LyXRC::RC_PRINT_COMMAND:
2212 case LyXRC::RC_RTL_SUPPORT:
2213 case LyXRC::RC_SCREEN_DPI:
2214 case LyXRC::RC_SCREEN_FONT_ENCODING:
2215 case LyXRC::RC_SCREEN_FONT_ROMAN:
2216 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2217 case LyXRC::RC_SCREEN_FONT_SANS:
2218 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2219 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2220 case LyXRC::RC_SCREEN_FONT_SIZES:
2221 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2222 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2223 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2224 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2225 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2226 case LyXRC::RC_SCREEN_ZOOM:
2227 case LyXRC::RC_SERVERPIPE:
2228 case LyXRC::RC_SET_COLOR:
2229 case LyXRC::RC_SHOW_BANNER:
2230 case LyXRC::RC_SPELL_COMMAND:
2231 case LyXRC::RC_TEMPDIRPATH:
2232 case LyXRC::RC_TEMPLATEPATH:
2233 case LyXRC::RC_TEX_ALLOWS_SPACES:
2234 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2235 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2236 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2238 case LyXRC::RC_UIFILE:
2239 case LyXRC::RC_USER_EMAIL:
2240 case LyXRC::RC_USER_NAME:
2241 case LyXRC::RC_USETEMPDIR:
2242 case LyXRC::RC_USE_ALT_LANG:
2243 case LyXRC::RC_USE_CONVERTER_CACHE:
2244 case LyXRC::RC_USE_ESC_CHARS:
2245 case LyXRC::RC_USE_INP_ENC:
2246 case LyXRC::RC_USE_PERS_DICT:
2247 case LyXRC::RC_USE_SPELL_LIB:
2248 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2249 case LyXRC::RC_VIEWER:
2250 case LyXRC::RC_LAST: