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"
31 #include "CutAndPaste.h"
33 #include "dispatchresult.h"
35 #include "errorlist.h"
38 #include "funcrequest.h"
39 #include "FuncStatus.h"
42 #include "insetiterator.h"
50 #include "LyXAction.h"
55 #include "lyxserver.h"
56 #include "lyxtextclasslist.h"
58 #include "paragraph.h"
59 #include "pariterator.h"
60 #include "ParagraphParameters.h"
63 #include "insets/insetbox.h"
64 #include "insets/insetbranch.h"
65 #include "insets/insetcommand.h"
66 #include "insets/insetert.h"
67 #include "insets/insetexternal.h"
68 #include "insets/insetfloat.h"
69 #include "insets/insetgraphics.h"
70 #include "insets/insetinclude.h"
71 #include "insets/insetnote.h"
72 #include "insets/insettabular.h"
73 #include "insets/insetvspace.h"
74 #include "insets/insetwrap.h"
76 #include "frontends/Application.h"
77 #include "frontends/Alert.h"
78 #include "frontends/Dialogs.h"
79 #include "frontends/FileDialog.h"
80 #include "frontends/FontLoader.h"
81 #include "frontends/Gui.h"
82 #include "frontends/LyXKeySym.h"
83 #include "frontends/LyXView.h"
84 #include "frontends/Menubar.h"
85 #include "frontends/Toolbars.h"
87 #include "support/environment.h"
88 #include "support/filefilterlist.h"
89 #include "support/filetools.h"
90 #include "support/forkedcontr.h"
91 #include "support/fs_extras.h"
92 #include "support/lstrings.h"
93 #include "support/path.h"
94 #include "support/package.h"
95 #include "support/systemcall.h"
96 #include "support/convert.h"
97 #include "support/os.h"
99 #include <boost/current_function.hpp>
100 #include <boost/filesystem/operations.hpp>
107 using bv_funcs::freefont2string;
109 using support::absolutePath;
110 using support::addName;
111 using support::addPath;
112 using support::bformat;
113 using support::changeExtension;
114 using support::contains;
115 using support::FileFilterList;
116 using support::FileName;
117 using support::fileSearch;
118 using support::ForkedcallsController;
119 using support::i18nLibFileSearch;
120 using support::isDirWriteable;
121 using support::isFileReadable;
122 using support::isStrInt;
123 using support::makeAbsPath;
124 using support::makeDisplayPath;
125 using support::package;
126 using support::quoteName;
127 using support::rtrim;
128 using support::split;
129 using support::subst;
130 using support::Systemcall;
131 using support::token;
133 using support::prefixIs;
136 using std::make_pair;
139 using std::istringstream;
140 using std::ostringstream;
142 namespace Alert = frontend::Alert;
143 namespace fs = boost::filesystem;
147 extern tex_accent_struct get_accent(kb_action action);
152 bool getLocalStatus(LCursor cursor,
153 FuncRequest const & cmd, FuncStatus & status)
155 // Try to fix cursor in case it is broken.
156 cursor.fixIfBroken();
158 // This is, of course, a mess. Better create a new doc iterator and use
159 // this in Inset::getStatus. This might require an additional
160 // BufferView * arg, though (which should be avoided)
161 //LCursor safe = *this;
163 for ( ; cursor.depth(); cursor.pop()) {
164 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
165 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
166 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
167 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
169 // The inset's getStatus() will return 'true' if it made
170 // a definitive decision on whether it want to handle the
171 // request or not. The result of this decision is put into
172 // the 'status' parameter.
173 if (cursor.inset().getStatus(cursor, cmd, status)) {
182 /** Return the change status at cursor position, taking in account the
183 * status at each level of the document iterator (a table in a deleted
184 * footnote is deleted).
185 * When \param outer is true, the top slice is not looked at.
187 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
189 size_t const depth = dit.depth() - (outer ? 1 : 0);
191 for (size_t i = 0 ; i < depth ; ++i) {
192 CursorSlice const & slice = dit[i];
193 if (!slice.inset().inMathed()
194 && slice.pos() < slice.paragraph().size()) {
195 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
196 if (ch != Change::UNCHANGED)
200 return Change::UNCHANGED;
208 meta_fake_bit(key_modifier::none)
213 void LyXFunc::initKeySequences(kb_keymap * kb)
215 keyseq.reset(new kb_sequence(kb, kb));
216 cancel_meta_seq.reset(new kb_sequence(kb, kb));
220 void LyXFunc::setLyXView(LyXView * lv)
226 void LyXFunc::handleKeyFunc(kb_action action)
228 char_type c = encoded_last_key;
230 if (keyseq->length())
233 lyx_view_->view()->getIntl().getTransManager().deadkey(
234 c, get_accent(action).accent, view()->getLyXText(), view()->cursor());
235 // Need to clear, in case the minibuffer calls these
238 // copied verbatim from do_accent_char
239 view()->cursor().resetAnchor();
244 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
246 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
248 // Do nothing if we have nothing (JMarc)
249 if (!keysym->isOK()) {
250 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
255 if (keysym->isModifier()) {
256 lyxerr[Debug::KEY] << "isModifier true" << endl;
260 //Encoding const * encoding = view()->cursor().getEncoding();
261 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
262 size_t encoded_last_key = keysym->getUCSEncoded();
264 // Do a one-deep top-level lookup for
265 // cancel and meta-fake keys. RVDK_PATCH_5
266 cancel_meta_seq->reset();
268 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
269 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
270 << " action first set to [" << func.action << ']'
273 // When not cancel or meta-fake, do the normal lookup.
274 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
275 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
276 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
277 // remove Caps Lock and Mod2 as a modifiers
278 func = keyseq->addkey(keysym, (state | meta_fake_bit));
279 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
280 << "action now set to ["
281 << func.action << ']' << endl;
284 // Dont remove this unless you know what you are doing.
285 meta_fake_bit = key_modifier::none;
287 // Can this happen now ?
288 if (func.action == LFUN_NOACTION) {
289 func = FuncRequest(LFUN_COMMAND_PREFIX);
292 if (lyxerr.debugging(Debug::KEY)) {
293 lyxerr << BOOST_CURRENT_FUNCTION
295 << func.action << "]["
296 << keyseq->print() << ']'
300 // already here we know if it any point in going further
301 // why not return already here if action == -1 and
302 // num_bytes == 0? (Lgb)
304 if (keyseq->length() > 1) {
305 lyx_view_->message(from_utf8(keyseq->print()));
309 // Maybe user can only reach the key via holding down shift.
310 // Let's see. But only if shift is the only modifier
311 if (func.action == LFUN_UNKNOWN_ACTION &&
312 state == key_modifier::shift) {
313 lyxerr[Debug::KEY] << "Trying without shift" << endl;
314 func = keyseq->addkey(keysym, key_modifier::none);
315 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
318 if (func.action == LFUN_UNKNOWN_ACTION) {
319 // Hmm, we didn't match any of the keysequences. See
320 // if it's normal insertable text not already covered
322 if (keysym->isText() && keyseq->length() == 1) {
323 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
324 func = FuncRequest(LFUN_SELF_INSERT,
325 FuncRequest::KEYBOARD);
327 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
328 lyx_view_->message(_("Unknown function."));
333 if (func.action == LFUN_SELF_INSERT) {
334 if (encoded_last_key != 0) {
335 docstring const arg(1, encoded_last_key);
336 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
337 FuncRequest::KEYBOARD));
339 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
347 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
349 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
352 if (cmd.action == LFUN_LYX_QUIT) {
353 flag.message(from_utf8(N_("Exiting")));
356 } else if (cmd.action == LFUN_BOOKMARK_GOTO) {
357 // bookmarks can be valid even if there is no opened buffer
358 flag.enabled(LyX::ref().session().bookmarks().isValid(convert<unsigned int>(to_utf8(cmd.argument()))));
360 } else if (cmd.action == LFUN_BOOKMARK_CLEAR) {
361 flag.enabled(LyX::ref().session().bookmarks().size() > 0);
363 } else if (cmd.action == LFUN_TOOLBAR_TOGGLE_STATE) {
364 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
365 if (!(flags & ToolbarBackend::AUTO))
366 flag.setOnOff(flags & ToolbarBackend::ON);
370 LCursor & cur = view()->cursor();
372 /* In LyX/Mac, when a dialog is open, the menus of the
373 application can still be accessed without giving focus to
374 the main window. In this case, we want to disable the menu
375 entries that are buffer-related.
377 Note that this code is not perfect, as bug 1941 attests:
378 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
380 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
381 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
384 if (cmd.action == LFUN_NOACTION) {
385 flag.message(from_utf8(N_("Nothing to do")));
390 switch (cmd.action) {
391 case LFUN_UNKNOWN_ACTION:
392 #ifndef HAVE_LIBAIKSAURUS
393 case LFUN_THESAURUS_ENTRY:
403 if (flag.unknown()) {
404 flag.message(from_utf8(N_("Unknown action")));
408 if (!flag.enabled()) {
409 if (flag.message().empty())
410 flag.message(from_utf8(N_("Command disabled")));
414 // Check whether we need a buffer
415 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
417 flag.message(from_utf8(N_("Command not allowed with"
418 "out any document open")));
423 // I would really like to avoid having this switch and rather try to
424 // encode this in the function itself.
425 // -- And I'd rather let an inset decide which LFUNs it is willing
426 // to handle (Andre')
428 switch (cmd.action) {
429 case LFUN_BUFFER_TOGGLE_READ_ONLY:
430 flag.setOnOff(buf->isReadonly());
433 case LFUN_BUFFER_SWITCH:
434 // toggle on the current buffer, but do not toggle off
435 // the other ones (is that a good idea?)
436 if (to_utf8(cmd.argument()) == buf->fileName())
440 case LFUN_BUFFER_EXPORT:
441 enable = cmd.argument() == "custom"
442 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
445 case LFUN_BUFFER_CHKTEX:
446 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
449 case LFUN_BUILD_PROGRAM:
450 enable = Exporter::isExportable(*buf, "program");
453 case LFUN_LAYOUT_TABULAR:
454 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
458 case LFUN_LAYOUT_PARAGRAPH:
459 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
462 case LFUN_VC_REGISTER:
463 enable = !buf->lyxvc().inUse();
465 case LFUN_VC_CHECK_IN:
466 enable = buf->lyxvc().inUse() && !buf->isReadonly();
468 case LFUN_VC_CHECK_OUT:
469 enable = buf->lyxvc().inUse() && buf->isReadonly();
472 case LFUN_VC_UNDO_LAST:
473 enable = buf->lyxvc().inUse();
475 case LFUN_BUFFER_RELOAD:
476 enable = !buf->isUnnamed() && !buf->isClean();
479 case LFUN_INSET_SETTINGS: {
483 InsetBase::Code code = cur.inset().lyxCode();
485 case InsetBase::TABULAR_CODE:
486 enable = cmd.argument() == "tabular";
488 case InsetBase::ERT_CODE:
489 enable = cmd.argument() == "ert";
491 case InsetBase::FLOAT_CODE:
492 enable = cmd.argument() == "float";
494 case InsetBase::WRAP_CODE:
495 enable = cmd.argument() == "wrap";
497 case InsetBase::NOTE_CODE:
498 enable = cmd.argument() == "note";
500 case InsetBase::BRANCH_CODE:
501 enable = cmd.argument() == "branch";
503 case InsetBase::BOX_CODE:
504 enable = cmd.argument() == "box";
512 case LFUN_INSET_APPLY: {
513 string const name = cmd.getArg(0);
514 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
516 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
518 bool const success = inset->getStatus(cur, fr, fs);
519 // Every inset is supposed to handle this
520 BOOST_ASSERT(success);
523 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
524 flag |= getStatus(fr);
526 enable = flag.enabled();
530 case LFUN_DIALOG_SHOW: {
531 string const name = cmd.getArg(0);
533 enable = name == "aboutlyx"
537 || name == "texinfo";
538 else if (name == "print")
539 enable = Exporter::isExportable(*buf, "dvi")
540 && lyxrc.print_command != "none";
541 else if (name == "character" || name == "mathpanel")
542 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
543 else if (name == "latexlog")
544 enable = isFileReadable(FileName(buf->getLogName().second));
545 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
546 else if (name == "spellchecker")
549 else if (name == "vclog")
550 enable = buf->lyxvc().inUse();
551 else if (name == "view-source")
556 case LFUN_DIALOG_SHOW_NEW_INSET:
557 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
560 case LFUN_DIALOG_UPDATE: {
561 string const name = cmd.getArg(0);
563 enable = name == "prefs";
567 case LFUN_CITATION_INSERT: {
568 FuncRequest fr(LFUN_INSET_INSERT, "citation");
569 enable = getStatus(fr).enabled();
573 case LFUN_BUFFER_WRITE: {
574 enable = view()->buffer()->isUnnamed()
575 || !view()->buffer()->isClean();
580 // this one is difficult to get right. As a half-baked
581 // solution, we consider only the first action of the sequence
582 case LFUN_COMMAND_SEQUENCE: {
583 // argument contains ';'-terminated commands
584 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
585 FuncRequest func(lyxaction.lookupFunc(firstcmd));
586 func.origin = cmd.origin;
587 flag = getStatus(func);
590 case LFUN_BUFFER_NEW:
591 case LFUN_BUFFER_NEW_TEMPLATE:
592 case LFUN_WORD_FIND_FORWARD:
593 case LFUN_WORD_FIND_BACKWARD:
594 case LFUN_COMMAND_PREFIX:
595 case LFUN_COMMAND_EXECUTE:
597 case LFUN_META_PREFIX:
598 case LFUN_BUFFER_CLOSE:
599 case LFUN_BUFFER_WRITE_AS:
600 case LFUN_BUFFER_UPDATE:
601 case LFUN_BUFFER_VIEW:
602 case LFUN_BUFFER_IMPORT:
604 case LFUN_BUFFER_AUTO_SAVE:
605 case LFUN_RECONFIGURE:
609 case LFUN_DROP_LAYOUTS_CHOICE:
611 case LFUN_SERVER_GET_NAME:
612 case LFUN_SERVER_NOTIFY:
613 case LFUN_SERVER_GOTO_FILE_ROW:
614 case LFUN_DIALOG_HIDE:
615 case LFUN_DIALOG_DISCONNECT_INSET:
616 case LFUN_BUFFER_CHILD_OPEN:
617 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
618 case LFUN_KEYMAP_OFF:
619 case LFUN_KEYMAP_PRIMARY:
620 case LFUN_KEYMAP_SECONDARY:
621 case LFUN_KEYMAP_TOGGLE:
623 case LFUN_BUFFER_EXPORT_CUSTOM:
624 case LFUN_BUFFER_PRINT:
625 case LFUN_PREFERENCES_SAVE:
626 case LFUN_SCREEN_FONT_UPDATE:
629 case LFUN_EXTERNAL_EDIT:
630 case LFUN_GRAPHICS_EDIT:
631 case LFUN_ALL_INSETS_TOGGLE:
632 case LFUN_BUFFER_LANGUAGE:
633 case LFUN_TEXTCLASS_APPLY:
634 case LFUN_TEXTCLASS_LOAD:
635 case LFUN_BUFFER_SAVE_AS_DEFAULT:
636 case LFUN_BUFFER_PARAMS_APPLY:
637 case LFUN_LYXRC_APPLY:
638 case LFUN_BUFFER_NEXT:
639 case LFUN_BUFFER_PREVIOUS:
640 case LFUN_WINDOW_NEW:
641 case LFUN_WINDOW_CLOSE:
642 // these are handled in our dispatch()
646 if (!getLocalStatus(cur, cmd, flag))
647 flag = view()->getStatus(cmd);
653 // Can we use a readonly buffer?
654 if (buf && buf->isReadonly()
655 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
656 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
657 flag.message(from_utf8(N_("Document is read-only")));
661 // Are we in a DELETED change-tracking region?
662 if (buf && lookupChangeType(cur, true) == Change::DELETED
663 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
664 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
665 flag.message(from_utf8(N_("This portion of the document is deleted.")));
669 // the default error message if we disable the command
670 if (!flag.enabled() && flag.message().empty())
671 flag.message(from_utf8(N_("Command disabled")));
677 bool LyXFunc::ensureBufferClean(BufferView * bv)
679 Buffer & buf = *bv->buffer();
683 docstring const file = makeDisplayPath(buf.fileName(), 30);
684 docstring text = bformat(_("The document %1$s has unsaved "
685 "changes.\n\nDo you want to save "
686 "the document?"), file);
687 int const ret = Alert::prompt(_("Save changed document?"),
688 text, 0, 1, _("&Save"),
692 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
694 return buf.isClean();
700 void showPrintError(string const & name)
702 docstring str = bformat(_("Could not print the document %1$s.\n"
703 "Check that your printer is set up correctly."),
704 makeDisplayPath(name, 50));
705 Alert::error(_("Print document failed"), str);
709 void loadTextclass(string const & name)
711 std::pair<bool, textclass_type> const tc_pair =
712 textclasslist.numberOfClass(name);
714 if (!tc_pair.first) {
715 lyxerr << "Document class \"" << name
716 << "\" does not exist."
721 textclass_type const tc = tc_pair.second;
723 if (!textclasslist[tc].load()) {
724 docstring s = bformat(_("The document could not be converted\n"
725 "into the document class %1$s."),
726 from_utf8(textclasslist[tc].name()));
727 Alert::error(_("Could not change class"), s);
732 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
737 void LyXFunc::dispatch(FuncRequest const & cmd)
739 string const argument = to_utf8(cmd.argument());
740 kb_action const action = cmd.action;
742 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
743 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
745 // we have not done anything wrong yet.
747 dispatch_buffer.erase();
749 // redraw the screen at the end (first of the two drawing steps).
750 //This is done unless explicitely requested otherwise
751 Update::flags updateFlags = Update::FitCursor;
753 FuncStatus const flag = getStatus(cmd);
754 if (!flag.enabled()) {
755 // We cannot use this function here
756 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
757 << lyxaction.getActionName(action)
758 << " [" << action << "] is disabled at this location"
760 setErrorMessage(flag.message());
764 case LFUN_WORD_FIND_FORWARD:
765 case LFUN_WORD_FIND_BACKWARD: {
766 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
767 static string last_search;
768 string searched_string;
770 if (!argument.empty()) {
771 last_search = argument;
772 searched_string = argument;
774 searched_string = last_search;
777 if (searched_string.empty())
780 bool const fw = action == LFUN_WORD_FIND_FORWARD;
782 find2string(searched_string, true, false, fw);
783 find(view(), FuncRequest(LFUN_WORD_FIND, data));
787 case LFUN_COMMAND_PREFIX:
788 BOOST_ASSERT(lyx_view_);
789 lyx_view_->message(from_utf8(keyseq->printOptions()));
792 case LFUN_COMMAND_EXECUTE:
793 BOOST_ASSERT(lyx_view_);
794 lyx_view_->getToolbars().display("minibuffer", true);
795 lyx_view_->focus_command_buffer();
799 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
801 meta_fake_bit = key_modifier::none;
802 if (view()->buffer())
803 // cancel any selection
804 dispatch(FuncRequest(LFUN_MARK_OFF));
805 setMessage(_("Cancel"));
808 case LFUN_META_PREFIX:
809 meta_fake_bit = key_modifier::alt;
810 setMessage(from_utf8(keyseq->print()));
813 case LFUN_BUFFER_TOGGLE_READ_ONLY:
814 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
815 if (lyx_view_->buffer()->lyxvc().inUse())
816 lyx_view_->buffer()->lyxvc().toggleReadOnly();
818 lyx_view_->buffer()->setReadonly(
819 !lyx_view_->buffer()->isReadonly());
822 // --- Menus -----------------------------------------------
823 case LFUN_BUFFER_NEW:
824 menuNew(argument, false);
827 case LFUN_BUFFER_NEW_TEMPLATE:
828 menuNew(argument, true);
831 case LFUN_BUFFER_CLOSE:
836 case LFUN_BUFFER_WRITE:
837 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
838 if (!lyx_view_->buffer()->isUnnamed()) {
839 docstring const str = bformat(_("Saving document %1$s..."),
840 makeDisplayPath(lyx_view_->buffer()->fileName()));
841 lyx_view_->message(str);
842 menuWrite(lyx_view_->buffer());
843 lyx_view_->message(str + _(" done."));
845 writeAs(lyx_view_->buffer());
846 updateFlags = Update::None;
849 case LFUN_BUFFER_WRITE_AS:
850 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
851 writeAs(lyx_view_->buffer(), argument);
852 updateFlags = Update::None;
855 case LFUN_BUFFER_RELOAD: {
856 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
857 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
858 docstring text = bformat(_("Any changes will be lost. Are you sure "
859 "you want to revert to the saved version of the document %1$s?"), file);
860 int const ret = Alert::prompt(_("Revert to saved document?"),
861 text, 0, 1, _("&Revert"), _("&Cancel"));
868 case LFUN_BUFFER_UPDATE:
869 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
870 Exporter::Export(lyx_view_->buffer(), argument, true);
873 case LFUN_BUFFER_VIEW:
874 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
875 Exporter::preview(lyx_view_->buffer(), argument);
878 case LFUN_BUILD_PROGRAM:
879 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
880 Exporter::Export(lyx_view_->buffer(), "program", true);
883 case LFUN_BUFFER_CHKTEX:
884 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
885 lyx_view_->buffer()->runChktex();
888 case LFUN_BUFFER_EXPORT:
889 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
890 if (argument == "custom")
891 lyx_view_->getDialogs().show("sendto");
893 Exporter::Export(lyx_view_->buffer(), argument, false);
897 case LFUN_BUFFER_EXPORT_CUSTOM: {
898 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
900 string command = split(argument, format_name, ' ');
901 Format const * format = formats.getFormat(format_name);
903 lyxerr << "Format \"" << format_name
904 << "\" not recognized!"
909 Buffer * buffer = lyx_view_->buffer();
911 // The name of the file created by the conversion process
914 // Output to filename
915 if (format->name() == "lyx") {
916 string const latexname =
917 buffer->getLatexName(false);
918 filename = changeExtension(latexname,
919 format->extension());
920 filename = addName(buffer->temppath(), filename);
922 if (!buffer->writeFile(FileName(filename)))
926 Exporter::Export(buffer, format_name, true, filename);
929 // Substitute $$FName for filename
930 if (!contains(command, "$$FName"))
931 command = "( " + command + " ) < $$FName";
932 command = subst(command, "$$FName", filename);
934 // Execute the command in the background
936 call.startscript(Systemcall::DontWait, command);
940 case LFUN_BUFFER_PRINT: {
941 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
944 string command = split(split(argument, target, ' '),
948 || target_name.empty()
949 || command.empty()) {
950 lyxerr << "Unable to parse \""
951 << argument << '"' << std::endl;
954 if (target != "printer" && target != "file") {
955 lyxerr << "Unrecognized target \""
956 << target << '"' << std::endl;
960 Buffer * buffer = lyx_view_->buffer();
962 if (!Exporter::Export(buffer, "dvi", true)) {
963 showPrintError(buffer->fileName());
967 // Push directory path.
968 string const path = buffer->temppath();
969 support::Path p(path);
971 // there are three cases here:
972 // 1. we print to a file
973 // 2. we print directly to a printer
974 // 3. we print using a spool command (print to file first)
977 string const dviname =
978 changeExtension(buffer->getLatexName(true),
981 if (target == "printer") {
982 if (!lyxrc.print_spool_command.empty()) {
983 // case 3: print using a spool
984 string const psname =
985 changeExtension(dviname,".ps");
986 command += lyxrc.print_to_file
989 + quoteName(dviname);
992 lyxrc.print_spool_command +' ';
993 if (target_name != "default") {
994 command2 += lyxrc.print_spool_printerprefix
998 command2 += quoteName(psname);
1000 // If successful, then spool command
1001 res = one.startscript(
1006 res = one.startscript(
1007 Systemcall::DontWait,
1010 // case 2: print directly to a printer
1011 res = one.startscript(
1012 Systemcall::DontWait,
1013 command + quoteName(dviname));
1017 // case 1: print to a file
1018 command += lyxrc.print_to_file
1019 + quoteName(makeAbsPath(target_name,
1022 + quoteName(dviname);
1023 res = one.startscript(Systemcall::DontWait,
1028 showPrintError(buffer->fileName());
1032 case LFUN_BUFFER_IMPORT:
1037 // FIXME: this code needs to be transfered somewhere else
1038 // as lyx_view_ will most certainly be null and a same buffer
1039 // might be visible in more than one LyXView.
1040 if (lyx_view_ && lyx_view_->view()->buffer()) {
1041 // save cursor Position for opened files to .lyx/session
1042 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
1043 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1046 // save the geometry of the current view
1047 lyx_view_->saveGeometry();
1048 // quitting is triggered by the gui code (leaving the event loop)
1049 theApp()->gui().closeAllViews();
1052 case LFUN_TOC_VIEW: {
1053 BOOST_ASSERT(lyx_view_);
1054 InsetCommandParams p("tableofcontents");
1055 string const data = InsetCommandMailer::params2string("toc", p);
1056 lyx_view_->getDialogs().show("toc", data, 0);
1060 case LFUN_BUFFER_AUTO_SAVE:
1064 case LFUN_RECONFIGURE:
1065 BOOST_ASSERT(lyx_view_);
1066 reconfigure(*lyx_view_);
1069 case LFUN_HELP_OPEN: {
1070 BOOST_ASSERT(lyx_view_);
1071 string const arg = argument;
1073 setErrorMessage(_("Missing argument"));
1076 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1077 if (fname.empty()) {
1078 lyxerr << "LyX: unable to find documentation file `"
1079 << arg << "'. Bad installation?" << endl;
1082 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1083 makeDisplayPath(fname.absFilename())));
1084 lyx_view_->loadLyXFile(fname, false);
1088 // --- version control -------------------------------
1089 case LFUN_VC_REGISTER:
1090 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1091 if (!ensureBufferClean(view()))
1093 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1094 lyx_view_->buffer()->lyxvc().registrer();
1099 case LFUN_VC_CHECK_IN:
1100 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1101 if (!ensureBufferClean(view()))
1103 if (lyx_view_->buffer()->lyxvc().inUse()
1104 && !lyx_view_->buffer()->isReadonly()) {
1105 lyx_view_->buffer()->lyxvc().checkIn();
1110 case LFUN_VC_CHECK_OUT:
1111 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1112 if (!ensureBufferClean(view()))
1114 if (lyx_view_->buffer()->lyxvc().inUse()
1115 && lyx_view_->buffer()->isReadonly()) {
1116 lyx_view_->buffer()->lyxvc().checkOut();
1121 case LFUN_VC_REVERT:
1122 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1123 lyx_view_->buffer()->lyxvc().revert();
1127 case LFUN_VC_UNDO_LAST:
1128 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1129 lyx_view_->buffer()->lyxvc().undoLast();
1133 // --- buffers ----------------------------------------
1134 case LFUN_BUFFER_SWITCH:
1135 BOOST_ASSERT(lyx_view_);
1136 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1139 case LFUN_BUFFER_NEXT:
1140 BOOST_ASSERT(lyx_view_);
1141 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1144 case LFUN_BUFFER_PREVIOUS:
1145 BOOST_ASSERT(lyx_view_);
1146 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1150 BOOST_ASSERT(lyx_view_);
1151 newFile(view(), argument);
1154 case LFUN_FILE_OPEN:
1155 BOOST_ASSERT(lyx_view_);
1159 case LFUN_DROP_LAYOUTS_CHOICE:
1160 BOOST_ASSERT(lyx_view_);
1161 lyx_view_->getToolbars().openLayoutList();
1164 case LFUN_MENU_OPEN:
1165 BOOST_ASSERT(lyx_view_);
1166 lyx_view_->getMenubar().openByName(from_utf8(argument));
1169 // --- lyxserver commands ----------------------------
1170 case LFUN_SERVER_GET_NAME:
1171 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1172 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1173 lyxerr[Debug::INFO] << "FNAME["
1174 << lyx_view_->buffer()->fileName()
1178 case LFUN_SERVER_NOTIFY:
1179 dispatch_buffer = from_utf8(keyseq->print());
1180 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1183 case LFUN_SERVER_GOTO_FILE_ROW: {
1184 BOOST_ASSERT(lyx_view_);
1187 istringstream is(argument);
1188 is >> file_name >> row;
1189 if (prefixIs(file_name, package().temp_dir())) {
1190 // Needed by inverse dvi search. If it is a file
1191 // in tmpdir, call the apropriated function
1192 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1194 // Must replace extension of the file to be .lyx
1195 // and get full path
1196 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1197 // Either change buffer or load the file
1198 if (theBufferList().exists(s.absFilename())) {
1199 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1201 lyx_view_->loadLyXFile(s);
1205 view()->setCursorFromRow(row);
1208 // see BufferView::center()
1212 case LFUN_DIALOG_SHOW: {
1213 BOOST_ASSERT(lyx_view_);
1214 string const name = cmd.getArg(0);
1215 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1217 if (name == "character") {
1218 data = freefont2string();
1220 lyx_view_->getDialogs().show("character", data);
1221 } else if (name == "latexlog") {
1222 pair<Buffer::LogType, string> const logfile =
1223 lyx_view_->buffer()->getLogName();
1224 switch (logfile.first) {
1225 case Buffer::latexlog:
1228 case Buffer::buildlog:
1232 data += LyXLex::quoteString(logfile.second);
1233 lyx_view_->getDialogs().show("log", data);
1234 } else if (name == "vclog") {
1235 string const data = "vc " +
1236 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1237 lyx_view_->getDialogs().show("log", data);
1239 lyx_view_->getDialogs().show(name, data);
1243 case LFUN_DIALOG_SHOW_NEW_INSET: {
1244 BOOST_ASSERT(lyx_view_);
1245 string const name = cmd.getArg(0);
1246 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1247 if (name == "bibitem" ||
1251 name == "nomenclature" ||
1255 InsetCommandParams p(name);
1256 data = InsetCommandMailer::params2string(name, p);
1257 } else if (name == "include") {
1258 InsetCommandParams p(data);
1259 data = InsetIncludeMailer::params2string(p);
1260 } else if (name == "box") {
1261 // \c data == "Boxed" || "Frameless" etc
1262 InsetBoxParams p(data);
1263 data = InsetBoxMailer::params2string(p);
1264 } else if (name == "branch") {
1265 InsetBranchParams p;
1266 data = InsetBranchMailer::params2string(p);
1267 } else if (name == "citation") {
1268 InsetCommandParams p("cite");
1269 data = InsetCommandMailer::params2string(name, p);
1270 } else if (name == "ert") {
1271 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1272 } else if (name == "external") {
1273 InsetExternalParams p;
1274 Buffer const & buffer = *lyx_view_->buffer();
1275 data = InsetExternalMailer::params2string(p, buffer);
1276 } else if (name == "float") {
1278 data = InsetFloatMailer::params2string(p);
1279 } else if (name == "graphics") {
1280 InsetGraphicsParams p;
1281 Buffer const & buffer = *lyx_view_->buffer();
1282 data = InsetGraphicsMailer::params2string(p, buffer);
1283 } else if (name == "note") {
1285 data = InsetNoteMailer::params2string(p);
1286 } else if (name == "vspace") {
1288 data = InsetVSpaceMailer::params2string(space);
1289 } else if (name == "wrap") {
1291 data = InsetWrapMailer::params2string(p);
1293 lyx_view_->getDialogs().show(name, data, 0);
1297 case LFUN_DIALOG_UPDATE: {
1298 BOOST_ASSERT(lyx_view_);
1299 string const & name = argument;
1300 // Can only update a dialog connected to an existing inset
1301 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1303 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1304 inset->dispatch(view()->cursor(), fr);
1305 } else if (name == "paragraph") {
1306 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1307 } else if (name == "prefs") {
1308 lyx_view_->getDialogs().update(name, string());
1313 case LFUN_DIALOG_HIDE:
1314 Dialogs::hide(argument, 0);
1317 case LFUN_DIALOG_DISCONNECT_INSET:
1318 BOOST_ASSERT(lyx_view_);
1319 lyx_view_->getDialogs().disconnect(argument);
1323 case LFUN_CITATION_INSERT: {
1324 BOOST_ASSERT(lyx_view_);
1325 if (!argument.empty()) {
1326 // we can have one optional argument, delimited by '|'
1327 // citation-insert <key>|<text_before>
1328 // this should be enhanced to also support text_after
1329 // and citation style
1330 string arg = argument;
1332 if (contains(argument, "|")) {
1333 arg = token(argument, '|', 0);
1334 opt1 = '[' + token(argument, '|', 1) + ']';
1336 std::ostringstream os;
1337 os << "citation LatexCommand\n"
1338 << "\\cite" << opt1 << "{" << arg << "}\n"
1340 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1343 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1347 case LFUN_BUFFER_CHILD_OPEN: {
1348 BOOST_ASSERT(lyx_view_);
1349 string const filename =
1350 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1351 // FIXME Should use bformat
1352 setMessage(_("Opening child document ") +
1353 makeDisplayPath(filename) + "...");
1354 view()->saveBookmark(false);
1355 string const parentfilename = lyx_view_->buffer()->fileName();
1356 if (theBufferList().exists(filename))
1357 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1359 lyx_view_->loadLyXFile(FileName(filename));
1360 // Set the parent name of the child document.
1361 // This makes insertion of citations and references in the child work,
1362 // when the target is in the parent or another child document.
1363 lyx_view_->buffer()->setParentName(parentfilename);
1367 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1368 BOOST_ASSERT(lyx_view_);
1369 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1372 case LFUN_KEYMAP_OFF:
1373 BOOST_ASSERT(lyx_view_);
1374 lyx_view_->view()->getIntl().keyMapOn(false);
1377 case LFUN_KEYMAP_PRIMARY:
1378 BOOST_ASSERT(lyx_view_);
1379 lyx_view_->view()->getIntl().keyMapPrim();
1382 case LFUN_KEYMAP_SECONDARY:
1383 BOOST_ASSERT(lyx_view_);
1384 lyx_view_->view()->getIntl().keyMapSec();
1387 case LFUN_KEYMAP_TOGGLE:
1388 BOOST_ASSERT(lyx_view_);
1389 lyx_view_->view()->getIntl().toggleKeyMap();
1395 string rest = split(argument, countstr, ' ');
1396 istringstream is(countstr);
1399 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1400 for (int i = 0; i < count; ++i)
1401 dispatch(lyxaction.lookupFunc(rest));
1405 case LFUN_COMMAND_SEQUENCE: {
1406 // argument contains ';'-terminated commands
1407 string arg = argument;
1408 while (!arg.empty()) {
1410 arg = split(arg, first, ';');
1411 FuncRequest func(lyxaction.lookupFunc(first));
1412 func.origin = cmd.origin;
1418 case LFUN_PREFERENCES_SAVE: {
1419 lyxrc.write(FileName(makeAbsPath("preferences",
1420 package().user_support())),
1425 case LFUN_SCREEN_FONT_UPDATE:
1426 BOOST_ASSERT(lyx_view_);
1427 // handle the screen font changes.
1428 lyxrc.set_font_norm_type();
1429 theFontLoader().update();
1430 // All visible buffers will need resize
1434 case LFUN_SET_COLOR: {
1436 string const x11_name = split(argument, lyx_name, ' ');
1437 if (lyx_name.empty() || x11_name.empty()) {
1438 setErrorMessage(_("Syntax: set-color <lyx_name>"
1443 bool const graphicsbg_changed =
1444 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1445 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1447 if (!lcolor.setColor(lyx_name, x11_name)) {
1449 bformat(_("Set-color \"%1$s\" failed "
1450 "- color is undefined or "
1451 "may not be redefined"),
1452 from_utf8(lyx_name)));
1456 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1458 if (graphicsbg_changed) {
1459 #ifdef WITH_WARNINGS
1460 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1463 graphics::GCache::get().changeDisplay(true);
1470 BOOST_ASSERT(lyx_view_);
1471 lyx_view_->message(from_utf8(argument));
1474 case LFUN_EXTERNAL_EDIT: {
1475 BOOST_ASSERT(lyx_view_);
1476 FuncRequest fr(action, argument);
1477 InsetExternal().dispatch(view()->cursor(), fr);
1481 case LFUN_GRAPHICS_EDIT: {
1482 FuncRequest fr(action, argument);
1483 InsetGraphics().dispatch(view()->cursor(), fr);
1487 case LFUN_INSET_APPLY: {
1488 BOOST_ASSERT(lyx_view_);
1489 string const name = cmd.getArg(0);
1490 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1492 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1493 inset->dispatch(view()->cursor(), fr);
1495 FuncRequest fr(LFUN_INSET_INSERT, argument);
1498 // ideally, the update flag should be set by the insets,
1499 // but this is not possible currently
1500 updateFlags = Update::Force | Update::FitCursor;
1504 case LFUN_ALL_INSETS_TOGGLE: {
1505 BOOST_ASSERT(lyx_view_);
1507 string const name = split(argument, action, ' ');
1508 InsetBase::Code const inset_code =
1509 InsetBase::translate(name);
1511 LCursor & cur = view()->cursor();
1512 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1514 InsetBase & inset = lyx_view_->buffer()->inset();
1515 InsetIterator it = inset_iterator_begin(inset);
1516 InsetIterator const end = inset_iterator_end(inset);
1517 for (; it != end; ++it) {
1518 if (inset_code == InsetBase::NO_CODE
1519 || inset_code == it->lyxCode()) {
1520 LCursor tmpcur = cur;
1521 tmpcur.pushLeft(*it);
1522 it->dispatch(tmpcur, fr);
1525 updateFlags = Update::Force | Update::FitCursor;
1529 case LFUN_BUFFER_LANGUAGE: {
1530 BOOST_ASSERT(lyx_view_);
1531 Buffer & buffer = *lyx_view_->buffer();
1532 Language const * oldL = buffer.params().language;
1533 Language const * newL = languages.getLanguage(argument);
1534 if (!newL || oldL == newL)
1537 if (oldL->rightToLeft() == newL->rightToLeft()
1538 && !buffer.isMultiLingual())
1539 buffer.changeLanguage(oldL, newL);
1541 buffer.updateDocLang(newL);
1545 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1546 string const fname =
1547 addName(addPath(package().user_support(), "templates/"),
1549 Buffer defaults(fname);
1551 istringstream ss(argument);
1554 int const unknown_tokens = defaults.readHeader(lex);
1556 if (unknown_tokens != 0) {
1557 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1558 << unknown_tokens << " unknown token"
1559 << (unknown_tokens == 1 ? "" : "s")
1563 if (defaults.writeFile(FileName(defaults.fileName())))
1564 // FIXME Should use bformat
1565 setMessage(_("Document defaults saved in ")
1566 + makeDisplayPath(fname));
1568 setErrorMessage(_("Unable to save document defaults"));
1572 case LFUN_BUFFER_PARAMS_APPLY: {
1573 BOOST_ASSERT(lyx_view_);
1574 biblio::CiteEngine const engine =
1575 lyx_view_->buffer()->params().cite_engine;
1577 istringstream ss(argument);
1580 int const unknown_tokens =
1581 lyx_view_->buffer()->readHeader(lex);
1583 if (unknown_tokens != 0) {
1584 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1585 << unknown_tokens << " unknown token"
1586 << (unknown_tokens == 1 ? "" : "s")
1589 if (engine == lyx_view_->buffer()->params().cite_engine)
1592 LCursor & cur = view()->cursor();
1593 FuncRequest fr(LFUN_INSET_REFRESH);
1595 InsetBase & inset = lyx_view_->buffer()->inset();
1596 InsetIterator it = inset_iterator_begin(inset);
1597 InsetIterator const end = inset_iterator_end(inset);
1598 for (; it != end; ++it)
1599 if (it->lyxCode() == InsetBase::CITE_CODE)
1600 it->dispatch(cur, fr);
1604 case LFUN_TEXTCLASS_APPLY: {
1605 BOOST_ASSERT(lyx_view_);
1606 Buffer * buffer = lyx_view_->buffer();
1608 textclass_type const old_class =
1609 buffer->params().textclass;
1611 loadTextclass(argument);
1613 std::pair<bool, textclass_type> const tc_pair =
1614 textclasslist.numberOfClass(argument);
1619 textclass_type const new_class = tc_pair.second;
1620 if (old_class == new_class)
1624 lyx_view_->message(_("Converting document to new document class..."));
1625 recordUndoFullDocument(view());
1626 buffer->params().textclass = new_class;
1627 StableDocIterator backcur(view()->cursor());
1628 ErrorList & el = buffer->errorList("Class Switch");
1629 cap::switchBetweenClasses(
1630 old_class, new_class,
1631 static_cast<InsetText &>(buffer->inset()), el);
1633 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1635 buffer->errors("Class Switch");
1636 updateLabels(*buffer);
1637 updateFlags = Update::Force | Update::FitCursor;
1641 case LFUN_TEXTCLASS_LOAD:
1642 loadTextclass(argument);
1645 case LFUN_LYXRC_APPLY: {
1646 LyXRC const lyxrc_orig = lyxrc;
1648 istringstream ss(argument);
1649 bool const success = lyxrc.read(ss) == 0;
1652 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1653 << "Unable to read lyxrc data"
1658 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1662 case LFUN_WINDOW_NEW:
1663 LyX::ref().newLyXView();
1666 case LFUN_WINDOW_CLOSE:
1667 BOOST_ASSERT(lyx_view_);
1668 BOOST_ASSERT(theApp());
1672 case LFUN_BOOKMARK_GOTO: {
1673 BOOST_ASSERT(lyx_view_);
1674 unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1675 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1676 BOOST_ASSERT(!bm.filename.empty());
1677 string const file = bm.filename.absFilename();
1678 // if the file is not opened, open it.
1679 if (!theBufferList().exists(file))
1680 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
1681 // open may fail, so we need to test it again
1682 if (theBufferList().exists(file)) {
1683 // if the current buffer is not that one, switch to it.
1684 if (lyx_view_->buffer()->fileName() != file)
1685 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
1686 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != file);
1687 view()->moveToPosition(bm.par_id, bm.par_pos);
1692 case LFUN_BOOKMARK_CLEAR:
1693 LyX::ref().session().bookmarks().clear();
1696 case LFUN_TOOLBAR_TOGGLE_STATE:
1697 lyx_view_->toggleToolbarState(argument);
1701 BOOST_ASSERT(lyx_view_);
1702 view()->cursor().dispatch(cmd);
1703 updateFlags = view()->cursor().result().update();
1704 if (!view()->cursor().result().dispatched())
1705 if (view()->dispatch(cmd))
1706 updateFlags = Update::Force | Update::FitCursor;
1711 if (lyx_view_ && view()->buffer()) {
1712 // BufferView::update() updates the ViewMetricsInfo and
1713 // also initializes the position cache for all insets in
1714 // (at least partially) visible top-level paragraphs.
1715 std::pair<bool, bool> needSecondUpdate
1716 = view()->update(updateFlags);
1718 // Redraw screen unless explicitly told otherwise.
1719 if (needSecondUpdate.first)
1720 // Buffer::changed() signals that a repaint is needed.
1721 // The frontend (WorkArea) knows which area to repaint
1722 // thanks to the ViewMetricsInfo updated above.
1723 view()->buffer()->changed();
1725 lyx_view_->updateStatusBar();
1727 // if we executed a mutating lfun, mark the buffer as dirty
1729 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1730 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1731 view()->buffer()->markDirty();
1733 if (view()->cursor().inTexted()) {
1734 lyx_view_->updateLayoutChoice();
1739 lyx_view_->updateMenubar();
1740 lyx_view_->updateToolbars();
1741 sendDispatchMessage(getMessage(), cmd);
1746 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1748 const bool verbose = (cmd.origin == FuncRequest::MENU
1749 || cmd.origin == FuncRequest::TOOLBAR
1750 || cmd.origin == FuncRequest::COMMANDBUFFER);
1752 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1753 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1755 lyx_view_->message(msg);
1759 docstring dispatch_msg = msg;
1760 if (!dispatch_msg.empty())
1761 dispatch_msg += ' ';
1763 string comname = lyxaction.getActionName(cmd.action);
1765 bool argsadded = false;
1767 if (!cmd.argument().empty()) {
1768 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1769 comname += ' ' + to_utf8(cmd.argument());
1774 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1776 if (!shortcuts.empty())
1777 comname += ": " + shortcuts;
1778 else if (!argsadded && !cmd.argument().empty())
1779 comname += ' ' + to_utf8(cmd.argument());
1781 if (!comname.empty()) {
1782 comname = rtrim(comname);
1783 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1786 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1787 << to_utf8(dispatch_msg) << endl;
1788 if (!dispatch_msg.empty())
1789 lyx_view_->message(dispatch_msg);
1793 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1795 // FIXME: initpath is not used. What to do?
1796 string initpath = lyxrc.document_path;
1797 string filename(name);
1799 if (view()->buffer()) {
1800 string const trypath = lyx_view_->buffer()->filePath();
1801 // If directory is writeable, use this as default.
1802 if (isDirWriteable(trypath))
1806 static int newfile_number;
1808 if (filename.empty()) {
1809 filename = addName(lyxrc.document_path,
1810 "newfile" + convert<string>(++newfile_number) + ".lyx");
1811 while (theBufferList().exists(filename) ||
1812 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1814 filename = addName(lyxrc.document_path,
1815 "newfile" + convert<string>(newfile_number) +
1820 // The template stuff
1823 FileDialog fileDlg(_("Select template file"),
1824 LFUN_SELECT_FILE_SYNC,
1825 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1826 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1828 FileDialog::Result result =
1829 fileDlg.open(from_utf8(lyxrc.template_path),
1830 FileFilterList(_("LyX Documents (*.lyx)")),
1833 if (result.first == FileDialog::Later)
1835 if (result.second.empty())
1837 templname = to_utf8(result.second);
1840 Buffer * const b = newFile(filename, templname, !name.empty());
1842 lyx_view_->setBuffer(b);
1846 void LyXFunc::open(string const & fname)
1848 string initpath = lyxrc.document_path;
1850 if (view()->buffer()) {
1851 string const trypath = lyx_view_->buffer()->filePath();
1852 // If directory is writeable, use this as default.
1853 if (isDirWriteable(trypath))
1859 if (fname.empty()) {
1860 FileDialog fileDlg(_("Select document to open"),
1862 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1863 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1865 FileDialog::Result result =
1866 fileDlg.open(from_utf8(initpath),
1867 FileFilterList(_("LyX Documents (*.lyx)")),
1870 if (result.first == FileDialog::Later)
1873 filename = to_utf8(result.second);
1875 // check selected filename
1876 if (filename.empty()) {
1877 lyx_view_->message(_("Canceled."));
1883 // get absolute path of file and add ".lyx" to the filename if
1885 FileName const fullname = fileSearch(string(), filename, "lyx");
1886 if (!fullname.empty())
1887 filename = fullname.absFilename();
1889 // if the file doesn't exist, let the user create one
1890 if (!fs::exists(fullname.toFilesystemEncoding())) {
1891 // the user specifically chose this name. Believe him.
1892 Buffer * const b = newFile(filename, string(), true);
1894 lyx_view_->setBuffer(b);
1898 docstring const disp_fn = makeDisplayPath(filename);
1899 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1902 if (lyx_view_->loadLyXFile(fullname)) {
1903 str2 = bformat(_("Document %1$s opened."), disp_fn);
1905 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1907 lyx_view_->message(str2);
1911 void LyXFunc::doImport(string const & argument)
1914 string filename = split(argument, format, ' ');
1916 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1917 << " file: " << filename << endl;
1919 // need user interaction
1920 if (filename.empty()) {
1921 string initpath = lyxrc.document_path;
1923 if (view()->buffer()) {
1924 string const trypath = lyx_view_->buffer()->filePath();
1925 // If directory is writeable, use this as default.
1926 if (isDirWriteable(trypath))
1930 docstring const text = bformat(_("Select %1$s file to import"),
1931 formats.prettyName(format));
1933 FileDialog fileDlg(text,
1935 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1936 make_pair(_("Examples|#E#e"),
1937 from_utf8(addPath(package().system_support(), "examples"))));
1939 docstring filter = formats.prettyName(format);
1942 filter += from_utf8(formats.extension(format));
1945 FileDialog::Result result =
1946 fileDlg.open(from_utf8(initpath),
1947 FileFilterList(filter),
1950 if (result.first == FileDialog::Later)
1953 filename = to_utf8(result.second);
1955 // check selected filename
1956 if (filename.empty())
1957 lyx_view_->message(_("Canceled."));
1960 if (filename.empty())
1963 // get absolute path of file
1964 FileName const fullname(makeAbsPath(filename));
1966 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
1968 // Check if the document already is open
1969 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
1970 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
1971 lyx_view_->message(_("Canceled."));
1976 // if the file exists already, and we didn't do
1977 // -i lyx thefile.lyx, warn
1978 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
1979 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
1981 docstring text = bformat(_("The document %1$s already exists.\n\n"
1982 "Do you want to over-write that document?"), file);
1983 int const ret = Alert::prompt(_("Over-write document?"),
1984 text, 0, 1, _("&Over-write"), _("&Cancel"));
1987 lyx_view_->message(_("Canceled."));
1992 ErrorList errorList;
1993 Importer::Import(lyx_view_, fullname, format, errorList);
1994 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1998 void LyXFunc::closeBuffer()
2000 // save current cursor position
2001 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2002 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2003 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2004 if (theBufferList().empty()) {
2005 // need this otherwise SEGV may occur while
2006 // trying to set variables that don't exist
2007 // since there's no current buffer
2008 lyx_view_->getDialogs().hideBufferDependent();
2010 lyx_view_->setBuffer(theBufferList().first());
2016 // Each "lyx_view_" should have it's own message method. lyxview and
2017 // the minibuffer would use the minibuffer, but lyxserver would
2018 // send an ERROR signal to its client. Alejandro 970603
2019 // This function is bit problematic when it comes to NLS, to make the
2020 // lyx servers client be language indepenent we must not translate
2021 // strings sent to this func.
2022 void LyXFunc::setErrorMessage(docstring const & m) const
2024 dispatch_buffer = m;
2029 void LyXFunc::setMessage(docstring const & m) const
2031 dispatch_buffer = m;
2035 string const LyXFunc::viewStatusMessage()
2037 // When meta-fake key is pressed, show the key sequence so far + "M-".
2039 return keyseq->print() + "M-";
2041 // Else, when a non-complete key sequence is pressed,
2042 // show the available options.
2043 if (keyseq->length() > 0 && !keyseq->deleted())
2044 return keyseq->printOptions();
2046 if (!view()->buffer())
2047 return to_utf8(_("Welcome to LyX!"));
2049 return view()->cursor().currentState();
2053 BufferView * LyXFunc::view() const
2055 BOOST_ASSERT(lyx_view_);
2056 return lyx_view_->view();
2060 bool LyXFunc::wasMetaKey() const
2062 return (meta_fake_bit != key_modifier::none);
2068 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2070 // Why the switch you might ask. It is a trick to ensure that all
2071 // the elements in the LyXRCTags enum is handled. As you can see
2072 // there are no breaks at all. So it is just a huge fall-through.
2073 // The nice thing is that we will get a warning from the compiler
2074 // if we forget an element.
2075 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2077 case LyXRC::RC_ACCEPT_COMPOUND:
2078 case LyXRC::RC_ALT_LANG:
2079 case LyXRC::RC_ASCIIROFF_COMMAND:
2080 case LyXRC::RC_ASCII_LINELEN:
2081 case LyXRC::RC_AUTOREGIONDELETE:
2082 case LyXRC::RC_AUTORESET_OPTIONS:
2083 case LyXRC::RC_AUTOSAVE:
2084 case LyXRC::RC_AUTO_NUMBER:
2085 case LyXRC::RC_BACKUPDIR_PATH:
2086 case LyXRC::RC_BIBTEX_COMMAND:
2087 case LyXRC::RC_BINDFILE:
2088 case LyXRC::RC_CHECKLASTFILES:
2089 case LyXRC::RC_USELASTFILEPOS:
2090 case LyXRC::RC_LOADSESSION:
2091 case LyXRC::RC_CHKTEX_COMMAND:
2092 case LyXRC::RC_CONVERTER:
2093 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2094 case LyXRC::RC_COPIER:
2095 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2096 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2097 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2098 case LyXRC::RC_DATE_INSERT_FORMAT:
2099 case LyXRC::RC_DEFAULT_LANGUAGE:
2100 case LyXRC::RC_DEFAULT_PAPERSIZE:
2101 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2102 case LyXRC::RC_DISPLAY_GRAPHICS:
2103 case LyXRC::RC_DOCUMENTPATH:
2104 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2105 string const encoded = FileName(
2106 lyxrc_new.document_path).toFilesystemEncoding();
2107 if (fs::exists(encoded) && fs::is_directory(encoded))
2108 support::package().document_dir() = lyxrc.document_path;
2110 case LyXRC::RC_ESC_CHARS:
2111 case LyXRC::RC_FONT_ENCODING:
2112 case LyXRC::RC_FORMAT:
2113 case LyXRC::RC_INDEX_COMMAND:
2114 case LyXRC::RC_INPUT:
2115 case LyXRC::RC_KBMAP:
2116 case LyXRC::RC_KBMAP_PRIMARY:
2117 case LyXRC::RC_KBMAP_SECONDARY:
2118 case LyXRC::RC_LABEL_INIT_LENGTH:
2119 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2120 case LyXRC::RC_LANGUAGE_AUTO_END:
2121 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2122 case LyXRC::RC_LANGUAGE_COMMAND_END:
2123 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2124 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2125 case LyXRC::RC_LANGUAGE_PACKAGE:
2126 case LyXRC::RC_LANGUAGE_USE_BABEL:
2127 case LyXRC::RC_MAKE_BACKUP:
2128 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2129 case LyXRC::RC_NUMLASTFILES:
2130 case LyXRC::RC_PATH_PREFIX:
2131 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2132 support::prependEnvPath("PATH", lyxrc.path_prefix);
2134 case LyXRC::RC_PERS_DICT:
2135 case LyXRC::RC_POPUP_BOLD_FONT:
2136 case LyXRC::RC_POPUP_FONT_ENCODING:
2137 case LyXRC::RC_POPUP_NORMAL_FONT:
2138 case LyXRC::RC_PREVIEW:
2139 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2140 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2141 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2142 case LyXRC::RC_PRINTCOPIESFLAG:
2143 case LyXRC::RC_PRINTER:
2144 case LyXRC::RC_PRINTEVENPAGEFLAG:
2145 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2146 case LyXRC::RC_PRINTFILEEXTENSION:
2147 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2148 case LyXRC::RC_PRINTODDPAGEFLAG:
2149 case LyXRC::RC_PRINTPAGERANGEFLAG:
2150 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2151 case LyXRC::RC_PRINTPAPERFLAG:
2152 case LyXRC::RC_PRINTREVERSEFLAG:
2153 case LyXRC::RC_PRINTSPOOL_COMMAND:
2154 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2155 case LyXRC::RC_PRINTTOFILE:
2156 case LyXRC::RC_PRINTTOPRINTER:
2157 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2158 case LyXRC::RC_PRINT_COMMAND:
2159 case LyXRC::RC_RTL_SUPPORT:
2160 case LyXRC::RC_SCREEN_DPI:
2161 case LyXRC::RC_SCREEN_FONT_ENCODING:
2162 case LyXRC::RC_SCREEN_FONT_ROMAN:
2163 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2164 case LyXRC::RC_SCREEN_FONT_SANS:
2165 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2166 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2167 case LyXRC::RC_SCREEN_FONT_SIZES:
2168 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2169 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2170 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2171 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2172 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2173 case LyXRC::RC_SCREEN_ZOOM:
2174 case LyXRC::RC_SERVERPIPE:
2175 case LyXRC::RC_SET_COLOR:
2176 case LyXRC::RC_SHOW_BANNER:
2177 case LyXRC::RC_SPELL_COMMAND:
2178 case LyXRC::RC_TEMPDIRPATH:
2179 case LyXRC::RC_TEMPLATEPATH:
2180 case LyXRC::RC_TEX_ALLOWS_SPACES:
2181 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2182 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2183 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2185 case LyXRC::RC_UIFILE:
2186 case LyXRC::RC_USER_EMAIL:
2187 case LyXRC::RC_USER_NAME:
2188 case LyXRC::RC_USETEMPDIR:
2189 case LyXRC::RC_USE_ALT_LANG:
2190 case LyXRC::RC_USE_CONVERTER_CACHE:
2191 case LyXRC::RC_USE_ESC_CHARS:
2192 case LyXRC::RC_USE_INP_ENC:
2193 case LyXRC::RC_USE_PERS_DICT:
2194 case LyXRC::RC_USE_SPELL_LIB:
2195 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2196 case LyXRC::RC_VIEWER:
2197 case LyXRC::RC_LAST: