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::fileSearch;
117 using support::ForkedcallsController;
118 using support::i18nLibFileSearch;
119 using support::isDirWriteable;
120 using support::isFileReadable;
121 using support::isStrInt;
122 using support::makeAbsPath;
123 using support::makeDisplayPath;
124 using support::package;
125 using support::quoteName;
126 using support::rtrim;
127 using support::split;
128 using support::subst;
129 using support::Systemcall;
130 using support::token;
132 using support::prefixIs;
135 using std::make_pair;
138 using std::istringstream;
139 using std::ostringstream;
141 namespace Alert = frontend::Alert;
142 namespace fs = boost::filesystem;
146 extern tex_accent_struct get_accent(kb_action action);
151 bool getLocalStatus(LCursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //LCursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
207 meta_fake_bit(key_modifier::none)
212 void LyXFunc::initKeySequences(kb_keymap * kb)
214 keyseq.reset(new kb_sequence(kb, kb));
215 cancel_meta_seq.reset(new kb_sequence(kb, kb));
219 void LyXFunc::setLyXView(LyXView * lv)
225 void LyXFunc::handleKeyFunc(kb_action action)
227 char_type c = encoded_last_key;
229 if (keyseq->length())
232 lyx_view_->view()->getIntl().getTransManager().deadkey(
233 c, get_accent(action).accent, view()->getLyXText(), view()->cursor());
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 view()->cursor().resetAnchor();
243 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
245 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
247 // Do nothing if we have nothing (JMarc)
248 if (!keysym->isOK()) {
249 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
254 if (keysym->isModifier()) {
255 lyxerr[Debug::KEY] << "isModifier true" << endl;
259 //Encoding const * encoding = view()->cursor().getEncoding();
260 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
261 size_t encoded_last_key = keysym->getUCSEncoded();
263 // Do a one-deep top-level lookup for
264 // cancel and meta-fake keys. RVDK_PATCH_5
265 cancel_meta_seq->reset();
267 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
268 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
269 << " action first set to [" << func.action << ']'
272 // When not cancel or meta-fake, do the normal lookup.
273 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
274 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
275 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
276 // remove Caps Lock and Mod2 as a modifiers
277 func = keyseq->addkey(keysym, (state | meta_fake_bit));
278 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
279 << "action now set to ["
280 << func.action << ']' << endl;
283 // Dont remove this unless you know what you are doing.
284 meta_fake_bit = key_modifier::none;
286 // Can this happen now ?
287 if (func.action == LFUN_NOACTION) {
288 func = FuncRequest(LFUN_COMMAND_PREFIX);
291 if (lyxerr.debugging(Debug::KEY)) {
292 lyxerr << BOOST_CURRENT_FUNCTION
294 << func.action << "]["
295 << keyseq->print() << ']'
299 // already here we know if it any point in going further
300 // why not return already here if action == -1 and
301 // num_bytes == 0? (Lgb)
303 if (keyseq->length() > 1) {
304 lyx_view_->message(from_utf8(keyseq->print()));
308 // Maybe user can only reach the key via holding down shift.
309 // Let's see. But only if shift is the only modifier
310 if (func.action == LFUN_UNKNOWN_ACTION &&
311 state == key_modifier::shift) {
312 lyxerr[Debug::KEY] << "Trying without shift" << endl;
313 func = keyseq->addkey(keysym, key_modifier::none);
314 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
317 if (func.action == LFUN_UNKNOWN_ACTION) {
318 // Hmm, we didn't match any of the keysequences. See
319 // if it's normal insertable text not already covered
321 if (keysym->isText() && keyseq->length() == 1) {
322 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
323 func = FuncRequest(LFUN_SELF_INSERT,
324 FuncRequest::KEYBOARD);
326 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
327 lyx_view_->message(_("Unknown function."));
332 if (func.action == LFUN_SELF_INSERT) {
333 if (encoded_last_key != 0) {
334 docstring const arg(1, encoded_last_key);
335 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
336 FuncRequest::KEYBOARD));
338 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
346 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
348 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
351 if (cmd.action == LFUN_LYX_QUIT) {
352 flag.message(from_utf8(N_("Exiting")));
355 } else if (cmd.action == LFUN_BOOKMARK_GOTO) {
356 // bookmarks can be valid even if there is no opened buffer
357 flag.enabled(LyX::ref().session().bookmarks().isValid(convert<unsigned int>(to_utf8(cmd.argument()))));
359 } else if (cmd.action == LFUN_BOOKMARK_CLEAR) {
360 flag.enabled(LyX::ref().session().bookmarks().size() > 0);
365 LCursor & cur = view()->cursor();
367 /* In LyX/Mac, when a dialog is open, the menus of the
368 application can still be accessed without giving focus to
369 the main window. In this case, we want to disable the menu
370 entries that are buffer-related.
372 Note that this code is not perfect, as bug 1941 attests:
373 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
376 if (cmd.origin == FuncRequest::UI && !lyx_view_->hasFocus())
379 buf = lyx_view_->buffer();
381 if (cmd.action == LFUN_NOACTION) {
382 flag.message(from_utf8(N_("Nothing to do")));
387 switch (cmd.action) {
388 case LFUN_UNKNOWN_ACTION:
389 #ifndef HAVE_LIBAIKSAURUS
390 case LFUN_THESAURUS_ENTRY:
400 if (flag.unknown()) {
401 flag.message(from_utf8(N_("Unknown action")));
405 if (!flag.enabled()) {
406 if (flag.message().empty())
407 flag.message(from_utf8(N_("Command disabled")));
411 // Check whether we need a buffer
412 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
414 flag.message(from_utf8(N_("Command not allowed with"
415 "out any document open")));
420 // I would really like to avoid having this switch and rather try to
421 // encode this in the function itself.
422 // -- And I'd rather let an inset decide which LFUNs it is willing
423 // to handle (Andre')
425 switch (cmd.action) {
426 case LFUN_BUFFER_TOGGLE_READ_ONLY:
427 flag.setOnOff(buf->isReadonly());
430 case LFUN_BUFFER_SWITCH:
431 // toggle on the current buffer, but do not toggle off
432 // the other ones (is that a good idea?)
433 if (to_utf8(cmd.argument()) == buf->fileName())
437 case LFUN_BUFFER_EXPORT:
438 enable = cmd.argument() == "custom"
439 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
442 case LFUN_BUFFER_CHKTEX:
443 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
446 case LFUN_BUILD_PROGRAM:
447 enable = Exporter::isExportable(*buf, "program");
450 case LFUN_LAYOUT_TABULAR:
451 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
455 case LFUN_LAYOUT_PARAGRAPH:
456 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
459 case LFUN_VC_REGISTER:
460 enable = !buf->lyxvc().inUse();
462 case LFUN_VC_CHECK_IN:
463 enable = buf->lyxvc().inUse() && !buf->isReadonly();
465 case LFUN_VC_CHECK_OUT:
466 enable = buf->lyxvc().inUse() && buf->isReadonly();
469 case LFUN_VC_UNDO_LAST:
470 enable = buf->lyxvc().inUse();
472 case LFUN_BUFFER_RELOAD:
473 enable = !buf->isUnnamed() && !buf->isClean();
476 case LFUN_INSET_SETTINGS: {
480 InsetBase::Code code = cur.inset().lyxCode();
482 case InsetBase::TABULAR_CODE:
483 enable = cmd.argument() == "tabular";
485 case InsetBase::ERT_CODE:
486 enable = cmd.argument() == "ert";
488 case InsetBase::FLOAT_CODE:
489 enable = cmd.argument() == "float";
491 case InsetBase::WRAP_CODE:
492 enable = cmd.argument() == "wrap";
494 case InsetBase::NOTE_CODE:
495 enable = cmd.argument() == "note";
497 case InsetBase::BRANCH_CODE:
498 enable = cmd.argument() == "branch";
500 case InsetBase::BOX_CODE:
501 enable = cmd.argument() == "box";
509 case LFUN_INSET_APPLY: {
510 string const name = cmd.getArg(0);
511 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
513 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
515 bool const success = inset->getStatus(cur, fr, fs);
516 // Every inset is supposed to handle this
517 BOOST_ASSERT(success);
520 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
521 flag |= getStatus(fr);
523 enable = flag.enabled();
527 case LFUN_DIALOG_SHOW: {
528 string const name = cmd.getArg(0);
530 enable = name == "aboutlyx"
534 || name == "texinfo";
535 else if (name == "print")
536 enable = Exporter::isExportable(*buf, "dvi")
537 && lyxrc.print_command != "none";
538 else if (name == "character" || name == "mathpanel")
539 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
540 else if (name == "latexlog")
541 enable = isFileReadable(buf->getLogName().second);
542 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
543 else if (name == "spellchecker")
546 else if (name == "vclog")
547 enable = buf->lyxvc().inUse();
548 else if (name == "view-source")
553 case LFUN_DIALOG_SHOW_NEW_INSET:
554 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
557 case LFUN_DIALOG_UPDATE: {
558 string const name = cmd.getArg(0);
560 enable = name == "prefs";
564 case LFUN_CITATION_INSERT: {
565 FuncRequest fr(LFUN_INSET_INSERT, "citation");
566 enable = getStatus(fr).enabled();
570 case LFUN_BUFFER_WRITE: {
571 enable = view()->buffer()->isUnnamed()
572 || !view()->buffer()->isClean();
576 case LFUN_TOOLBAR_TOGGLE_STATE: {
577 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
578 if (!(flags & ToolbarBackend::AUTO))
579 flag.setOnOff(flags & ToolbarBackend::ON);
583 // this one is difficult to get right. As a half-baked
584 // solution, we consider only the first action of the sequence
585 case LFUN_COMMAND_SEQUENCE: {
586 // argument contains ';'-terminated commands
587 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
588 FuncRequest func(lyxaction.lookupFunc(firstcmd));
589 func.origin = cmd.origin;
590 flag = getStatus(func);
593 case LFUN_BUFFER_NEW:
594 case LFUN_BUFFER_NEW_TEMPLATE:
595 case LFUN_WORD_FIND_FORWARD:
596 case LFUN_WORD_FIND_BACKWARD:
597 case LFUN_COMMAND_PREFIX:
598 case LFUN_COMMAND_EXECUTE:
600 case LFUN_META_PREFIX:
601 case LFUN_BUFFER_CLOSE:
602 case LFUN_BUFFER_WRITE_AS:
603 case LFUN_BUFFER_UPDATE:
604 case LFUN_BUFFER_VIEW:
605 case LFUN_BUFFER_IMPORT:
607 case LFUN_BUFFER_AUTO_SAVE:
608 case LFUN_RECONFIGURE:
612 case LFUN_DROP_LAYOUTS_CHOICE:
614 case LFUN_SERVER_GET_NAME:
615 case LFUN_SERVER_NOTIFY:
616 case LFUN_SERVER_GOTO_FILE_ROW:
617 case LFUN_DIALOG_HIDE:
618 case LFUN_DIALOG_DISCONNECT_INSET:
619 case LFUN_BUFFER_CHILD_OPEN:
620 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
621 case LFUN_KEYMAP_OFF:
622 case LFUN_KEYMAP_PRIMARY:
623 case LFUN_KEYMAP_SECONDARY:
624 case LFUN_KEYMAP_TOGGLE:
626 case LFUN_BUFFER_EXPORT_CUSTOM:
627 case LFUN_BUFFER_PRINT:
628 case LFUN_PREFERENCES_SAVE:
629 case LFUN_SCREEN_FONT_UPDATE:
632 case LFUN_EXTERNAL_EDIT:
633 case LFUN_GRAPHICS_EDIT:
634 case LFUN_ALL_INSETS_TOGGLE:
635 case LFUN_BUFFER_LANGUAGE:
636 case LFUN_TEXTCLASS_APPLY:
637 case LFUN_TEXTCLASS_LOAD:
638 case LFUN_BUFFER_SAVE_AS_DEFAULT:
639 case LFUN_BUFFER_PARAMS_APPLY:
640 case LFUN_LYXRC_APPLY:
641 case LFUN_BUFFER_NEXT:
642 case LFUN_BUFFER_PREVIOUS:
643 case LFUN_WINDOW_NEW:
644 case LFUN_WINDOW_CLOSE:
645 // these are handled in our dispatch()
649 if (!getLocalStatus(cur, cmd, flag))
650 flag = view()->getStatus(cmd);
656 // Can we use a readonly buffer?
657 if (buf && buf->isReadonly()
658 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
659 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
660 flag.message(from_utf8(N_("Document is read-only")));
664 // Are we in a DELETED change-tracking region?
665 if (buf && lookupChangeType(cur, true) == Change::DELETED
666 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
667 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
668 flag.message(from_utf8(N_("This portion of the document is deleted.")));
672 // the default error message if we disable the command
673 if (!flag.enabled() && flag.message().empty())
674 flag.message(from_utf8(N_("Command disabled")));
680 bool LyXFunc::ensureBufferClean(BufferView * bv)
682 Buffer & buf = *bv->buffer();
686 docstring const file = makeDisplayPath(buf.fileName(), 30);
687 docstring text = bformat(_("The document %1$s has unsaved "
688 "changes.\n\nDo you want to save "
689 "the document?"), file);
690 int const ret = Alert::prompt(_("Save changed document?"),
691 text, 0, 1, _("&Save"),
695 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
697 return buf.isClean();
703 void showPrintError(string const & name)
705 docstring str = bformat(_("Could not print the document %1$s.\n"
706 "Check that your printer is set up correctly."),
707 makeDisplayPath(name, 50));
708 Alert::error(_("Print document failed"), str);
712 void loadTextclass(string const & name)
714 std::pair<bool, textclass_type> const tc_pair =
715 textclasslist.numberOfClass(name);
717 if (!tc_pair.first) {
718 lyxerr << "Document class \"" << name
719 << "\" does not exist."
724 textclass_type const tc = tc_pair.second;
726 if (!textclasslist[tc].load()) {
727 docstring s = bformat(_("The document could not be converted\n"
728 "into the document class %1$s."),
729 from_utf8(textclasslist[tc].name()));
730 Alert::error(_("Could not change class"), s);
735 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
740 void LyXFunc::dispatch(FuncRequest const & cmd)
742 string const argument = to_utf8(cmd.argument());
743 kb_action const action = cmd.action;
745 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
746 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
748 // we have not done anything wrong yet.
750 dispatch_buffer.erase();
752 // redraw the screen at the end (first of the two drawing steps).
753 //This is done unless explicitely requested otherwise
754 Update::flags updateFlags = Update::FitCursor;
756 FuncStatus const flag = getStatus(cmd);
757 if (!flag.enabled()) {
758 // We cannot use this function here
759 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
760 << lyxaction.getActionName(action)
761 << " [" << action << "] is disabled at this location"
763 setErrorMessage(flag.message());
767 case LFUN_WORD_FIND_FORWARD:
768 case LFUN_WORD_FIND_BACKWARD: {
769 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
770 static string last_search;
771 string searched_string;
773 if (!argument.empty()) {
774 last_search = argument;
775 searched_string = argument;
777 searched_string = last_search;
780 if (searched_string.empty())
783 bool const fw = action == LFUN_WORD_FIND_FORWARD;
785 find2string(searched_string, true, false, fw);
786 find(view(), FuncRequest(LFUN_WORD_FIND, data));
790 case LFUN_COMMAND_PREFIX:
791 BOOST_ASSERT(lyx_view_);
792 lyx_view_->message(from_utf8(keyseq->printOptions()));
795 case LFUN_COMMAND_EXECUTE:
796 BOOST_ASSERT(lyx_view_);
797 lyx_view_->getToolbars().display("minibuffer", true);
798 lyx_view_->focus_command_buffer();
802 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
804 meta_fake_bit = key_modifier::none;
805 if (view()->buffer())
806 // cancel any selection
807 dispatch(FuncRequest(LFUN_MARK_OFF));
808 setMessage(_("Cancel"));
811 case LFUN_META_PREFIX:
812 meta_fake_bit = key_modifier::alt;
813 setMessage(from_utf8(keyseq->print()));
816 case LFUN_BUFFER_TOGGLE_READ_ONLY:
817 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
818 if (lyx_view_->buffer()->lyxvc().inUse())
819 lyx_view_->buffer()->lyxvc().toggleReadOnly();
821 lyx_view_->buffer()->setReadonly(
822 !lyx_view_->buffer()->isReadonly());
825 // --- Menus -----------------------------------------------
826 case LFUN_BUFFER_NEW:
827 menuNew(argument, false);
830 case LFUN_BUFFER_NEW_TEMPLATE:
831 menuNew(argument, true);
834 case LFUN_BUFFER_CLOSE:
839 case LFUN_BUFFER_WRITE:
840 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
841 if (!lyx_view_->buffer()->isUnnamed()) {
842 docstring const str = bformat(_("Saving document %1$s..."),
843 makeDisplayPath(lyx_view_->buffer()->fileName()));
844 lyx_view_->message(str);
845 menuWrite(lyx_view_->buffer());
846 lyx_view_->message(str + _(" done."));
848 writeAs(lyx_view_->buffer());
849 updateFlags = Update::None;
852 case LFUN_BUFFER_WRITE_AS:
853 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
854 writeAs(lyx_view_->buffer(), argument);
855 updateFlags = Update::None;
858 case LFUN_BUFFER_RELOAD: {
859 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
860 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
861 docstring text = bformat(_("Any changes will be lost. Are you sure "
862 "you want to revert to the saved version of the document %1$s?"), file);
863 int const ret = Alert::prompt(_("Revert to saved document?"),
864 text, 0, 1, _("&Revert"), _("&Cancel"));
871 case LFUN_BUFFER_UPDATE:
872 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
873 Exporter::Export(lyx_view_->buffer(), argument, true);
876 case LFUN_BUFFER_VIEW:
877 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
878 Exporter::preview(lyx_view_->buffer(), argument);
881 case LFUN_BUILD_PROGRAM:
882 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
883 Exporter::Export(lyx_view_->buffer(), "program", true);
886 case LFUN_BUFFER_CHKTEX:
887 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
888 lyx_view_->buffer()->runChktex();
891 case LFUN_BUFFER_EXPORT:
892 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
893 if (argument == "custom")
894 lyx_view_->getDialogs().show("sendto");
896 Exporter::Export(lyx_view_->buffer(), argument, false);
900 case LFUN_BUFFER_EXPORT_CUSTOM: {
901 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
903 string command = split(argument, format_name, ' ');
904 Format const * format = formats.getFormat(format_name);
906 lyxerr << "Format \"" << format_name
907 << "\" not recognized!"
912 Buffer * buffer = lyx_view_->buffer();
914 // The name of the file created by the conversion process
917 // Output to filename
918 if (format->name() == "lyx") {
919 string const latexname =
920 buffer->getLatexName(false);
921 filename = changeExtension(latexname,
922 format->extension());
923 filename = addName(buffer->temppath(), filename);
925 if (!buffer->writeFile(filename))
929 Exporter::Export(buffer, format_name, true, filename);
932 // Substitute $$FName for filename
933 if (!contains(command, "$$FName"))
934 command = "( " + command + " ) < $$FName";
935 command = subst(command, "$$FName", filename);
937 // Execute the command in the background
939 call.startscript(Systemcall::DontWait, command);
943 case LFUN_BUFFER_PRINT: {
944 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
947 string command = split(split(argument, target, ' '),
951 || target_name.empty()
952 || command.empty()) {
953 lyxerr << "Unable to parse \""
954 << argument << '"' << std::endl;
957 if (target != "printer" && target != "file") {
958 lyxerr << "Unrecognized target \""
959 << target << '"' << std::endl;
963 Buffer * buffer = lyx_view_->buffer();
965 if (!Exporter::Export(buffer, "dvi", true)) {
966 showPrintError(buffer->fileName());
970 // Push directory path.
971 string const path = buffer->temppath();
972 support::Path p(path);
974 // there are three cases here:
975 // 1. we print to a file
976 // 2. we print directly to a printer
977 // 3. we print using a spool command (print to file first)
980 string const dviname =
981 changeExtension(buffer->getLatexName(true),
984 if (target == "printer") {
985 if (!lyxrc.print_spool_command.empty()) {
986 // case 3: print using a spool
987 string const psname =
988 changeExtension(dviname,".ps");
989 command += lyxrc.print_to_file
992 + quoteName(dviname);
995 lyxrc.print_spool_command +' ';
996 if (target_name != "default") {
997 command2 += lyxrc.print_spool_printerprefix
1001 command2 += quoteName(psname);
1003 // If successful, then spool command
1004 res = one.startscript(
1009 res = one.startscript(
1010 Systemcall::DontWait,
1013 // case 2: print directly to a printer
1014 res = one.startscript(
1015 Systemcall::DontWait,
1016 command + quoteName(dviname));
1020 // case 1: print to a file
1021 command += lyxrc.print_to_file
1022 + quoteName(makeAbsPath(target_name,
1025 + quoteName(dviname);
1026 res = one.startscript(Systemcall::DontWait,
1031 showPrintError(buffer->fileName());
1035 case LFUN_BUFFER_IMPORT:
1040 if (argument != "force") {
1041 if (!theApp->gui().closeAll())
1046 // FIXME: this code needs to be transfered somewhere else
1047 // as lyx_view_ will most certainly be null and a same buffer
1048 // might be visible in more than one LyXView.
1049 if (lyx_view_ && lyx_view_->view()->buffer()) {
1050 // save cursor Position for opened files to .lyx/session
1051 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
1052 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1058 case LFUN_TOC_VIEW: {
1059 BOOST_ASSERT(lyx_view_);
1060 InsetCommandParams p("tableofcontents");
1061 string const data = InsetCommandMailer::params2string("toc", p);
1062 lyx_view_->getDialogs().show("toc", data, 0);
1066 case LFUN_BUFFER_AUTO_SAVE:
1070 case LFUN_RECONFIGURE:
1071 BOOST_ASSERT(lyx_view_);
1072 reconfigure(*lyx_view_);
1075 case LFUN_HELP_OPEN: {
1076 BOOST_ASSERT(lyx_view_);
1077 string const arg = argument;
1079 setErrorMessage(_("Missing argument"));
1082 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1083 if (fname.empty()) {
1084 lyxerr << "LyX: unable to find documentation file `"
1085 << arg << "'. Bad installation?" << endl;
1088 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1089 makeDisplayPath(fname)));
1090 lyx_view_->loadLyXFile(fname, false);
1094 // --- version control -------------------------------
1095 case LFUN_VC_REGISTER:
1096 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1097 if (!ensureBufferClean(view()))
1099 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1100 lyx_view_->buffer()->lyxvc().registrer();
1105 case LFUN_VC_CHECK_IN:
1106 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1107 if (!ensureBufferClean(view()))
1109 if (lyx_view_->buffer()->lyxvc().inUse()
1110 && !lyx_view_->buffer()->isReadonly()) {
1111 lyx_view_->buffer()->lyxvc().checkIn();
1116 case LFUN_VC_CHECK_OUT:
1117 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1118 if (!ensureBufferClean(view()))
1120 if (lyx_view_->buffer()->lyxvc().inUse()
1121 && lyx_view_->buffer()->isReadonly()) {
1122 lyx_view_->buffer()->lyxvc().checkOut();
1127 case LFUN_VC_REVERT:
1128 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1129 lyx_view_->buffer()->lyxvc().revert();
1133 case LFUN_VC_UNDO_LAST:
1134 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1135 lyx_view_->buffer()->lyxvc().undoLast();
1139 // --- buffers ----------------------------------------
1140 case LFUN_BUFFER_SWITCH:
1141 BOOST_ASSERT(lyx_view_);
1142 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1145 case LFUN_BUFFER_NEXT:
1146 BOOST_ASSERT(lyx_view_);
1147 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1150 case LFUN_BUFFER_PREVIOUS:
1151 BOOST_ASSERT(lyx_view_);
1152 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1156 BOOST_ASSERT(lyx_view_);
1157 newFile(view(), argument);
1160 case LFUN_FILE_OPEN:
1161 BOOST_ASSERT(lyx_view_);
1165 case LFUN_DROP_LAYOUTS_CHOICE:
1166 BOOST_ASSERT(lyx_view_);
1167 lyx_view_->getToolbars().openLayoutList();
1170 case LFUN_MENU_OPEN:
1171 BOOST_ASSERT(lyx_view_);
1172 lyx_view_->getMenubar().openByName(from_utf8(argument));
1175 // --- lyxserver commands ----------------------------
1176 case LFUN_SERVER_GET_NAME:
1177 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1178 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1179 lyxerr[Debug::INFO] << "FNAME["
1180 << lyx_view_->buffer()->fileName()
1184 case LFUN_SERVER_NOTIFY:
1185 dispatch_buffer = from_utf8(keyseq->print());
1186 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1189 case LFUN_SERVER_GOTO_FILE_ROW: {
1190 BOOST_ASSERT(lyx_view_);
1193 istringstream is(argument);
1194 is >> file_name >> row;
1195 if (prefixIs(file_name, package().temp_dir())) {
1196 // Needed by inverse dvi search. If it is a file
1197 // in tmpdir, call the apropriated function
1198 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1200 // Must replace extension of the file to be .lyx
1201 // and get full path
1202 string const s = changeExtension(file_name, ".lyx");
1203 // Either change buffer or load the file
1204 if (theBufferList().exists(s)) {
1205 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1207 lyx_view_->loadLyXFile(s);
1211 view()->setCursorFromRow(row);
1214 // see BufferView::center()
1218 case LFUN_DIALOG_SHOW: {
1219 BOOST_ASSERT(lyx_view_);
1220 string const name = cmd.getArg(0);
1221 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1223 if (name == "character") {
1224 data = freefont2string();
1226 lyx_view_->getDialogs().show("character", data);
1227 } else if (name == "latexlog") {
1228 pair<Buffer::LogType, string> const logfile =
1229 lyx_view_->buffer()->getLogName();
1230 switch (logfile.first) {
1231 case Buffer::latexlog:
1234 case Buffer::buildlog:
1238 data += LyXLex::quoteString(logfile.second);
1239 lyx_view_->getDialogs().show("log", data);
1240 } else if (name == "vclog") {
1241 string const data = "vc " +
1242 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1243 lyx_view_->getDialogs().show("log", data);
1245 lyx_view_->getDialogs().show(name, data);
1249 case LFUN_DIALOG_SHOW_NEW_INSET: {
1250 BOOST_ASSERT(lyx_view_);
1251 string const name = cmd.getArg(0);
1252 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1253 if (name == "bibitem" ||
1260 InsetCommandParams p(name);
1261 data = InsetCommandMailer::params2string(name, p);
1262 } else if (name == "include") {
1263 InsetCommandParams p(data);
1264 data = InsetIncludeMailer::params2string(p);
1265 } else if (name == "box") {
1266 // \c data == "Boxed" || "Frameless" etc
1267 InsetBoxParams p(data);
1268 data = InsetBoxMailer::params2string(p);
1269 } else if (name == "branch") {
1270 InsetBranchParams p;
1271 data = InsetBranchMailer::params2string(p);
1272 } else if (name == "citation") {
1273 InsetCommandParams p("cite");
1274 data = InsetCommandMailer::params2string(name, p);
1275 } else if (name == "ert") {
1276 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1277 } else if (name == "external") {
1278 InsetExternalParams p;
1279 Buffer const & buffer = *lyx_view_->buffer();
1280 data = InsetExternalMailer::params2string(p, buffer);
1281 } else if (name == "float") {
1283 data = InsetFloatMailer::params2string(p);
1284 } else if (name == "graphics") {
1285 InsetGraphicsParams p;
1286 Buffer const & buffer = *lyx_view_->buffer();
1287 data = InsetGraphicsMailer::params2string(p, buffer);
1288 } else if (name == "note") {
1290 data = InsetNoteMailer::params2string(p);
1291 } else if (name == "vspace") {
1293 data = InsetVSpaceMailer::params2string(space);
1294 } else if (name == "wrap") {
1296 data = InsetWrapMailer::params2string(p);
1298 lyx_view_->getDialogs().show(name, data, 0);
1302 case LFUN_DIALOG_UPDATE: {
1303 BOOST_ASSERT(lyx_view_);
1304 string const & name = argument;
1305 // Can only update a dialog connected to an existing inset
1306 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1308 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1309 inset->dispatch(view()->cursor(), fr);
1310 } else if (name == "paragraph") {
1311 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1312 } else if (name == "prefs") {
1313 lyx_view_->getDialogs().update(name, string());
1318 case LFUN_DIALOG_HIDE:
1319 Dialogs::hide(argument, 0);
1322 case LFUN_DIALOG_DISCONNECT_INSET:
1323 BOOST_ASSERT(lyx_view_);
1324 lyx_view_->getDialogs().disconnect(argument);
1328 case LFUN_CITATION_INSERT: {
1329 BOOST_ASSERT(lyx_view_);
1330 if (!argument.empty()) {
1331 // we can have one optional argument, delimited by '|'
1332 // citation-insert <key>|<text_before>
1333 // this should be enhanced to also support text_after
1334 // and citation style
1335 string arg = argument;
1337 if (contains(argument, "|")) {
1338 arg = token(argument, '|', 0);
1339 opt1 = '[' + token(argument, '|', 1) + ']';
1341 std::ostringstream os;
1342 os << "citation LatexCommand\n"
1343 << "\\cite" << opt1 << "{" << arg << "}\n"
1345 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1348 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1352 case LFUN_BUFFER_CHILD_OPEN: {
1353 BOOST_ASSERT(lyx_view_);
1354 string const filename =
1355 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1356 // FIXME Should use bformat
1357 setMessage(_("Opening child document ") +
1358 makeDisplayPath(filename) + "...");
1359 view()->saveBookmark(false);
1360 string const parentfilename = lyx_view_->buffer()->fileName();
1361 if (theBufferList().exists(filename))
1362 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1364 lyx_view_->loadLyXFile(filename);
1365 // Set the parent name of the child document.
1366 // This makes insertion of citations and references in the child work,
1367 // when the target is in the parent or another child document.
1368 lyx_view_->buffer()->setParentName(parentfilename);
1372 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1373 BOOST_ASSERT(lyx_view_);
1374 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1377 case LFUN_KEYMAP_OFF:
1378 BOOST_ASSERT(lyx_view_);
1379 lyx_view_->view()->getIntl().keyMapOn(false);
1382 case LFUN_KEYMAP_PRIMARY:
1383 BOOST_ASSERT(lyx_view_);
1384 lyx_view_->view()->getIntl().keyMapPrim();
1387 case LFUN_KEYMAP_SECONDARY:
1388 BOOST_ASSERT(lyx_view_);
1389 lyx_view_->view()->getIntl().keyMapSec();
1392 case LFUN_KEYMAP_TOGGLE:
1393 BOOST_ASSERT(lyx_view_);
1394 lyx_view_->view()->getIntl().toggleKeyMap();
1400 string rest = split(argument, countstr, ' ');
1401 istringstream is(countstr);
1404 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1405 for (int i = 0; i < count; ++i)
1406 dispatch(lyxaction.lookupFunc(rest));
1410 case LFUN_COMMAND_SEQUENCE: {
1411 // argument contains ';'-terminated commands
1412 string arg = argument;
1413 while (!arg.empty()) {
1415 arg = split(arg, first, ';');
1416 FuncRequest func(lyxaction.lookupFunc(first));
1417 func.origin = cmd.origin;
1423 case LFUN_PREFERENCES_SAVE: {
1424 support::Path p(package().user_support());
1425 lyxrc.write("preferences", false);
1429 case LFUN_SCREEN_FONT_UPDATE:
1430 BOOST_ASSERT(lyx_view_);
1431 // handle the screen font changes.
1432 lyxrc.set_font_norm_type();
1433 theFontLoader().update();
1434 // All visible buffers will need resize
1438 case LFUN_SET_COLOR: {
1440 string const x11_name = split(argument, lyx_name, ' ');
1441 if (lyx_name.empty() || x11_name.empty()) {
1442 setErrorMessage(_("Syntax: set-color <lyx_name>"
1447 bool const graphicsbg_changed =
1448 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1449 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1451 if (!lcolor.setColor(lyx_name, x11_name)) {
1453 bformat(_("Set-color \"%1$s\" failed "
1454 "- color is undefined or "
1455 "may not be redefined"),
1456 from_utf8(lyx_name)));
1460 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1462 if (graphicsbg_changed) {
1463 #ifdef WITH_WARNINGS
1464 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1467 graphics::GCache::get().changeDisplay(true);
1474 BOOST_ASSERT(lyx_view_);
1475 lyx_view_->message(from_utf8(argument));
1478 case LFUN_EXTERNAL_EDIT: {
1479 BOOST_ASSERT(lyx_view_);
1480 FuncRequest fr(action, argument);
1481 InsetExternal().dispatch(view()->cursor(), fr);
1485 case LFUN_GRAPHICS_EDIT: {
1486 FuncRequest fr(action, argument);
1487 InsetGraphics().dispatch(view()->cursor(), fr);
1491 case LFUN_INSET_APPLY: {
1492 BOOST_ASSERT(lyx_view_);
1493 string const name = cmd.getArg(0);
1494 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1496 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1497 inset->dispatch(view()->cursor(), fr);
1499 FuncRequest fr(LFUN_INSET_INSERT, argument);
1502 // ideally, the update flag should be set by the insets,
1503 // but this is not possible currently
1504 updateFlags = Update::Force | Update::FitCursor;
1508 case LFUN_ALL_INSETS_TOGGLE: {
1509 BOOST_ASSERT(lyx_view_);
1511 string const name = split(argument, action, ' ');
1512 InsetBase::Code const inset_code =
1513 InsetBase::translate(name);
1515 LCursor & cur = view()->cursor();
1516 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1518 InsetBase & inset = lyx_view_->buffer()->inset();
1519 InsetIterator it = inset_iterator_begin(inset);
1520 InsetIterator const end = inset_iterator_end(inset);
1521 for (; it != end; ++it) {
1522 if (inset_code == InsetBase::NO_CODE
1523 || inset_code == it->lyxCode()) {
1524 LCursor tmpcur = cur;
1525 tmpcur.pushLeft(*it);
1526 it->dispatch(tmpcur, fr);
1529 updateFlags = Update::Force | Update::FitCursor;
1533 case LFUN_BUFFER_LANGUAGE: {
1534 BOOST_ASSERT(lyx_view_);
1535 Buffer & buffer = *lyx_view_->buffer();
1536 Language const * oldL = buffer.params().language;
1537 Language const * newL = languages.getLanguage(argument);
1538 if (!newL || oldL == newL)
1541 if (oldL->rightToLeft() == newL->rightToLeft()
1542 && !buffer.isMultiLingual())
1543 buffer.changeLanguage(oldL, newL);
1545 buffer.updateDocLang(newL);
1549 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1550 string const fname =
1551 addName(addPath(package().user_support(), "templates/"),
1553 Buffer defaults(fname);
1555 istringstream ss(argument);
1558 int const unknown_tokens = defaults.readHeader(lex);
1560 if (unknown_tokens != 0) {
1561 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1562 << unknown_tokens << " unknown token"
1563 << (unknown_tokens == 1 ? "" : "s")
1567 if (defaults.writeFile(defaults.fileName()))
1568 // FIXME Should use bformat
1569 setMessage(_("Document defaults saved in ")
1570 + makeDisplayPath(fname));
1572 setErrorMessage(_("Unable to save document defaults"));
1576 case LFUN_BUFFER_PARAMS_APPLY: {
1577 BOOST_ASSERT(lyx_view_);
1578 biblio::CiteEngine const engine =
1579 lyx_view_->buffer()->params().cite_engine;
1581 istringstream ss(argument);
1584 int const unknown_tokens =
1585 lyx_view_->buffer()->readHeader(lex);
1587 if (unknown_tokens != 0) {
1588 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1589 << unknown_tokens << " unknown token"
1590 << (unknown_tokens == 1 ? "" : "s")
1593 if (engine == lyx_view_->buffer()->params().cite_engine)
1596 LCursor & cur = view()->cursor();
1597 FuncRequest fr(LFUN_INSET_REFRESH);
1599 InsetBase & inset = lyx_view_->buffer()->inset();
1600 InsetIterator it = inset_iterator_begin(inset);
1601 InsetIterator const end = inset_iterator_end(inset);
1602 for (; it != end; ++it)
1603 if (it->lyxCode() == InsetBase::CITE_CODE)
1604 it->dispatch(cur, fr);
1608 case LFUN_TEXTCLASS_APPLY: {
1609 BOOST_ASSERT(lyx_view_);
1610 Buffer * buffer = lyx_view_->buffer();
1612 textclass_type const old_class =
1613 buffer->params().textclass;
1615 loadTextclass(argument);
1617 std::pair<bool, textclass_type> const tc_pair =
1618 textclasslist.numberOfClass(argument);
1623 textclass_type const new_class = tc_pair.second;
1624 if (old_class == new_class)
1628 lyx_view_->message(_("Converting document to new document class..."));
1629 recordUndoFullDocument(view());
1630 buffer->params().textclass = new_class;
1631 StableDocIterator backcur(view()->cursor());
1632 ErrorList & el = buffer->errorList("Class Switch");
1633 cap::switchBetweenClasses(
1634 old_class, new_class,
1635 static_cast<InsetText &>(buffer->inset()), el);
1637 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1639 buffer->errors("Class Switch");
1640 updateLabels(*buffer);
1641 updateFlags = Update::Force | Update::FitCursor;
1645 case LFUN_TEXTCLASS_LOAD:
1646 loadTextclass(argument);
1649 case LFUN_LYXRC_APPLY: {
1650 LyXRC const lyxrc_orig = lyxrc;
1652 istringstream ss(argument);
1653 bool const success = lyxrc.read(ss) == 0;
1656 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1657 << "Unable to read lyxrc data"
1662 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1666 case LFUN_WINDOW_NEW:
1667 LyX::ref().newLyXView();
1670 case LFUN_WINDOW_CLOSE:
1671 BOOST_ASSERT(lyx_view_);
1672 BOOST_ASSERT(theApp);
1674 // We return here because lyx_view does not exists anymore.
1677 case LFUN_BOOKMARK_GOTO: {
1678 BOOST_ASSERT(lyx_view_);
1679 unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1680 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1681 BOOST_ASSERT(!bm.filename.empty());
1682 // if the file is not opened, open it.
1683 if (!theBufferList().exists(bm.filename))
1684 dispatch(FuncRequest(LFUN_FILE_OPEN, bm.filename));
1685 // open may fail, so we need to test it again
1686 if (theBufferList().exists(bm.filename)) {
1687 // if the current buffer is not that one, switch to it.
1688 if (lyx_view_->buffer()->fileName() != bm.filename)
1689 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, bm.filename));
1690 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != bm.filename);
1691 view()->moveToPosition(bm.par_id, bm.par_pos);
1696 case LFUN_BOOKMARK_CLEAR:
1697 LyX::ref().session().bookmarks().clear();
1700 case LFUN_TOOLBAR_TOGGLE_STATE:
1701 lyx_view_->toggleToolbarState(argument);
1705 BOOST_ASSERT(lyx_view_);
1706 view()->cursor().dispatch(cmd);
1707 updateFlags = view()->cursor().result().update();
1708 if (!view()->cursor().result().dispatched())
1709 if (view()->dispatch(cmd))
1710 updateFlags = Update::Force | Update::FitCursor;
1715 if (lyx_view_ && view()->buffer()) {
1716 // Redraw screen unless explicitly told otherwise.
1717 // This also initializes the position cache for all insets
1718 // in (at least partially) visible top-level paragraphs.
1719 bool needSecondUpdate = false;
1720 if (updateFlags != Update::None)
1721 view()->update(updateFlags);
1723 needSecondUpdate = view()->fitCursor();
1725 if (needSecondUpdate || updateFlags != Update::None) {
1726 view()->buffer()->changed();
1728 lyx_view_->updateStatusBar();
1730 // if we executed a mutating lfun, mark the buffer as dirty
1732 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1733 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1734 view()->buffer()->markDirty();
1736 if (view()->cursor().inTexted()) {
1737 lyx_view_->updateLayoutChoice();
1742 // FIXME UNICODE: _() does not support anything but ascii.
1743 // Do we need a to_ascii() method?
1744 sendDispatchMessage(getMessage(), cmd);
1748 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1750 /* When an action did not originate from the UI/kbd, it makes
1751 * sense to avoid updating the GUI. It turns out that this
1752 * fixes bug 1941, for reasons that are described here:
1753 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1755 if (cmd.origin != FuncRequest::INTERNAL) {
1756 lyx_view_->updateMenubar();
1757 lyx_view_->updateToolbars();
1760 const bool verbose = (cmd.origin == FuncRequest::UI
1761 || cmd.origin == FuncRequest::COMMANDBUFFER);
1763 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1764 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1766 lyx_view_->message(msg);
1770 docstring dispatch_msg = msg;
1771 if (!dispatch_msg.empty())
1772 dispatch_msg += ' ';
1774 string comname = lyxaction.getActionName(cmd.action);
1776 bool argsadded = false;
1778 if (!cmd.argument().empty()) {
1779 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1780 comname += ' ' + to_utf8(cmd.argument());
1785 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1787 if (!shortcuts.empty())
1788 comname += ": " + shortcuts;
1789 else if (!argsadded && !cmd.argument().empty())
1790 comname += ' ' + to_utf8(cmd.argument());
1792 if (!comname.empty()) {
1793 comname = rtrim(comname);
1794 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1797 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1798 << to_utf8(dispatch_msg) << endl;
1799 if (!dispatch_msg.empty())
1800 lyx_view_->message(dispatch_msg);
1804 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1806 string initpath = lyxrc.document_path;
1807 string filename(name);
1809 if (view()->buffer()) {
1810 string const trypath = lyx_view_->buffer()->filePath();
1811 // If directory is writeable, use this as default.
1812 if (isDirWriteable(trypath))
1816 static int newfile_number;
1818 if (filename.empty()) {
1819 filename = addName(lyxrc.document_path,
1820 "newfile" + convert<string>(++newfile_number) + ".lyx");
1821 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1823 filename = addName(lyxrc.document_path,
1824 "newfile" + convert<string>(newfile_number) +
1829 // The template stuff
1832 FileDialog fileDlg(_("Select template file"),
1833 LFUN_SELECT_FILE_SYNC,
1834 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1835 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1837 FileDialog::Result result =
1838 fileDlg.open(from_utf8(lyxrc.template_path),
1839 FileFilterList(_("LyX Documents (*.lyx)")),
1842 if (result.first == FileDialog::Later)
1844 if (result.second.empty())
1846 templname = to_utf8(result.second);
1849 Buffer * const b = newFile(filename, templname, !name.empty());
1851 lyx_view_->setBuffer(b);
1855 void LyXFunc::open(string const & fname)
1857 string initpath = lyxrc.document_path;
1859 if (view()->buffer()) {
1860 string const trypath = lyx_view_->buffer()->filePath();
1861 // If directory is writeable, use this as default.
1862 if (isDirWriteable(trypath))
1868 if (fname.empty()) {
1869 FileDialog fileDlg(_("Select document to open"),
1871 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1872 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1874 FileDialog::Result result =
1875 fileDlg.open(from_utf8(initpath),
1876 FileFilterList(_("LyX Documents (*.lyx)")),
1879 if (result.first == FileDialog::Later)
1882 filename = to_utf8(result.second);
1884 // check selected filename
1885 if (filename.empty()) {
1886 lyx_view_->message(_("Canceled."));
1892 // get absolute path of file and add ".lyx" to the filename if
1894 string const fullpath = fileSearch(string(), filename, "lyx");
1895 if (!fullpath.empty()) {
1896 filename = fullpath;
1899 docstring const disp_fn = makeDisplayPath(filename);
1901 // if the file doesn't exist, let the user create one
1902 if (!fs::exists(filename)) {
1903 // the user specifically chose this name. Believe him.
1904 Buffer * const b = newFile(filename, string(), true);
1906 lyx_view_->setBuffer(b);
1910 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1913 if (lyx_view_->loadLyXFile(filename)) {
1914 str2 = bformat(_("Document %1$s opened."), disp_fn);
1916 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1918 lyx_view_->message(str2);
1922 void LyXFunc::doImport(string const & argument)
1925 string filename = split(argument, format, ' ');
1927 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1928 << " file: " << filename << endl;
1930 // need user interaction
1931 if (filename.empty()) {
1932 string initpath = lyxrc.document_path;
1934 if (view()->buffer()) {
1935 string const trypath = lyx_view_->buffer()->filePath();
1936 // If directory is writeable, use this as default.
1937 if (isDirWriteable(trypath))
1941 docstring const text = bformat(_("Select %1$s file to import"),
1942 formats.prettyName(format));
1944 FileDialog fileDlg(text,
1946 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1947 make_pair(_("Examples|#E#e"),
1948 from_utf8(addPath(package().system_support(), "examples"))));
1950 docstring filter = formats.prettyName(format);
1953 filter += from_utf8(formats.extension(format));
1956 FileDialog::Result result =
1957 fileDlg.open(from_utf8(initpath),
1958 FileFilterList(filter),
1961 if (result.first == FileDialog::Later)
1964 filename = to_utf8(result.second);
1966 // check selected filename
1967 if (filename.empty())
1968 lyx_view_->message(_("Canceled."));
1971 if (filename.empty())
1974 // get absolute path of file
1975 filename = makeAbsPath(filename);
1977 string const lyxfile = changeExtension(filename, ".lyx");
1979 // Check if the document already is open
1980 if (use_gui && theBufferList().exists(lyxfile)) {
1981 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1982 lyx_view_->message(_("Canceled."));
1987 // if the file exists already, and we didn't do
1988 // -i lyx thefile.lyx, warn
1989 if (fs::exists(lyxfile) && filename != lyxfile) {
1990 docstring const file = makeDisplayPath(lyxfile, 30);
1992 docstring text = bformat(_("The document %1$s already exists.\n\n"
1993 "Do you want to over-write that document?"), file);
1994 int const ret = Alert::prompt(_("Over-write document?"),
1995 text, 0, 1, _("&Over-write"), _("&Cancel"));
1998 lyx_view_->message(_("Canceled."));
2003 ErrorList errorList;
2004 Importer::Import(lyx_view_, filename, format, errorList);
2005 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2009 void LyXFunc::closeBuffer()
2011 // save current cursor position
2012 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
2013 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2014 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2015 if (theBufferList().empty()) {
2016 // need this otherwise SEGV may occur while
2017 // trying to set variables that don't exist
2018 // since there's no current buffer
2019 lyx_view_->getDialogs().hideBufferDependent();
2021 lyx_view_->setBuffer(theBufferList().first());
2027 // Each "lyx_view_" should have it's own message method. lyxview and
2028 // the minibuffer would use the minibuffer, but lyxserver would
2029 // send an ERROR signal to its client. Alejandro 970603
2030 // This function is bit problematic when it comes to NLS, to make the
2031 // lyx servers client be language indepenent we must not translate
2032 // strings sent to this func.
2033 void LyXFunc::setErrorMessage(docstring const & m) const
2035 dispatch_buffer = m;
2040 void LyXFunc::setMessage(docstring const & m) const
2042 dispatch_buffer = m;
2046 string const LyXFunc::viewStatusMessage()
2048 // When meta-fake key is pressed, show the key sequence so far + "M-".
2050 return keyseq->print() + "M-";
2052 // Else, when a non-complete key sequence is pressed,
2053 // show the available options.
2054 if (keyseq->length() > 0 && !keyseq->deleted())
2055 return keyseq->printOptions();
2057 if (!view()->buffer())
2058 return to_utf8(_("Welcome to LyX!"));
2060 return view()->cursor().currentState();
2064 BufferView * LyXFunc::view() const
2066 BOOST_ASSERT(lyx_view_);
2067 return lyx_view_->view();
2071 bool LyXFunc::wasMetaKey() const
2073 return (meta_fake_bit != key_modifier::none);
2079 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2081 // Why the switch you might ask. It is a trick to ensure that all
2082 // the elements in the LyXRCTags enum is handled. As you can see
2083 // there are no breaks at all. So it is just a huge fall-through.
2084 // The nice thing is that we will get a warning from the compiler
2085 // if we forget an element.
2086 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2088 case LyXRC::RC_ACCEPT_COMPOUND:
2089 case LyXRC::RC_ALT_LANG:
2090 case LyXRC::RC_ASCIIROFF_COMMAND:
2091 case LyXRC::RC_ASCII_LINELEN:
2092 case LyXRC::RC_AUTOREGIONDELETE:
2093 case LyXRC::RC_AUTORESET_OPTIONS:
2094 case LyXRC::RC_AUTOSAVE:
2095 case LyXRC::RC_AUTO_NUMBER:
2096 case LyXRC::RC_BACKUPDIR_PATH:
2097 case LyXRC::RC_BIBTEX_COMMAND:
2098 case LyXRC::RC_BINDFILE:
2099 case LyXRC::RC_CHECKLASTFILES:
2100 case LyXRC::RC_USELASTFILEPOS:
2101 case LyXRC::RC_LOADSESSION:
2102 case LyXRC::RC_CHKTEX_COMMAND:
2103 case LyXRC::RC_CONVERTER:
2104 case LyXRC::RC_COPIER:
2105 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2106 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2107 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2108 case LyXRC::RC_DATE_INSERT_FORMAT:
2109 case LyXRC::RC_DEFAULT_LANGUAGE:
2110 case LyXRC::RC_DEFAULT_PAPERSIZE:
2111 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2112 case LyXRC::RC_DISPLAY_GRAPHICS:
2113 case LyXRC::RC_DOCUMENTPATH:
2114 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2115 if (fs::exists(lyxrc_new.document_path) &&
2116 fs::is_directory(lyxrc_new.document_path)) {
2117 support::package().document_dir() = lyxrc.document_path;
2120 case LyXRC::RC_ESC_CHARS:
2121 case LyXRC::RC_FONT_ENCODING:
2122 case LyXRC::RC_FORMAT:
2123 case LyXRC::RC_INDEX_COMMAND:
2124 case LyXRC::RC_INPUT:
2125 case LyXRC::RC_KBMAP:
2126 case LyXRC::RC_KBMAP_PRIMARY:
2127 case LyXRC::RC_KBMAP_SECONDARY:
2128 case LyXRC::RC_LABEL_INIT_LENGTH:
2129 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2130 case LyXRC::RC_LANGUAGE_AUTO_END:
2131 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2132 case LyXRC::RC_LANGUAGE_COMMAND_END:
2133 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2134 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2135 case LyXRC::RC_LANGUAGE_PACKAGE:
2136 case LyXRC::RC_LANGUAGE_USE_BABEL:
2137 case LyXRC::RC_MAKE_BACKUP:
2138 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2139 case LyXRC::RC_NUMLASTFILES:
2140 case LyXRC::RC_PATH_PREFIX:
2141 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2142 support::prependEnvPath("PATH", lyxrc.path_prefix);
2144 case LyXRC::RC_PERS_DICT:
2145 case LyXRC::RC_POPUP_BOLD_FONT:
2146 case LyXRC::RC_POPUP_FONT_ENCODING:
2147 case LyXRC::RC_POPUP_NORMAL_FONT:
2148 case LyXRC::RC_PREVIEW:
2149 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2150 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2151 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2152 case LyXRC::RC_PRINTCOPIESFLAG:
2153 case LyXRC::RC_PRINTER:
2154 case LyXRC::RC_PRINTEVENPAGEFLAG:
2155 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2156 case LyXRC::RC_PRINTFILEEXTENSION:
2157 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2158 case LyXRC::RC_PRINTODDPAGEFLAG:
2159 case LyXRC::RC_PRINTPAGERANGEFLAG:
2160 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2161 case LyXRC::RC_PRINTPAPERFLAG:
2162 case LyXRC::RC_PRINTREVERSEFLAG:
2163 case LyXRC::RC_PRINTSPOOL_COMMAND:
2164 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2165 case LyXRC::RC_PRINTTOFILE:
2166 case LyXRC::RC_PRINTTOPRINTER:
2167 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2168 case LyXRC::RC_PRINT_COMMAND:
2169 case LyXRC::RC_RTL_SUPPORT:
2170 case LyXRC::RC_SCREEN_DPI:
2171 case LyXRC::RC_SCREEN_FONT_ENCODING:
2172 case LyXRC::RC_SCREEN_FONT_ROMAN:
2173 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2174 case LyXRC::RC_SCREEN_FONT_SANS:
2175 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2176 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2177 case LyXRC::RC_SCREEN_FONT_SIZES:
2178 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2179 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2180 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2181 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2182 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2183 case LyXRC::RC_SCREEN_ZOOM:
2184 case LyXRC::RC_SERVERPIPE:
2185 case LyXRC::RC_SET_COLOR:
2186 case LyXRC::RC_SHOW_BANNER:
2187 case LyXRC::RC_SPELL_COMMAND:
2188 case LyXRC::RC_TEMPDIRPATH:
2189 case LyXRC::RC_TEMPLATEPATH:
2190 case LyXRC::RC_TEX_ALLOWS_SPACES:
2191 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2192 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2193 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2195 case LyXRC::RC_UIFILE:
2196 case LyXRC::RC_USER_EMAIL:
2197 case LyXRC::RC_USER_NAME:
2198 case LyXRC::RC_USETEMPDIR:
2199 case LyXRC::RC_USE_ALT_LANG:
2200 case LyXRC::RC_USE_ESC_CHARS:
2201 case LyXRC::RC_USE_INP_ENC:
2202 case LyXRC::RC_USE_PERS_DICT:
2203 case LyXRC::RC_USE_SPELL_LIB:
2204 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2205 case LyXRC::RC_VIEWER:
2206 case LyXRC::RC_LAST: