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 if (cur.inset().lyxCode() == InsetBase::CAPTION_CODE) {
582 if (cur.inset().getStatus(cur, cmd, flag))
587 case LFUN_DIALOG_UPDATE: {
588 string const name = cmd.getArg(0);
590 enable = name == "prefs";
594 case LFUN_CITATION_INSERT: {
595 FuncRequest fr(LFUN_INSET_INSERT, "citation");
596 enable = getStatus(fr).enabled();
600 case LFUN_BUFFER_WRITE: {
601 enable = view()->buffer()->isUnnamed()
602 || !view()->buffer()->isClean();
606 case LFUN_BOOKMARK_GOTO: {
607 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
608 enable = LyX::ref().session().bookmarks().isValid(num);
612 case LFUN_BOOKMARK_CLEAR:
613 enable = LyX::ref().session().bookmarks().size() > 0;
616 case LFUN_TOOLBAR_TOGGLE_STATE: {
617 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
618 if (!(flags & ToolbarBackend::AUTO))
619 flag.setOnOff(flags & ToolbarBackend::ON);
623 // this one is difficult to get right. As a half-baked
624 // solution, we consider only the first action of the sequence
625 case LFUN_COMMAND_SEQUENCE: {
626 // argument contains ';'-terminated commands
627 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
628 FuncRequest func(lyxaction.lookupFunc(firstcmd));
629 func.origin = cmd.origin;
630 flag = getStatus(func);
633 case LFUN_BUFFER_NEW:
634 case LFUN_BUFFER_NEW_TEMPLATE:
635 case LFUN_WORD_FIND_FORWARD:
636 case LFUN_WORD_FIND_BACKWARD:
637 case LFUN_COMMAND_PREFIX:
638 case LFUN_COMMAND_EXECUTE:
640 case LFUN_META_PREFIX:
641 case LFUN_BUFFER_CLOSE:
642 case LFUN_BUFFER_WRITE_AS:
643 case LFUN_BUFFER_UPDATE:
644 case LFUN_BUFFER_VIEW:
645 case LFUN_BUFFER_IMPORT:
647 case LFUN_BUFFER_AUTO_SAVE:
648 case LFUN_RECONFIGURE:
652 case LFUN_DROP_LAYOUTS_CHOICE:
654 case LFUN_SERVER_GET_NAME:
655 case LFUN_SERVER_NOTIFY:
656 case LFUN_SERVER_GOTO_FILE_ROW:
657 case LFUN_DIALOG_HIDE:
658 case LFUN_DIALOG_DISCONNECT_INSET:
659 case LFUN_BUFFER_CHILD_OPEN:
660 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
661 case LFUN_KEYMAP_OFF:
662 case LFUN_KEYMAP_PRIMARY:
663 case LFUN_KEYMAP_SECONDARY:
664 case LFUN_KEYMAP_TOGGLE:
666 case LFUN_BUFFER_EXPORT_CUSTOM:
667 case LFUN_BUFFER_PRINT:
668 case LFUN_PREFERENCES_SAVE:
669 case LFUN_SCREEN_FONT_UPDATE:
672 case LFUN_EXTERNAL_EDIT:
673 case LFUN_GRAPHICS_EDIT:
674 case LFUN_ALL_INSETS_TOGGLE:
675 case LFUN_BUFFER_LANGUAGE:
676 case LFUN_TEXTCLASS_APPLY:
677 case LFUN_TEXTCLASS_LOAD:
678 case LFUN_BUFFER_SAVE_AS_DEFAULT:
679 case LFUN_BUFFER_PARAMS_APPLY:
680 case LFUN_LYXRC_APPLY:
681 case LFUN_BUFFER_NEXT:
682 case LFUN_BUFFER_PREVIOUS:
683 case LFUN_WINDOW_NEW:
684 case LFUN_WINDOW_CLOSE:
686 // these are handled in our dispatch()
690 if (!getLocalStatus(cur, cmd, flag))
691 flag = view()->getStatus(cmd);
697 // Can we use a readonly buffer?
698 if (buf && buf->isReadonly()
699 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
700 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
701 flag.message(from_utf8(N_("Document is read-only")));
705 // Are we in a DELETED change-tracking region?
706 if (buf && lookupChangeType(cur, true) == Change::DELETED
707 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
708 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
709 flag.message(from_utf8(N_("This portion of the document is deleted.")));
713 // the default error message if we disable the command
714 if (!flag.enabled() && flag.message().empty())
715 flag.message(from_utf8(N_("Command disabled")));
721 bool LyXFunc::ensureBufferClean(BufferView * bv)
723 Buffer & buf = *bv->buffer();
727 docstring const file = makeDisplayPath(buf.fileName(), 30);
728 docstring text = bformat(_("The document %1$s has unsaved "
729 "changes.\n\nDo you want to save "
730 "the document?"), file);
731 int const ret = Alert::prompt(_("Save changed document?"),
732 text, 0, 1, _("&Save"),
736 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
738 return buf.isClean();
744 void showPrintError(string const & name)
746 docstring str = bformat(_("Could not print the document %1$s.\n"
747 "Check that your printer is set up correctly."),
748 makeDisplayPath(name, 50));
749 Alert::error(_("Print document failed"), str);
753 void loadTextclass(string const & name)
755 std::pair<bool, textclass_type> const tc_pair =
756 textclasslist.numberOfClass(name);
758 if (!tc_pair.first) {
759 lyxerr << "Document class \"" << name
760 << "\" does not exist."
765 textclass_type const tc = tc_pair.second;
767 if (!textclasslist[tc].load()) {
768 docstring s = bformat(_("The document could not be converted\n"
769 "into the document class %1$s."),
770 from_utf8(textclasslist[tc].name()));
771 Alert::error(_("Could not change class"), s);
776 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
781 void LyXFunc::dispatch(FuncRequest const & cmd)
783 string const argument = to_utf8(cmd.argument());
784 kb_action const action = cmd.action;
786 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
787 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
789 // we have not done anything wrong yet.
791 dispatch_buffer.erase();
793 // redraw the screen at the end (first of the two drawing steps).
794 //This is done unless explicitely requested otherwise
795 Update::flags updateFlags = Update::FitCursor;
797 FuncStatus const flag = getStatus(cmd);
798 if (!flag.enabled()) {
799 // We cannot use this function here
800 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
801 << lyxaction.getActionName(action)
802 << " [" << action << "] is disabled at this location"
804 setErrorMessage(flag.message());
808 case LFUN_WORD_FIND_FORWARD:
809 case LFUN_WORD_FIND_BACKWARD: {
810 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
811 static docstring last_search;
812 docstring searched_string;
814 if (!cmd.argument().empty()) {
815 last_search = cmd.argument();
816 searched_string = cmd.argument();
818 searched_string = last_search;
821 if (searched_string.empty())
824 bool const fw = action == LFUN_WORD_FIND_FORWARD;
825 docstring const data =
826 find2string(searched_string, true, false, fw);
827 find(view(), FuncRequest(LFUN_WORD_FIND, data));
831 case LFUN_COMMAND_PREFIX:
832 BOOST_ASSERT(lyx_view_);
833 lyx_view_->message(keyseq->printOptions(true));
836 case LFUN_COMMAND_EXECUTE:
837 BOOST_ASSERT(lyx_view_);
838 lyx_view_->getToolbars().display("minibuffer", true);
839 lyx_view_->focus_command_buffer();
843 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
845 meta_fake_bit = key_modifier::none;
846 if (view()->buffer())
847 // cancel any selection
848 dispatch(FuncRequest(LFUN_MARK_OFF));
849 setMessage(_("Cancel"));
852 case LFUN_META_PREFIX:
853 meta_fake_bit = key_modifier::alt;
854 setMessage(keyseq->print(true));
857 case LFUN_BUFFER_TOGGLE_READ_ONLY:
858 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
859 if (lyx_view_->buffer()->lyxvc().inUse())
860 lyx_view_->buffer()->lyxvc().toggleReadOnly();
862 lyx_view_->buffer()->setReadonly(
863 !lyx_view_->buffer()->isReadonly());
866 // --- Menus -----------------------------------------------
867 case LFUN_BUFFER_NEW:
868 menuNew(argument, false);
871 case LFUN_BUFFER_NEW_TEMPLATE:
872 menuNew(argument, true);
875 case LFUN_BUFFER_CLOSE:
880 case LFUN_BUFFER_WRITE:
881 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
882 if (!lyx_view_->buffer()->isUnnamed()) {
883 docstring const str = bformat(_("Saving document %1$s..."),
884 makeDisplayPath(lyx_view_->buffer()->fileName()));
885 lyx_view_->message(str);
886 menuWrite(lyx_view_->buffer());
887 lyx_view_->message(str + _(" done."));
889 writeAs(lyx_view_->buffer());
891 updateFlags = Update::None;
894 case LFUN_BUFFER_WRITE_AS:
895 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
896 writeAs(lyx_view_->buffer(), argument);
897 updateFlags = Update::None;
900 case LFUN_BUFFER_RELOAD: {
901 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
902 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
903 docstring text = bformat(_("Any changes will be lost. Are you sure "
904 "you want to revert to the saved version of the document %1$s?"), file);
905 int const ret = Alert::prompt(_("Revert to saved document?"),
906 text, 0, 1, _("&Revert"), _("&Cancel"));
913 case LFUN_BUFFER_UPDATE:
914 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
915 Exporter::Export(lyx_view_->buffer(), argument, true);
918 case LFUN_BUFFER_VIEW:
919 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
920 Exporter::preview(lyx_view_->buffer(), argument);
923 case LFUN_BUILD_PROGRAM:
924 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
925 Exporter::Export(lyx_view_->buffer(), "program", true);
928 case LFUN_BUFFER_CHKTEX:
929 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
930 lyx_view_->buffer()->runChktex();
933 case LFUN_BUFFER_EXPORT:
934 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
935 if (argument == "custom")
936 lyx_view_->getDialogs().show("sendto");
938 Exporter::Export(lyx_view_->buffer(), argument, false);
942 case LFUN_BUFFER_EXPORT_CUSTOM: {
943 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
945 string command = split(argument, format_name, ' ');
946 Format const * format = formats.getFormat(format_name);
948 lyxerr << "Format \"" << format_name
949 << "\" not recognized!"
954 Buffer * buffer = lyx_view_->buffer();
956 // The name of the file created by the conversion process
959 // Output to filename
960 if (format->name() == "lyx") {
961 string const latexname =
962 buffer->getLatexName(false);
963 filename = changeExtension(latexname,
964 format->extension());
965 filename = addName(buffer->temppath(), filename);
967 if (!buffer->writeFile(FileName(filename)))
971 Exporter::Export(buffer, format_name, true, filename);
974 // Substitute $$FName for filename
975 if (!contains(command, "$$FName"))
976 command = "( " + command + " ) < $$FName";
977 command = subst(command, "$$FName", filename);
979 // Execute the command in the background
981 call.startscript(Systemcall::DontWait, command);
985 case LFUN_BUFFER_PRINT: {
986 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
989 string command = split(split(argument, target, ' '),
993 || target_name.empty()
994 || command.empty()) {
995 lyxerr << "Unable to parse \""
996 << argument << '"' << std::endl;
999 if (target != "printer" && target != "file") {
1000 lyxerr << "Unrecognized target \""
1001 << target << '"' << std::endl;
1005 Buffer * buffer = lyx_view_->buffer();
1007 if (!Exporter::Export(buffer, "dvi", true)) {
1008 showPrintError(buffer->fileName());
1012 // Push directory path.
1013 string const path = buffer->temppath();
1014 support::Path p(path);
1016 // there are three cases here:
1017 // 1. we print to a file
1018 // 2. we print directly to a printer
1019 // 3. we print using a spool command (print to file first)
1022 string const dviname =
1023 changeExtension(buffer->getLatexName(true),
1026 if (target == "printer") {
1027 if (!lyxrc.print_spool_command.empty()) {
1028 // case 3: print using a spool
1029 string const psname =
1030 changeExtension(dviname,".ps");
1031 command += lyxrc.print_to_file
1034 + quoteName(dviname);
1037 lyxrc.print_spool_command +' ';
1038 if (target_name != "default") {
1039 command2 += lyxrc.print_spool_printerprefix
1043 command2 += quoteName(psname);
1045 // If successful, then spool command
1046 res = one.startscript(
1051 res = one.startscript(
1052 Systemcall::DontWait,
1055 // case 2: print directly to a printer
1056 res = one.startscript(
1057 Systemcall::DontWait,
1058 command + quoteName(dviname));
1062 // case 1: print to a file
1063 FileName const filename(makeAbsPath(target_name, path));
1064 if (fs::exists(filename.toFilesystemEncoding())) {
1065 docstring text = bformat(
1066 _("The file %1$s already exists.\n\n"
1067 "Do you want to over-write that file?"),
1068 makeDisplayPath(filename.absFilename()));
1069 if (Alert::prompt(_("Over-write file?"),
1070 text, 0, 1, _("&Over-write"), _("&Cancel")) != 0) {
1071 showPrintError(buffer->fileName());
1075 command += lyxrc.print_to_file
1076 + quoteName(filename.toFilesystemEncoding())
1078 + quoteName(dviname);
1079 res = one.startscript(Systemcall::DontWait,
1084 showPrintError(buffer->fileName());
1088 case LFUN_BUFFER_IMPORT:
1093 // quitting is triggered by the gui code
1094 // (leaving the event loop).
1095 lyx_view_->message(from_utf8(N_("Exiting.")));
1096 if (theBufferList().quitWriteAll())
1097 theApp()->gui().closeAllViews();
1100 case LFUN_TOC_VIEW: {
1101 BOOST_ASSERT(lyx_view_);
1102 InsetCommandParams p("tableofcontents");
1103 string const data = InsetCommandMailer::params2string("toc", p);
1104 lyx_view_->getDialogs().show("toc", data, 0);
1108 case LFUN_BUFFER_AUTO_SAVE:
1112 case LFUN_RECONFIGURE:
1113 BOOST_ASSERT(lyx_view_);
1114 reconfigure(*lyx_view_);
1117 case LFUN_HELP_OPEN: {
1118 BOOST_ASSERT(lyx_view_);
1119 string const arg = argument;
1121 setErrorMessage(_("Missing argument"));
1124 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1125 if (fname.empty()) {
1126 lyxerr << "LyX: unable to find documentation file `"
1127 << arg << "'. Bad installation?" << endl;
1130 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1131 makeDisplayPath(fname.absFilename())));
1132 lyx_view_->loadLyXFile(fname, false);
1136 // --- version control -------------------------------
1137 case LFUN_VC_REGISTER:
1138 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1139 if (!ensureBufferClean(view()))
1141 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1142 lyx_view_->buffer()->lyxvc().registrer();
1145 updateFlags = Update::Force;
1148 case LFUN_VC_CHECK_IN:
1149 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1150 if (!ensureBufferClean(view()))
1152 if (lyx_view_->buffer()->lyxvc().inUse()
1153 && !lyx_view_->buffer()->isReadonly()) {
1154 lyx_view_->buffer()->lyxvc().checkIn();
1159 case LFUN_VC_CHECK_OUT:
1160 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1161 if (!ensureBufferClean(view()))
1163 if (lyx_view_->buffer()->lyxvc().inUse()
1164 && lyx_view_->buffer()->isReadonly()) {
1165 lyx_view_->buffer()->lyxvc().checkOut();
1170 case LFUN_VC_REVERT:
1171 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1172 lyx_view_->buffer()->lyxvc().revert();
1176 case LFUN_VC_UNDO_LAST:
1177 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1178 lyx_view_->buffer()->lyxvc().undoLast();
1182 // --- buffers ----------------------------------------
1183 case LFUN_BUFFER_SWITCH:
1184 BOOST_ASSERT(lyx_view_);
1185 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1188 case LFUN_BUFFER_NEXT:
1189 BOOST_ASSERT(lyx_view_);
1190 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1193 case LFUN_BUFFER_PREVIOUS:
1194 BOOST_ASSERT(lyx_view_);
1195 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1199 BOOST_ASSERT(lyx_view_);
1200 newFile(view(), argument);
1203 case LFUN_FILE_OPEN:
1204 BOOST_ASSERT(lyx_view_);
1208 case LFUN_DROP_LAYOUTS_CHOICE:
1209 BOOST_ASSERT(lyx_view_);
1210 lyx_view_->getToolbars().openLayoutList();
1213 case LFUN_MENU_OPEN:
1214 BOOST_ASSERT(lyx_view_);
1215 lyx_view_->getMenubar().openByName(from_utf8(argument));
1218 // --- lyxserver commands ----------------------------
1219 case LFUN_SERVER_GET_NAME:
1220 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1221 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1222 lyxerr[Debug::INFO] << "FNAME["
1223 << lyx_view_->buffer()->fileName()
1227 case LFUN_SERVER_NOTIFY:
1228 dispatch_buffer = keyseq->print(false);
1229 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1232 case LFUN_SERVER_GOTO_FILE_ROW: {
1233 BOOST_ASSERT(lyx_view_);
1236 istringstream is(argument);
1237 is >> file_name >> row;
1238 if (prefixIs(file_name, package().temp_dir())) {
1239 // Needed by inverse dvi search. If it is a file
1240 // in tmpdir, call the apropriated function
1241 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1243 // Must replace extension of the file to be .lyx
1244 // and get full path
1245 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1246 // Either change buffer or load the file
1247 if (theBufferList().exists(s.absFilename())) {
1248 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1250 lyx_view_->loadLyXFile(s);
1254 view()->setCursorFromRow(row);
1256 updateFlags = Update::FitCursor;
1260 case LFUN_DIALOG_SHOW: {
1261 BOOST_ASSERT(lyx_view_);
1262 string const name = cmd.getArg(0);
1263 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1265 if (name == "character") {
1266 data = freefont2string();
1268 lyx_view_->getDialogs().show("character", data);
1269 } else if (name == "latexlog") {
1270 pair<Buffer::LogType, string> const logfile =
1271 lyx_view_->buffer()->getLogName();
1272 switch (logfile.first) {
1273 case Buffer::latexlog:
1276 case Buffer::buildlog:
1280 data += LyXLex::quoteString(logfile.second);
1281 lyx_view_->getDialogs().show("log", data);
1282 } else if (name == "vclog") {
1283 string const data = "vc " +
1284 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1285 lyx_view_->getDialogs().show("log", data);
1287 lyx_view_->getDialogs().show(name, data);
1291 case LFUN_DIALOG_SHOW_NEW_INSET: {
1292 BOOST_ASSERT(lyx_view_);
1293 string const name = cmd.getArg(0);
1294 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1295 if (name == "bibitem" ||
1299 name == "nomenclature" ||
1303 InsetCommandParams p(name);
1304 data = InsetCommandMailer::params2string(name, p);
1305 } else if (name == "include") {
1306 // data is the include type: one of "include",
1307 // "input", "verbatiminput" or "verbatiminput*"
1309 // default type is requested
1311 InsetCommandParams p(data);
1312 data = InsetIncludeMailer::params2string(p);
1313 } else if (name == "box") {
1314 // \c data == "Boxed" || "Frameless" etc
1315 InsetBoxParams p(data);
1316 data = InsetBoxMailer::params2string(p);
1317 } else if (name == "branch") {
1318 InsetBranchParams p;
1319 data = InsetBranchMailer::params2string(p);
1320 } else if (name == "citation") {
1321 InsetCommandParams p("cite");
1322 data = InsetCommandMailer::params2string(name, p);
1323 } else if (name == "ert") {
1324 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1325 } else if (name == "external") {
1326 InsetExternalParams p;
1327 Buffer const & buffer = *lyx_view_->buffer();
1328 data = InsetExternalMailer::params2string(p, buffer);
1329 } else if (name == "float") {
1331 data = InsetFloatMailer::params2string(p);
1332 } else if (name == "graphics") {
1333 InsetGraphicsParams p;
1334 Buffer const & buffer = *lyx_view_->buffer();
1335 data = InsetGraphicsMailer::params2string(p, buffer);
1336 } else if (name == "note") {
1338 data = InsetNoteMailer::params2string(p);
1339 } else if (name == "vspace") {
1341 data = InsetVSpaceMailer::params2string(space);
1342 } else if (name == "wrap") {
1344 data = InsetWrapMailer::params2string(p);
1346 lyx_view_->getDialogs().show(name, data, 0);
1350 case LFUN_DIALOG_UPDATE: {
1351 BOOST_ASSERT(lyx_view_);
1352 string const & name = argument;
1353 // Can only update a dialog connected to an existing inset
1354 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1356 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1357 inset->dispatch(view()->cursor(), fr);
1358 } else if (name == "paragraph") {
1359 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1360 } else if (name == "prefs") {
1361 lyx_view_->getDialogs().update(name, string());
1366 case LFUN_DIALOG_HIDE:
1367 Dialogs::hide(argument, 0);
1370 case LFUN_DIALOG_DISCONNECT_INSET:
1371 BOOST_ASSERT(lyx_view_);
1372 lyx_view_->getDialogs().disconnect(argument);
1376 case LFUN_CITATION_INSERT: {
1377 BOOST_ASSERT(lyx_view_);
1378 if (!argument.empty()) {
1379 // we can have one optional argument, delimited by '|'
1380 // citation-insert <key>|<text_before>
1381 // this should be enhanced to also support text_after
1382 // and citation style
1383 string arg = argument;
1385 if (contains(argument, "|")) {
1386 arg = token(argument, '|', 0);
1387 opt1 = '[' + token(argument, '|', 1) + ']';
1389 std::ostringstream os;
1390 os << "citation LatexCommand\n"
1391 << "\\cite" << opt1 << "{" << arg << "}\n"
1393 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1396 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1400 case LFUN_BUFFER_CHILD_OPEN: {
1401 BOOST_ASSERT(lyx_view_);
1402 FileName const filename =
1403 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1404 // FIXME Should use bformat
1405 setMessage(_("Opening child document ") +
1406 makeDisplayPath(filename.absFilename()) + "...");
1407 view()->saveBookmark(false);
1408 string const parentfilename = lyx_view_->buffer()->fileName();
1409 if (theBufferList().exists(filename.absFilename()))
1410 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1412 lyx_view_->loadLyXFile(filename);
1413 // Set the parent name of the child document.
1414 // This makes insertion of citations and references in the child work,
1415 // when the target is in the parent or another child document.
1416 lyx_view_->buffer()->setParentName(parentfilename);
1420 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1421 BOOST_ASSERT(lyx_view_);
1422 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1425 case LFUN_KEYMAP_OFF:
1426 BOOST_ASSERT(lyx_view_);
1427 lyx_view_->view()->getIntl().keyMapOn(false);
1430 case LFUN_KEYMAP_PRIMARY:
1431 BOOST_ASSERT(lyx_view_);
1432 lyx_view_->view()->getIntl().keyMapPrim();
1435 case LFUN_KEYMAP_SECONDARY:
1436 BOOST_ASSERT(lyx_view_);
1437 lyx_view_->view()->getIntl().keyMapSec();
1440 case LFUN_KEYMAP_TOGGLE:
1441 BOOST_ASSERT(lyx_view_);
1442 lyx_view_->view()->getIntl().toggleKeyMap();
1448 string rest = split(argument, countstr, ' ');
1449 istringstream is(countstr);
1452 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1453 for (int i = 0; i < count; ++i)
1454 dispatch(lyxaction.lookupFunc(rest));
1458 case LFUN_COMMAND_SEQUENCE: {
1459 // argument contains ';'-terminated commands
1460 string arg = argument;
1461 while (!arg.empty()) {
1463 arg = split(arg, first, ';');
1464 FuncRequest func(lyxaction.lookupFunc(first));
1465 func.origin = cmd.origin;
1471 case LFUN_PREFERENCES_SAVE: {
1472 lyxrc.write(makeAbsPath("preferences",
1473 package().user_support()),
1478 case LFUN_SCREEN_FONT_UPDATE:
1479 BOOST_ASSERT(lyx_view_);
1480 // handle the screen font changes.
1481 lyxrc.set_font_norm_type();
1482 theFontLoader().update();
1483 /// FIXME: only the current view will be updated. the Gui
1484 /// class is able to furnish the list of views.
1485 updateFlags = Update::Force;
1488 case LFUN_SET_COLOR: {
1490 string const x11_name = split(argument, lyx_name, ' ');
1491 if (lyx_name.empty() || x11_name.empty()) {
1492 setErrorMessage(_("Syntax: set-color <lyx_name>"
1497 bool const graphicsbg_changed =
1498 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1499 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1501 if (!lcolor.setColor(lyx_name, x11_name)) {
1503 bformat(_("Set-color \"%1$s\" failed "
1504 "- color is undefined or "
1505 "may not be redefined"),
1506 from_utf8(lyx_name)));
1510 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1512 if (graphicsbg_changed) {
1513 #ifdef WITH_WARNINGS
1514 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1517 graphics::GCache::get().changeDisplay(true);
1524 BOOST_ASSERT(lyx_view_);
1525 lyx_view_->message(from_utf8(argument));
1528 case LFUN_EXTERNAL_EDIT: {
1529 BOOST_ASSERT(lyx_view_);
1530 FuncRequest fr(action, argument);
1531 InsetExternal().dispatch(view()->cursor(), fr);
1535 case LFUN_GRAPHICS_EDIT: {
1536 FuncRequest fr(action, argument);
1537 InsetGraphics().dispatch(view()->cursor(), fr);
1541 case LFUN_INSET_APPLY: {
1542 BOOST_ASSERT(lyx_view_);
1543 string const name = cmd.getArg(0);
1544 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1546 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1547 inset->dispatch(view()->cursor(), fr);
1549 FuncRequest fr(LFUN_INSET_INSERT, argument);
1552 // ideally, the update flag should be set by the insets,
1553 // but this is not possible currently
1554 updateFlags = Update::Force | Update::FitCursor;
1558 case LFUN_ALL_INSETS_TOGGLE: {
1559 BOOST_ASSERT(lyx_view_);
1561 string const name = split(argument, action, ' ');
1562 InsetBase::Code const inset_code =
1563 InsetBase::translate(name);
1565 LCursor & cur = view()->cursor();
1566 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1568 InsetBase & inset = lyx_view_->buffer()->inset();
1569 InsetIterator it = inset_iterator_begin(inset);
1570 InsetIterator const end = inset_iterator_end(inset);
1571 for (; it != end; ++it) {
1572 if (inset_code == InsetBase::NO_CODE
1573 || inset_code == it->lyxCode()) {
1574 LCursor tmpcur = cur;
1575 tmpcur.pushLeft(*it);
1576 it->dispatch(tmpcur, fr);
1579 updateFlags = Update::Force | Update::FitCursor;
1583 case LFUN_BUFFER_LANGUAGE: {
1584 BOOST_ASSERT(lyx_view_);
1585 Buffer & buffer = *lyx_view_->buffer();
1586 Language const * oldL = buffer.params().language;
1587 Language const * newL = languages.getLanguage(argument);
1588 if (!newL || oldL == newL)
1591 if (oldL->rightToLeft() == newL->rightToLeft()
1592 && !buffer.isMultiLingual())
1593 buffer.changeLanguage(oldL, newL);
1595 buffer.updateDocLang(newL);
1599 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1600 string const fname =
1601 addName(addPath(package().user_support(), "templates/"),
1603 Buffer defaults(fname);
1605 istringstream ss(argument);
1608 int const unknown_tokens = defaults.readHeader(lex);
1610 if (unknown_tokens != 0) {
1611 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1612 << unknown_tokens << " unknown token"
1613 << (unknown_tokens == 1 ? "" : "s")
1617 if (defaults.writeFile(FileName(defaults.fileName())))
1618 // FIXME Should use bformat
1619 setMessage(_("Document defaults saved in ")
1620 + makeDisplayPath(fname));
1622 setErrorMessage(_("Unable to save document defaults"));
1626 case LFUN_BUFFER_PARAMS_APPLY: {
1627 BOOST_ASSERT(lyx_view_);
1628 biblio::CiteEngine const engine =
1629 lyx_view_->buffer()->params().cite_engine;
1631 istringstream ss(argument);
1634 int const unknown_tokens =
1635 lyx_view_->buffer()->readHeader(lex);
1637 if (unknown_tokens != 0) {
1638 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1639 << unknown_tokens << " unknown token"
1640 << (unknown_tokens == 1 ? "" : "s")
1643 if (engine == lyx_view_->buffer()->params().cite_engine)
1646 LCursor & cur = view()->cursor();
1647 FuncRequest fr(LFUN_INSET_REFRESH);
1649 InsetBase & inset = lyx_view_->buffer()->inset();
1650 InsetIterator it = inset_iterator_begin(inset);
1651 InsetIterator const end = inset_iterator_end(inset);
1652 for (; it != end; ++it)
1653 if (it->lyxCode() == InsetBase::CITE_CODE)
1654 it->dispatch(cur, fr);
1658 case LFUN_TEXTCLASS_APPLY: {
1659 BOOST_ASSERT(lyx_view_);
1660 Buffer * buffer = lyx_view_->buffer();
1662 textclass_type const old_class =
1663 buffer->params().textclass;
1665 loadTextclass(argument);
1667 std::pair<bool, textclass_type> const tc_pair =
1668 textclasslist.numberOfClass(argument);
1673 textclass_type const new_class = tc_pair.second;
1674 if (old_class == new_class)
1678 lyx_view_->message(_("Converting document to new document class..."));
1679 recordUndoFullDocument(view());
1680 buffer->params().textclass = new_class;
1681 StableDocIterator backcur(view()->cursor());
1682 ErrorList & el = buffer->errorList("Class Switch");
1683 cap::switchBetweenClasses(
1684 old_class, new_class,
1685 static_cast<InsetText &>(buffer->inset()), el);
1687 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1689 buffer->errors("Class Switch");
1690 updateLabels(*buffer);
1691 updateFlags = Update::Force | Update::FitCursor;
1695 case LFUN_TEXTCLASS_LOAD:
1696 loadTextclass(argument);
1699 case LFUN_LYXRC_APPLY: {
1700 LyXRC const lyxrc_orig = lyxrc;
1702 istringstream ss(argument);
1703 bool const success = lyxrc.read(ss) == 0;
1706 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1707 << "Unable to read lyxrc data"
1712 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1714 /// We force the redraw in any case because there might be
1715 /// some screen font changes.
1716 /// FIXME: only the current view will be updated. the Gui
1717 /// class is able to furnish the list of views.
1718 updateFlags = Update::Force;
1722 case LFUN_WINDOW_NEW:
1723 LyX::ref().newLyXView();
1726 case LFUN_WINDOW_CLOSE:
1727 BOOST_ASSERT(lyx_view_);
1728 BOOST_ASSERT(theApp());
1729 // update bookmark pit of the current buffer before window close
1730 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1731 gotoBookmark(i+1, false, false);
1732 // ask the user for saving changes or cancel quit
1733 if (!theBufferList().quitWriteAll())
1738 case LFUN_BOOKMARK_GOTO:
1739 // go to bookmark, open unopened file and switch to buffer if necessary
1740 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1743 case LFUN_BOOKMARK_CLEAR:
1744 LyX::ref().session().bookmarks().clear();
1747 case LFUN_TOOLBAR_TOGGLE_STATE:
1748 lyx_view_->toggleToolbarState(argument);
1752 BOOST_ASSERT(lyx_view_);
1753 view()->cursor().dispatch(cmd);
1754 updateFlags = view()->cursor().result().update();
1755 if (!view()->cursor().result().dispatched())
1756 updateFlags = view()->dispatch(cmd);
1761 if (lyx_view_ && view()->buffer()) {
1762 // BufferView::update() updates the ViewMetricsInfo and
1763 // also initializes the position cache for all insets in
1764 // (at least partially) visible top-level paragraphs.
1765 // We will redraw the screen only if needed.
1766 if (view()->update(updateFlags)) {
1767 // Buffer::changed() signals that a repaint is needed.
1768 // The frontend (WorkArea) knows which area to repaint
1769 // thanks to the ViewMetricsInfo updated above.
1770 view()->buffer()->changed();
1773 lyx_view_->updateStatusBar();
1775 // if we executed a mutating lfun, mark the buffer as dirty
1777 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1778 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1779 view()->buffer()->markDirty();
1781 if (view()->cursor().inTexted()) {
1782 lyx_view_->updateLayoutChoice();
1787 lyx_view_->updateMenubar();
1788 lyx_view_->updateToolbars();
1789 sendDispatchMessage(getMessage(), cmd);
1794 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1796 const bool verbose = (cmd.origin == FuncRequest::MENU
1797 || cmd.origin == FuncRequest::TOOLBAR
1798 || cmd.origin == FuncRequest::COMMANDBUFFER);
1800 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1801 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1803 lyx_view_->message(msg);
1807 docstring dispatch_msg = msg;
1808 if (!dispatch_msg.empty())
1809 dispatch_msg += ' ';
1811 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1813 bool argsadded = false;
1815 if (!cmd.argument().empty()) {
1816 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1817 comname += ' ' + cmd.argument();
1822 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1824 if (!shortcuts.empty())
1825 comname += ": " + shortcuts;
1826 else if (!argsadded && !cmd.argument().empty())
1827 comname += ' ' + cmd.argument();
1829 if (!comname.empty()) {
1830 comname = rtrim(comname);
1831 dispatch_msg += '(' + rtrim(comname) + ')';
1834 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1835 << to_utf8(dispatch_msg) << endl;
1836 if (!dispatch_msg.empty())
1837 lyx_view_->message(dispatch_msg);
1841 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1843 // FIXME: initpath is not used. What to do?
1844 string initpath = lyxrc.document_path;
1845 string filename(name);
1847 if (view()->buffer()) {
1848 string const trypath = lyx_view_->buffer()->filePath();
1849 // If directory is writeable, use this as default.
1850 if (isDirWriteable(FileName(trypath)))
1854 static int newfile_number;
1856 if (filename.empty()) {
1857 filename = addName(lyxrc.document_path,
1858 "newfile" + convert<string>(++newfile_number) + ".lyx");
1859 while (theBufferList().exists(filename) ||
1860 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1862 filename = addName(lyxrc.document_path,
1863 "newfile" + convert<string>(newfile_number) +
1868 // The template stuff
1871 FileDialog fileDlg(_("Select template file"),
1872 LFUN_SELECT_FILE_SYNC,
1873 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1874 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1876 FileDialog::Result result =
1877 fileDlg.open(from_utf8(lyxrc.template_path),
1878 FileFilterList(_("LyX Documents (*.lyx)")),
1881 if (result.first == FileDialog::Later)
1883 if (result.second.empty())
1885 templname = to_utf8(result.second);
1888 Buffer * const b = newFile(filename, templname, !name.empty());
1890 lyx_view_->setBuffer(b);
1894 void LyXFunc::open(string const & fname)
1896 string initpath = lyxrc.document_path;
1898 if (view()->buffer()) {
1899 string const trypath = lyx_view_->buffer()->filePath();
1900 // If directory is writeable, use this as default.
1901 if (isDirWriteable(FileName(trypath)))
1907 if (fname.empty()) {
1908 FileDialog fileDlg(_("Select document to open"),
1910 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1911 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1913 FileDialog::Result result =
1914 fileDlg.open(from_utf8(initpath),
1915 FileFilterList(_("LyX Documents (*.lyx)")),
1918 if (result.first == FileDialog::Later)
1921 filename = to_utf8(result.second);
1923 // check selected filename
1924 if (filename.empty()) {
1925 lyx_view_->message(_("Canceled."));
1931 // get absolute path of file and add ".lyx" to the filename if
1933 FileName const fullname = fileSearch(string(), filename, "lyx");
1934 if (!fullname.empty())
1935 filename = fullname.absFilename();
1937 // if the file doesn't exist, let the user create one
1938 if (!fs::exists(fullname.toFilesystemEncoding())) {
1939 // the user specifically chose this name. Believe him.
1940 Buffer * const b = newFile(filename, string(), true);
1942 lyx_view_->setBuffer(b);
1946 docstring const disp_fn = makeDisplayPath(filename);
1947 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1950 if (lyx_view_->loadLyXFile(fullname)) {
1951 str2 = bformat(_("Document %1$s opened."), disp_fn);
1953 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1955 lyx_view_->message(str2);
1959 void LyXFunc::doImport(string const & argument)
1962 string filename = split(argument, format, ' ');
1964 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1965 << " file: " << filename << endl;
1967 // need user interaction
1968 if (filename.empty()) {
1969 string initpath = lyxrc.document_path;
1971 if (view()->buffer()) {
1972 string const trypath = lyx_view_->buffer()->filePath();
1973 // If directory is writeable, use this as default.
1974 if (isDirWriteable(FileName(trypath)))
1978 docstring const text = bformat(_("Select %1$s file to import"),
1979 formats.prettyName(format));
1981 FileDialog fileDlg(text,
1983 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1984 make_pair(_("Examples|#E#e"),
1985 from_utf8(addPath(package().system_support(), "examples"))));
1987 docstring filter = formats.prettyName(format);
1990 filter += from_utf8(formats.extension(format));
1993 FileDialog::Result result =
1994 fileDlg.open(from_utf8(initpath),
1995 FileFilterList(filter),
1998 if (result.first == FileDialog::Later)
2001 filename = to_utf8(result.second);
2003 // check selected filename
2004 if (filename.empty())
2005 lyx_view_->message(_("Canceled."));
2008 if (filename.empty())
2011 // get absolute path of file
2012 FileName const fullname(makeAbsPath(filename));
2014 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2016 // Check if the document already is open
2017 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2018 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2019 lyx_view_->message(_("Canceled."));
2024 // if the file exists already, and we didn't do
2025 // -i lyx thefile.lyx, warn
2026 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2027 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2029 docstring text = bformat(_("The document %1$s already exists.\n\n"
2030 "Do you want to over-write that document?"), file);
2031 int const ret = Alert::prompt(_("Over-write document?"),
2032 text, 0, 1, _("&Over-write"), _("&Cancel"));
2035 lyx_view_->message(_("Canceled."));
2040 ErrorList errorList;
2041 Importer::Import(lyx_view_, fullname, format, errorList);
2042 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2046 void LyXFunc::closeBuffer()
2048 // save current cursor position
2049 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2050 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2051 // goto bookmark to update bookmark pit.
2052 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2053 gotoBookmark(i+1, false, false);
2054 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2055 if (theBufferList().empty()) {
2056 // need this otherwise SEGV may occur while
2057 // trying to set variables that don't exist
2058 // since there's no current buffer
2059 lyx_view_->getDialogs().hideBufferDependent();
2061 lyx_view_->setBuffer(theBufferList().first());
2067 void LyXFunc::reloadBuffer()
2069 FileName filename(lyx_view_->buffer()->fileName());
2071 lyx_view_->loadLyXFile(filename);
2074 // Each "lyx_view_" should have it's own message method. lyxview and
2075 // the minibuffer would use the minibuffer, but lyxserver would
2076 // send an ERROR signal to its client. Alejandro 970603
2077 // This function is bit problematic when it comes to NLS, to make the
2078 // lyx servers client be language indepenent we must not translate
2079 // strings sent to this func.
2080 void LyXFunc::setErrorMessage(docstring const & m) const
2082 dispatch_buffer = m;
2087 void LyXFunc::setMessage(docstring const & m) const
2089 dispatch_buffer = m;
2093 docstring const LyXFunc::viewStatusMessage()
2095 // When meta-fake key is pressed, show the key sequence so far + "M-".
2097 return keyseq->print(true) + "M-";
2099 // Else, when a non-complete key sequence is pressed,
2100 // show the available options.
2101 if (keyseq->length() > 0 && !keyseq->deleted())
2102 return keyseq->printOptions(true);
2104 if (!view()->buffer())
2105 return _("Welcome to LyX!");
2107 return view()->cursor().currentState();
2111 BufferView * LyXFunc::view() const
2113 BOOST_ASSERT(lyx_view_);
2114 return lyx_view_->view();
2118 bool LyXFunc::wasMetaKey() const
2120 return (meta_fake_bit != key_modifier::none);
2126 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2128 // Why the switch you might ask. It is a trick to ensure that all
2129 // the elements in the LyXRCTags enum is handled. As you can see
2130 // there are no breaks at all. So it is just a huge fall-through.
2131 // The nice thing is that we will get a warning from the compiler
2132 // if we forget an element.
2133 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2135 case LyXRC::RC_ACCEPT_COMPOUND:
2136 case LyXRC::RC_ALT_LANG:
2137 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2138 case LyXRC::RC_PLAINTEXT_LINELEN:
2139 case LyXRC::RC_AUTOREGIONDELETE:
2140 case LyXRC::RC_AUTORESET_OPTIONS:
2141 case LyXRC::RC_AUTOSAVE:
2142 case LyXRC::RC_AUTO_NUMBER:
2143 case LyXRC::RC_BACKUPDIR_PATH:
2144 case LyXRC::RC_BIBTEX_COMMAND:
2145 case LyXRC::RC_BINDFILE:
2146 case LyXRC::RC_CHECKLASTFILES:
2147 case LyXRC::RC_USELASTFILEPOS:
2148 case LyXRC::RC_LOADSESSION:
2149 case LyXRC::RC_CHKTEX_COMMAND:
2150 case LyXRC::RC_CONVERTER:
2151 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2152 case LyXRC::RC_COPIER:
2153 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2154 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2155 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2156 case LyXRC::RC_DATE_INSERT_FORMAT:
2157 case LyXRC::RC_DEFAULT_LANGUAGE:
2158 case LyXRC::RC_DEFAULT_PAPERSIZE:
2159 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2160 case LyXRC::RC_DISPLAY_GRAPHICS:
2161 case LyXRC::RC_DOCUMENTPATH:
2162 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2163 string const encoded = FileName(
2164 lyxrc_new.document_path).toFilesystemEncoding();
2165 if (fs::exists(encoded) && fs::is_directory(encoded))
2166 support::package().document_dir() = lyxrc.document_path;
2168 case LyXRC::RC_ESC_CHARS:
2169 case LyXRC::RC_FONT_ENCODING:
2170 case LyXRC::RC_FORMAT:
2171 case LyXRC::RC_INDEX_COMMAND:
2172 case LyXRC::RC_INPUT:
2173 case LyXRC::RC_KBMAP:
2174 case LyXRC::RC_KBMAP_PRIMARY:
2175 case LyXRC::RC_KBMAP_SECONDARY:
2176 case LyXRC::RC_LABEL_INIT_LENGTH:
2177 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2178 case LyXRC::RC_LANGUAGE_AUTO_END:
2179 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2180 case LyXRC::RC_LANGUAGE_COMMAND_END:
2181 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2182 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2183 case LyXRC::RC_LANGUAGE_PACKAGE:
2184 case LyXRC::RC_LANGUAGE_USE_BABEL:
2185 case LyXRC::RC_MAKE_BACKUP:
2186 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2187 case LyXRC::RC_NUMLASTFILES:
2188 case LyXRC::RC_PATH_PREFIX:
2189 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2190 support::prependEnvPath("PATH", lyxrc.path_prefix);
2192 case LyXRC::RC_PERS_DICT:
2193 case LyXRC::RC_POPUP_BOLD_FONT:
2194 case LyXRC::RC_POPUP_FONT_ENCODING:
2195 case LyXRC::RC_POPUP_NORMAL_FONT:
2196 case LyXRC::RC_PREVIEW:
2197 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2198 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2199 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2200 case LyXRC::RC_PRINTCOPIESFLAG:
2201 case LyXRC::RC_PRINTER:
2202 case LyXRC::RC_PRINTEVENPAGEFLAG:
2203 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2204 case LyXRC::RC_PRINTFILEEXTENSION:
2205 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2206 case LyXRC::RC_PRINTODDPAGEFLAG:
2207 case LyXRC::RC_PRINTPAGERANGEFLAG:
2208 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2209 case LyXRC::RC_PRINTPAPERFLAG:
2210 case LyXRC::RC_PRINTREVERSEFLAG:
2211 case LyXRC::RC_PRINTSPOOL_COMMAND:
2212 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2213 case LyXRC::RC_PRINTTOFILE:
2214 case LyXRC::RC_PRINTTOPRINTER:
2215 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2216 case LyXRC::RC_PRINT_COMMAND:
2217 case LyXRC::RC_RTL_SUPPORT:
2218 case LyXRC::RC_SCREEN_DPI:
2219 case LyXRC::RC_SCREEN_FONT_ENCODING:
2220 case LyXRC::RC_SCREEN_FONT_ROMAN:
2221 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2222 case LyXRC::RC_SCREEN_FONT_SANS:
2223 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2224 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2225 case LyXRC::RC_SCREEN_FONT_SIZES:
2226 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2227 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2228 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2229 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2230 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2231 case LyXRC::RC_SCREEN_ZOOM:
2232 case LyXRC::RC_SERVERPIPE:
2233 case LyXRC::RC_SET_COLOR:
2234 case LyXRC::RC_SHOW_BANNER:
2235 case LyXRC::RC_SPELL_COMMAND:
2236 case LyXRC::RC_TEMPDIRPATH:
2237 case LyXRC::RC_TEMPLATEPATH:
2238 case LyXRC::RC_TEX_ALLOWS_SPACES:
2239 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2240 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2241 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2243 case LyXRC::RC_UIFILE:
2244 case LyXRC::RC_USER_EMAIL:
2245 case LyXRC::RC_USER_NAME:
2246 case LyXRC::RC_USETEMPDIR:
2247 case LyXRC::RC_USE_ALT_LANG:
2248 case LyXRC::RC_USE_CONVERTER_CACHE:
2249 case LyXRC::RC_USE_ESC_CHARS:
2250 case LyXRC::RC_USE_INP_ENC:
2251 case LyXRC::RC_USE_PERS_DICT:
2252 case LyXRC::RC_USE_SPELL_LIB:
2253 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2254 case LyXRC::RC_VIEWER:
2255 case LyXRC::RC_LAST: