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 << to_utf8(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(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 docstring last_search;
768 docstring searched_string;
770 if (!cmd.argument().empty()) {
771 last_search = cmd.argument();
772 searched_string = cmd.argument();
774 searched_string = last_search;
777 if (searched_string.empty())
780 bool const fw = action == LFUN_WORD_FIND_FORWARD;
781 docstring const data =
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(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(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 // quitting is triggered by the gui code
1038 // (leaving the event loop).
1039 if (theBufferList().quitWriteAll())
1040 theApp()->gui().closeAllViews();
1043 case LFUN_TOC_VIEW: {
1044 BOOST_ASSERT(lyx_view_);
1045 InsetCommandParams p("tableofcontents");
1046 string const data = InsetCommandMailer::params2string("toc", p);
1047 lyx_view_->getDialogs().show("toc", data, 0);
1051 case LFUN_BUFFER_AUTO_SAVE:
1055 case LFUN_RECONFIGURE:
1056 BOOST_ASSERT(lyx_view_);
1057 reconfigure(*lyx_view_);
1060 case LFUN_HELP_OPEN: {
1061 BOOST_ASSERT(lyx_view_);
1062 string const arg = argument;
1064 setErrorMessage(_("Missing argument"));
1067 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1068 if (fname.empty()) {
1069 lyxerr << "LyX: unable to find documentation file `"
1070 << arg << "'. Bad installation?" << endl;
1073 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1074 makeDisplayPath(fname.absFilename())));
1075 lyx_view_->loadLyXFile(fname, false);
1079 // --- version control -------------------------------
1080 case LFUN_VC_REGISTER:
1081 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1082 if (!ensureBufferClean(view()))
1084 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1085 lyx_view_->buffer()->lyxvc().registrer();
1090 case LFUN_VC_CHECK_IN:
1091 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1092 if (!ensureBufferClean(view()))
1094 if (lyx_view_->buffer()->lyxvc().inUse()
1095 && !lyx_view_->buffer()->isReadonly()) {
1096 lyx_view_->buffer()->lyxvc().checkIn();
1101 case LFUN_VC_CHECK_OUT:
1102 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1103 if (!ensureBufferClean(view()))
1105 if (lyx_view_->buffer()->lyxvc().inUse()
1106 && lyx_view_->buffer()->isReadonly()) {
1107 lyx_view_->buffer()->lyxvc().checkOut();
1112 case LFUN_VC_REVERT:
1113 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1114 lyx_view_->buffer()->lyxvc().revert();
1118 case LFUN_VC_UNDO_LAST:
1119 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1120 lyx_view_->buffer()->lyxvc().undoLast();
1124 // --- buffers ----------------------------------------
1125 case LFUN_BUFFER_SWITCH:
1126 BOOST_ASSERT(lyx_view_);
1127 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1130 case LFUN_BUFFER_NEXT:
1131 BOOST_ASSERT(lyx_view_);
1132 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1135 case LFUN_BUFFER_PREVIOUS:
1136 BOOST_ASSERT(lyx_view_);
1137 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1141 BOOST_ASSERT(lyx_view_);
1142 newFile(view(), argument);
1145 case LFUN_FILE_OPEN:
1146 BOOST_ASSERT(lyx_view_);
1150 case LFUN_DROP_LAYOUTS_CHOICE:
1151 BOOST_ASSERT(lyx_view_);
1152 lyx_view_->getToolbars().openLayoutList();
1155 case LFUN_MENU_OPEN:
1156 BOOST_ASSERT(lyx_view_);
1157 lyx_view_->getMenubar().openByName(from_utf8(argument));
1160 // --- lyxserver commands ----------------------------
1161 case LFUN_SERVER_GET_NAME:
1162 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1163 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1164 lyxerr[Debug::INFO] << "FNAME["
1165 << lyx_view_->buffer()->fileName()
1169 case LFUN_SERVER_NOTIFY:
1170 dispatch_buffer = keyseq->print();
1171 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1174 case LFUN_SERVER_GOTO_FILE_ROW: {
1175 BOOST_ASSERT(lyx_view_);
1178 istringstream is(argument);
1179 is >> file_name >> row;
1180 if (prefixIs(file_name, package().temp_dir())) {
1181 // Needed by inverse dvi search. If it is a file
1182 // in tmpdir, call the apropriated function
1183 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1185 // Must replace extension of the file to be .lyx
1186 // and get full path
1187 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1188 // Either change buffer or load the file
1189 if (theBufferList().exists(s.absFilename())) {
1190 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1192 lyx_view_->loadLyXFile(s);
1196 view()->setCursorFromRow(row);
1199 // see BufferView::center()
1203 case LFUN_DIALOG_SHOW: {
1204 BOOST_ASSERT(lyx_view_);
1205 string const name = cmd.getArg(0);
1206 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1208 if (name == "character") {
1209 data = freefont2string();
1211 lyx_view_->getDialogs().show("character", data);
1212 } else if (name == "latexlog") {
1213 pair<Buffer::LogType, string> const logfile =
1214 lyx_view_->buffer()->getLogName();
1215 switch (logfile.first) {
1216 case Buffer::latexlog:
1219 case Buffer::buildlog:
1223 data += LyXLex::quoteString(logfile.second);
1224 lyx_view_->getDialogs().show("log", data);
1225 } else if (name == "vclog") {
1226 string const data = "vc " +
1227 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1228 lyx_view_->getDialogs().show("log", data);
1230 lyx_view_->getDialogs().show(name, data);
1234 case LFUN_DIALOG_SHOW_NEW_INSET: {
1235 BOOST_ASSERT(lyx_view_);
1236 string const name = cmd.getArg(0);
1237 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1238 if (name == "bibitem" ||
1242 name == "nomenclature" ||
1246 InsetCommandParams p(name);
1247 data = InsetCommandMailer::params2string(name, p);
1248 } else if (name == "include") {
1249 // data is the include type: one of "include",
1250 // "input", "verbatiminput" or "verbatiminput*"
1252 // default type is requested
1254 InsetCommandParams p(data);
1255 data = InsetIncludeMailer::params2string(p);
1256 } else if (name == "box") {
1257 // \c data == "Boxed" || "Frameless" etc
1258 InsetBoxParams p(data);
1259 data = InsetBoxMailer::params2string(p);
1260 } else if (name == "branch") {
1261 InsetBranchParams p;
1262 data = InsetBranchMailer::params2string(p);
1263 } else if (name == "citation") {
1264 InsetCommandParams p("cite");
1265 data = InsetCommandMailer::params2string(name, p);
1266 } else if (name == "ert") {
1267 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1268 } else if (name == "external") {
1269 InsetExternalParams p;
1270 Buffer const & buffer = *lyx_view_->buffer();
1271 data = InsetExternalMailer::params2string(p, buffer);
1272 } else if (name == "float") {
1274 data = InsetFloatMailer::params2string(p);
1275 } else if (name == "graphics") {
1276 InsetGraphicsParams p;
1277 Buffer const & buffer = *lyx_view_->buffer();
1278 data = InsetGraphicsMailer::params2string(p, buffer);
1279 } else if (name == "note") {
1281 data = InsetNoteMailer::params2string(p);
1282 } else if (name == "vspace") {
1284 data = InsetVSpaceMailer::params2string(space);
1285 } else if (name == "wrap") {
1287 data = InsetWrapMailer::params2string(p);
1289 lyx_view_->getDialogs().show(name, data, 0);
1293 case LFUN_DIALOG_UPDATE: {
1294 BOOST_ASSERT(lyx_view_);
1295 string const & name = argument;
1296 // Can only update a dialog connected to an existing inset
1297 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1299 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1300 inset->dispatch(view()->cursor(), fr);
1301 } else if (name == "paragraph") {
1302 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1303 } else if (name == "prefs") {
1304 lyx_view_->getDialogs().update(name, string());
1309 case LFUN_DIALOG_HIDE:
1310 Dialogs::hide(argument, 0);
1313 case LFUN_DIALOG_DISCONNECT_INSET:
1314 BOOST_ASSERT(lyx_view_);
1315 lyx_view_->getDialogs().disconnect(argument);
1319 case LFUN_CITATION_INSERT: {
1320 BOOST_ASSERT(lyx_view_);
1321 if (!argument.empty()) {
1322 // we can have one optional argument, delimited by '|'
1323 // citation-insert <key>|<text_before>
1324 // this should be enhanced to also support text_after
1325 // and citation style
1326 string arg = argument;
1328 if (contains(argument, "|")) {
1329 arg = token(argument, '|', 0);
1330 opt1 = '[' + token(argument, '|', 1) + ']';
1332 std::ostringstream os;
1333 os << "citation LatexCommand\n"
1334 << "\\cite" << opt1 << "{" << arg << "}\n"
1336 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1339 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1343 case LFUN_BUFFER_CHILD_OPEN: {
1344 BOOST_ASSERT(lyx_view_);
1345 string const filename =
1346 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1347 // FIXME Should use bformat
1348 setMessage(_("Opening child document ") +
1349 makeDisplayPath(filename) + "...");
1350 view()->saveBookmark(false);
1351 string const parentfilename = lyx_view_->buffer()->fileName();
1352 if (theBufferList().exists(filename))
1353 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1355 lyx_view_->loadLyXFile(FileName(filename));
1356 // Set the parent name of the child document.
1357 // This makes insertion of citations and references in the child work,
1358 // when the target is in the parent or another child document.
1359 lyx_view_->buffer()->setParentName(parentfilename);
1363 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1364 BOOST_ASSERT(lyx_view_);
1365 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1368 case LFUN_KEYMAP_OFF:
1369 BOOST_ASSERT(lyx_view_);
1370 lyx_view_->view()->getIntl().keyMapOn(false);
1373 case LFUN_KEYMAP_PRIMARY:
1374 BOOST_ASSERT(lyx_view_);
1375 lyx_view_->view()->getIntl().keyMapPrim();
1378 case LFUN_KEYMAP_SECONDARY:
1379 BOOST_ASSERT(lyx_view_);
1380 lyx_view_->view()->getIntl().keyMapSec();
1383 case LFUN_KEYMAP_TOGGLE:
1384 BOOST_ASSERT(lyx_view_);
1385 lyx_view_->view()->getIntl().toggleKeyMap();
1391 string rest = split(argument, countstr, ' ');
1392 istringstream is(countstr);
1395 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1396 for (int i = 0; i < count; ++i)
1397 dispatch(lyxaction.lookupFunc(rest));
1401 case LFUN_COMMAND_SEQUENCE: {
1402 // argument contains ';'-terminated commands
1403 string arg = argument;
1404 while (!arg.empty()) {
1406 arg = split(arg, first, ';');
1407 FuncRequest func(lyxaction.lookupFunc(first));
1408 func.origin = cmd.origin;
1414 case LFUN_PREFERENCES_SAVE: {
1415 lyxrc.write(FileName(makeAbsPath("preferences",
1416 package().user_support())),
1421 case LFUN_SCREEN_FONT_UPDATE:
1422 BOOST_ASSERT(lyx_view_);
1423 // handle the screen font changes.
1424 lyxrc.set_font_norm_type();
1425 theFontLoader().update();
1426 // All visible buffers will need resize
1430 case LFUN_SET_COLOR: {
1432 string const x11_name = split(argument, lyx_name, ' ');
1433 if (lyx_name.empty() || x11_name.empty()) {
1434 setErrorMessage(_("Syntax: set-color <lyx_name>"
1439 bool const graphicsbg_changed =
1440 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1441 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1443 if (!lcolor.setColor(lyx_name, x11_name)) {
1445 bformat(_("Set-color \"%1$s\" failed "
1446 "- color is undefined or "
1447 "may not be redefined"),
1448 from_utf8(lyx_name)));
1452 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1454 if (graphicsbg_changed) {
1455 #ifdef WITH_WARNINGS
1456 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1459 graphics::GCache::get().changeDisplay(true);
1466 BOOST_ASSERT(lyx_view_);
1467 lyx_view_->message(from_utf8(argument));
1470 case LFUN_EXTERNAL_EDIT: {
1471 BOOST_ASSERT(lyx_view_);
1472 FuncRequest fr(action, argument);
1473 InsetExternal().dispatch(view()->cursor(), fr);
1477 case LFUN_GRAPHICS_EDIT: {
1478 FuncRequest fr(action, argument);
1479 InsetGraphics().dispatch(view()->cursor(), fr);
1483 case LFUN_INSET_APPLY: {
1484 BOOST_ASSERT(lyx_view_);
1485 string const name = cmd.getArg(0);
1486 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1488 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1489 inset->dispatch(view()->cursor(), fr);
1491 FuncRequest fr(LFUN_INSET_INSERT, argument);
1494 // ideally, the update flag should be set by the insets,
1495 // but this is not possible currently
1496 updateFlags = Update::Force | Update::FitCursor;
1500 case LFUN_ALL_INSETS_TOGGLE: {
1501 BOOST_ASSERT(lyx_view_);
1503 string const name = split(argument, action, ' ');
1504 InsetBase::Code const inset_code =
1505 InsetBase::translate(name);
1507 LCursor & cur = view()->cursor();
1508 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1510 InsetBase & inset = lyx_view_->buffer()->inset();
1511 InsetIterator it = inset_iterator_begin(inset);
1512 InsetIterator const end = inset_iterator_end(inset);
1513 for (; it != end; ++it) {
1514 if (inset_code == InsetBase::NO_CODE
1515 || inset_code == it->lyxCode()) {
1516 LCursor tmpcur = cur;
1517 tmpcur.pushLeft(*it);
1518 it->dispatch(tmpcur, fr);
1521 updateFlags = Update::Force | Update::FitCursor;
1525 case LFUN_BUFFER_LANGUAGE: {
1526 BOOST_ASSERT(lyx_view_);
1527 Buffer & buffer = *lyx_view_->buffer();
1528 Language const * oldL = buffer.params().language;
1529 Language const * newL = languages.getLanguage(argument);
1530 if (!newL || oldL == newL)
1533 if (oldL->rightToLeft() == newL->rightToLeft()
1534 && !buffer.isMultiLingual())
1535 buffer.changeLanguage(oldL, newL);
1537 buffer.updateDocLang(newL);
1541 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1542 string const fname =
1543 addName(addPath(package().user_support(), "templates/"),
1545 Buffer defaults(fname);
1547 istringstream ss(argument);
1550 int const unknown_tokens = defaults.readHeader(lex);
1552 if (unknown_tokens != 0) {
1553 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1554 << unknown_tokens << " unknown token"
1555 << (unknown_tokens == 1 ? "" : "s")
1559 if (defaults.writeFile(FileName(defaults.fileName())))
1560 // FIXME Should use bformat
1561 setMessage(_("Document defaults saved in ")
1562 + makeDisplayPath(fname));
1564 setErrorMessage(_("Unable to save document defaults"));
1568 case LFUN_BUFFER_PARAMS_APPLY: {
1569 BOOST_ASSERT(lyx_view_);
1570 biblio::CiteEngine const engine =
1571 lyx_view_->buffer()->params().cite_engine;
1573 istringstream ss(argument);
1576 int const unknown_tokens =
1577 lyx_view_->buffer()->readHeader(lex);
1579 if (unknown_tokens != 0) {
1580 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1581 << unknown_tokens << " unknown token"
1582 << (unknown_tokens == 1 ? "" : "s")
1585 if (engine == lyx_view_->buffer()->params().cite_engine)
1588 LCursor & cur = view()->cursor();
1589 FuncRequest fr(LFUN_INSET_REFRESH);
1591 InsetBase & inset = lyx_view_->buffer()->inset();
1592 InsetIterator it = inset_iterator_begin(inset);
1593 InsetIterator const end = inset_iterator_end(inset);
1594 for (; it != end; ++it)
1595 if (it->lyxCode() == InsetBase::CITE_CODE)
1596 it->dispatch(cur, fr);
1600 case LFUN_TEXTCLASS_APPLY: {
1601 BOOST_ASSERT(lyx_view_);
1602 Buffer * buffer = lyx_view_->buffer();
1604 textclass_type const old_class =
1605 buffer->params().textclass;
1607 loadTextclass(argument);
1609 std::pair<bool, textclass_type> const tc_pair =
1610 textclasslist.numberOfClass(argument);
1615 textclass_type const new_class = tc_pair.second;
1616 if (old_class == new_class)
1620 lyx_view_->message(_("Converting document to new document class..."));
1621 recordUndoFullDocument(view());
1622 buffer->params().textclass = new_class;
1623 StableDocIterator backcur(view()->cursor());
1624 ErrorList & el = buffer->errorList("Class Switch");
1625 cap::switchBetweenClasses(
1626 old_class, new_class,
1627 static_cast<InsetText &>(buffer->inset()), el);
1629 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1631 buffer->errors("Class Switch");
1632 updateLabels(*buffer);
1633 updateFlags = Update::Force | Update::FitCursor;
1637 case LFUN_TEXTCLASS_LOAD:
1638 loadTextclass(argument);
1641 case LFUN_LYXRC_APPLY: {
1642 LyXRC const lyxrc_orig = lyxrc;
1644 istringstream ss(argument);
1645 bool const success = lyxrc.read(ss) == 0;
1648 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1649 << "Unable to read lyxrc data"
1654 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1658 case LFUN_WINDOW_NEW:
1659 LyX::ref().newLyXView();
1662 case LFUN_WINDOW_CLOSE:
1663 BOOST_ASSERT(lyx_view_);
1664 BOOST_ASSERT(theApp());
1665 // ask the user for saving changes or cancel quit
1666 if (!theBufferList().quitWriteAll())
1671 case LFUN_BOOKMARK_GOTO: {
1672 BOOST_ASSERT(lyx_view_);
1673 unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1674 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1675 BOOST_ASSERT(!bm.filename.empty());
1676 string const file = bm.filename.absFilename();
1677 // if the file is not opened, open it.
1678 if (!theBufferList().exists(file))
1679 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
1680 // open may fail, so we need to test it again
1681 if (theBufferList().exists(file)) {
1682 // if the current buffer is not that one, switch to it.
1683 if (lyx_view_->buffer()->fileName() != file)
1684 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
1685 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != file);
1686 view()->moveToPosition(bm.par_id, bm.par_pos);
1691 case LFUN_BOOKMARK_CLEAR:
1692 LyX::ref().session().bookmarks().clear();
1695 case LFUN_TOOLBAR_TOGGLE_STATE:
1696 lyx_view_->toggleToolbarState(argument);
1700 BOOST_ASSERT(lyx_view_);
1701 view()->cursor().dispatch(cmd);
1702 updateFlags = view()->cursor().result().update();
1703 if (!view()->cursor().result().dispatched())
1704 if (view()->dispatch(cmd))
1705 updateFlags = Update::Force | Update::FitCursor;
1710 if (lyx_view_ && view()->buffer()) {
1711 // BufferView::update() updates the ViewMetricsInfo and
1712 // also initializes the position cache for all insets in
1713 // (at least partially) visible top-level paragraphs.
1714 // We will redraw the screen only if needed.
1715 if (view()->update(updateFlags)) {
1716 // Buffer::changed() signals that a repaint is needed.
1717 // The frontend (WorkArea) knows which area to repaint
1718 // thanks to the ViewMetricsInfo updated above.
1719 view()->buffer()->changed();
1722 lyx_view_->updateStatusBar();
1724 // if we executed a mutating lfun, mark the buffer as dirty
1726 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1727 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1728 view()->buffer()->markDirty();
1730 if (view()->cursor().inTexted()) {
1731 lyx_view_->updateLayoutChoice();
1736 lyx_view_->updateMenubar();
1737 lyx_view_->updateToolbars();
1738 sendDispatchMessage(getMessage(), cmd);
1743 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1745 const bool verbose = (cmd.origin == FuncRequest::MENU
1746 || cmd.origin == FuncRequest::TOOLBAR
1747 || cmd.origin == FuncRequest::COMMANDBUFFER);
1749 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1750 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1752 lyx_view_->message(msg);
1756 docstring dispatch_msg = msg;
1757 if (!dispatch_msg.empty())
1758 dispatch_msg += ' ';
1760 string comname = lyxaction.getActionName(cmd.action);
1762 bool argsadded = false;
1764 if (!cmd.argument().empty()) {
1765 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1766 comname += ' ' + to_utf8(cmd.argument());
1771 string const shortcuts = to_utf8(theTopLevelKeymap().printbindings(cmd));
1773 if (!shortcuts.empty())
1774 comname += ": " + shortcuts;
1775 else if (!argsadded && !cmd.argument().empty())
1776 comname += ' ' + to_utf8(cmd.argument());
1778 if (!comname.empty()) {
1779 comname = rtrim(comname);
1780 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1783 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1784 << to_utf8(dispatch_msg) << endl;
1785 if (!dispatch_msg.empty())
1786 lyx_view_->message(dispatch_msg);
1790 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1792 // FIXME: initpath is not used. What to do?
1793 string initpath = lyxrc.document_path;
1794 string filename(name);
1796 if (view()->buffer()) {
1797 string const trypath = lyx_view_->buffer()->filePath();
1798 // If directory is writeable, use this as default.
1799 if (isDirWriteable(trypath))
1803 static int newfile_number;
1805 if (filename.empty()) {
1806 filename = addName(lyxrc.document_path,
1807 "newfile" + convert<string>(++newfile_number) + ".lyx");
1808 while (theBufferList().exists(filename) ||
1809 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1811 filename = addName(lyxrc.document_path,
1812 "newfile" + convert<string>(newfile_number) +
1817 // The template stuff
1820 FileDialog fileDlg(_("Select template file"),
1821 LFUN_SELECT_FILE_SYNC,
1822 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1823 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1825 FileDialog::Result result =
1826 fileDlg.open(from_utf8(lyxrc.template_path),
1827 FileFilterList(_("LyX Documents (*.lyx)")),
1830 if (result.first == FileDialog::Later)
1832 if (result.second.empty())
1834 templname = to_utf8(result.second);
1837 Buffer * const b = newFile(filename, templname, !name.empty());
1839 lyx_view_->setBuffer(b);
1843 void LyXFunc::open(string const & fname)
1845 string initpath = lyxrc.document_path;
1847 if (view()->buffer()) {
1848 string const trypath = lyx_view_->buffer()->filePath();
1849 // If directory is writeable, use this as default.
1850 if (isDirWriteable(trypath))
1856 if (fname.empty()) {
1857 FileDialog fileDlg(_("Select document to open"),
1859 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1860 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1862 FileDialog::Result result =
1863 fileDlg.open(from_utf8(initpath),
1864 FileFilterList(_("LyX Documents (*.lyx)")),
1867 if (result.first == FileDialog::Later)
1870 filename = to_utf8(result.second);
1872 // check selected filename
1873 if (filename.empty()) {
1874 lyx_view_->message(_("Canceled."));
1880 // get absolute path of file and add ".lyx" to the filename if
1882 FileName const fullname = fileSearch(string(), filename, "lyx");
1883 if (!fullname.empty())
1884 filename = fullname.absFilename();
1886 // if the file doesn't exist, let the user create one
1887 if (!fs::exists(fullname.toFilesystemEncoding())) {
1888 // the user specifically chose this name. Believe him.
1889 Buffer * const b = newFile(filename, string(), true);
1891 lyx_view_->setBuffer(b);
1895 docstring const disp_fn = makeDisplayPath(filename);
1896 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1899 if (lyx_view_->loadLyXFile(fullname)) {
1900 str2 = bformat(_("Document %1$s opened."), disp_fn);
1902 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1904 lyx_view_->message(str2);
1908 void LyXFunc::doImport(string const & argument)
1911 string filename = split(argument, format, ' ');
1913 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1914 << " file: " << filename << endl;
1916 // need user interaction
1917 if (filename.empty()) {
1918 string initpath = lyxrc.document_path;
1920 if (view()->buffer()) {
1921 string const trypath = lyx_view_->buffer()->filePath();
1922 // If directory is writeable, use this as default.
1923 if (isDirWriteable(trypath))
1927 docstring const text = bformat(_("Select %1$s file to import"),
1928 formats.prettyName(format));
1930 FileDialog fileDlg(text,
1932 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1933 make_pair(_("Examples|#E#e"),
1934 from_utf8(addPath(package().system_support(), "examples"))));
1936 docstring filter = formats.prettyName(format);
1939 filter += from_utf8(formats.extension(format));
1942 FileDialog::Result result =
1943 fileDlg.open(from_utf8(initpath),
1944 FileFilterList(filter),
1947 if (result.first == FileDialog::Later)
1950 filename = to_utf8(result.second);
1952 // check selected filename
1953 if (filename.empty())
1954 lyx_view_->message(_("Canceled."));
1957 if (filename.empty())
1960 // get absolute path of file
1961 FileName const fullname(makeAbsPath(filename));
1963 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
1965 // Check if the document already is open
1966 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
1967 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
1968 lyx_view_->message(_("Canceled."));
1973 // if the file exists already, and we didn't do
1974 // -i lyx thefile.lyx, warn
1975 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
1976 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
1978 docstring text = bformat(_("The document %1$s already exists.\n\n"
1979 "Do you want to over-write that document?"), file);
1980 int const ret = Alert::prompt(_("Over-write document?"),
1981 text, 0, 1, _("&Over-write"), _("&Cancel"));
1984 lyx_view_->message(_("Canceled."));
1989 ErrorList errorList;
1990 Importer::Import(lyx_view_, fullname, format, errorList);
1991 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1995 void LyXFunc::closeBuffer()
1997 // save current cursor position
1998 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
1999 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2000 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2001 if (theBufferList().empty()) {
2002 // need this otherwise SEGV may occur while
2003 // trying to set variables that don't exist
2004 // since there's no current buffer
2005 lyx_view_->getDialogs().hideBufferDependent();
2007 lyx_view_->setBuffer(theBufferList().first());
2013 // Each "lyx_view_" should have it's own message method. lyxview and
2014 // the minibuffer would use the minibuffer, but lyxserver would
2015 // send an ERROR signal to its client. Alejandro 970603
2016 // This function is bit problematic when it comes to NLS, to make the
2017 // lyx servers client be language indepenent we must not translate
2018 // strings sent to this func.
2019 void LyXFunc::setErrorMessage(docstring const & m) const
2021 dispatch_buffer = m;
2026 void LyXFunc::setMessage(docstring const & m) const
2028 dispatch_buffer = m;
2032 string const LyXFunc::viewStatusMessage()
2034 // When meta-fake key is pressed, show the key sequence so far + "M-".
2036 return to_utf8(keyseq->print() + "M-");
2038 // Else, when a non-complete key sequence is pressed,
2039 // show the available options.
2040 if (keyseq->length() > 0 && !keyseq->deleted())
2041 return to_utf8(keyseq->printOptions());
2043 if (!view()->buffer())
2044 return to_utf8(_("Welcome to LyX!"));
2046 return view()->cursor().currentState();
2050 BufferView * LyXFunc::view() const
2052 BOOST_ASSERT(lyx_view_);
2053 return lyx_view_->view();
2057 bool LyXFunc::wasMetaKey() const
2059 return (meta_fake_bit != key_modifier::none);
2065 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2067 // Why the switch you might ask. It is a trick to ensure that all
2068 // the elements in the LyXRCTags enum is handled. As you can see
2069 // there are no breaks at all. So it is just a huge fall-through.
2070 // The nice thing is that we will get a warning from the compiler
2071 // if we forget an element.
2072 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2074 case LyXRC::RC_ACCEPT_COMPOUND:
2075 case LyXRC::RC_ALT_LANG:
2076 case LyXRC::RC_ASCIIROFF_COMMAND:
2077 case LyXRC::RC_ASCII_LINELEN:
2078 case LyXRC::RC_AUTOREGIONDELETE:
2079 case LyXRC::RC_AUTORESET_OPTIONS:
2080 case LyXRC::RC_AUTOSAVE:
2081 case LyXRC::RC_AUTO_NUMBER:
2082 case LyXRC::RC_BACKUPDIR_PATH:
2083 case LyXRC::RC_BIBTEX_COMMAND:
2084 case LyXRC::RC_BINDFILE:
2085 case LyXRC::RC_CHECKLASTFILES:
2086 case LyXRC::RC_USELASTFILEPOS:
2087 case LyXRC::RC_LOADSESSION:
2088 case LyXRC::RC_CHKTEX_COMMAND:
2089 case LyXRC::RC_CONVERTER:
2090 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2091 case LyXRC::RC_COPIER:
2092 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2093 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2094 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2095 case LyXRC::RC_DATE_INSERT_FORMAT:
2096 case LyXRC::RC_DEFAULT_LANGUAGE:
2097 case LyXRC::RC_DEFAULT_PAPERSIZE:
2098 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2099 case LyXRC::RC_DISPLAY_GRAPHICS:
2100 case LyXRC::RC_DOCUMENTPATH:
2101 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2102 string const encoded = FileName(
2103 lyxrc_new.document_path).toFilesystemEncoding();
2104 if (fs::exists(encoded) && fs::is_directory(encoded))
2105 support::package().document_dir() = lyxrc.document_path;
2107 case LyXRC::RC_ESC_CHARS:
2108 case LyXRC::RC_FONT_ENCODING:
2109 case LyXRC::RC_FORMAT:
2110 case LyXRC::RC_INDEX_COMMAND:
2111 case LyXRC::RC_INPUT:
2112 case LyXRC::RC_KBMAP:
2113 case LyXRC::RC_KBMAP_PRIMARY:
2114 case LyXRC::RC_KBMAP_SECONDARY:
2115 case LyXRC::RC_LABEL_INIT_LENGTH:
2116 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2117 case LyXRC::RC_LANGUAGE_AUTO_END:
2118 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2119 case LyXRC::RC_LANGUAGE_COMMAND_END:
2120 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2121 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2122 case LyXRC::RC_LANGUAGE_PACKAGE:
2123 case LyXRC::RC_LANGUAGE_USE_BABEL:
2124 case LyXRC::RC_MAKE_BACKUP:
2125 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2126 case LyXRC::RC_NUMLASTFILES:
2127 case LyXRC::RC_PATH_PREFIX:
2128 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2129 support::prependEnvPath("PATH", lyxrc.path_prefix);
2131 case LyXRC::RC_PERS_DICT:
2132 case LyXRC::RC_POPUP_BOLD_FONT:
2133 case LyXRC::RC_POPUP_FONT_ENCODING:
2134 case LyXRC::RC_POPUP_NORMAL_FONT:
2135 case LyXRC::RC_PREVIEW:
2136 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2137 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2138 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2139 case LyXRC::RC_PRINTCOPIESFLAG:
2140 case LyXRC::RC_PRINTER:
2141 case LyXRC::RC_PRINTEVENPAGEFLAG:
2142 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2143 case LyXRC::RC_PRINTFILEEXTENSION:
2144 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2145 case LyXRC::RC_PRINTODDPAGEFLAG:
2146 case LyXRC::RC_PRINTPAGERANGEFLAG:
2147 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2148 case LyXRC::RC_PRINTPAPERFLAG:
2149 case LyXRC::RC_PRINTREVERSEFLAG:
2150 case LyXRC::RC_PRINTSPOOL_COMMAND:
2151 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2152 case LyXRC::RC_PRINTTOFILE:
2153 case LyXRC::RC_PRINTTOPRINTER:
2154 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2155 case LyXRC::RC_PRINT_COMMAND:
2156 case LyXRC::RC_RTL_SUPPORT:
2157 case LyXRC::RC_SCREEN_DPI:
2158 case LyXRC::RC_SCREEN_FONT_ENCODING:
2159 case LyXRC::RC_SCREEN_FONT_ROMAN:
2160 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2161 case LyXRC::RC_SCREEN_FONT_SANS:
2162 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2163 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2164 case LyXRC::RC_SCREEN_FONT_SIZES:
2165 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2166 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2167 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2168 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2169 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2170 case LyXRC::RC_SCREEN_ZOOM:
2171 case LyXRC::RC_SERVERPIPE:
2172 case LyXRC::RC_SET_COLOR:
2173 case LyXRC::RC_SHOW_BANNER:
2174 case LyXRC::RC_SPELL_COMMAND:
2175 case LyXRC::RC_TEMPDIRPATH:
2176 case LyXRC::RC_TEMPLATEPATH:
2177 case LyXRC::RC_TEX_ALLOWS_SPACES:
2178 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2179 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2180 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2182 case LyXRC::RC_UIFILE:
2183 case LyXRC::RC_USER_EMAIL:
2184 case LyXRC::RC_USER_NAME:
2185 case LyXRC::RC_USETEMPDIR:
2186 case LyXRC::RC_USE_ALT_LANG:
2187 case LyXRC::RC_USE_CONVERTER_CACHE:
2188 case LyXRC::RC_USE_ESC_CHARS:
2189 case LyXRC::RC_USE_INP_ENC:
2190 case LyXRC::RC_USE_PERS_DICT:
2191 case LyXRC::RC_USE_SPELL_LIB:
2192 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2193 case LyXRC::RC_VIEWER:
2194 case LyXRC::RC_LAST: