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);
362 } else if (cmd.action == LFUN_TOOLBAR_TOGGLE_STATE) {
363 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
364 if (!(flags & ToolbarBackend::AUTO))
365 flag.setOnOff(flags & ToolbarBackend::ON);
369 LCursor & cur = view()->cursor();
371 /* In LyX/Mac, when a dialog is open, the menus of the
372 application can still be accessed without giving focus to
373 the main window. In this case, we want to disable the menu
374 entries that are buffer-related.
376 Note that this code is not perfect, as bug 1941 attests:
377 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
380 if (cmd.origin == FuncRequest::UI && !lyx_view_->hasFocus())
383 buf = lyx_view_->buffer();
385 if (cmd.action == LFUN_NOACTION) {
386 flag.message(from_utf8(N_("Nothing to do")));
391 switch (cmd.action) {
392 case LFUN_UNKNOWN_ACTION:
393 #ifndef HAVE_LIBAIKSAURUS
394 case LFUN_THESAURUS_ENTRY:
404 if (flag.unknown()) {
405 flag.message(from_utf8(N_("Unknown action")));
409 if (!flag.enabled()) {
410 if (flag.message().empty())
411 flag.message(from_utf8(N_("Command disabled")));
415 // Check whether we need a buffer
416 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
418 flag.message(from_utf8(N_("Command not allowed with"
419 "out any document open")));
424 // I would really like to avoid having this switch and rather try to
425 // encode this in the function itself.
426 // -- And I'd rather let an inset decide which LFUNs it is willing
427 // to handle (Andre')
429 switch (cmd.action) {
430 case LFUN_BUFFER_TOGGLE_READ_ONLY:
431 flag.setOnOff(buf->isReadonly());
434 case LFUN_BUFFER_SWITCH:
435 // toggle on the current buffer, but do not toggle off
436 // the other ones (is that a good idea?)
437 if (to_utf8(cmd.argument()) == buf->fileName())
441 case LFUN_BUFFER_EXPORT:
442 enable = cmd.argument() == "custom"
443 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
446 case LFUN_BUFFER_CHKTEX:
447 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
450 case LFUN_BUILD_PROGRAM:
451 enable = Exporter::isExportable(*buf, "program");
454 case LFUN_LAYOUT_TABULAR:
455 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
459 case LFUN_LAYOUT_PARAGRAPH:
460 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
463 case LFUN_VC_REGISTER:
464 enable = !buf->lyxvc().inUse();
466 case LFUN_VC_CHECK_IN:
467 enable = buf->lyxvc().inUse() && !buf->isReadonly();
469 case LFUN_VC_CHECK_OUT:
470 enable = buf->lyxvc().inUse() && buf->isReadonly();
473 case LFUN_VC_UNDO_LAST:
474 enable = buf->lyxvc().inUse();
476 case LFUN_BUFFER_RELOAD:
477 enable = !buf->isUnnamed() && !buf->isClean();
480 case LFUN_INSET_SETTINGS: {
484 InsetBase::Code code = cur.inset().lyxCode();
486 case InsetBase::TABULAR_CODE:
487 enable = cmd.argument() == "tabular";
489 case InsetBase::ERT_CODE:
490 enable = cmd.argument() == "ert";
492 case InsetBase::FLOAT_CODE:
493 enable = cmd.argument() == "float";
495 case InsetBase::WRAP_CODE:
496 enable = cmd.argument() == "wrap";
498 case InsetBase::NOTE_CODE:
499 enable = cmd.argument() == "note";
501 case InsetBase::BRANCH_CODE:
502 enable = cmd.argument() == "branch";
504 case InsetBase::BOX_CODE:
505 enable = cmd.argument() == "box";
513 case LFUN_INSET_APPLY: {
514 string const name = cmd.getArg(0);
515 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
517 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
519 bool const success = inset->getStatus(cur, fr, fs);
520 // Every inset is supposed to handle this
521 BOOST_ASSERT(success);
524 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
525 flag |= getStatus(fr);
527 enable = flag.enabled();
531 case LFUN_DIALOG_SHOW: {
532 string const name = cmd.getArg(0);
534 enable = name == "aboutlyx"
538 || name == "texinfo";
539 else if (name == "print")
540 enable = Exporter::isExportable(*buf, "dvi")
541 && lyxrc.print_command != "none";
542 else if (name == "character" || name == "mathpanel")
543 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
544 else if (name == "latexlog")
545 enable = isFileReadable(buf->getLogName().second);
546 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
547 else if (name == "spellchecker")
550 else if (name == "vclog")
551 enable = buf->lyxvc().inUse();
552 else if (name == "view-source")
557 case LFUN_DIALOG_SHOW_NEW_INSET:
558 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
561 case LFUN_DIALOG_UPDATE: {
562 string const name = cmd.getArg(0);
564 enable = name == "prefs";
568 case LFUN_CITATION_INSERT: {
569 FuncRequest fr(LFUN_INSET_INSERT, "citation");
570 enable = getStatus(fr).enabled();
574 case LFUN_BUFFER_WRITE: {
575 enable = view()->buffer()->isUnnamed()
576 || !view()->buffer()->isClean();
581 // this one is difficult to get right. As a half-baked
582 // solution, we consider only the first action of the sequence
583 case LFUN_COMMAND_SEQUENCE: {
584 // argument contains ';'-terminated commands
585 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
586 FuncRequest func(lyxaction.lookupFunc(firstcmd));
587 func.origin = cmd.origin;
588 flag = getStatus(func);
591 case LFUN_BUFFER_NEW:
592 case LFUN_BUFFER_NEW_TEMPLATE:
593 case LFUN_WORD_FIND_FORWARD:
594 case LFUN_WORD_FIND_BACKWARD:
595 case LFUN_COMMAND_PREFIX:
596 case LFUN_COMMAND_EXECUTE:
598 case LFUN_META_PREFIX:
599 case LFUN_BUFFER_CLOSE:
600 case LFUN_BUFFER_WRITE_AS:
601 case LFUN_BUFFER_UPDATE:
602 case LFUN_BUFFER_VIEW:
603 case LFUN_BUFFER_IMPORT:
605 case LFUN_BUFFER_AUTO_SAVE:
606 case LFUN_RECONFIGURE:
610 case LFUN_DROP_LAYOUTS_CHOICE:
612 case LFUN_SERVER_GET_NAME:
613 case LFUN_SERVER_NOTIFY:
614 case LFUN_SERVER_GOTO_FILE_ROW:
615 case LFUN_DIALOG_HIDE:
616 case LFUN_DIALOG_DISCONNECT_INSET:
617 case LFUN_BUFFER_CHILD_OPEN:
618 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
619 case LFUN_KEYMAP_OFF:
620 case LFUN_KEYMAP_PRIMARY:
621 case LFUN_KEYMAP_SECONDARY:
622 case LFUN_KEYMAP_TOGGLE:
624 case LFUN_BUFFER_EXPORT_CUSTOM:
625 case LFUN_BUFFER_PRINT:
626 case LFUN_PREFERENCES_SAVE:
627 case LFUN_SCREEN_FONT_UPDATE:
630 case LFUN_EXTERNAL_EDIT:
631 case LFUN_GRAPHICS_EDIT:
632 case LFUN_ALL_INSETS_TOGGLE:
633 case LFUN_BUFFER_LANGUAGE:
634 case LFUN_TEXTCLASS_APPLY:
635 case LFUN_TEXTCLASS_LOAD:
636 case LFUN_BUFFER_SAVE_AS_DEFAULT:
637 case LFUN_BUFFER_PARAMS_APPLY:
638 case LFUN_LYXRC_APPLY:
639 case LFUN_BUFFER_NEXT:
640 case LFUN_BUFFER_PREVIOUS:
641 case LFUN_WINDOW_NEW:
642 case LFUN_WINDOW_CLOSE:
643 // these are handled in our dispatch()
647 if (!getLocalStatus(cur, cmd, flag))
648 flag = view()->getStatus(cmd);
654 // Can we use a readonly buffer?
655 if (buf && buf->isReadonly()
656 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
657 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
658 flag.message(from_utf8(N_("Document is read-only")));
662 // Are we in a DELETED change-tracking region?
663 if (buf && lookupChangeType(cur, true) == Change::DELETED
664 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
665 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
666 flag.message(from_utf8(N_("This portion of the document is deleted.")));
670 // the default error message if we disable the command
671 if (!flag.enabled() && flag.message().empty())
672 flag.message(from_utf8(N_("Command disabled")));
678 bool LyXFunc::ensureBufferClean(BufferView * bv)
680 Buffer & buf = *bv->buffer();
684 docstring const file = makeDisplayPath(buf.fileName(), 30);
685 docstring text = bformat(_("The document %1$s has unsaved "
686 "changes.\n\nDo you want to save "
687 "the document?"), file);
688 int const ret = Alert::prompt(_("Save changed document?"),
689 text, 0, 1, _("&Save"),
693 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
695 return buf.isClean();
701 void showPrintError(string const & name)
703 docstring str = bformat(_("Could not print the document %1$s.\n"
704 "Check that your printer is set up correctly."),
705 makeDisplayPath(name, 50));
706 Alert::error(_("Print document failed"), str);
710 void loadTextclass(string const & name)
712 std::pair<bool, textclass_type> const tc_pair =
713 textclasslist.numberOfClass(name);
715 if (!tc_pair.first) {
716 lyxerr << "Document class \"" << name
717 << "\" does not exist."
722 textclass_type const tc = tc_pair.second;
724 if (!textclasslist[tc].load()) {
725 docstring s = bformat(_("The document could not be converted\n"
726 "into the document class %1$s."),
727 from_utf8(textclasslist[tc].name()));
728 Alert::error(_("Could not change class"), s);
733 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
738 void LyXFunc::dispatch(FuncRequest const & cmd)
740 string const argument = to_utf8(cmd.argument());
741 kb_action const action = cmd.action;
743 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
744 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
746 // we have not done anything wrong yet.
748 dispatch_buffer.erase();
750 // redraw the screen at the end (first of the two drawing steps).
751 //This is done unless explicitely requested otherwise
752 Update::flags updateFlags = Update::FitCursor;
754 FuncStatus const flag = getStatus(cmd);
755 if (!flag.enabled()) {
756 // We cannot use this function here
757 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
758 << lyxaction.getActionName(action)
759 << " [" << action << "] is disabled at this location"
761 setErrorMessage(flag.message());
765 case LFUN_WORD_FIND_FORWARD:
766 case LFUN_WORD_FIND_BACKWARD: {
767 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
768 static string last_search;
769 string searched_string;
771 if (!argument.empty()) {
772 last_search = argument;
773 searched_string = argument;
775 searched_string = last_search;
778 if (searched_string.empty())
781 bool const fw = action == LFUN_WORD_FIND_FORWARD;
783 find2string(searched_string, true, false, fw);
784 find(view(), FuncRequest(LFUN_WORD_FIND, data));
788 case LFUN_COMMAND_PREFIX:
789 BOOST_ASSERT(lyx_view_);
790 lyx_view_->message(from_utf8(keyseq->printOptions()));
793 case LFUN_COMMAND_EXECUTE:
794 BOOST_ASSERT(lyx_view_);
795 lyx_view_->getToolbars().display("minibuffer", true);
796 lyx_view_->focus_command_buffer();
800 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
802 meta_fake_bit = key_modifier::none;
803 if (view()->buffer())
804 // cancel any selection
805 dispatch(FuncRequest(LFUN_MARK_OFF));
806 setMessage(_("Cancel"));
809 case LFUN_META_PREFIX:
810 meta_fake_bit = key_modifier::alt;
811 setMessage(from_utf8(keyseq->print()));
814 case LFUN_BUFFER_TOGGLE_READ_ONLY:
815 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
816 if (lyx_view_->buffer()->lyxvc().inUse())
817 lyx_view_->buffer()->lyxvc().toggleReadOnly();
819 lyx_view_->buffer()->setReadonly(
820 !lyx_view_->buffer()->isReadonly());
823 // --- Menus -----------------------------------------------
824 case LFUN_BUFFER_NEW:
825 menuNew(argument, false);
828 case LFUN_BUFFER_NEW_TEMPLATE:
829 menuNew(argument, true);
832 case LFUN_BUFFER_CLOSE:
837 case LFUN_BUFFER_WRITE:
838 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
839 if (!lyx_view_->buffer()->isUnnamed()) {
840 docstring const str = bformat(_("Saving document %1$s..."),
841 makeDisplayPath(lyx_view_->buffer()->fileName()));
842 lyx_view_->message(str);
843 menuWrite(lyx_view_->buffer());
844 lyx_view_->message(str + _(" done."));
846 writeAs(lyx_view_->buffer());
847 updateFlags = Update::None;
850 case LFUN_BUFFER_WRITE_AS:
851 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
852 writeAs(lyx_view_->buffer(), argument);
853 updateFlags = Update::None;
856 case LFUN_BUFFER_RELOAD: {
857 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
858 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
859 docstring text = bformat(_("Any changes will be lost. Are you sure "
860 "you want to revert to the saved version of the document %1$s?"), file);
861 int const ret = Alert::prompt(_("Revert to saved document?"),
862 text, 0, 1, _("&Revert"), _("&Cancel"));
869 case LFUN_BUFFER_UPDATE:
870 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
871 Exporter::Export(lyx_view_->buffer(), argument, true);
874 case LFUN_BUFFER_VIEW:
875 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
876 Exporter::preview(lyx_view_->buffer(), argument);
879 case LFUN_BUILD_PROGRAM:
880 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
881 Exporter::Export(lyx_view_->buffer(), "program", true);
884 case LFUN_BUFFER_CHKTEX:
885 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
886 lyx_view_->buffer()->runChktex();
889 case LFUN_BUFFER_EXPORT:
890 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
891 if (argument == "custom")
892 lyx_view_->getDialogs().show("sendto");
894 Exporter::Export(lyx_view_->buffer(), argument, false);
898 case LFUN_BUFFER_EXPORT_CUSTOM: {
899 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
901 string command = split(argument, format_name, ' ');
902 Format const * format = formats.getFormat(format_name);
904 lyxerr << "Format \"" << format_name
905 << "\" not recognized!"
910 Buffer * buffer = lyx_view_->buffer();
912 // The name of the file created by the conversion process
915 // Output to filename
916 if (format->name() == "lyx") {
917 string const latexname =
918 buffer->getLatexName(false);
919 filename = changeExtension(latexname,
920 format->extension());
921 filename = addName(buffer->temppath(), filename);
923 if (!buffer->writeFile(filename))
927 Exporter::Export(buffer, format_name, true, filename);
930 // Substitute $$FName for filename
931 if (!contains(command, "$$FName"))
932 command = "( " + command + " ) < $$FName";
933 command = subst(command, "$$FName", filename);
935 // Execute the command in the background
937 call.startscript(Systemcall::DontWait, command);
941 case LFUN_BUFFER_PRINT: {
942 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
945 string command = split(split(argument, target, ' '),
949 || target_name.empty()
950 || command.empty()) {
951 lyxerr << "Unable to parse \""
952 << argument << '"' << std::endl;
955 if (target != "printer" && target != "file") {
956 lyxerr << "Unrecognized target \""
957 << target << '"' << std::endl;
961 Buffer * buffer = lyx_view_->buffer();
963 if (!Exporter::Export(buffer, "dvi", true)) {
964 showPrintError(buffer->fileName());
968 // Push directory path.
969 string const path = buffer->temppath();
970 support::Path p(path);
972 // there are three cases here:
973 // 1. we print to a file
974 // 2. we print directly to a printer
975 // 3. we print using a spool command (print to file first)
978 string const dviname =
979 changeExtension(buffer->getLatexName(true),
982 if (target == "printer") {
983 if (!lyxrc.print_spool_command.empty()) {
984 // case 3: print using a spool
985 string const psname =
986 changeExtension(dviname,".ps");
987 command += lyxrc.print_to_file
990 + quoteName(dviname);
993 lyxrc.print_spool_command +' ';
994 if (target_name != "default") {
995 command2 += lyxrc.print_spool_printerprefix
999 command2 += quoteName(psname);
1001 // If successful, then spool command
1002 res = one.startscript(
1007 res = one.startscript(
1008 Systemcall::DontWait,
1011 // case 2: print directly to a printer
1012 res = one.startscript(
1013 Systemcall::DontWait,
1014 command + quoteName(dviname));
1018 // case 1: print to a file
1019 command += lyxrc.print_to_file
1020 + quoteName(makeAbsPath(target_name,
1023 + quoteName(dviname);
1024 res = one.startscript(Systemcall::DontWait,
1029 showPrintError(buffer->fileName());
1033 case LFUN_BUFFER_IMPORT:
1038 if (argument != "force") {
1039 if (!theApp->gui().closeAll())
1044 // FIXME: this code needs to be transfered somewhere else
1045 // as lyx_view_ will most certainly be null and a same buffer
1046 // might be visible in more than one LyXView.
1047 if (lyx_view_ && lyx_view_->view()->buffer()) {
1048 // save cursor Position for opened files to .lyx/session
1049 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
1050 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1056 case LFUN_TOC_VIEW: {
1057 BOOST_ASSERT(lyx_view_);
1058 InsetCommandParams p("tableofcontents");
1059 string const data = InsetCommandMailer::params2string("toc", p);
1060 lyx_view_->getDialogs().show("toc", data, 0);
1064 case LFUN_BUFFER_AUTO_SAVE:
1068 case LFUN_RECONFIGURE:
1069 BOOST_ASSERT(lyx_view_);
1070 reconfigure(*lyx_view_);
1073 case LFUN_HELP_OPEN: {
1074 BOOST_ASSERT(lyx_view_);
1075 string const arg = argument;
1077 setErrorMessage(_("Missing argument"));
1080 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1081 if (fname.empty()) {
1082 lyxerr << "LyX: unable to find documentation file `"
1083 << arg << "'. Bad installation?" << endl;
1086 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1087 makeDisplayPath(fname)));
1088 lyx_view_->loadLyXFile(fname, false);
1092 // --- version control -------------------------------
1093 case LFUN_VC_REGISTER:
1094 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1095 if (!ensureBufferClean(view()))
1097 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1098 lyx_view_->buffer()->lyxvc().registrer();
1103 case LFUN_VC_CHECK_IN:
1104 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1105 if (!ensureBufferClean(view()))
1107 if (lyx_view_->buffer()->lyxvc().inUse()
1108 && !lyx_view_->buffer()->isReadonly()) {
1109 lyx_view_->buffer()->lyxvc().checkIn();
1114 case LFUN_VC_CHECK_OUT:
1115 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1116 if (!ensureBufferClean(view()))
1118 if (lyx_view_->buffer()->lyxvc().inUse()
1119 && lyx_view_->buffer()->isReadonly()) {
1120 lyx_view_->buffer()->lyxvc().checkOut();
1125 case LFUN_VC_REVERT:
1126 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1127 lyx_view_->buffer()->lyxvc().revert();
1131 case LFUN_VC_UNDO_LAST:
1132 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1133 lyx_view_->buffer()->lyxvc().undoLast();
1137 // --- buffers ----------------------------------------
1138 case LFUN_BUFFER_SWITCH:
1139 BOOST_ASSERT(lyx_view_);
1140 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1143 case LFUN_BUFFER_NEXT:
1144 BOOST_ASSERT(lyx_view_);
1145 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1148 case LFUN_BUFFER_PREVIOUS:
1149 BOOST_ASSERT(lyx_view_);
1150 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1154 BOOST_ASSERT(lyx_view_);
1155 newFile(view(), argument);
1158 case LFUN_FILE_OPEN:
1159 BOOST_ASSERT(lyx_view_);
1163 case LFUN_DROP_LAYOUTS_CHOICE:
1164 BOOST_ASSERT(lyx_view_);
1165 lyx_view_->getToolbars().openLayoutList();
1168 case LFUN_MENU_OPEN:
1169 BOOST_ASSERT(lyx_view_);
1170 lyx_view_->getMenubar().openByName(from_utf8(argument));
1173 // --- lyxserver commands ----------------------------
1174 case LFUN_SERVER_GET_NAME:
1175 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1176 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1177 lyxerr[Debug::INFO] << "FNAME["
1178 << lyx_view_->buffer()->fileName()
1182 case LFUN_SERVER_NOTIFY:
1183 dispatch_buffer = from_utf8(keyseq->print());
1184 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1187 case LFUN_SERVER_GOTO_FILE_ROW: {
1188 BOOST_ASSERT(lyx_view_);
1191 istringstream is(argument);
1192 is >> file_name >> row;
1193 if (prefixIs(file_name, package().temp_dir())) {
1194 // Needed by inverse dvi search. If it is a file
1195 // in tmpdir, call the apropriated function
1196 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1198 // Must replace extension of the file to be .lyx
1199 // and get full path
1200 string const s = changeExtension(file_name, ".lyx");
1201 // Either change buffer or load the file
1202 if (theBufferList().exists(s)) {
1203 lyx_view_->setBuffer(theBufferList().getBuffer(s));
1205 lyx_view_->loadLyXFile(s);
1209 view()->setCursorFromRow(row);
1212 // see BufferView::center()
1216 case LFUN_DIALOG_SHOW: {
1217 BOOST_ASSERT(lyx_view_);
1218 string const name = cmd.getArg(0);
1219 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1221 if (name == "character") {
1222 data = freefont2string();
1224 lyx_view_->getDialogs().show("character", data);
1225 } else if (name == "latexlog") {
1226 pair<Buffer::LogType, string> const logfile =
1227 lyx_view_->buffer()->getLogName();
1228 switch (logfile.first) {
1229 case Buffer::latexlog:
1232 case Buffer::buildlog:
1236 data += LyXLex::quoteString(logfile.second);
1237 lyx_view_->getDialogs().show("log", data);
1238 } else if (name == "vclog") {
1239 string const data = "vc " +
1240 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1241 lyx_view_->getDialogs().show("log", data);
1243 lyx_view_->getDialogs().show(name, data);
1247 case LFUN_DIALOG_SHOW_NEW_INSET: {
1248 BOOST_ASSERT(lyx_view_);
1249 string const name = cmd.getArg(0);
1250 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1251 if (name == "bibitem" ||
1258 InsetCommandParams p(name);
1259 data = InsetCommandMailer::params2string(name, p);
1260 } else if (name == "include") {
1261 InsetCommandParams p(data);
1262 data = InsetIncludeMailer::params2string(p);
1263 } else if (name == "box") {
1264 // \c data == "Boxed" || "Frameless" etc
1265 InsetBoxParams p(data);
1266 data = InsetBoxMailer::params2string(p);
1267 } else if (name == "branch") {
1268 InsetBranchParams p;
1269 data = InsetBranchMailer::params2string(p);
1270 } else if (name == "citation") {
1271 InsetCommandParams p("cite");
1272 data = InsetCommandMailer::params2string(name, p);
1273 } else if (name == "ert") {
1274 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1275 } else if (name == "external") {
1276 InsetExternalParams p;
1277 Buffer const & buffer = *lyx_view_->buffer();
1278 data = InsetExternalMailer::params2string(p, buffer);
1279 } else if (name == "float") {
1281 data = InsetFloatMailer::params2string(p);
1282 } else if (name == "graphics") {
1283 InsetGraphicsParams p;
1284 Buffer const & buffer = *lyx_view_->buffer();
1285 data = InsetGraphicsMailer::params2string(p, buffer);
1286 } else if (name == "note") {
1288 data = InsetNoteMailer::params2string(p);
1289 } else if (name == "vspace") {
1291 data = InsetVSpaceMailer::params2string(space);
1292 } else if (name == "wrap") {
1294 data = InsetWrapMailer::params2string(p);
1296 lyx_view_->getDialogs().show(name, data, 0);
1300 case LFUN_DIALOG_UPDATE: {
1301 BOOST_ASSERT(lyx_view_);
1302 string const & name = argument;
1303 // Can only update a dialog connected to an existing inset
1304 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1306 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1307 inset->dispatch(view()->cursor(), fr);
1308 } else if (name == "paragraph") {
1309 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1310 } else if (name == "prefs") {
1311 lyx_view_->getDialogs().update(name, string());
1316 case LFUN_DIALOG_HIDE:
1317 Dialogs::hide(argument, 0);
1320 case LFUN_DIALOG_DISCONNECT_INSET:
1321 BOOST_ASSERT(lyx_view_);
1322 lyx_view_->getDialogs().disconnect(argument);
1326 case LFUN_CITATION_INSERT: {
1327 BOOST_ASSERT(lyx_view_);
1328 if (!argument.empty()) {
1329 // we can have one optional argument, delimited by '|'
1330 // citation-insert <key>|<text_before>
1331 // this should be enhanced to also support text_after
1332 // and citation style
1333 string arg = argument;
1335 if (contains(argument, "|")) {
1336 arg = token(argument, '|', 0);
1337 opt1 = '[' + token(argument, '|', 1) + ']';
1339 std::ostringstream os;
1340 os << "citation LatexCommand\n"
1341 << "\\cite" << opt1 << "{" << arg << "}\n"
1343 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1346 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1350 case LFUN_BUFFER_CHILD_OPEN: {
1351 BOOST_ASSERT(lyx_view_);
1352 string const filename =
1353 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1354 // FIXME Should use bformat
1355 setMessage(_("Opening child document ") +
1356 makeDisplayPath(filename) + "...");
1357 view()->saveBookmark(false);
1358 string const parentfilename = lyx_view_->buffer()->fileName();
1359 if (theBufferList().exists(filename))
1360 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1362 lyx_view_->loadLyXFile(filename);
1363 // Set the parent name of the child document.
1364 // This makes insertion of citations and references in the child work,
1365 // when the target is in the parent or another child document.
1366 lyx_view_->buffer()->setParentName(parentfilename);
1370 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1371 BOOST_ASSERT(lyx_view_);
1372 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1375 case LFUN_KEYMAP_OFF:
1376 BOOST_ASSERT(lyx_view_);
1377 lyx_view_->view()->getIntl().keyMapOn(false);
1380 case LFUN_KEYMAP_PRIMARY:
1381 BOOST_ASSERT(lyx_view_);
1382 lyx_view_->view()->getIntl().keyMapPrim();
1385 case LFUN_KEYMAP_SECONDARY:
1386 BOOST_ASSERT(lyx_view_);
1387 lyx_view_->view()->getIntl().keyMapSec();
1390 case LFUN_KEYMAP_TOGGLE:
1391 BOOST_ASSERT(lyx_view_);
1392 lyx_view_->view()->getIntl().toggleKeyMap();
1398 string rest = split(argument, countstr, ' ');
1399 istringstream is(countstr);
1402 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1403 for (int i = 0; i < count; ++i)
1404 dispatch(lyxaction.lookupFunc(rest));
1408 case LFUN_COMMAND_SEQUENCE: {
1409 // argument contains ';'-terminated commands
1410 string arg = argument;
1411 while (!arg.empty()) {
1413 arg = split(arg, first, ';');
1414 FuncRequest func(lyxaction.lookupFunc(first));
1415 func.origin = cmd.origin;
1421 case LFUN_PREFERENCES_SAVE: {
1422 support::Path p(package().user_support());
1423 lyxrc.write("preferences", false);
1427 case LFUN_SCREEN_FONT_UPDATE:
1428 BOOST_ASSERT(lyx_view_);
1429 // handle the screen font changes.
1430 lyxrc.set_font_norm_type();
1431 theFontLoader().update();
1432 // All visible buffers will need resize
1436 case LFUN_SET_COLOR: {
1438 string const x11_name = split(argument, lyx_name, ' ');
1439 if (lyx_name.empty() || x11_name.empty()) {
1440 setErrorMessage(_("Syntax: set-color <lyx_name>"
1445 bool const graphicsbg_changed =
1446 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1447 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1449 if (!lcolor.setColor(lyx_name, x11_name)) {
1451 bformat(_("Set-color \"%1$s\" failed "
1452 "- color is undefined or "
1453 "may not be redefined"),
1454 from_utf8(lyx_name)));
1458 theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1460 if (graphicsbg_changed) {
1461 #ifdef WITH_WARNINGS
1462 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1465 graphics::GCache::get().changeDisplay(true);
1472 BOOST_ASSERT(lyx_view_);
1473 lyx_view_->message(from_utf8(argument));
1476 case LFUN_EXTERNAL_EDIT: {
1477 BOOST_ASSERT(lyx_view_);
1478 FuncRequest fr(action, argument);
1479 InsetExternal().dispatch(view()->cursor(), fr);
1483 case LFUN_GRAPHICS_EDIT: {
1484 FuncRequest fr(action, argument);
1485 InsetGraphics().dispatch(view()->cursor(), fr);
1489 case LFUN_INSET_APPLY: {
1490 BOOST_ASSERT(lyx_view_);
1491 string const name = cmd.getArg(0);
1492 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1494 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1495 inset->dispatch(view()->cursor(), fr);
1497 FuncRequest fr(LFUN_INSET_INSERT, argument);
1500 // ideally, the update flag should be set by the insets,
1501 // but this is not possible currently
1502 updateFlags = Update::Force | Update::FitCursor;
1506 case LFUN_ALL_INSETS_TOGGLE: {
1507 BOOST_ASSERT(lyx_view_);
1509 string const name = split(argument, action, ' ');
1510 InsetBase::Code const inset_code =
1511 InsetBase::translate(name);
1513 LCursor & cur = view()->cursor();
1514 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1516 InsetBase & inset = lyx_view_->buffer()->inset();
1517 InsetIterator it = inset_iterator_begin(inset);
1518 InsetIterator const end = inset_iterator_end(inset);
1519 for (; it != end; ++it) {
1520 if (inset_code == InsetBase::NO_CODE
1521 || inset_code == it->lyxCode()) {
1522 LCursor tmpcur = cur;
1523 tmpcur.pushLeft(*it);
1524 it->dispatch(tmpcur, fr);
1527 updateFlags = Update::Force | Update::FitCursor;
1531 case LFUN_BUFFER_LANGUAGE: {
1532 BOOST_ASSERT(lyx_view_);
1533 Buffer & buffer = *lyx_view_->buffer();
1534 Language const * oldL = buffer.params().language;
1535 Language const * newL = languages.getLanguage(argument);
1536 if (!newL || oldL == newL)
1539 if (oldL->rightToLeft() == newL->rightToLeft()
1540 && !buffer.isMultiLingual())
1541 buffer.changeLanguage(oldL, newL);
1543 buffer.updateDocLang(newL);
1547 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1548 string const fname =
1549 addName(addPath(package().user_support(), "templates/"),
1551 Buffer defaults(fname);
1553 istringstream ss(argument);
1556 int const unknown_tokens = defaults.readHeader(lex);
1558 if (unknown_tokens != 0) {
1559 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1560 << unknown_tokens << " unknown token"
1561 << (unknown_tokens == 1 ? "" : "s")
1565 if (defaults.writeFile(defaults.fileName()))
1566 // FIXME Should use bformat
1567 setMessage(_("Document defaults saved in ")
1568 + makeDisplayPath(fname));
1570 setErrorMessage(_("Unable to save document defaults"));
1574 case LFUN_BUFFER_PARAMS_APPLY: {
1575 BOOST_ASSERT(lyx_view_);
1576 biblio::CiteEngine const engine =
1577 lyx_view_->buffer()->params().cite_engine;
1579 istringstream ss(argument);
1582 int const unknown_tokens =
1583 lyx_view_->buffer()->readHeader(lex);
1585 if (unknown_tokens != 0) {
1586 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1587 << unknown_tokens << " unknown token"
1588 << (unknown_tokens == 1 ? "" : "s")
1591 if (engine == lyx_view_->buffer()->params().cite_engine)
1594 LCursor & cur = view()->cursor();
1595 FuncRequest fr(LFUN_INSET_REFRESH);
1597 InsetBase & inset = lyx_view_->buffer()->inset();
1598 InsetIterator it = inset_iterator_begin(inset);
1599 InsetIterator const end = inset_iterator_end(inset);
1600 for (; it != end; ++it)
1601 if (it->lyxCode() == InsetBase::CITE_CODE)
1602 it->dispatch(cur, fr);
1606 case LFUN_TEXTCLASS_APPLY: {
1607 BOOST_ASSERT(lyx_view_);
1608 Buffer * buffer = lyx_view_->buffer();
1610 textclass_type const old_class =
1611 buffer->params().textclass;
1613 loadTextclass(argument);
1615 std::pair<bool, textclass_type> const tc_pair =
1616 textclasslist.numberOfClass(argument);
1621 textclass_type const new_class = tc_pair.second;
1622 if (old_class == new_class)
1626 lyx_view_->message(_("Converting document to new document class..."));
1627 recordUndoFullDocument(view());
1628 buffer->params().textclass = new_class;
1629 StableDocIterator backcur(view()->cursor());
1630 ErrorList & el = buffer->errorList("Class Switch");
1631 cap::switchBetweenClasses(
1632 old_class, new_class,
1633 static_cast<InsetText &>(buffer->inset()), el);
1635 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1637 buffer->errors("Class Switch");
1638 updateLabels(*buffer);
1639 updateFlags = Update::Force | Update::FitCursor;
1643 case LFUN_TEXTCLASS_LOAD:
1644 loadTextclass(argument);
1647 case LFUN_LYXRC_APPLY: {
1648 LyXRC const lyxrc_orig = lyxrc;
1650 istringstream ss(argument);
1651 bool const success = lyxrc.read(ss) == 0;
1654 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1655 << "Unable to read lyxrc data"
1660 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1664 case LFUN_WINDOW_NEW:
1665 LyX::ref().newLyXView();
1668 case LFUN_WINDOW_CLOSE:
1669 BOOST_ASSERT(lyx_view_);
1670 BOOST_ASSERT(theApp);
1672 // We return here because lyx_view does not exists anymore.
1675 case LFUN_BOOKMARK_GOTO: {
1676 BOOST_ASSERT(lyx_view_);
1677 unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1678 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1679 BOOST_ASSERT(!bm.filename.empty());
1680 // if the file is not opened, open it.
1681 if (!theBufferList().exists(bm.filename))
1682 dispatch(FuncRequest(LFUN_FILE_OPEN, bm.filename));
1683 // open may fail, so we need to test it again
1684 if (theBufferList().exists(bm.filename)) {
1685 // if the current buffer is not that one, switch to it.
1686 if (lyx_view_->buffer()->fileName() != bm.filename)
1687 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, bm.filename));
1688 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != bm.filename);
1689 view()->moveToPosition(bm.par_id, bm.par_pos);
1694 case LFUN_BOOKMARK_CLEAR:
1695 LyX::ref().session().bookmarks().clear();
1698 case LFUN_TOOLBAR_TOGGLE_STATE:
1699 lyx_view_->toggleToolbarState(argument);
1703 BOOST_ASSERT(lyx_view_);
1704 view()->cursor().dispatch(cmd);
1705 updateFlags = view()->cursor().result().update();
1706 if (!view()->cursor().result().dispatched())
1707 if (view()->dispatch(cmd))
1708 updateFlags = Update::Force | Update::FitCursor;
1713 if (lyx_view_ && view()->buffer()) {
1714 // Redraw screen unless explicitly told otherwise.
1715 // This also initializes the position cache for all insets
1716 // in (at least partially) visible top-level paragraphs.
1717 bool needSecondUpdate = false;
1718 if (updateFlags != Update::None)
1719 view()->update(updateFlags);
1721 needSecondUpdate = view()->fitCursor();
1723 if (needSecondUpdate || updateFlags != Update::None) {
1724 view()->buffer()->changed(updateFlags & Update::SinglePar);
1726 lyx_view_->updateStatusBar();
1728 // if we executed a mutating lfun, mark the buffer as dirty
1730 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1731 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1732 view()->buffer()->markDirty();
1734 if (view()->cursor().inTexted()) {
1735 lyx_view_->updateLayoutChoice();
1740 // FIXME UNICODE: _() does not support anything but ascii.
1741 // Do we need a to_ascii() method?
1742 sendDispatchMessage(getMessage(), cmd);
1746 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1748 /* When an action did not originate from the UI/kbd, it makes
1749 * sense to avoid updating the GUI. It turns out that this
1750 * fixes bug 1941, for reasons that are described here:
1751 * http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
1753 if (cmd.origin != FuncRequest::INTERNAL) {
1754 lyx_view_->updateMenubar();
1755 lyx_view_->updateToolbars();
1758 const bool verbose = (cmd.origin == FuncRequest::UI
1759 || cmd.origin == FuncRequest::COMMANDBUFFER);
1761 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1762 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1764 lyx_view_->message(msg);
1768 docstring dispatch_msg = msg;
1769 if (!dispatch_msg.empty())
1770 dispatch_msg += ' ';
1772 string comname = lyxaction.getActionName(cmd.action);
1774 bool argsadded = false;
1776 if (!cmd.argument().empty()) {
1777 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1778 comname += ' ' + to_utf8(cmd.argument());
1783 string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1785 if (!shortcuts.empty())
1786 comname += ": " + shortcuts;
1787 else if (!argsadded && !cmd.argument().empty())
1788 comname += ' ' + to_utf8(cmd.argument());
1790 if (!comname.empty()) {
1791 comname = rtrim(comname);
1792 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1795 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1796 << to_utf8(dispatch_msg) << endl;
1797 if (!dispatch_msg.empty())
1798 lyx_view_->message(dispatch_msg);
1802 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1804 string initpath = lyxrc.document_path;
1805 string filename(name);
1807 if (view()->buffer()) {
1808 string const trypath = lyx_view_->buffer()->filePath();
1809 // If directory is writeable, use this as default.
1810 if (isDirWriteable(trypath))
1814 static int newfile_number;
1816 if (filename.empty()) {
1817 filename = addName(lyxrc.document_path,
1818 "newfile" + convert<string>(++newfile_number) + ".lyx");
1819 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1821 filename = addName(lyxrc.document_path,
1822 "newfile" + convert<string>(newfile_number) +
1827 // The template stuff
1830 FileDialog fileDlg(_("Select template file"),
1831 LFUN_SELECT_FILE_SYNC,
1832 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1833 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1835 FileDialog::Result result =
1836 fileDlg.open(from_utf8(lyxrc.template_path),
1837 FileFilterList(_("LyX Documents (*.lyx)")),
1840 if (result.first == FileDialog::Later)
1842 if (result.second.empty())
1844 templname = to_utf8(result.second);
1847 Buffer * const b = newFile(filename, templname, !name.empty());
1849 lyx_view_->setBuffer(b);
1853 void LyXFunc::open(string const & fname)
1855 string initpath = lyxrc.document_path;
1857 if (view()->buffer()) {
1858 string const trypath = lyx_view_->buffer()->filePath();
1859 // If directory is writeable, use this as default.
1860 if (isDirWriteable(trypath))
1866 if (fname.empty()) {
1867 FileDialog fileDlg(_("Select document to open"),
1869 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1870 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1872 FileDialog::Result result =
1873 fileDlg.open(from_utf8(initpath),
1874 FileFilterList(_("LyX Documents (*.lyx)")),
1877 if (result.first == FileDialog::Later)
1880 filename = to_utf8(result.second);
1882 // check selected filename
1883 if (filename.empty()) {
1884 lyx_view_->message(_("Canceled."));
1890 // get absolute path of file and add ".lyx" to the filename if
1892 string const fullpath = fileSearch(string(), filename, "lyx");
1893 if (!fullpath.empty()) {
1894 filename = fullpath;
1897 docstring const disp_fn = makeDisplayPath(filename);
1899 // if the file doesn't exist, let the user create one
1900 if (!fs::exists(filename)) {
1901 // the user specifically chose this name. Believe him.
1902 Buffer * const b = newFile(filename, string(), true);
1904 lyx_view_->setBuffer(b);
1908 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1911 if (lyx_view_->loadLyXFile(filename)) {
1912 str2 = bformat(_("Document %1$s opened."), disp_fn);
1914 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1916 lyx_view_->message(str2);
1920 void LyXFunc::doImport(string const & argument)
1923 string filename = split(argument, format, ' ');
1925 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1926 << " file: " << filename << endl;
1928 // need user interaction
1929 if (filename.empty()) {
1930 string initpath = lyxrc.document_path;
1932 if (view()->buffer()) {
1933 string const trypath = lyx_view_->buffer()->filePath();
1934 // If directory is writeable, use this as default.
1935 if (isDirWriteable(trypath))
1939 docstring const text = bformat(_("Select %1$s file to import"),
1940 formats.prettyName(format));
1942 FileDialog fileDlg(text,
1944 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1945 make_pair(_("Examples|#E#e"),
1946 from_utf8(addPath(package().system_support(), "examples"))));
1948 docstring filter = formats.prettyName(format);
1951 filter += from_utf8(formats.extension(format));
1954 FileDialog::Result result =
1955 fileDlg.open(from_utf8(initpath),
1956 FileFilterList(filter),
1959 if (result.first == FileDialog::Later)
1962 filename = to_utf8(result.second);
1964 // check selected filename
1965 if (filename.empty())
1966 lyx_view_->message(_("Canceled."));
1969 if (filename.empty())
1972 // get absolute path of file
1973 filename = makeAbsPath(filename);
1975 string const lyxfile = changeExtension(filename, ".lyx");
1977 // Check if the document already is open
1978 if (use_gui && theBufferList().exists(lyxfile)) {
1979 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1980 lyx_view_->message(_("Canceled."));
1985 // if the file exists already, and we didn't do
1986 // -i lyx thefile.lyx, warn
1987 if (fs::exists(lyxfile) && filename != lyxfile) {
1988 docstring const file = makeDisplayPath(lyxfile, 30);
1990 docstring text = bformat(_("The document %1$s already exists.\n\n"
1991 "Do you want to over-write that document?"), file);
1992 int const ret = Alert::prompt(_("Over-write document?"),
1993 text, 0, 1, _("&Over-write"), _("&Cancel"));
1996 lyx_view_->message(_("Canceled."));
2001 ErrorList errorList;
2002 Importer::Import(lyx_view_, filename, format, errorList);
2003 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2007 void LyXFunc::closeBuffer()
2009 // save current cursor position
2010 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
2011 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2012 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2013 if (theBufferList().empty()) {
2014 // need this otherwise SEGV may occur while
2015 // trying to set variables that don't exist
2016 // since there's no current buffer
2017 lyx_view_->getDialogs().hideBufferDependent();
2019 lyx_view_->setBuffer(theBufferList().first());
2025 // Each "lyx_view_" should have it's own message method. lyxview and
2026 // the minibuffer would use the minibuffer, but lyxserver would
2027 // send an ERROR signal to its client. Alejandro 970603
2028 // This function is bit problematic when it comes to NLS, to make the
2029 // lyx servers client be language indepenent we must not translate
2030 // strings sent to this func.
2031 void LyXFunc::setErrorMessage(docstring const & m) const
2033 dispatch_buffer = m;
2038 void LyXFunc::setMessage(docstring const & m) const
2040 dispatch_buffer = m;
2044 string const LyXFunc::viewStatusMessage()
2046 // When meta-fake key is pressed, show the key sequence so far + "M-".
2048 return keyseq->print() + "M-";
2050 // Else, when a non-complete key sequence is pressed,
2051 // show the available options.
2052 if (keyseq->length() > 0 && !keyseq->deleted())
2053 return keyseq->printOptions();
2055 if (!view()->buffer())
2056 return to_utf8(_("Welcome to LyX!"));
2058 return view()->cursor().currentState();
2062 BufferView * LyXFunc::view() const
2064 BOOST_ASSERT(lyx_view_);
2065 return lyx_view_->view();
2069 bool LyXFunc::wasMetaKey() const
2071 return (meta_fake_bit != key_modifier::none);
2077 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2079 // Why the switch you might ask. It is a trick to ensure that all
2080 // the elements in the LyXRCTags enum is handled. As you can see
2081 // there are no breaks at all. So it is just a huge fall-through.
2082 // The nice thing is that we will get a warning from the compiler
2083 // if we forget an element.
2084 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2086 case LyXRC::RC_ACCEPT_COMPOUND:
2087 case LyXRC::RC_ALT_LANG:
2088 case LyXRC::RC_ASCIIROFF_COMMAND:
2089 case LyXRC::RC_ASCII_LINELEN:
2090 case LyXRC::RC_AUTOREGIONDELETE:
2091 case LyXRC::RC_AUTORESET_OPTIONS:
2092 case LyXRC::RC_AUTOSAVE:
2093 case LyXRC::RC_AUTO_NUMBER:
2094 case LyXRC::RC_BACKUPDIR_PATH:
2095 case LyXRC::RC_BIBTEX_COMMAND:
2096 case LyXRC::RC_BINDFILE:
2097 case LyXRC::RC_CHECKLASTFILES:
2098 case LyXRC::RC_USELASTFILEPOS:
2099 case LyXRC::RC_LOADSESSION:
2100 case LyXRC::RC_CHKTEX_COMMAND:
2101 case LyXRC::RC_CONVERTER:
2102 case LyXRC::RC_COPIER:
2103 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2104 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2105 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2106 case LyXRC::RC_DATE_INSERT_FORMAT:
2107 case LyXRC::RC_DEFAULT_LANGUAGE:
2108 case LyXRC::RC_DEFAULT_PAPERSIZE:
2109 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2110 case LyXRC::RC_DISPLAY_GRAPHICS:
2111 case LyXRC::RC_DOCUMENTPATH:
2112 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2113 if (fs::exists(lyxrc_new.document_path) &&
2114 fs::is_directory(lyxrc_new.document_path)) {
2115 support::package().document_dir() = lyxrc.document_path;
2118 case LyXRC::RC_ESC_CHARS:
2119 case LyXRC::RC_FONT_ENCODING:
2120 case LyXRC::RC_FORMAT:
2121 case LyXRC::RC_INDEX_COMMAND:
2122 case LyXRC::RC_INPUT:
2123 case LyXRC::RC_KBMAP:
2124 case LyXRC::RC_KBMAP_PRIMARY:
2125 case LyXRC::RC_KBMAP_SECONDARY:
2126 case LyXRC::RC_LABEL_INIT_LENGTH:
2127 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2128 case LyXRC::RC_LANGUAGE_AUTO_END:
2129 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2130 case LyXRC::RC_LANGUAGE_COMMAND_END:
2131 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2132 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2133 case LyXRC::RC_LANGUAGE_PACKAGE:
2134 case LyXRC::RC_LANGUAGE_USE_BABEL:
2135 case LyXRC::RC_MAKE_BACKUP:
2136 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2137 case LyXRC::RC_NUMLASTFILES:
2138 case LyXRC::RC_PATH_PREFIX:
2139 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2140 support::prependEnvPath("PATH", lyxrc.path_prefix);
2142 case LyXRC::RC_PERS_DICT:
2143 case LyXRC::RC_POPUP_BOLD_FONT:
2144 case LyXRC::RC_POPUP_FONT_ENCODING:
2145 case LyXRC::RC_POPUP_NORMAL_FONT:
2146 case LyXRC::RC_PREVIEW:
2147 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2148 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2149 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2150 case LyXRC::RC_PRINTCOPIESFLAG:
2151 case LyXRC::RC_PRINTER:
2152 case LyXRC::RC_PRINTEVENPAGEFLAG:
2153 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2154 case LyXRC::RC_PRINTFILEEXTENSION:
2155 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2156 case LyXRC::RC_PRINTODDPAGEFLAG:
2157 case LyXRC::RC_PRINTPAGERANGEFLAG:
2158 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2159 case LyXRC::RC_PRINTPAPERFLAG:
2160 case LyXRC::RC_PRINTREVERSEFLAG:
2161 case LyXRC::RC_PRINTSPOOL_COMMAND:
2162 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2163 case LyXRC::RC_PRINTTOFILE:
2164 case LyXRC::RC_PRINTTOPRINTER:
2165 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2166 case LyXRC::RC_PRINT_COMMAND:
2167 case LyXRC::RC_RTL_SUPPORT:
2168 case LyXRC::RC_SCREEN_DPI:
2169 case LyXRC::RC_SCREEN_FONT_ENCODING:
2170 case LyXRC::RC_SCREEN_FONT_ROMAN:
2171 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2172 case LyXRC::RC_SCREEN_FONT_SANS:
2173 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2174 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2175 case LyXRC::RC_SCREEN_FONT_SIZES:
2176 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2177 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2178 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2179 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2180 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2181 case LyXRC::RC_SCREEN_ZOOM:
2182 case LyXRC::RC_SERVERPIPE:
2183 case LyXRC::RC_SET_COLOR:
2184 case LyXRC::RC_SHOW_BANNER:
2185 case LyXRC::RC_SPELL_COMMAND:
2186 case LyXRC::RC_TEMPDIRPATH:
2187 case LyXRC::RC_TEMPLATEPATH:
2188 case LyXRC::RC_TEX_ALLOWS_SPACES:
2189 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2190 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2191 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2193 case LyXRC::RC_UIFILE:
2194 case LyXRC::RC_USER_EMAIL:
2195 case LyXRC::RC_USER_NAME:
2196 case LyXRC::RC_USETEMPDIR:
2197 case LyXRC::RC_USE_ALT_LANG:
2198 case LyXRC::RC_USE_ESC_CHARS:
2199 case LyXRC::RC_USE_INP_ENC:
2200 case LyXRC::RC_USE_PERS_DICT:
2201 case LyXRC::RC_USE_SPELL_LIB:
2202 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2203 case LyXRC::RC_VIEWER:
2204 case LyXRC::RC_LAST: