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 case LFUN_TOOLBAR_TOGGLE_STATE:
639 // these are handled in our dispatch()
643 if (!getLocalStatus(cur, cmd, flag))
644 flag = view()->getStatus(cmd);
650 // Can we use a readonly buffer?
651 if (buf && buf->isReadonly()
652 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
653 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
654 flag.message(from_utf8(N_("Document is read-only")));
658 // Are we in a DELETED change-tracking region?
659 if (buf && lookupChangeType(cur, true) == Change::DELETED
660 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
661 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
662 flag.message(from_utf8(N_("This portion of the document is deleted.")));
666 // the default error message if we disable the command
667 if (!flag.enabled() && flag.message().empty())
668 flag.message(from_utf8(N_("Command disabled")));
674 bool LyXFunc::ensureBufferClean(BufferView * bv)
676 Buffer & buf = *bv->buffer();
680 docstring const file = makeDisplayPath(buf.fileName(), 30);
681 docstring text = bformat(_("The document %1$s has unsaved "
682 "changes.\n\nDo you want to save "
683 "the document?"), file);
684 int const ret = Alert::prompt(_("Save changed document?"),
685 text, 0, 1, _("&Save"),
689 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
691 return buf.isClean();
697 void showPrintError(string const & name)
699 docstring str = bformat(_("Could not print the document %1$s.\n"
700 "Check that your printer is set up correctly."),
701 makeDisplayPath(name, 50));
702 Alert::error(_("Print document failed"), str);
706 void loadTextclass(string const & name)
708 std::pair<bool, textclass_type> const tc_pair =
709 textclasslist.numberOfClass(name);
711 if (!tc_pair.first) {
712 lyxerr << "Document class \"" << name
713 << "\" does not exist."
718 textclass_type const tc = tc_pair.second;
720 if (!textclasslist[tc].load()) {
721 docstring s = bformat(_("The document could not be converted\n"
722 "into the document class %1$s."),
723 from_utf8(textclasslist[tc].name()));
724 Alert::error(_("Could not change class"), s);
729 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
734 void LyXFunc::dispatch(FuncRequest const & cmd)
736 string const argument = to_utf8(cmd.argument());
737 kb_action const action = cmd.action;
739 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
740 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
742 // we have not done anything wrong yet.
744 dispatch_buffer.erase();
746 // redraw the screen at the end (first of the two drawing steps).
747 //This is done unless explicitely requested otherwise
748 Update::flags updateFlags = Update::FitCursor;
750 FuncStatus const flag = getStatus(cmd);
751 if (!flag.enabled()) {
752 // We cannot use this function here
753 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
754 << lyxaction.getActionName(action)
755 << " [" << action << "] is disabled at this location"
757 setErrorMessage(flag.message());
761 case LFUN_WORD_FIND_FORWARD:
762 case LFUN_WORD_FIND_BACKWARD: {
763 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
764 static string last_search;
765 string searched_string;
767 if (!argument.empty()) {
768 last_search = argument;
769 searched_string = argument;
771 searched_string = last_search;
774 if (searched_string.empty())
777 bool const fw = action == LFUN_WORD_FIND_FORWARD;
779 find2string(searched_string, true, false, fw);
780 find(view(), FuncRequest(LFUN_WORD_FIND, data));
784 case LFUN_COMMAND_PREFIX:
785 BOOST_ASSERT(lyx_view_);
786 lyx_view_->message(from_utf8(keyseq->printOptions()));
789 case LFUN_COMMAND_EXECUTE:
790 BOOST_ASSERT(lyx_view_);
791 lyx_view_->getToolbars().display("minibuffer", true);
792 lyx_view_->focus_command_buffer();
796 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
798 meta_fake_bit = key_modifier::none;
799 if (view()->buffer())
800 // cancel any selection
801 dispatch(FuncRequest(LFUN_MARK_OFF));
802 setMessage(_("Cancel"));
805 case LFUN_META_PREFIX:
806 meta_fake_bit = key_modifier::alt;
807 setMessage(from_utf8(keyseq->print()));
810 case LFUN_BUFFER_TOGGLE_READ_ONLY:
811 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
812 if (lyx_view_->buffer()->lyxvc().inUse())
813 lyx_view_->buffer()->lyxvc().toggleReadOnly();
815 lyx_view_->buffer()->setReadonly(
816 !lyx_view_->buffer()->isReadonly());
819 // --- Menus -----------------------------------------------
820 case LFUN_BUFFER_NEW:
821 menuNew(argument, false);
824 case LFUN_BUFFER_NEW_TEMPLATE:
825 menuNew(argument, true);
828 case LFUN_BUFFER_CLOSE:
833 case LFUN_BUFFER_WRITE:
834 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
835 if (!lyx_view_->buffer()->isUnnamed()) {
836 docstring const str = bformat(_("Saving document %1$s..."),
837 makeDisplayPath(lyx_view_->buffer()->fileName()));
838 lyx_view_->message(str);
839 menuWrite(lyx_view_->buffer());
840 lyx_view_->message(str + _(" done."));
842 writeAs(lyx_view_->buffer());
843 updateFlags = Update::None;
846 case LFUN_BUFFER_WRITE_AS:
847 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
848 writeAs(lyx_view_->buffer(), argument);
849 updateFlags = Update::None;
852 case LFUN_BUFFER_RELOAD: {
853 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
854 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
855 docstring text = bformat(_("Any changes will be lost. Are you sure "
856 "you want to revert to the saved version of the document %1$s?"), file);
857 int const ret = Alert::prompt(_("Revert to saved document?"),
858 text, 0, 1, _("&Revert"), _("&Cancel"));
865 case LFUN_BUFFER_UPDATE:
866 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
867 Exporter::Export(lyx_view_->buffer(), argument, true);
870 case LFUN_BUFFER_VIEW:
871 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
872 Exporter::preview(lyx_view_->buffer(), argument);
875 case LFUN_BUILD_PROGRAM:
876 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
877 Exporter::Export(lyx_view_->buffer(), "program", true);
880 case LFUN_BUFFER_CHKTEX:
881 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
882 lyx_view_->buffer()->runChktex();
885 case LFUN_BUFFER_EXPORT:
886 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
887 if (argument == "custom")
888 lyx_view_->getDialogs().show("sendto");
890 Exporter::Export(lyx_view_->buffer(), argument, false);
894 case LFUN_BUFFER_EXPORT_CUSTOM: {
895 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
897 string command = split(argument, format_name, ' ');
898 Format const * format = formats.getFormat(format_name);
900 lyxerr << "Format \"" << format_name
901 << "\" not recognized!"
906 Buffer * buffer = lyx_view_->buffer();
908 // The name of the file created by the conversion process
911 // Output to filename
912 if (format->name() == "lyx") {
913 string const latexname =
914 buffer->getLatexName(false);
915 filename = changeExtension(latexname,
916 format->extension());
917 filename = addName(buffer->temppath(), filename);
919 if (!buffer->writeFile(filename))
923 Exporter::Export(buffer, format_name, true, filename);
926 // Substitute $$FName for filename
927 if (!contains(command, "$$FName"))
928 command = "( " + command + " ) < $$FName";
929 command = subst(command, "$$FName", filename);
931 // Execute the command in the background
933 call.startscript(Systemcall::DontWait, command);
937 case LFUN_BUFFER_PRINT: {
938 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
941 string command = split(split(argument, target, ' '),
945 || target_name.empty()
946 || command.empty()) {
947 lyxerr << "Unable to parse \""
948 << argument << '"' << std::endl;
951 if (target != "printer" && target != "file") {
952 lyxerr << "Unrecognized target \""
953 << target << '"' << std::endl;
957 Buffer * buffer = lyx_view_->buffer();
959 if (!Exporter::Export(buffer, "dvi", true)) {
960 showPrintError(buffer->fileName());
964 // Push directory path.
965 string const path = buffer->temppath();
966 support::Path p(path);
968 // there are three cases here:
969 // 1. we print to a file
970 // 2. we print directly to a printer
971 // 3. we print using a spool command (print to file first)
974 string const dviname =
975 changeExtension(buffer->getLatexName(true),
978 if (target == "printer") {
979 if (!lyxrc.print_spool_command.empty()) {
980 // case 3: print using a spool
981 string const psname =
982 changeExtension(dviname,".ps");
983 command += lyxrc.print_to_file
986 + quoteName(dviname);
989 lyxrc.print_spool_command +' ';
990 if (target_name != "default") {
991 command2 += lyxrc.print_spool_printerprefix
995 command2 += quoteName(psname);
997 // If successful, then spool command
998 res = one.startscript(
1003 res = one.startscript(
1004 Systemcall::DontWait,
1007 // case 2: print directly to a printer
1008 res = one.startscript(
1009 Systemcall::DontWait,
1010 command + quoteName(dviname));
1014 // case 1: print to a file
1015 command += lyxrc.print_to_file
1016 + quoteName(makeAbsPath(target_name,
1019 + quoteName(dviname);
1020 res = one.startscript(Systemcall::DontWait,
1025 showPrintError(buffer->fileName());
1029 case LFUN_BUFFER_IMPORT:
1034 if (argument != "force") {
1035 if (!theApp->gui().closeAll())
1040 // FIXME: this code needs to be transfered somewhere else
1041 // as lyx_view_ will most certainly be null and a same buffer
1042 // might be visible in more than one LyXView.
1043 if (lyx_view_ && lyx_view_->view()->buffer()) {
1044 // save cursor Position for opened files to .lyx/session
1045 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
1046 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1052 case LFUN_TOC_VIEW: {
1053 BOOST_ASSERT(lyx_view_);
1054 InsetCommandParams p("tableofcontents");
1055 string const data = InsetCommandMailer::params2string("toc", p);
1056 lyx_view_->getDialogs().show("toc", data, 0);
1060 case LFUN_BUFFER_AUTO_SAVE:
1064 case LFUN_RECONFIGURE:
1065 BOOST_ASSERT(lyx_view_);
1066 reconfigure(*lyx_view_);
1069 case LFUN_HELP_OPEN: {
1070 BOOST_ASSERT(lyx_view_);
1071 string const arg = argument;
1073 setErrorMessage(_("Missing argument"));
1076 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1077 if (fname.empty()) {
1078 lyxerr << "LyX: unable to find documentation file `"
1079 << arg << "'. Bad installation?" << endl;
1082 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1083 makeDisplayPath(fname)));
1084 lyx_view_->loadLyXFile(fname, false);
1088 // --- version control -------------------------------
1089 case LFUN_VC_REGISTER:
1090 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1091 if (!ensureBufferClean(view()))
1093 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1094 lyx_view_->buffer()->lyxvc().registrer();
1099 case LFUN_VC_CHECK_IN:
1100 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1101 if (!ensureBufferClean(view()))
1103 if (lyx_view_->buffer()->lyxvc().inUse()
1104 && !lyx_view_->buffer()->isReadonly()) {
1105 lyx_view_->buffer()->lyxvc().checkIn();
1110 case LFUN_VC_CHECK_OUT:
1111 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1112 if (!ensureBufferClean(view()))
1114 if (lyx_view_->buffer()->lyxvc().inUse()
1115 && lyx_view_->buffer()->isReadonly()) {
1116 lyx_view_->buffer()->lyxvc().checkOut();
1121 case LFUN_VC_REVERT:
1122 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1123 lyx_view_->buffer()->lyxvc().revert();
1127 case LFUN_VC_UNDO_LAST:
1128 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1129 lyx_view_->buffer()->lyxvc().undoLast();
1133 // --- buffers ----------------------------------------
1134 case LFUN_BUFFER_SWITCH:
1135 BOOST_ASSERT(lyx_view_);
1136 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1139 case LFUN_BUFFER_NEXT:
1140 BOOST_ASSERT(lyx_view_);
1141 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1144 case LFUN_BUFFER_PREVIOUS:
1145 BOOST_ASSERT(lyx_view_);
1146 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1150 BOOST_ASSERT(lyx_view_);
1151 newFile(view(), argument);
1154 case LFUN_FILE_OPEN:
1155 BOOST_ASSERT(lyx_view_);
1159 case LFUN_DROP_LAYOUTS_CHOICE:
1160 BOOST_ASSERT(lyx_view_);
1161 lyx_view_->getToolbars().openLayoutList();
1164 case LFUN_MENU_OPEN:
1165 BOOST_ASSERT(lyx_view_);
1166 lyx_view_->getMenubar().openByName(from_utf8(argument));
1169 // --- lyxserver commands ----------------------------
1170 case LFUN_SERVER_GET_NAME:
1171 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1172 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1173 lyxerr[Debug::INFO] << "FNAME["
1174 << lyx_view_->buffer()->fileName()
1178 case LFUN_SERVER_NOTIFY:
1179 dispatch_buffer = from_utf8(keyseq->print());
1180 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1183 case LFUN_SERVER_GOTO_FILE_ROW: {
1184 BOOST_ASSERT(lyx_view_);
1187 istringstream is(argument);
1188 is >> file_name >> row;
1189 if (prefixIs(file_name, package().temp_dir())) {
1190 // Needed by inverse dvi search. If it is a file
1191 // in tmpdir, call the apropriated function
1192 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1194 // Must replace extension of the file to be .lyx
1195 // and get full path
1196 string const s = changeExtension(file_name, ".lyx");
1197 // Either change buffer or load the file
1198 if (theBufferList().exists(s)) {
1199 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1201 lyx_view_->loadLyXFile(s);
1205 view()->setCursorFromRow(row);
1208 // see BufferView::center()
1212 case LFUN_DIALOG_SHOW: {
1213 BOOST_ASSERT(lyx_view_);
1214 string const name = cmd.getArg(0);
1215 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1217 if (name == "character") {
1218 data = freefont2string();
1220 lyx_view_->getDialogs().show("character", data);
1221 } else if (name == "latexlog") {
1222 pair<Buffer::LogType, string> const logfile =
1223 lyx_view_->buffer()->getLogName();
1224 switch (logfile.first) {
1225 case Buffer::latexlog:
1228 case Buffer::buildlog:
1232 data += LyXLex::quoteString(logfile.second);
1233 lyx_view_->getDialogs().show("log", data);
1234 } else if (name == "vclog") {
1235 string const data = "vc " +
1236 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1237 lyx_view_->getDialogs().show("log", data);
1239 lyx_view_->getDialogs().show(name, data);
1243 case LFUN_DIALOG_SHOW_NEW_INSET: {
1244 BOOST_ASSERT(lyx_view_);
1245 string const name = cmd.getArg(0);
1246 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1247 if (name == "bibitem" ||
1254 InsetCommandParams p(name);
1255 data = InsetCommandMailer::params2string(name, p);
1256 } else if (name == "include") {
1257 InsetCommandParams p(data);
1258 data = InsetIncludeMailer::params2string(p);
1259 } else if (name == "box") {
1260 // \c data == "Boxed" || "Frameless" etc
1261 InsetBoxParams p(data);
1262 data = InsetBoxMailer::params2string(p);
1263 } else if (name == "branch") {
1264 InsetBranchParams p;
1265 data = InsetBranchMailer::params2string(p);
1266 } else if (name == "citation") {
1267 InsetCommandParams p("cite");
1268 data = InsetCommandMailer::params2string(name, p);
1269 } else if (name == "ert") {
1270 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1271 } else if (name == "external") {
1272 InsetExternalParams p;
1273 Buffer const & buffer = *lyx_view_->buffer();
1274 data = InsetExternalMailer::params2string(p, buffer);
1275 } else if (name == "float") {
1277 data = InsetFloatMailer::params2string(p);
1278 } else if (name == "graphics") {
1279 InsetGraphicsParams p;
1280 Buffer const & buffer = *lyx_view_->buffer();
1281 data = InsetGraphicsMailer::params2string(p, buffer);
1282 } else if (name == "note") {
1284 data = InsetNoteMailer::params2string(p);
1285 } else if (name == "vspace") {
1287 data = InsetVSpaceMailer::params2string(space);
1288 } else if (name == "wrap") {
1290 data = InsetWrapMailer::params2string(p);
1292 lyx_view_->getDialogs().show(name, data, 0);
1296 case LFUN_DIALOG_UPDATE: {
1297 BOOST_ASSERT(lyx_view_);
1298 string const & name = argument;
1299 // Can only update a dialog connected to an existing inset
1300 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1302 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1303 inset->dispatch(view()->cursor(), fr);
1304 } else if (name == "paragraph") {
1305 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1306 } else if (name == "prefs") {
1307 lyx_view_->getDialogs().update(name, string());
1312 case LFUN_DIALOG_HIDE:
1313 Dialogs::hide(argument, 0);
1316 case LFUN_DIALOG_DISCONNECT_INSET:
1317 BOOST_ASSERT(lyx_view_);
1318 lyx_view_->getDialogs().disconnect(argument);
1322 case LFUN_CITATION_INSERT: {
1323 BOOST_ASSERT(lyx_view_);
1324 if (!argument.empty()) {
1325 // we can have one optional argument, delimited by '|'
1326 // citation-insert <key>|<text_before>
1327 // this should be enhanced to also support text_after
1328 // and citation style
1329 string arg = argument;
1331 if (contains(argument, "|")) {
1332 arg = token(argument, '|', 0);
1333 opt1 = '[' + token(argument, '|', 1) + ']';
1335 std::ostringstream os;
1336 os << "citation LatexCommand\n"
1337 << "\\cite" << opt1 << "{" << arg << "}\n"
1339 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1342 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1346 case LFUN_BUFFER_CHILD_OPEN: {
1347 BOOST_ASSERT(lyx_view_);
1348 string const filename =
1349 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1350 // FIXME Should use bformat
1351 setMessage(_("Opening child document ") +
1352 makeDisplayPath(filename) + "...");
1353 view()->saveBookmark(false);
1354 string const parentfilename = lyx_view_->buffer()->fileName();
1355 if (theBufferList().exists(filename))
1356 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1358 lyx_view_->loadLyXFile(filename);
1359 // Set the parent name of the child document.
1360 // This makes insertion of citations and references in the child work,
1361 // when the target is in the parent or another child document.
1362 lyx_view_->buffer()->setParentName(parentfilename);
1366 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1367 BOOST_ASSERT(lyx_view_);
1368 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1371 case LFUN_KEYMAP_OFF:
1372 BOOST_ASSERT(lyx_view_);
1373 lyx_view_->view()->getIntl().keyMapOn(false);
1376 case LFUN_KEYMAP_PRIMARY:
1377 BOOST_ASSERT(lyx_view_);
1378 lyx_view_->view()->getIntl().keyMapPrim();
1381 case LFUN_KEYMAP_SECONDARY:
1382 BOOST_ASSERT(lyx_view_);
1383 lyx_view_->view()->getIntl().keyMapSec();
1386 case LFUN_KEYMAP_TOGGLE:
1387 BOOST_ASSERT(lyx_view_);
1388 lyx_view_->view()->getIntl().toggleKeyMap();
1394 string rest = split(argument, countstr, ' ');
1395 istringstream is(countstr);
1398 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1399 for (int i = 0; i < count; ++i)
1400 dispatch(lyxaction.lookupFunc(rest));
1404 case LFUN_COMMAND_SEQUENCE: {
1405 // argument contains ';'-terminated commands
1406 string arg = argument;
1407 while (!arg.empty()) {
1409 arg = split(arg, first, ';');
1410 FuncRequest func(lyxaction.lookupFunc(first));
1411 func.origin = cmd.origin;
1417 case LFUN_PREFERENCES_SAVE: {
1418 support::Path p(package().user_support());
1419 lyxrc.write("preferences", false);
1423 case LFUN_SCREEN_FONT_UPDATE:
1424 BOOST_ASSERT(lyx_view_);
1425 // handle the screen font changes.
1426 lyxrc.set_font_norm_type();
1427 theFontLoader().update();
1428 // All visible buffers will need resize
1432 case LFUN_SET_COLOR: {
1434 string const x11_name = split(argument, lyx_name, ' ');
1435 if (lyx_name.empty() || x11_name.empty()) {
1436 setErrorMessage(_("Syntax: set-color <lyx_name>"
1441 bool const graphicsbg_changed =
1442 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1443 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1445 if (!lcolor.setColor(lyx_name, x11_name)) {
1447 bformat(_("Set-color \"%1$s\" failed "
1448 "- color is undefined or "
1449 "may not be redefined"),
1450 from_utf8(lyx_name)));
1454 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1456 if (graphicsbg_changed) {
1457 #ifdef WITH_WARNINGS
1458 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1461 graphics::GCache::get().changeDisplay(true);
1468 BOOST_ASSERT(lyx_view_);
1469 lyx_view_->message(from_utf8(argument));
1472 case LFUN_EXTERNAL_EDIT: {
1473 BOOST_ASSERT(lyx_view_);
1474 FuncRequest fr(action, argument);
1475 InsetExternal().dispatch(view()->cursor(), fr);
1479 case LFUN_GRAPHICS_EDIT: {
1480 FuncRequest fr(action, argument);
1481 InsetGraphics().dispatch(view()->cursor(), fr);
1485 case LFUN_INSET_APPLY: {
1486 BOOST_ASSERT(lyx_view_);
1487 string const name = cmd.getArg(0);
1488 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1490 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1491 inset->dispatch(view()->cursor(), fr);
1493 FuncRequest fr(LFUN_INSET_INSERT, argument);
1496 // ideally, the update flag should be set by the insets,
1497 // but this is not possible currently
1498 updateFlags = Update::Force | Update::FitCursor;
1502 case LFUN_ALL_INSETS_TOGGLE: {
1503 BOOST_ASSERT(lyx_view_);
1505 string const name = split(argument, action, ' ');
1506 InsetBase::Code const inset_code =
1507 InsetBase::translate(name);
1509 LCursor & cur = view()->cursor();
1510 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1512 InsetBase & inset = lyx_view_->buffer()->inset();
1513 InsetIterator it = inset_iterator_begin(inset);
1514 InsetIterator const end = inset_iterator_end(inset);
1515 for (; it != end; ++it) {
1516 if (inset_code == InsetBase::NO_CODE
1517 || inset_code == it->lyxCode()) {
1518 LCursor tmpcur = cur;
1519 tmpcur.pushLeft(*it);
1520 it->dispatch(tmpcur, fr);
1523 updateFlags = Update::Force | Update::FitCursor;
1527 case LFUN_BUFFER_LANGUAGE: {
1528 BOOST_ASSERT(lyx_view_);
1529 Buffer & buffer = *lyx_view_->buffer();
1530 Language const * oldL = buffer.params().language;
1531 Language const * newL = languages.getLanguage(argument);
1532 if (!newL || oldL == newL)
1535 if (oldL->rightToLeft() == newL->rightToLeft()
1536 && !buffer.isMultiLingual())
1537 buffer.changeLanguage(oldL, newL);
1539 buffer.updateDocLang(newL);
1543 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1544 string const fname =
1545 addName(addPath(package().user_support(), "templates/"),
1547 Buffer defaults(fname);
1549 istringstream ss(argument);
1552 int const unknown_tokens = defaults.readHeader(lex);
1554 if (unknown_tokens != 0) {
1555 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1556 << unknown_tokens << " unknown token"
1557 << (unknown_tokens == 1 ? "" : "s")
1561 if (defaults.writeFile(defaults.fileName()))
1562 // FIXME Should use bformat
1563 setMessage(_("Document defaults saved in ")
1564 + makeDisplayPath(fname));
1566 setErrorMessage(_("Unable to save document defaults"));
1570 case LFUN_BUFFER_PARAMS_APPLY: {
1571 BOOST_ASSERT(lyx_view_);
1572 biblio::CiteEngine const engine =
1573 lyx_view_->buffer()->params().cite_engine;
1575 istringstream ss(argument);
1578 int const unknown_tokens =
1579 lyx_view_->buffer()->readHeader(lex);
1581 if (unknown_tokens != 0) {
1582 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1583 << unknown_tokens << " unknown token"
1584 << (unknown_tokens == 1 ? "" : "s")
1587 if (engine == lyx_view_->buffer()->params().cite_engine)
1590 LCursor & cur = view()->cursor();
1591 FuncRequest fr(LFUN_INSET_REFRESH);
1593 InsetBase & inset = lyx_view_->buffer()->inset();
1594 InsetIterator it = inset_iterator_begin(inset);
1595 InsetIterator const end = inset_iterator_end(inset);
1596 for (; it != end; ++it)
1597 if (it->lyxCode() == InsetBase::CITE_CODE)
1598 it->dispatch(cur, fr);
1602 case LFUN_TEXTCLASS_APPLY: {
1603 BOOST_ASSERT(lyx_view_);
1604 Buffer * buffer = lyx_view_->buffer();
1606 textclass_type const old_class =
1607 buffer->params().textclass;
1609 loadTextclass(argument);
1611 std::pair<bool, textclass_type> const tc_pair =
1612 textclasslist.numberOfClass(argument);
1617 textclass_type const new_class = tc_pair.second;
1618 if (old_class == new_class)
1622 lyx_view_->message(_("Converting document to new document class..."));
1623 recordUndoFullDocument(view());
1624 buffer->params().textclass = new_class;
1625 StableDocIterator backcur(view()->cursor());
1626 ErrorList & el = buffer->errorList("Class Switch");
1627 cap::switchBetweenClasses(
1628 old_class, new_class,
1629 static_cast<InsetText &>(buffer->inset()), el);
1631 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1633 buffer->errors("Class Switch");
1634 updateLabels(*buffer);
1635 updateFlags = Update::Force | Update::FitCursor;
1639 case LFUN_TEXTCLASS_LOAD:
1640 loadTextclass(argument);
1643 case LFUN_LYXRC_APPLY: {
1644 LyXRC const lyxrc_orig = lyxrc;
1646 istringstream ss(argument);
1647 bool const success = lyxrc.read(ss) == 0;
1650 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1651 << "Unable to read lyxrc data"
1656 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1660 case LFUN_WINDOW_NEW:
1661 LyX::ref().newLyXView();
1664 case LFUN_WINDOW_CLOSE:
1665 BOOST_ASSERT(lyx_view_);
1666 BOOST_ASSERT(theApp);
1668 // We return here because lyx_view does not exists anymore.
1671 case LFUN_BOOKMARK_GOTO: {
1672 BOOST_ASSERT(lyx_view_);
1673 unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1674 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1675 BOOST_ASSERT(!bm.filename.empty());
1676 // if the file is not opened, open it.
1677 if (!theBufferList().exists(bm.filename))
1678 dispatch(FuncRequest(LFUN_FILE_OPEN, bm.filename));
1679 // open may fail, so we need to test it again
1680 if (theBufferList().exists(bm.filename)) {
1681 // if the current buffer is not that one, switch to it.
1682 if (lyx_view_->buffer()->fileName() != bm.filename)
1683 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, bm.filename));
1684 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != bm.filename);
1685 view()->moveToPosition(bm.par_id, bm.par_pos);
1690 case LFUN_BOOKMARK_CLEAR:
1691 LyX::ref().session().bookmarks().clear();
1694 case LFUN_TOOLBAR_TOGGLE_STATE:
1695 lyx_view_->toggleToolbarState(argument);
1699 BOOST_ASSERT(lyx_view_);
1700 view()->cursor().dispatch(cmd);
1701 updateFlags = view()->cursor().result().update();
1702 if (!view()->cursor().result().dispatched())
1703 if (view()->dispatch(cmd))
1704 updateFlags = Update::Force | Update::FitCursor;
1709 if (lyx_view_ && view()->buffer()) {
1710 // Redraw screen unless explicitly told otherwise.
1711 // This also initializes the position cache for all insets
1712 // in (at least partially) visible top-level paragraphs.
1713 bool needSecondUpdate = false;
1714 if (updateFlags != Update::None)
1715 view()->update(updateFlags);
1717 needSecondUpdate = view()->fitCursor();
1719 if (needSecondUpdate || updateFlags != Update::None) {
1720 view()->buffer()->changed();
1722 lyx_view_->updateStatusBar();
1724 // if we executed a mutating lfun, mark the buffer as dirty
1726 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1727 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1728 view()->buffer()->markDirty();
1730 if (view()->cursor().inTexted()) {
1731 lyx_view_->updateLayoutChoice();
1736 // FIXME UNICODE: _() does not support anything but ascii.
1737 // Do we need a to_ascii() method?
1738 sendDispatchMessage(getMessage(), cmd);
1742 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1744 /* When an action did not originate from the UI/kbd, it makes
1745 * sense to avoid updating the GUI. It turns out that this
1746 * fixes bug 1941, for reasons that are described here:
1747 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1749 if (cmd.origin != FuncRequest::INTERNAL) {
1750 lyx_view_->updateMenubar();
1751 lyx_view_->updateToolbars();
1754 const bool verbose = (cmd.origin == FuncRequest::UI
1755 || cmd.origin == FuncRequest::COMMANDBUFFER);
1757 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1758 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1760 lyx_view_->message(msg);
1764 docstring dispatch_msg = msg;
1765 if (!dispatch_msg.empty())
1766 dispatch_msg += ' ';
1768 string comname = lyxaction.getActionName(cmd.action);
1770 bool argsadded = false;
1772 if (!cmd.argument().empty()) {
1773 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1774 comname += ' ' + to_utf8(cmd.argument());
1779 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1781 if (!shortcuts.empty())
1782 comname += ": " + shortcuts;
1783 else if (!argsadded && !cmd.argument().empty())
1784 comname += ' ' + to_utf8(cmd.argument());
1786 if (!comname.empty()) {
1787 comname = rtrim(comname);
1788 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1791 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1792 << to_utf8(dispatch_msg) << endl;
1793 if (!dispatch_msg.empty())
1794 lyx_view_->message(dispatch_msg);
1798 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1800 string initpath = lyxrc.document_path;
1801 string filename(name);
1803 if (view()->buffer()) {
1804 string const trypath = lyx_view_->buffer()->filePath();
1805 // If directory is writeable, use this as default.
1806 if (isDirWriteable(trypath))
1810 static int newfile_number;
1812 if (filename.empty()) {
1813 filename = addName(lyxrc.document_path,
1814 "newfile" + convert<string>(++newfile_number) + ".lyx");
1815 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1817 filename = addName(lyxrc.document_path,
1818 "newfile" + convert<string>(newfile_number) +
1823 // The template stuff
1826 FileDialog fileDlg(_("Select template file"),
1827 LFUN_SELECT_FILE_SYNC,
1828 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1829 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1831 FileDialog::Result result =
1832 fileDlg.open(from_utf8(lyxrc.template_path),
1833 FileFilterList(_("LyX Documents (*.lyx)")),
1836 if (result.first == FileDialog::Later)
1838 if (result.second.empty())
1840 templname = to_utf8(result.second);
1843 Buffer * const b = newFile(filename, templname, !name.empty());
1845 lyx_view_->setBuffer(b);
1849 void LyXFunc::open(string const & fname)
1851 string initpath = lyxrc.document_path;
1853 if (view()->buffer()) {
1854 string const trypath = lyx_view_->buffer()->filePath();
1855 // If directory is writeable, use this as default.
1856 if (isDirWriteable(trypath))
1862 if (fname.empty()) {
1863 FileDialog fileDlg(_("Select document to open"),
1865 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1866 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1868 FileDialog::Result result =
1869 fileDlg.open(from_utf8(initpath),
1870 FileFilterList(_("LyX Documents (*.lyx)")),
1873 if (result.first == FileDialog::Later)
1876 filename = to_utf8(result.second);
1878 // check selected filename
1879 if (filename.empty()) {
1880 lyx_view_->message(_("Canceled."));
1886 // get absolute path of file and add ".lyx" to the filename if
1888 string const fullpath = fileSearch(string(), filename, "lyx");
1889 if (!fullpath.empty()) {
1890 filename = fullpath;
1893 docstring const disp_fn = makeDisplayPath(filename);
1895 // if the file doesn't exist, let the user create one
1896 if (!fs::exists(filename)) {
1897 // the user specifically chose this name. Believe him.
1898 Buffer * const b = newFile(filename, string(), true);
1900 lyx_view_->setBuffer(b);
1904 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1907 if (lyx_view_->loadLyXFile(filename)) {
1908 str2 = bformat(_("Document %1$s opened."), disp_fn);
1910 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1912 lyx_view_->message(str2);
1916 void LyXFunc::doImport(string const & argument)
1919 string filename = split(argument, format, ' ');
1921 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1922 << " file: " << filename << endl;
1924 // need user interaction
1925 if (filename.empty()) {
1926 string initpath = lyxrc.document_path;
1928 if (view()->buffer()) {
1929 string const trypath = lyx_view_->buffer()->filePath();
1930 // If directory is writeable, use this as default.
1931 if (isDirWriteable(trypath))
1935 docstring const text = bformat(_("Select %1$s file to import"),
1936 formats.prettyName(format));
1938 FileDialog fileDlg(text,
1940 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1941 make_pair(_("Examples|#E#e"),
1942 from_utf8(addPath(package().system_support(), "examples"))));
1944 docstring filter = formats.prettyName(format);
1947 filter += from_utf8(formats.extension(format));
1950 FileDialog::Result result =
1951 fileDlg.open(from_utf8(initpath),
1952 FileFilterList(filter),
1955 if (result.first == FileDialog::Later)
1958 filename = to_utf8(result.second);
1960 // check selected filename
1961 if (filename.empty())
1962 lyx_view_->message(_("Canceled."));
1965 if (filename.empty())
1968 // get absolute path of file
1969 filename = makeAbsPath(filename);
1971 string const lyxfile = changeExtension(filename, ".lyx");
1973 // Check if the document already is open
1974 if (use_gui && theBufferList().exists(lyxfile)) {
1975 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1976 lyx_view_->message(_("Canceled."));
1981 // if the file exists already, and we didn't do
1982 // -i lyx thefile.lyx, warn
1983 if (fs::exists(lyxfile) && filename != lyxfile) {
1984 docstring const file = makeDisplayPath(lyxfile, 30);
1986 docstring text = bformat(_("The document %1$s already exists.\n\n"
1987 "Do you want to over-write that document?"), file);
1988 int const ret = Alert::prompt(_("Over-write document?"),
1989 text, 0, 1, _("&Over-write"), _("&Cancel"));
1992 lyx_view_->message(_("Canceled."));
1997 ErrorList errorList;
1998 Importer::Import(lyx_view_, filename, format, errorList);
1999 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2003 void LyXFunc::closeBuffer()
2005 // save current cursor position
2006 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
2007 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2008 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2009 if (theBufferList().empty()) {
2010 // need this otherwise SEGV may occur while
2011 // trying to set variables that don't exist
2012 // since there's no current buffer
2013 lyx_view_->getDialogs().hideBufferDependent();
2015 lyx_view_->setBuffer(theBufferList().first());
2021 // Each "lyx_view_" should have it's own message method. lyxview and
2022 // the minibuffer would use the minibuffer, but lyxserver would
2023 // send an ERROR signal to its client. Alejandro 970603
2024 // This function is bit problematic when it comes to NLS, to make the
2025 // lyx servers client be language indepenent we must not translate
2026 // strings sent to this func.
2027 void LyXFunc::setErrorMessage(docstring const & m) const
2029 dispatch_buffer = m;
2034 void LyXFunc::setMessage(docstring const & m) const
2036 dispatch_buffer = m;
2040 string const LyXFunc::viewStatusMessage()
2042 // When meta-fake key is pressed, show the key sequence so far + "M-".
2044 return keyseq->print() + "M-";
2046 // Else, when a non-complete key sequence is pressed,
2047 // show the available options.
2048 if (keyseq->length() > 0 && !keyseq->deleted())
2049 return keyseq->printOptions();
2051 if (!view()->buffer())
2052 return to_utf8(_("Welcome to LyX!"));
2054 return view()->cursor().currentState();
2058 BufferView * LyXFunc::view() const
2060 BOOST_ASSERT(lyx_view_);
2061 return lyx_view_->view();
2065 bool LyXFunc::wasMetaKey() const
2067 return (meta_fake_bit != key_modifier::none);
2073 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2075 // Why the switch you might ask. It is a trick to ensure that all
2076 // the elements in the LyXRCTags enum is handled. As you can see
2077 // there are no breaks at all. So it is just a huge fall-through.
2078 // The nice thing is that we will get a warning from the compiler
2079 // if we forget an element.
2080 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2082 case LyXRC::RC_ACCEPT_COMPOUND:
2083 case LyXRC::RC_ALT_LANG:
2084 case LyXRC::RC_ASCIIROFF_COMMAND:
2085 case LyXRC::RC_ASCII_LINELEN:
2086 case LyXRC::RC_AUTOREGIONDELETE:
2087 case LyXRC::RC_AUTORESET_OPTIONS:
2088 case LyXRC::RC_AUTOSAVE:
2089 case LyXRC::RC_AUTO_NUMBER:
2090 case LyXRC::RC_BACKUPDIR_PATH:
2091 case LyXRC::RC_BIBTEX_COMMAND:
2092 case LyXRC::RC_BINDFILE:
2093 case LyXRC::RC_CHECKLASTFILES:
2094 case LyXRC::RC_USELASTFILEPOS:
2095 case LyXRC::RC_LOADSESSION:
2096 case LyXRC::RC_CHKTEX_COMMAND:
2097 case LyXRC::RC_CONVERTER:
2098 case LyXRC::RC_COPIER:
2099 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2100 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2101 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2102 case LyXRC::RC_DATE_INSERT_FORMAT:
2103 case LyXRC::RC_DEFAULT_LANGUAGE:
2104 case LyXRC::RC_DEFAULT_PAPERSIZE:
2105 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2106 case LyXRC::RC_DISPLAY_GRAPHICS:
2107 case LyXRC::RC_DOCUMENTPATH:
2108 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2109 if (fs::exists(lyxrc_new.document_path) &&
2110 fs::is_directory(lyxrc_new.document_path)) {
2111 support::package().document_dir() = lyxrc.document_path;
2114 case LyXRC::RC_ESC_CHARS:
2115 case LyXRC::RC_FONT_ENCODING:
2116 case LyXRC::RC_FORMAT:
2117 case LyXRC::RC_INDEX_COMMAND:
2118 case LyXRC::RC_INPUT:
2119 case LyXRC::RC_KBMAP:
2120 case LyXRC::RC_KBMAP_PRIMARY:
2121 case LyXRC::RC_KBMAP_SECONDARY:
2122 case LyXRC::RC_LABEL_INIT_LENGTH:
2123 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2124 case LyXRC::RC_LANGUAGE_AUTO_END:
2125 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2126 case LyXRC::RC_LANGUAGE_COMMAND_END:
2127 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2128 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2129 case LyXRC::RC_LANGUAGE_PACKAGE:
2130 case LyXRC::RC_LANGUAGE_USE_BABEL:
2131 case LyXRC::RC_MAKE_BACKUP:
2132 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2133 case LyXRC::RC_NUMLASTFILES:
2134 case LyXRC::RC_PATH_PREFIX:
2135 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2136 support::prependEnvPath("PATH", lyxrc.path_prefix);
2138 case LyXRC::RC_PERS_DICT:
2139 case LyXRC::RC_POPUP_BOLD_FONT:
2140 case LyXRC::RC_POPUP_FONT_ENCODING:
2141 case LyXRC::RC_POPUP_NORMAL_FONT:
2142 case LyXRC::RC_PREVIEW:
2143 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2144 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2145 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2146 case LyXRC::RC_PRINTCOPIESFLAG:
2147 case LyXRC::RC_PRINTER:
2148 case LyXRC::RC_PRINTEVENPAGEFLAG:
2149 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2150 case LyXRC::RC_PRINTFILEEXTENSION:
2151 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2152 case LyXRC::RC_PRINTODDPAGEFLAG:
2153 case LyXRC::RC_PRINTPAGERANGEFLAG:
2154 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2155 case LyXRC::RC_PRINTPAPERFLAG:
2156 case LyXRC::RC_PRINTREVERSEFLAG:
2157 case LyXRC::RC_PRINTSPOOL_COMMAND:
2158 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2159 case LyXRC::RC_PRINTTOFILE:
2160 case LyXRC::RC_PRINTTOPRINTER:
2161 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2162 case LyXRC::RC_PRINT_COMMAND:
2163 case LyXRC::RC_RTL_SUPPORT:
2164 case LyXRC::RC_SCREEN_DPI:
2165 case LyXRC::RC_SCREEN_FONT_ENCODING:
2166 case LyXRC::RC_SCREEN_FONT_ROMAN:
2167 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2168 case LyXRC::RC_SCREEN_FONT_SANS:
2169 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2170 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2171 case LyXRC::RC_SCREEN_FONT_SIZES:
2172 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2173 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2174 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2175 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2176 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2177 case LyXRC::RC_SCREEN_ZOOM:
2178 case LyXRC::RC_SERVERPIPE:
2179 case LyXRC::RC_SET_COLOR:
2180 case LyXRC::RC_SHOW_BANNER:
2181 case LyXRC::RC_SPELL_COMMAND:
2182 case LyXRC::RC_TEMPDIRPATH:
2183 case LyXRC::RC_TEMPLATEPATH:
2184 case LyXRC::RC_TEX_ALLOWS_SPACES:
2185 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2186 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2187 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2189 case LyXRC::RC_UIFILE:
2190 case LyXRC::RC_USER_EMAIL:
2191 case LyXRC::RC_USER_NAME:
2192 case LyXRC::RC_USETEMPDIR:
2193 case LyXRC::RC_USE_ALT_LANG:
2194 case LyXRC::RC_USE_ESC_CHARS:
2195 case LyXRC::RC_USE_INP_ENC:
2196 case LyXRC::RC_USE_PERS_DICT:
2197 case LyXRC::RC_USE_SPELL_LIB:
2198 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2199 case LyXRC::RC_VIEWER:
2200 case LyXRC::RC_LAST: