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 // this one is difficult to get right. As a half-baked
577 // solution, we consider only the first action of the sequence
578 case LFUN_COMMAND_SEQUENCE: {
579 // argument contains ';'-terminated commands
580 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
581 FuncRequest func(lyxaction.lookupFunc(firstcmd));
582 func.origin = cmd.origin;
583 flag = getStatus(func);
586 case LFUN_BUFFER_NEW:
587 case LFUN_BUFFER_NEW_TEMPLATE:
588 case LFUN_WORD_FIND_FORWARD:
589 case LFUN_WORD_FIND_BACKWARD:
590 case LFUN_COMMAND_PREFIX:
591 case LFUN_COMMAND_EXECUTE:
593 case LFUN_META_PREFIX:
594 case LFUN_BUFFER_CLOSE:
595 case LFUN_BUFFER_WRITE_AS:
596 case LFUN_BUFFER_UPDATE:
597 case LFUN_BUFFER_VIEW:
598 case LFUN_BUFFER_IMPORT:
600 case LFUN_BUFFER_AUTO_SAVE:
601 case LFUN_RECONFIGURE:
605 case LFUN_DROP_LAYOUTS_CHOICE:
607 case LFUN_SERVER_GET_NAME:
608 case LFUN_SERVER_NOTIFY:
609 case LFUN_SERVER_GOTO_FILE_ROW:
610 case LFUN_DIALOG_HIDE:
611 case LFUN_DIALOG_DISCONNECT_INSET:
612 case LFUN_BUFFER_CHILD_OPEN:
613 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
614 case LFUN_KEYMAP_OFF:
615 case LFUN_KEYMAP_PRIMARY:
616 case LFUN_KEYMAP_SECONDARY:
617 case LFUN_KEYMAP_TOGGLE:
619 case LFUN_BUFFER_EXPORT_CUSTOM:
620 case LFUN_BUFFER_PRINT:
621 case LFUN_PREFERENCES_SAVE:
622 case LFUN_SCREEN_FONT_UPDATE:
625 case LFUN_EXTERNAL_EDIT:
626 case LFUN_GRAPHICS_EDIT:
627 case LFUN_ALL_INSETS_TOGGLE:
628 case LFUN_BUFFER_LANGUAGE:
629 case LFUN_TEXTCLASS_APPLY:
630 case LFUN_TEXTCLASS_LOAD:
631 case LFUN_BUFFER_SAVE_AS_DEFAULT:
632 case LFUN_BUFFER_PARAMS_APPLY:
633 case LFUN_LYXRC_APPLY:
634 case LFUN_BUFFER_NEXT:
635 case LFUN_BUFFER_PREVIOUS:
636 case LFUN_WINDOW_NEW:
637 case LFUN_WINDOW_CLOSE:
638 // these are handled in our dispatch()
642 if (!getLocalStatus(cur, cmd, flag))
643 flag = view()->getStatus(cmd);
649 // Can we use a readonly buffer?
650 if (buf && buf->isReadonly()
651 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
652 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
653 flag.message(from_utf8(N_("Document is read-only")));
657 // Are we in a DELETED change-tracking region?
658 if (buf && lookupChangeType(cur, true) == Change::DELETED
659 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
660 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
661 flag.message(from_utf8(N_("This portion of the document is deleted.")));
665 // the default error message if we disable the command
666 if (!flag.enabled() && flag.message().empty())
667 flag.message(from_utf8(N_("Command disabled")));
673 bool LyXFunc::ensureBufferClean(BufferView * bv)
675 Buffer & buf = *bv->buffer();
679 docstring const file = makeDisplayPath(buf.fileName(), 30);
680 docstring text = bformat(_("The document %1$s has unsaved "
681 "changes.\n\nDo you want to save "
682 "the document?"), file);
683 int const ret = Alert::prompt(_("Save changed document?"),
684 text, 0, 1, _("&Save"),
688 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
690 return buf.isClean();
696 void showPrintError(string const & name)
698 docstring str = bformat(_("Could not print the document %1$s.\n"
699 "Check that your printer is set up correctly."),
700 makeDisplayPath(name, 50));
701 Alert::error(_("Print document failed"), str);
705 void loadTextclass(string const & name)
707 std::pair<bool, textclass_type> const tc_pair =
708 textclasslist.numberOfClass(name);
710 if (!tc_pair.first) {
711 lyxerr << "Document class \"" << name
712 << "\" does not exist."
717 textclass_type const tc = tc_pair.second;
719 if (!textclasslist[tc].load()) {
720 docstring s = bformat(_("The document could not be converted\n"
721 "into the document class %1$s."),
722 from_utf8(textclasslist[tc].name()));
723 Alert::error(_("Could not change class"), s);
728 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
733 void LyXFunc::dispatch(FuncRequest const & cmd)
735 string const argument = to_utf8(cmd.argument());
736 kb_action const action = cmd.action;
738 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
739 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
741 // we have not done anything wrong yet.
743 dispatch_buffer.erase();
745 // redraw the screen at the end (first of the two drawing steps).
746 //This is done unless explicitely requested otherwise
747 Update::flags updateFlags = Update::FitCursor;
749 FuncStatus const flag = getStatus(cmd);
750 if (!flag.enabled()) {
751 // We cannot use this function here
752 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
753 << lyxaction.getActionName(action)
754 << " [" << action << "] is disabled at this location"
756 setErrorMessage(flag.message());
760 case LFUN_WORD_FIND_FORWARD:
761 case LFUN_WORD_FIND_BACKWARD: {
762 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
763 static string last_search;
764 string searched_string;
766 if (!argument.empty()) {
767 last_search = argument;
768 searched_string = argument;
770 searched_string = last_search;
773 if (searched_string.empty())
776 bool const fw = action == LFUN_WORD_FIND_FORWARD;
778 find2string(searched_string, true, false, fw);
779 find(view(), FuncRequest(LFUN_WORD_FIND, data));
783 case LFUN_COMMAND_PREFIX:
784 BOOST_ASSERT(lyx_view_);
785 lyx_view_->message(from_utf8(keyseq->printOptions()));
788 case LFUN_COMMAND_EXECUTE:
789 BOOST_ASSERT(lyx_view_);
790 lyx_view_->getToolbars().display("minibuffer", true);
791 lyx_view_->focus_command_buffer();
795 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
797 meta_fake_bit = key_modifier::none;
798 if (view()->buffer())
799 // cancel any selection
800 dispatch(FuncRequest(LFUN_MARK_OFF));
801 setMessage(_("Cancel"));
804 case LFUN_META_PREFIX:
805 meta_fake_bit = key_modifier::alt;
806 setMessage(from_utf8(keyseq->print()));
809 case LFUN_BUFFER_TOGGLE_READ_ONLY:
810 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
811 if (lyx_view_->buffer()->lyxvc().inUse())
812 lyx_view_->buffer()->lyxvc().toggleReadOnly();
814 lyx_view_->buffer()->setReadonly(
815 !lyx_view_->buffer()->isReadonly());
818 // --- Menus -----------------------------------------------
819 case LFUN_BUFFER_NEW:
820 menuNew(argument, false);
823 case LFUN_BUFFER_NEW_TEMPLATE:
824 menuNew(argument, true);
827 case LFUN_BUFFER_CLOSE:
832 case LFUN_BUFFER_WRITE:
833 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
834 if (!lyx_view_->buffer()->isUnnamed()) {
835 docstring const str = bformat(_("Saving document %1$s..."),
836 makeDisplayPath(lyx_view_->buffer()->fileName()));
837 lyx_view_->message(str);
838 menuWrite(lyx_view_->buffer());
839 lyx_view_->message(str + _(" done."));
841 writeAs(lyx_view_->buffer());
842 updateFlags = Update::None;
845 case LFUN_BUFFER_WRITE_AS:
846 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
847 writeAs(lyx_view_->buffer(), argument);
848 updateFlags = Update::None;
851 case LFUN_BUFFER_RELOAD: {
852 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
853 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
854 docstring text = bformat(_("Any changes will be lost. Are you sure "
855 "you want to revert to the saved version of the document %1$s?"), file);
856 int const ret = Alert::prompt(_("Revert to saved document?"),
857 text, 0, 1, _("&Revert"), _("&Cancel"));
864 case LFUN_BUFFER_UPDATE:
865 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
866 Exporter::Export(lyx_view_->buffer(), argument, true);
869 case LFUN_BUFFER_VIEW:
870 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
871 Exporter::preview(lyx_view_->buffer(), argument);
874 case LFUN_BUILD_PROGRAM:
875 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
876 Exporter::Export(lyx_view_->buffer(), "program", true);
879 case LFUN_BUFFER_CHKTEX:
880 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
881 lyx_view_->buffer()->runChktex();
884 case LFUN_BUFFER_EXPORT:
885 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
886 if (argument == "custom")
887 lyx_view_->getDialogs().show("sendto");
889 Exporter::Export(lyx_view_->buffer(), argument, false);
893 case LFUN_BUFFER_EXPORT_CUSTOM: {
894 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
896 string command = split(argument, format_name, ' ');
897 Format const * format = formats.getFormat(format_name);
899 lyxerr << "Format \"" << format_name
900 << "\" not recognized!"
905 Buffer * buffer = lyx_view_->buffer();
907 // The name of the file created by the conversion process
910 // Output to filename
911 if (format->name() == "lyx") {
912 string const latexname =
913 buffer->getLatexName(false);
914 filename = changeExtension(latexname,
915 format->extension());
916 filename = addName(buffer->temppath(), filename);
918 if (!buffer->writeFile(filename))
922 Exporter::Export(buffer, format_name, true, filename);
925 // Substitute $$FName for filename
926 if (!contains(command, "$$FName"))
927 command = "( " + command + " ) < $$FName";
928 command = subst(command, "$$FName", filename);
930 // Execute the command in the background
932 call.startscript(Systemcall::DontWait, command);
936 case LFUN_BUFFER_PRINT: {
937 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
940 string command = split(split(argument, target, ' '),
944 || target_name.empty()
945 || command.empty()) {
946 lyxerr << "Unable to parse \""
947 << argument << '"' << std::endl;
950 if (target != "printer" && target != "file") {
951 lyxerr << "Unrecognized target \""
952 << target << '"' << std::endl;
956 Buffer * buffer = lyx_view_->buffer();
958 if (!Exporter::Export(buffer, "dvi", true)) {
959 showPrintError(buffer->fileName());
963 // Push directory path.
964 string const path = buffer->temppath();
965 support::Path p(path);
967 // there are three cases here:
968 // 1. we print to a file
969 // 2. we print directly to a printer
970 // 3. we print using a spool command (print to file first)
973 string const dviname =
974 changeExtension(buffer->getLatexName(true),
977 if (target == "printer") {
978 if (!lyxrc.print_spool_command.empty()) {
979 // case 3: print using a spool
980 string const psname =
981 changeExtension(dviname,".ps");
982 command += lyxrc.print_to_file
985 + quoteName(dviname);
988 lyxrc.print_spool_command +' ';
989 if (target_name != "default") {
990 command2 += lyxrc.print_spool_printerprefix
994 command2 += quoteName(psname);
996 // If successful, then spool command
997 res = one.startscript(
1002 res = one.startscript(
1003 Systemcall::DontWait,
1006 // case 2: print directly to a printer
1007 res = one.startscript(
1008 Systemcall::DontWait,
1009 command + quoteName(dviname));
1013 // case 1: print to a file
1014 command += lyxrc.print_to_file
1015 + quoteName(makeAbsPath(target_name,
1018 + quoteName(dviname);
1019 res = one.startscript(Systemcall::DontWait,
1024 showPrintError(buffer->fileName());
1028 case LFUN_BUFFER_IMPORT:
1033 if (argument != "force") {
1034 if (!theApp->gui().closeAll())
1039 // FIXME: this code needs to be transfered somewhere else
1040 // as lyx_view_ will most certainly be null and a same buffer
1041 // might be visible in more than one LyXView.
1042 if (lyx_view_ && lyx_view_->view()->buffer()) {
1043 // save cursor Position for opened files to .lyx/session
1044 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
1045 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1051 case LFUN_TOC_VIEW: {
1052 BOOST_ASSERT(lyx_view_);
1053 InsetCommandParams p("tableofcontents");
1054 string const data = InsetCommandMailer::params2string("toc", p);
1055 lyx_view_->getDialogs().show("toc", data, 0);
1059 case LFUN_BUFFER_AUTO_SAVE:
1063 case LFUN_RECONFIGURE:
1064 BOOST_ASSERT(lyx_view_);
1065 reconfigure(*lyx_view_);
1068 case LFUN_HELP_OPEN: {
1069 BOOST_ASSERT(lyx_view_);
1070 string const arg = argument;
1072 setErrorMessage(_("Missing argument"));
1075 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1076 if (fname.empty()) {
1077 lyxerr << "LyX: unable to find documentation file `"
1078 << arg << "'. Bad installation?" << endl;
1081 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1082 makeDisplayPath(fname)));
1083 lyx_view_->loadLyXFile(fname, false);
1087 // --- version control -------------------------------
1088 case LFUN_VC_REGISTER:
1089 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1090 if (!ensureBufferClean(view()))
1092 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1093 lyx_view_->buffer()->lyxvc().registrer();
1098 case LFUN_VC_CHECK_IN:
1099 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1100 if (!ensureBufferClean(view()))
1102 if (lyx_view_->buffer()->lyxvc().inUse()
1103 && !lyx_view_->buffer()->isReadonly()) {
1104 lyx_view_->buffer()->lyxvc().checkIn();
1109 case LFUN_VC_CHECK_OUT:
1110 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1111 if (!ensureBufferClean(view()))
1113 if (lyx_view_->buffer()->lyxvc().inUse()
1114 && lyx_view_->buffer()->isReadonly()) {
1115 lyx_view_->buffer()->lyxvc().checkOut();
1120 case LFUN_VC_REVERT:
1121 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1122 lyx_view_->buffer()->lyxvc().revert();
1126 case LFUN_VC_UNDO_LAST:
1127 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1128 lyx_view_->buffer()->lyxvc().undoLast();
1132 // --- buffers ----------------------------------------
1133 case LFUN_BUFFER_SWITCH:
1134 BOOST_ASSERT(lyx_view_);
1135 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1138 case LFUN_BUFFER_NEXT:
1139 BOOST_ASSERT(lyx_view_);
1140 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1143 case LFUN_BUFFER_PREVIOUS:
1144 BOOST_ASSERT(lyx_view_);
1145 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1149 BOOST_ASSERT(lyx_view_);
1150 newFile(view(), argument);
1153 case LFUN_FILE_OPEN:
1154 BOOST_ASSERT(lyx_view_);
1158 case LFUN_DROP_LAYOUTS_CHOICE:
1159 BOOST_ASSERT(lyx_view_);
1160 lyx_view_->getToolbars().openLayoutList();
1163 case LFUN_MENU_OPEN:
1164 BOOST_ASSERT(lyx_view_);
1165 lyx_view_->getMenubar().openByName(from_utf8(argument));
1168 // --- lyxserver commands ----------------------------
1169 case LFUN_SERVER_GET_NAME:
1170 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1171 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1172 lyxerr[Debug::INFO] << "FNAME["
1173 << lyx_view_->buffer()->fileName()
1177 case LFUN_SERVER_NOTIFY:
1178 dispatch_buffer = from_utf8(keyseq->print());
1179 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1182 case LFUN_SERVER_GOTO_FILE_ROW: {
1183 BOOST_ASSERT(lyx_view_);
1186 istringstream is(argument);
1187 is >> file_name >> row;
1188 if (prefixIs(file_name, package().temp_dir())) {
1189 // Needed by inverse dvi search. If it is a file
1190 // in tmpdir, call the apropriated function
1191 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1193 // Must replace extension of the file to be .lyx
1194 // and get full path
1195 string const s = changeExtension(file_name, ".lyx");
1196 // Either change buffer or load the file
1197 if (theBufferList().exists(s)) {
1198 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1200 lyx_view_->loadLyXFile(s);
1204 view()->setCursorFromRow(row);
1207 // see BufferView::center()
1211 case LFUN_DIALOG_SHOW: {
1212 BOOST_ASSERT(lyx_view_);
1213 string const name = cmd.getArg(0);
1214 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1216 if (name == "character") {
1217 data = freefont2string();
1219 lyx_view_->getDialogs().show("character", data);
1220 } else if (name == "latexlog") {
1221 pair<Buffer::LogType, string> const logfile =
1222 lyx_view_->buffer()->getLogName();
1223 switch (logfile.first) {
1224 case Buffer::latexlog:
1227 case Buffer::buildlog:
1231 data += LyXLex::quoteString(logfile.second);
1232 lyx_view_->getDialogs().show("log", data);
1233 } else if (name == "vclog") {
1234 string const data = "vc " +
1235 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1236 lyx_view_->getDialogs().show("log", data);
1238 lyx_view_->getDialogs().show(name, data);
1242 case LFUN_DIALOG_SHOW_NEW_INSET: {
1243 BOOST_ASSERT(lyx_view_);
1244 string const name = cmd.getArg(0);
1245 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1246 if (name == "bibitem" ||
1253 InsetCommandParams p(name);
1254 data = InsetCommandMailer::params2string(name, p);
1255 } else if (name == "include") {
1256 InsetCommandParams p(data);
1257 data = InsetIncludeMailer::params2string(p);
1258 } else if (name == "box") {
1259 // \c data == "Boxed" || "Frameless" etc
1260 InsetBoxParams p(data);
1261 data = InsetBoxMailer::params2string(p);
1262 } else if (name == "branch") {
1263 InsetBranchParams p;
1264 data = InsetBranchMailer::params2string(p);
1265 } else if (name == "citation") {
1266 InsetCommandParams p("cite");
1267 data = InsetCommandMailer::params2string(name, p);
1268 } else if (name == "ert") {
1269 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1270 } else if (name == "external") {
1271 InsetExternalParams p;
1272 Buffer const & buffer = *lyx_view_->buffer();
1273 data = InsetExternalMailer::params2string(p, buffer);
1274 } else if (name == "float") {
1276 data = InsetFloatMailer::params2string(p);
1277 } else if (name == "graphics") {
1278 InsetGraphicsParams p;
1279 Buffer const & buffer = *lyx_view_->buffer();
1280 data = InsetGraphicsMailer::params2string(p, buffer);
1281 } else if (name == "note") {
1283 data = InsetNoteMailer::params2string(p);
1284 } else if (name == "vspace") {
1286 data = InsetVSpaceMailer::params2string(space);
1287 } else if (name == "wrap") {
1289 data = InsetWrapMailer::params2string(p);
1291 lyx_view_->getDialogs().show(name, data, 0);
1295 case LFUN_DIALOG_UPDATE: {
1296 BOOST_ASSERT(lyx_view_);
1297 string const & name = argument;
1298 // Can only update a dialog connected to an existing inset
1299 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1301 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1302 inset->dispatch(view()->cursor(), fr);
1303 } else if (name == "paragraph") {
1304 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1305 } else if (name == "prefs") {
1306 lyx_view_->getDialogs().update(name, string());
1311 case LFUN_DIALOG_HIDE:
1312 Dialogs::hide(argument, 0);
1315 case LFUN_DIALOG_DISCONNECT_INSET:
1316 BOOST_ASSERT(lyx_view_);
1317 lyx_view_->getDialogs().disconnect(argument);
1321 case LFUN_CITATION_INSERT: {
1322 BOOST_ASSERT(lyx_view_);
1323 if (!argument.empty()) {
1324 // we can have one optional argument, delimited by '|'
1325 // citation-insert <key>|<text_before>
1326 // this should be enhanced to also support text_after
1327 // and citation style
1328 string arg = argument;
1330 if (contains(argument, "|")) {
1331 arg = token(argument, '|', 0);
1332 opt1 = '[' + token(argument, '|', 1) + ']';
1334 std::ostringstream os;
1335 os << "citation LatexCommand\n"
1336 << "\\cite" << opt1 << "{" << arg << "}\n"
1338 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1341 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1345 case LFUN_BUFFER_CHILD_OPEN: {
1346 BOOST_ASSERT(lyx_view_);
1347 string const filename =
1348 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1349 // FIXME Should use bformat
1350 setMessage(_("Opening child document ") +
1351 makeDisplayPath(filename) + "...");
1352 view()->saveBookmark(false);
1353 string const parentfilename = lyx_view_->buffer()->fileName();
1354 if (theBufferList().exists(filename))
1355 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1357 lyx_view_->loadLyXFile(filename);
1358 // Set the parent name of the child document.
1359 // This makes insertion of citations and references in the child work,
1360 // when the target is in the parent or another child document.
1361 lyx_view_->buffer()->setParentName(parentfilename);
1365 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1366 BOOST_ASSERT(lyx_view_);
1367 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1370 case LFUN_KEYMAP_OFF:
1371 BOOST_ASSERT(lyx_view_);
1372 lyx_view_->view()->getIntl().keyMapOn(false);
1375 case LFUN_KEYMAP_PRIMARY:
1376 BOOST_ASSERT(lyx_view_);
1377 lyx_view_->view()->getIntl().keyMapPrim();
1380 case LFUN_KEYMAP_SECONDARY:
1381 BOOST_ASSERT(lyx_view_);
1382 lyx_view_->view()->getIntl().keyMapSec();
1385 case LFUN_KEYMAP_TOGGLE:
1386 BOOST_ASSERT(lyx_view_);
1387 lyx_view_->view()->getIntl().toggleKeyMap();
1393 string rest = split(argument, countstr, ' ');
1394 istringstream is(countstr);
1397 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1398 for (int i = 0; i < count; ++i)
1399 dispatch(lyxaction.lookupFunc(rest));
1403 case LFUN_COMMAND_SEQUENCE: {
1404 // argument contains ';'-terminated commands
1405 string arg = argument;
1406 while (!arg.empty()) {
1408 arg = split(arg, first, ';');
1409 FuncRequest func(lyxaction.lookupFunc(first));
1410 func.origin = cmd.origin;
1416 case LFUN_PREFERENCES_SAVE: {
1417 support::Path p(package().user_support());
1418 lyxrc.write("preferences", false);
1422 case LFUN_SCREEN_FONT_UPDATE:
1423 BOOST_ASSERT(lyx_view_);
1424 // handle the screen font changes.
1425 lyxrc.set_font_norm_type();
1426 theFontLoader().update();
1427 // All visible buffers will need resize
1431 case LFUN_SET_COLOR: {
1433 string const x11_name = split(argument, lyx_name, ' ');
1434 if (lyx_name.empty() || x11_name.empty()) {
1435 setErrorMessage(_("Syntax: set-color <lyx_name>"
1440 bool const graphicsbg_changed =
1441 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1442 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1444 if (!lcolor.setColor(lyx_name, x11_name)) {
1446 bformat(_("Set-color \"%1$s\" failed "
1447 "- color is undefined or "
1448 "may not be redefined"),
1449 from_utf8(lyx_name)));
1453 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1455 if (graphicsbg_changed) {
1456 #ifdef WITH_WARNINGS
1457 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1460 graphics::GCache::get().changeDisplay(true);
1467 BOOST_ASSERT(lyx_view_);
1468 lyx_view_->message(from_utf8(argument));
1471 case LFUN_EXTERNAL_EDIT: {
1472 BOOST_ASSERT(lyx_view_);
1473 FuncRequest fr(action, argument);
1474 InsetExternal().dispatch(view()->cursor(), fr);
1478 case LFUN_GRAPHICS_EDIT: {
1479 FuncRequest fr(action, argument);
1480 InsetGraphics().dispatch(view()->cursor(), fr);
1484 case LFUN_INSET_APPLY: {
1485 BOOST_ASSERT(lyx_view_);
1486 string const name = cmd.getArg(0);
1487 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1489 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1490 inset->dispatch(view()->cursor(), fr);
1492 FuncRequest fr(LFUN_INSET_INSERT, argument);
1495 // ideally, the update flag should be set by the insets,
1496 // but this is not possible currently
1497 updateFlags = Update::Force | Update::FitCursor;
1501 case LFUN_ALL_INSETS_TOGGLE: {
1502 BOOST_ASSERT(lyx_view_);
1504 string const name = split(argument, action, ' ');
1505 InsetBase::Code const inset_code =
1506 InsetBase::translate(name);
1508 LCursor & cur = view()->cursor();
1509 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1511 InsetBase & inset = lyx_view_->buffer()->inset();
1512 InsetIterator it = inset_iterator_begin(inset);
1513 InsetIterator const end = inset_iterator_end(inset);
1514 for (; it != end; ++it) {
1515 if (inset_code == InsetBase::NO_CODE
1516 || inset_code == it->lyxCode()) {
1517 LCursor tmpcur = cur;
1518 tmpcur.pushLeft(*it);
1519 it->dispatch(tmpcur, fr);
1522 updateFlags = Update::Force | Update::FitCursor;
1526 case LFUN_BUFFER_LANGUAGE: {
1527 BOOST_ASSERT(lyx_view_);
1528 Buffer & buffer = *lyx_view_->buffer();
1529 Language const * oldL = buffer.params().language;
1530 Language const * newL = languages.getLanguage(argument);
1531 if (!newL || oldL == newL)
1534 if (oldL->rightToLeft() == newL->rightToLeft()
1535 && !buffer.isMultiLingual())
1536 buffer.changeLanguage(oldL, newL);
1538 buffer.updateDocLang(newL);
1542 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1543 string const fname =
1544 addName(addPath(package().user_support(), "templates/"),
1546 Buffer defaults(fname);
1548 istringstream ss(argument);
1551 int const unknown_tokens = defaults.readHeader(lex);
1553 if (unknown_tokens != 0) {
1554 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1555 << unknown_tokens << " unknown token"
1556 << (unknown_tokens == 1 ? "" : "s")
1560 if (defaults.writeFile(defaults.fileName()))
1561 // FIXME Should use bformat
1562 setMessage(_("Document defaults saved in ")
1563 + makeDisplayPath(fname));
1565 setErrorMessage(_("Unable to save document defaults"));
1569 case LFUN_BUFFER_PARAMS_APPLY: {
1570 BOOST_ASSERT(lyx_view_);
1571 biblio::CiteEngine const engine =
1572 lyx_view_->buffer()->params().cite_engine;
1574 istringstream ss(argument);
1577 int const unknown_tokens =
1578 lyx_view_->buffer()->readHeader(lex);
1580 if (unknown_tokens != 0) {
1581 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1582 << unknown_tokens << " unknown token"
1583 << (unknown_tokens == 1 ? "" : "s")
1586 if (engine == lyx_view_->buffer()->params().cite_engine)
1589 LCursor & cur = view()->cursor();
1590 FuncRequest fr(LFUN_INSET_REFRESH);
1592 InsetBase & inset = lyx_view_->buffer()->inset();
1593 InsetIterator it = inset_iterator_begin(inset);
1594 InsetIterator const end = inset_iterator_end(inset);
1595 for (; it != end; ++it)
1596 if (it->lyxCode() == InsetBase::CITE_CODE)
1597 it->dispatch(cur, fr);
1601 case LFUN_TEXTCLASS_APPLY: {
1602 BOOST_ASSERT(lyx_view_);
1603 Buffer * buffer = lyx_view_->buffer();
1605 textclass_type const old_class =
1606 buffer->params().textclass;
1608 loadTextclass(argument);
1610 std::pair<bool, textclass_type> const tc_pair =
1611 textclasslist.numberOfClass(argument);
1616 textclass_type const new_class = tc_pair.second;
1617 if (old_class == new_class)
1621 lyx_view_->message(_("Converting document to new document class..."));
1622 recordUndoFullDocument(view());
1623 buffer->params().textclass = new_class;
1624 StableDocIterator backcur(view()->cursor());
1625 ErrorList & el = buffer->errorList("Class Switch");
1626 cap::switchBetweenClasses(
1627 old_class, new_class,
1628 static_cast<InsetText &>(buffer->inset()), el);
1630 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1632 buffer->errors("Class Switch");
1633 updateLabels(*buffer);
1634 updateFlags = Update::Force | Update::FitCursor;
1638 case LFUN_TEXTCLASS_LOAD:
1639 loadTextclass(argument);
1642 case LFUN_LYXRC_APPLY: {
1643 LyXRC const lyxrc_orig = lyxrc;
1645 istringstream ss(argument);
1646 bool const success = lyxrc.read(ss) == 0;
1649 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1650 << "Unable to read lyxrc data"
1655 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1659 case LFUN_WINDOW_NEW:
1660 LyX::ref().newLyXView();
1663 case LFUN_WINDOW_CLOSE:
1664 BOOST_ASSERT(lyx_view_);
1665 BOOST_ASSERT(theApp);
1667 // We return here because lyx_view does not exists anymore.
1670 case LFUN_BOOKMARK_GOTO: {
1671 BOOST_ASSERT(lyx_view_);
1672 unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1673 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1674 BOOST_ASSERT(!bm.filename.empty());
1675 // if the file is not opened, open it.
1676 if (!theBufferList().exists(bm.filename))
1677 dispatch(FuncRequest(LFUN_FILE_OPEN, bm.filename));
1678 // open may fail, so we need to test it again
1679 if (theBufferList().exists(bm.filename)) {
1680 // if the current buffer is not that one, switch to it.
1681 if (lyx_view_->buffer()->fileName() != bm.filename)
1682 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, bm.filename));
1683 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != bm.filename);
1684 view()->moveToPosition(bm.par_id, bm.par_pos);
1689 case LFUN_BOOKMARK_CLEAR:
1690 LyX::ref().session().bookmarks().clear();
1694 BOOST_ASSERT(lyx_view_);
1695 view()->cursor().dispatch(cmd);
1696 updateFlags = view()->cursor().result().update();
1697 if (!view()->cursor().result().dispatched())
1698 if (view()->dispatch(cmd))
1699 updateFlags = Update::Force | Update::FitCursor;
1704 if (lyx_view_ && view()->buffer()) {
1705 // Redraw screen unless explicitly told otherwise.
1706 // This also initializes the position cache for all insets
1707 // in (at least partially) visible top-level paragraphs.
1708 bool needSecondUpdate = false;
1709 if (updateFlags != Update::None)
1710 view()->update(updateFlags);
1712 needSecondUpdate = view()->fitCursor();
1714 if (needSecondUpdate || updateFlags != Update::None) {
1715 view()->buffer()->changed();
1717 lyx_view_->updateStatusBar();
1719 // if we executed a mutating lfun, mark the buffer as dirty
1721 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1722 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1723 view()->buffer()->markDirty();
1725 if (view()->cursor().inTexted()) {
1726 lyx_view_->updateLayoutChoice();
1731 // FIXME UNICODE: _() does not support anything but ascii.
1732 // Do we need a to_ascii() method?
1733 sendDispatchMessage(getMessage(), cmd);
1737 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1739 /* When an action did not originate from the UI/kbd, it makes
1740 * sense to avoid updating the GUI. It turns out that this
1741 * fixes bug 1941, for reasons that are described here:
1742 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1744 if (cmd.origin != FuncRequest::INTERNAL) {
1745 lyx_view_->updateMenubar();
1746 lyx_view_->updateToolbars();
1749 const bool verbose = (cmd.origin == FuncRequest::UI
1750 || cmd.origin == FuncRequest::COMMANDBUFFER);
1752 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1753 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1755 lyx_view_->message(msg);
1759 docstring dispatch_msg = msg;
1760 if (!dispatch_msg.empty())
1761 dispatch_msg += ' ';
1763 string comname = lyxaction.getActionName(cmd.action);
1765 bool argsadded = false;
1767 if (!cmd.argument().empty()) {
1768 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1769 comname += ' ' + to_utf8(cmd.argument());
1774 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1776 if (!shortcuts.empty())
1777 comname += ": " + shortcuts;
1778 else if (!argsadded && !cmd.argument().empty())
1779 comname += ' ' + to_utf8(cmd.argument());
1781 if (!comname.empty()) {
1782 comname = rtrim(comname);
1783 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1786 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1787 << to_utf8(dispatch_msg) << endl;
1788 if (!dispatch_msg.empty())
1789 lyx_view_->message(dispatch_msg);
1793 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1795 string initpath = lyxrc.document_path;
1796 string filename(name);
1798 if (view()->buffer()) {
1799 string const trypath = lyx_view_->buffer()->filePath();
1800 // If directory is writeable, use this as default.
1801 if (isDirWriteable(trypath))
1805 static int newfile_number;
1807 if (filename.empty()) {
1808 filename = addName(lyxrc.document_path,
1809 "newfile" + convert<string>(++newfile_number) + ".lyx");
1810 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1812 filename = addName(lyxrc.document_path,
1813 "newfile" + convert<string>(newfile_number) +
1818 // The template stuff
1821 FileDialog fileDlg(_("Select template file"),
1822 LFUN_SELECT_FILE_SYNC,
1823 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1824 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1826 FileDialog::Result result =
1827 fileDlg.open(from_utf8(lyxrc.template_path),
1828 FileFilterList(_("LyX Documents (*.lyx)")),
1831 if (result.first == FileDialog::Later)
1833 if (result.second.empty())
1835 templname = to_utf8(result.second);
1838 Buffer * const b = newFile(filename, templname, !name.empty());
1840 lyx_view_->setBuffer(b);
1844 void LyXFunc::open(string const & fname)
1846 string initpath = lyxrc.document_path;
1848 if (view()->buffer()) {
1849 string const trypath = lyx_view_->buffer()->filePath();
1850 // If directory is writeable, use this as default.
1851 if (isDirWriteable(trypath))
1857 if (fname.empty()) {
1858 FileDialog fileDlg(_("Select document to open"),
1860 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1861 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1863 FileDialog::Result result =
1864 fileDlg.open(from_utf8(initpath),
1865 FileFilterList(_("LyX Documents (*.lyx)")),
1868 if (result.first == FileDialog::Later)
1871 filename = to_utf8(result.second);
1873 // check selected filename
1874 if (filename.empty()) {
1875 lyx_view_->message(_("Canceled."));
1881 // get absolute path of file and add ".lyx" to the filename if
1883 string const fullpath = fileSearch(string(), filename, "lyx");
1884 if (!fullpath.empty()) {
1885 filename = fullpath;
1888 docstring const disp_fn = makeDisplayPath(filename);
1890 // if the file doesn't exist, let the user create one
1891 if (!fs::exists(filename)) {
1892 // the user specifically chose this name. Believe him.
1893 Buffer * const b = newFile(filename, string(), true);
1895 lyx_view_->setBuffer(b);
1899 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1902 if (lyx_view_->loadLyXFile(filename)) {
1903 str2 = bformat(_("Document %1$s opened."), disp_fn);
1905 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1907 lyx_view_->message(str2);
1911 void LyXFunc::doImport(string const & argument)
1914 string filename = split(argument, format, ' ');
1916 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1917 << " file: " << filename << endl;
1919 // need user interaction
1920 if (filename.empty()) {
1921 string initpath = lyxrc.document_path;
1923 if (view()->buffer()) {
1924 string const trypath = lyx_view_->buffer()->filePath();
1925 // If directory is writeable, use this as default.
1926 if (isDirWriteable(trypath))
1930 docstring const text = bformat(_("Select %1$s file to import"),
1931 formats.prettyName(format));
1933 FileDialog fileDlg(text,
1935 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1936 make_pair(_("Examples|#E#e"),
1937 from_utf8(addPath(package().system_support(), "examples"))));
1939 docstring filter = formats.prettyName(format);
1942 filter += from_utf8(formats.extension(format));
1945 FileDialog::Result result =
1946 fileDlg.open(from_utf8(initpath),
1947 FileFilterList(filter),
1950 if (result.first == FileDialog::Later)
1953 filename = to_utf8(result.second);
1955 // check selected filename
1956 if (filename.empty())
1957 lyx_view_->message(_("Canceled."));
1960 if (filename.empty())
1963 // get absolute path of file
1964 filename = makeAbsPath(filename);
1966 string const lyxfile = changeExtension(filename, ".lyx");
1968 // Check if the document already is open
1969 if (use_gui && theBufferList().exists(lyxfile)) {
1970 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1971 lyx_view_->message(_("Canceled."));
1976 // if the file exists already, and we didn't do
1977 // -i lyx thefile.lyx, warn
1978 if (fs::exists(lyxfile) && filename != lyxfile) {
1979 docstring const file = makeDisplayPath(lyxfile, 30);
1981 docstring text = bformat(_("The document %1$s already exists.\n\n"
1982 "Do you want to over-write that document?"), file);
1983 int const ret = Alert::prompt(_("Over-write document?"),
1984 text, 0, 1, _("&Over-write"), _("&Cancel"));
1987 lyx_view_->message(_("Canceled."));
1992 ErrorList errorList;
1993 Importer::Import(lyx_view_, filename, format, errorList);
1994 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1998 void LyXFunc::closeBuffer()
2000 // save current cursor position
2001 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
2002 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2003 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2004 if (theBufferList().empty()) {
2005 // need this otherwise SEGV may occur while
2006 // trying to set variables that don't exist
2007 // since there's no current buffer
2008 lyx_view_->getDialogs().hideBufferDependent();
2010 lyx_view_->setBuffer(theBufferList().first());
2016 // Each "lyx_view_" should have it's own message method. lyxview and
2017 // the minibuffer would use the minibuffer, but lyxserver would
2018 // send an ERROR signal to its client. Alejandro 970603
2019 // This function is bit problematic when it comes to NLS, to make the
2020 // lyx servers client be language indepenent we must not translate
2021 // strings sent to this func.
2022 void LyXFunc::setErrorMessage(docstring const & m) const
2024 dispatch_buffer = m;
2029 void LyXFunc::setMessage(docstring const & m) const
2031 dispatch_buffer = m;
2035 string const LyXFunc::viewStatusMessage()
2037 // When meta-fake key is pressed, show the key sequence so far + "M-".
2039 return keyseq->print() + "M-";
2041 // Else, when a non-complete key sequence is pressed,
2042 // show the available options.
2043 if (keyseq->length() > 0 && !keyseq->deleted())
2044 return keyseq->printOptions();
2046 if (!view()->buffer())
2047 return to_utf8(_("Welcome to LyX!"));
2049 return view()->cursor().currentState();
2053 BufferView * LyXFunc::view() const
2055 BOOST_ASSERT(lyx_view_);
2056 return lyx_view_->view();
2060 bool LyXFunc::wasMetaKey() const
2062 return (meta_fake_bit != key_modifier::none);
2068 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2070 // Why the switch you might ask. It is a trick to ensure that all
2071 // the elements in the LyXRCTags enum is handled. As you can see
2072 // there are no breaks at all. So it is just a huge fall-through.
2073 // The nice thing is that we will get a warning from the compiler
2074 // if we forget an element.
2075 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2077 case LyXRC::RC_ACCEPT_COMPOUND:
2078 case LyXRC::RC_ALT_LANG:
2079 case LyXRC::RC_ASCIIROFF_COMMAND:
2080 case LyXRC::RC_ASCII_LINELEN:
2081 case LyXRC::RC_AUTOREGIONDELETE:
2082 case LyXRC::RC_AUTORESET_OPTIONS:
2083 case LyXRC::RC_AUTOSAVE:
2084 case LyXRC::RC_AUTO_NUMBER:
2085 case LyXRC::RC_BACKUPDIR_PATH:
2086 case LyXRC::RC_BIBTEX_COMMAND:
2087 case LyXRC::RC_BINDFILE:
2088 case LyXRC::RC_CHECKLASTFILES:
2089 case LyXRC::RC_USELASTFILEPOS:
2090 case LyXRC::RC_LOADSESSION:
2091 case LyXRC::RC_CHKTEX_COMMAND:
2092 case LyXRC::RC_CONVERTER:
2093 case LyXRC::RC_COPIER:
2094 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2095 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2096 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2097 case LyXRC::RC_DATE_INSERT_FORMAT:
2098 case LyXRC::RC_DEFAULT_LANGUAGE:
2099 case LyXRC::RC_DEFAULT_PAPERSIZE:
2100 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2101 case LyXRC::RC_DISPLAY_GRAPHICS:
2102 case LyXRC::RC_DOCUMENTPATH:
2103 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2104 if (fs::exists(lyxrc_new.document_path) &&
2105 fs::is_directory(lyxrc_new.document_path)) {
2106 support::package().document_dir() = lyxrc.document_path;
2109 case LyXRC::RC_ESC_CHARS:
2110 case LyXRC::RC_FONT_ENCODING:
2111 case LyXRC::RC_FORMAT:
2112 case LyXRC::RC_INDEX_COMMAND:
2113 case LyXRC::RC_INPUT:
2114 case LyXRC::RC_KBMAP:
2115 case LyXRC::RC_KBMAP_PRIMARY:
2116 case LyXRC::RC_KBMAP_SECONDARY:
2117 case LyXRC::RC_LABEL_INIT_LENGTH:
2118 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2119 case LyXRC::RC_LANGUAGE_AUTO_END:
2120 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2121 case LyXRC::RC_LANGUAGE_COMMAND_END:
2122 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2123 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2124 case LyXRC::RC_LANGUAGE_PACKAGE:
2125 case LyXRC::RC_LANGUAGE_USE_BABEL:
2126 case LyXRC::RC_MAKE_BACKUP:
2127 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2128 case LyXRC::RC_NUMLASTFILES:
2129 case LyXRC::RC_PATH_PREFIX:
2130 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2131 support::prependEnvPath("PATH", lyxrc.path_prefix);
2133 case LyXRC::RC_PERS_DICT:
2134 case LyXRC::RC_POPUP_BOLD_FONT:
2135 case LyXRC::RC_POPUP_FONT_ENCODING:
2136 case LyXRC::RC_POPUP_NORMAL_FONT:
2137 case LyXRC::RC_PREVIEW:
2138 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2139 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2140 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2141 case LyXRC::RC_PRINTCOPIESFLAG:
2142 case LyXRC::RC_PRINTER:
2143 case LyXRC::RC_PRINTEVENPAGEFLAG:
2144 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2145 case LyXRC::RC_PRINTFILEEXTENSION:
2146 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2147 case LyXRC::RC_PRINTODDPAGEFLAG:
2148 case LyXRC::RC_PRINTPAGERANGEFLAG:
2149 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2150 case LyXRC::RC_PRINTPAPERFLAG:
2151 case LyXRC::RC_PRINTREVERSEFLAG:
2152 case LyXRC::RC_PRINTSPOOL_COMMAND:
2153 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2154 case LyXRC::RC_PRINTTOFILE:
2155 case LyXRC::RC_PRINTTOPRINTER:
2156 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2157 case LyXRC::RC_PRINT_COMMAND:
2158 case LyXRC::RC_RTL_SUPPORT:
2159 case LyXRC::RC_SCREEN_DPI:
2160 case LyXRC::RC_SCREEN_FONT_ENCODING:
2161 case LyXRC::RC_SCREEN_FONT_ROMAN:
2162 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2163 case LyXRC::RC_SCREEN_FONT_SANS:
2164 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2165 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2166 case LyXRC::RC_SCREEN_FONT_SIZES:
2167 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2168 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2169 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2170 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2171 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2172 case LyXRC::RC_SCREEN_ZOOM:
2173 case LyXRC::RC_SERVERPIPE:
2174 case LyXRC::RC_SET_COLOR:
2175 case LyXRC::RC_SHOW_BANNER:
2176 case LyXRC::RC_SPELL_COMMAND:
2177 case LyXRC::RC_TEMPDIRPATH:
2178 case LyXRC::RC_TEMPLATEPATH:
2179 case LyXRC::RC_TEX_ALLOWS_SPACES:
2180 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2181 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2182 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2184 case LyXRC::RC_UIFILE:
2185 case LyXRC::RC_USER_EMAIL:
2186 case LyXRC::RC_USER_NAME:
2187 case LyXRC::RC_USETEMPDIR:
2188 case LyXRC::RC_USE_ALT_LANG:
2189 case LyXRC::RC_USE_ESC_CHARS:
2190 case LyXRC::RC_USE_INP_ENC:
2191 case LyXRC::RC_USE_PERS_DICT:
2192 case LyXRC::RC_USE_SPELL_LIB:
2193 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2194 case LyXRC::RC_VIEWER:
2195 case LyXRC::RC_LAST: