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"
30 #include "bufferview_funcs.h"
32 #include "CutAndPaste.h"
34 #include "dispatchresult.h"
36 #include "errorlist.h"
39 #include "funcrequest.h"
40 #include "FuncStatus.h"
43 #include "insetiterator.h"
51 #include "LyXAction.h"
56 #include "lyxserver.h"
57 #include "lyxtextclasslist.h"
59 #include "paragraph.h"
60 #include "pariterator.h"
61 #include "ParagraphParameters.h"
64 #include "insets/insetbox.h"
65 #include "insets/insetbranch.h"
66 #include "insets/insetcommand.h"
67 #include "insets/insetert.h"
68 #include "insets/insetexternal.h"
69 #include "insets/insetfloat.h"
70 #include "insets/insetgraphics.h"
71 #include "insets/insetinclude.h"
72 #include "insets/insetnote.h"
73 #include "insets/insettabular.h"
74 #include "insets/insetvspace.h"
75 #include "insets/insetwrap.h"
77 #include "frontends/Application.h"
78 #include "frontends/Alert.h"
79 #include "frontends/Dialogs.h"
80 #include "frontends/FileDialog.h"
81 #include "frontends/FontLoader.h"
82 #include "frontends/Gui.h"
83 #include "frontends/LyXKeySym.h"
84 #include "frontends/LyXView.h"
85 #include "frontends/Menubar.h"
86 #include "frontends/Toolbars.h"
88 #include "support/environment.h"
89 #include "support/filefilterlist.h"
90 #include "support/filetools.h"
91 #include "support/forkedcontr.h"
92 #include "support/fs_extras.h"
93 #include "support/lstrings.h"
94 #include "support/path.h"
95 #include "support/package.h"
96 #include "support/systemcall.h"
97 #include "support/convert.h"
98 #include "support/os.h"
100 #include <boost/current_function.hpp>
101 #include <boost/filesystem/operations.hpp>
108 using bv_funcs::freefont2string;
110 using support::absolutePath;
111 using support::addName;
112 using support::addPath;
113 using support::bformat;
114 using support::changeExtension;
115 using support::contains;
116 using support::FileFilterList;
117 using support::FileName;
118 using support::fileSearch;
119 using support::ForkedcallsController;
120 using support::i18nLibFileSearch;
121 using support::isDirWriteable;
122 using support::isFileReadable;
123 using support::isStrInt;
124 using support::makeAbsPath;
125 using support::makeDisplayPath;
126 using support::package;
127 using support::quoteName;
128 using support::rtrim;
129 using support::split;
130 using support::subst;
131 using support::Systemcall;
132 using support::token;
134 using support::prefixIs;
137 using std::make_pair;
140 using std::istringstream;
141 using std::ostringstream;
143 namespace Alert = frontend::Alert;
144 namespace fs = boost::filesystem;
148 extern tex_accent_struct get_accent(kb_action action);
153 bool getLocalStatus(LCursor cursor,
154 FuncRequest const & cmd, FuncStatus & status)
156 // Try to fix cursor in case it is broken.
157 cursor.fixIfBroken();
159 // This is, of course, a mess. Better create a new doc iterator and use
160 // this in Inset::getStatus. This might require an additional
161 // BufferView * arg, though (which should be avoided)
162 //LCursor safe = *this;
164 for ( ; cursor.depth(); cursor.pop()) {
165 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
166 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
167 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
168 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
170 // The inset's getStatus() will return 'true' if it made
171 // a definitive decision on whether it want to handle the
172 // request or not. The result of this decision is put into
173 // the 'status' parameter.
174 if (cursor.inset().getStatus(cursor, cmd, status)) {
183 /** Return the change status at cursor position, taking in account the
184 * status at each level of the document iterator (a table in a deleted
185 * footnote is deleted).
186 * When \param outer is true, the top slice is not looked at.
188 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
190 size_t const depth = dit.depth() - (outer ? 1 : 0);
192 for (size_t i = 0 ; i < depth ; ++i) {
193 CursorSlice const & slice = dit[i];
194 if (!slice.inset().inMathed()
195 && slice.pos() < slice.paragraph().size()) {
196 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
197 if (ch != Change::UNCHANGED)
201 return Change::UNCHANGED;
209 meta_fake_bit(key_modifier::none)
214 void LyXFunc::initKeySequences(kb_keymap * kb)
216 keyseq.reset(new kb_sequence(kb, kb));
217 cancel_meta_seq.reset(new kb_sequence(kb, kb));
221 void LyXFunc::setLyXView(LyXView * lv)
227 void LyXFunc::handleKeyFunc(kb_action action)
229 char_type c = encoded_last_key;
231 if (keyseq->length())
234 lyx_view_->view()->getIntl().getTransManager().deadkey(
235 c, get_accent(action).accent, view()->getLyXText(), view()->cursor());
236 // Need to clear, in case the minibuffer calls these
239 // copied verbatim from do_accent_char
240 view()->cursor().resetAnchor();
245 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
247 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
249 // Do nothing if we have nothing (JMarc)
250 if (!keysym->isOK()) {
251 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
256 if (keysym->isModifier()) {
257 lyxerr[Debug::KEY] << "isModifier true" << endl;
261 //Encoding const * encoding = view()->cursor().getEncoding();
262 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
263 size_t encoded_last_key = keysym->getUCSEncoded();
265 // Do a one-deep top-level lookup for
266 // cancel and meta-fake keys. RVDK_PATCH_5
267 cancel_meta_seq->reset();
269 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
270 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
271 << " action first set to [" << func.action << ']'
274 // When not cancel or meta-fake, do the normal lookup.
275 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
276 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
277 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
278 // remove Caps Lock and Mod2 as a modifiers
279 func = keyseq->addkey(keysym, (state | meta_fake_bit));
280 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
281 << "action now set to ["
282 << func.action << ']' << endl;
285 // Dont remove this unless you know what you are doing.
286 meta_fake_bit = key_modifier::none;
288 // Can this happen now ?
289 if (func.action == LFUN_NOACTION) {
290 func = FuncRequest(LFUN_COMMAND_PREFIX);
293 if (lyxerr.debugging(Debug::KEY)) {
294 lyxerr << BOOST_CURRENT_FUNCTION
296 << func.action << "]["
297 << to_utf8(keyseq->print()) << ']'
301 // already here we know if it any point in going further
302 // why not return already here if action == -1 and
303 // num_bytes == 0? (Lgb)
305 if (keyseq->length() > 1) {
306 lyx_view_->message(keyseq->print());
310 // Maybe user can only reach the key via holding down shift.
311 // Let's see. But only if shift is the only modifier
312 if (func.action == LFUN_UNKNOWN_ACTION &&
313 state == key_modifier::shift) {
314 lyxerr[Debug::KEY] << "Trying without shift" << endl;
315 func = keyseq->addkey(keysym, key_modifier::none);
316 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
319 if (func.action == LFUN_UNKNOWN_ACTION) {
320 // Hmm, we didn't match any of the keysequences. See
321 // if it's normal insertable text not already covered
323 if (keysym->isText() && keyseq->length() == 1) {
324 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
325 func = FuncRequest(LFUN_SELF_INSERT,
326 FuncRequest::KEYBOARD);
328 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
329 lyx_view_->message(_("Unknown function."));
334 if (func.action == LFUN_SELF_INSERT) {
335 if (encoded_last_key != 0) {
336 docstring const arg(1, encoded_last_key);
337 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
338 FuncRequest::KEYBOARD));
340 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
348 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
350 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
353 if (cmd.action == LFUN_LYX_QUIT) {
354 flag.message(from_utf8(N_("Exiting")));
357 } else if (cmd.action == LFUN_BOOKMARK_GOTO) {
358 // bookmarks can be valid even if there is no opened buffer
359 flag.enabled(LyX::ref().session().bookmarks().isValid(convert<unsigned int>(to_utf8(cmd.argument()))));
361 } else if (cmd.action == LFUN_BOOKMARK_CLEAR) {
362 flag.enabled(LyX::ref().session().bookmarks().size() > 0);
364 } else if (cmd.action == LFUN_TOOLBAR_TOGGLE_STATE) {
365 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
366 if (!(flags & ToolbarBackend::AUTO))
367 flag.setOnOff(flags & ToolbarBackend::ON);
371 LCursor & cur = view()->cursor();
373 /* In LyX/Mac, when a dialog is open, the menus of the
374 application can still be accessed without giving focus to
375 the main window. In this case, we want to disable the menu
376 entries that are buffer-related.
378 Note that this code is not perfect, as bug 1941 attests:
379 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
381 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
382 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
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(FileName(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 docstring last_search;
769 docstring searched_string;
771 if (!cmd.argument().empty()) {
772 last_search = cmd.argument();
773 searched_string = cmd.argument();
775 searched_string = last_search;
778 if (searched_string.empty())
781 bool const fw = action == LFUN_WORD_FIND_FORWARD;
782 docstring const data =
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(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(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(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,
1021 path).toFilesystemEncoding())
1023 + quoteName(dviname);
1024 res = one.startscript(Systemcall::DontWait,
1029 showPrintError(buffer->fileName());
1033 case LFUN_BUFFER_IMPORT:
1038 // quitting is triggered by the gui code
1039 // (leaving the event loop).
1040 if (theBufferList().quitWriteAll())
1041 theApp()->gui().closeAllViews();
1044 case LFUN_TOC_VIEW: {
1045 BOOST_ASSERT(lyx_view_);
1046 InsetCommandParams p("tableofcontents");
1047 string const data = InsetCommandMailer::params2string("toc", p);
1048 lyx_view_->getDialogs().show("toc", data, 0);
1052 case LFUN_BUFFER_AUTO_SAVE:
1056 case LFUN_RECONFIGURE:
1057 BOOST_ASSERT(lyx_view_);
1058 reconfigure(*lyx_view_);
1061 case LFUN_HELP_OPEN: {
1062 BOOST_ASSERT(lyx_view_);
1063 string const arg = argument;
1065 setErrorMessage(_("Missing argument"));
1068 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1069 if (fname.empty()) {
1070 lyxerr << "LyX: unable to find documentation file `"
1071 << arg << "'. Bad installation?" << endl;
1074 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1075 makeDisplayPath(fname.absFilename())));
1076 lyx_view_->loadLyXFile(fname, false);
1080 // --- version control -------------------------------
1081 case LFUN_VC_REGISTER:
1082 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1083 if (!ensureBufferClean(view()))
1085 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1086 lyx_view_->buffer()->lyxvc().registrer();
1091 case LFUN_VC_CHECK_IN:
1092 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1093 if (!ensureBufferClean(view()))
1095 if (lyx_view_->buffer()->lyxvc().inUse()
1096 && !lyx_view_->buffer()->isReadonly()) {
1097 lyx_view_->buffer()->lyxvc().checkIn();
1102 case LFUN_VC_CHECK_OUT:
1103 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1104 if (!ensureBufferClean(view()))
1106 if (lyx_view_->buffer()->lyxvc().inUse()
1107 && lyx_view_->buffer()->isReadonly()) {
1108 lyx_view_->buffer()->lyxvc().checkOut();
1113 case LFUN_VC_REVERT:
1114 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1115 lyx_view_->buffer()->lyxvc().revert();
1119 case LFUN_VC_UNDO_LAST:
1120 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1121 lyx_view_->buffer()->lyxvc().undoLast();
1125 // --- buffers ----------------------------------------
1126 case LFUN_BUFFER_SWITCH:
1127 BOOST_ASSERT(lyx_view_);
1128 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1131 case LFUN_BUFFER_NEXT:
1132 BOOST_ASSERT(lyx_view_);
1133 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1136 case LFUN_BUFFER_PREVIOUS:
1137 BOOST_ASSERT(lyx_view_);
1138 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1142 BOOST_ASSERT(lyx_view_);
1143 newFile(view(), argument);
1146 case LFUN_FILE_OPEN:
1147 BOOST_ASSERT(lyx_view_);
1151 case LFUN_DROP_LAYOUTS_CHOICE:
1152 BOOST_ASSERT(lyx_view_);
1153 lyx_view_->getToolbars().openLayoutList();
1156 case LFUN_MENU_OPEN:
1157 BOOST_ASSERT(lyx_view_);
1158 lyx_view_->getMenubar().openByName(from_utf8(argument));
1161 // --- lyxserver commands ----------------------------
1162 case LFUN_SERVER_GET_NAME:
1163 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1164 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1165 lyxerr[Debug::INFO] << "FNAME["
1166 << lyx_view_->buffer()->fileName()
1170 case LFUN_SERVER_NOTIFY:
1171 dispatch_buffer = keyseq->print();
1172 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1175 case LFUN_SERVER_GOTO_FILE_ROW: {
1176 BOOST_ASSERT(lyx_view_);
1179 istringstream is(argument);
1180 is >> file_name >> row;
1181 if (prefixIs(file_name, package().temp_dir())) {
1182 // Needed by inverse dvi search. If it is a file
1183 // in tmpdir, call the apropriated function
1184 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1186 // Must replace extension of the file to be .lyx
1187 // and get full path
1188 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1189 // Either change buffer or load the file
1190 if (theBufferList().exists(s.absFilename())) {
1191 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1193 lyx_view_->loadLyXFile(s);
1197 view()->setCursorFromRow(row);
1200 // see BufferView::center()
1204 case LFUN_DIALOG_SHOW: {
1205 BOOST_ASSERT(lyx_view_);
1206 string const name = cmd.getArg(0);
1207 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1209 if (name == "character") {
1210 data = freefont2string();
1212 lyx_view_->getDialogs().show("character", data);
1213 } else if (name == "latexlog") {
1214 pair<Buffer::LogType, string> const logfile =
1215 lyx_view_->buffer()->getLogName();
1216 switch (logfile.first) {
1217 case Buffer::latexlog:
1220 case Buffer::buildlog:
1224 data += LyXLex::quoteString(logfile.second);
1225 lyx_view_->getDialogs().show("log", data);
1226 } else if (name == "vclog") {
1227 string const data = "vc " +
1228 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1229 lyx_view_->getDialogs().show("log", data);
1231 lyx_view_->getDialogs().show(name, data);
1235 case LFUN_DIALOG_SHOW_NEW_INSET: {
1236 BOOST_ASSERT(lyx_view_);
1237 string const name = cmd.getArg(0);
1238 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1239 if (name == "bibitem" ||
1243 name == "nomenclature" ||
1247 InsetCommandParams p(name);
1248 data = InsetCommandMailer::params2string(name, p);
1249 } else if (name == "include") {
1250 // data is the include type: one of "include",
1251 // "input", "verbatiminput" or "verbatiminput*"
1253 // default type is requested
1255 InsetCommandParams p(data);
1256 data = InsetIncludeMailer::params2string(p);
1257 } else if (name == "box") {
1258 // \c data == "Boxed" || "Frameless" etc
1259 InsetBoxParams p(data);
1260 data = InsetBoxMailer::params2string(p);
1261 } else if (name == "branch") {
1262 InsetBranchParams p;
1263 data = InsetBranchMailer::params2string(p);
1264 } else if (name == "citation") {
1265 InsetCommandParams p("cite");
1266 data = InsetCommandMailer::params2string(name, p);
1267 } else if (name == "ert") {
1268 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1269 } else if (name == "external") {
1270 InsetExternalParams p;
1271 Buffer const & buffer = *lyx_view_->buffer();
1272 data = InsetExternalMailer::params2string(p, buffer);
1273 } else if (name == "float") {
1275 data = InsetFloatMailer::params2string(p);
1276 } else if (name == "graphics") {
1277 InsetGraphicsParams p;
1278 Buffer const & buffer = *lyx_view_->buffer();
1279 data = InsetGraphicsMailer::params2string(p, buffer);
1280 } else if (name == "note") {
1282 data = InsetNoteMailer::params2string(p);
1283 } else if (name == "vspace") {
1285 data = InsetVSpaceMailer::params2string(space);
1286 } else if (name == "wrap") {
1288 data = InsetWrapMailer::params2string(p);
1290 lyx_view_->getDialogs().show(name, data, 0);
1294 case LFUN_DIALOG_UPDATE: {
1295 BOOST_ASSERT(lyx_view_);
1296 string const & name = argument;
1297 // Can only update a dialog connected to an existing inset
1298 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1300 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1301 inset->dispatch(view()->cursor(), fr);
1302 } else if (name == "paragraph") {
1303 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1304 } else if (name == "prefs") {
1305 lyx_view_->getDialogs().update(name, string());
1310 case LFUN_DIALOG_HIDE:
1311 Dialogs::hide(argument, 0);
1314 case LFUN_DIALOG_DISCONNECT_INSET:
1315 BOOST_ASSERT(lyx_view_);
1316 lyx_view_->getDialogs().disconnect(argument);
1320 case LFUN_CITATION_INSERT: {
1321 BOOST_ASSERT(lyx_view_);
1322 if (!argument.empty()) {
1323 // we can have one optional argument, delimited by '|'
1324 // citation-insert <key>|<text_before>
1325 // this should be enhanced to also support text_after
1326 // and citation style
1327 string arg = argument;
1329 if (contains(argument, "|")) {
1330 arg = token(argument, '|', 0);
1331 opt1 = '[' + token(argument, '|', 1) + ']';
1333 std::ostringstream os;
1334 os << "citation LatexCommand\n"
1335 << "\\cite" << opt1 << "{" << arg << "}\n"
1337 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1340 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1344 case LFUN_BUFFER_CHILD_OPEN: {
1345 BOOST_ASSERT(lyx_view_);
1346 FileName const filename =
1347 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1348 // FIXME Should use bformat
1349 setMessage(_("Opening child document ") +
1350 makeDisplayPath(filename.absFilename()) + "...");
1351 view()->saveBookmark(false);
1352 string const parentfilename = lyx_view_->buffer()->fileName();
1353 if (theBufferList().exists(filename.absFilename()))
1354 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1356 lyx_view_->loadLyXFile(filename);
1357 // Set the parent name of the child document.
1358 // This makes insertion of citations and references in the child work,
1359 // when the target is in the parent or another child document.
1360 lyx_view_->buffer()->setParentName(parentfilename);
1364 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1365 BOOST_ASSERT(lyx_view_);
1366 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1369 case LFUN_KEYMAP_OFF:
1370 BOOST_ASSERT(lyx_view_);
1371 lyx_view_->view()->getIntl().keyMapOn(false);
1374 case LFUN_KEYMAP_PRIMARY:
1375 BOOST_ASSERT(lyx_view_);
1376 lyx_view_->view()->getIntl().keyMapPrim();
1379 case LFUN_KEYMAP_SECONDARY:
1380 BOOST_ASSERT(lyx_view_);
1381 lyx_view_->view()->getIntl().keyMapSec();
1384 case LFUN_KEYMAP_TOGGLE:
1385 BOOST_ASSERT(lyx_view_);
1386 lyx_view_->view()->getIntl().toggleKeyMap();
1392 string rest = split(argument, countstr, ' ');
1393 istringstream is(countstr);
1396 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1397 for (int i = 0; i < count; ++i)
1398 dispatch(lyxaction.lookupFunc(rest));
1402 case LFUN_COMMAND_SEQUENCE: {
1403 // argument contains ';'-terminated commands
1404 string arg = argument;
1405 while (!arg.empty()) {
1407 arg = split(arg, first, ';');
1408 FuncRequest func(lyxaction.lookupFunc(first));
1409 func.origin = cmd.origin;
1415 case LFUN_PREFERENCES_SAVE: {
1416 lyxrc.write(makeAbsPath("preferences",
1417 package().user_support()),
1422 case LFUN_SCREEN_FONT_UPDATE:
1423 BOOST_ASSERT(lyx_view_);
1424 // handle the screen font changes.
1425 lyxrc.set_font_norm_type();
1426 theFontLoader().update();
1427 // All visible buffers will need resize
1431 case LFUN_SET_COLOR: {
1433 string const x11_name = split(argument, lyx_name, ' ');
1434 if (lyx_name.empty() || x11_name.empty()) {
1435 setErrorMessage(_("Syntax: set-color <lyx_name>"
1440 bool const graphicsbg_changed =
1441 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1442 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1444 if (!lcolor.setColor(lyx_name, x11_name)) {
1446 bformat(_("Set-color \"%1$s\" failed "
1447 "- color is undefined or "
1448 "may not be redefined"),
1449 from_utf8(lyx_name)));
1453 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1455 if (graphicsbg_changed) {
1456 #ifdef WITH_WARNINGS
1457 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1460 graphics::GCache::get().changeDisplay(true);
1467 BOOST_ASSERT(lyx_view_);
1468 lyx_view_->message(from_utf8(argument));
1471 case LFUN_EXTERNAL_EDIT: {
1472 BOOST_ASSERT(lyx_view_);
1473 FuncRequest fr(action, argument);
1474 InsetExternal().dispatch(view()->cursor(), fr);
1478 case LFUN_GRAPHICS_EDIT: {
1479 FuncRequest fr(action, argument);
1480 InsetGraphics().dispatch(view()->cursor(), fr);
1484 case LFUN_INSET_APPLY: {
1485 BOOST_ASSERT(lyx_view_);
1486 string const name = cmd.getArg(0);
1487 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1489 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1490 inset->dispatch(view()->cursor(), fr);
1492 FuncRequest fr(LFUN_INSET_INSERT, argument);
1495 // ideally, the update flag should be set by the insets,
1496 // but this is not possible currently
1497 updateFlags = Update::Force | Update::FitCursor;
1501 case LFUN_ALL_INSETS_TOGGLE: {
1502 BOOST_ASSERT(lyx_view_);
1504 string const name = split(argument, action, ' ');
1505 InsetBase::Code const inset_code =
1506 InsetBase::translate(name);
1508 LCursor & cur = view()->cursor();
1509 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1511 InsetBase & inset = lyx_view_->buffer()->inset();
1512 InsetIterator it = inset_iterator_begin(inset);
1513 InsetIterator const end = inset_iterator_end(inset);
1514 for (; it != end; ++it) {
1515 if (inset_code == InsetBase::NO_CODE
1516 || inset_code == it->lyxCode()) {
1517 LCursor tmpcur = cur;
1518 tmpcur.pushLeft(*it);
1519 it->dispatch(tmpcur, fr);
1522 updateFlags = Update::Force | Update::FitCursor;
1526 case LFUN_BUFFER_LANGUAGE: {
1527 BOOST_ASSERT(lyx_view_);
1528 Buffer & buffer = *lyx_view_->buffer();
1529 Language const * oldL = buffer.params().language;
1530 Language const * newL = languages.getLanguage(argument);
1531 if (!newL || oldL == newL)
1534 if (oldL->rightToLeft() == newL->rightToLeft()
1535 && !buffer.isMultiLingual())
1536 buffer.changeLanguage(oldL, newL);
1538 buffer.updateDocLang(newL);
1542 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1543 string const fname =
1544 addName(addPath(package().user_support(), "templates/"),
1546 Buffer defaults(fname);
1548 istringstream ss(argument);
1551 int const unknown_tokens = defaults.readHeader(lex);
1553 if (unknown_tokens != 0) {
1554 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1555 << unknown_tokens << " unknown token"
1556 << (unknown_tokens == 1 ? "" : "s")
1560 if (defaults.writeFile(FileName(defaults.fileName())))
1561 // FIXME Should use bformat
1562 setMessage(_("Document defaults saved in ")
1563 + makeDisplayPath(fname));
1565 setErrorMessage(_("Unable to save document defaults"));
1569 case LFUN_BUFFER_PARAMS_APPLY: {
1570 BOOST_ASSERT(lyx_view_);
1571 biblio::CiteEngine const engine =
1572 lyx_view_->buffer()->params().cite_engine;
1574 istringstream ss(argument);
1577 int const unknown_tokens =
1578 lyx_view_->buffer()->readHeader(lex);
1580 if (unknown_tokens != 0) {
1581 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1582 << unknown_tokens << " unknown token"
1583 << (unknown_tokens == 1 ? "" : "s")
1586 if (engine == lyx_view_->buffer()->params().cite_engine)
1589 LCursor & cur = view()->cursor();
1590 FuncRequest fr(LFUN_INSET_REFRESH);
1592 InsetBase & inset = lyx_view_->buffer()->inset();
1593 InsetIterator it = inset_iterator_begin(inset);
1594 InsetIterator const end = inset_iterator_end(inset);
1595 for (; it != end; ++it)
1596 if (it->lyxCode() == InsetBase::CITE_CODE)
1597 it->dispatch(cur, fr);
1601 case LFUN_TEXTCLASS_APPLY: {
1602 BOOST_ASSERT(lyx_view_);
1603 Buffer * buffer = lyx_view_->buffer();
1605 textclass_type const old_class =
1606 buffer->params().textclass;
1608 loadTextclass(argument);
1610 std::pair<bool, textclass_type> const tc_pair =
1611 textclasslist.numberOfClass(argument);
1616 textclass_type const new_class = tc_pair.second;
1617 if (old_class == new_class)
1621 lyx_view_->message(_("Converting document to new document class..."));
1622 recordUndoFullDocument(view());
1623 buffer->params().textclass = new_class;
1624 StableDocIterator backcur(view()->cursor());
1625 ErrorList & el = buffer->errorList("Class Switch");
1626 cap::switchBetweenClasses(
1627 old_class, new_class,
1628 static_cast<InsetText &>(buffer->inset()), el);
1630 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1632 buffer->errors("Class Switch");
1633 updateLabels(*buffer);
1634 updateFlags = Update::Force | Update::FitCursor;
1638 case LFUN_TEXTCLASS_LOAD:
1639 loadTextclass(argument);
1642 case LFUN_LYXRC_APPLY: {
1643 LyXRC const lyxrc_orig = lyxrc;
1645 istringstream ss(argument);
1646 bool const success = lyxrc.read(ss) == 0;
1649 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1650 << "Unable to read lyxrc data"
1655 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1659 case LFUN_WINDOW_NEW:
1660 LyX::ref().newLyXView();
1663 case LFUN_WINDOW_CLOSE:
1664 BOOST_ASSERT(lyx_view_);
1665 BOOST_ASSERT(theApp());
1666 // ask the user for saving changes or cancel quit
1667 if (!theBufferList().quitWriteAll())
1672 case LFUN_BOOKMARK_GOTO: {
1673 BOOST_ASSERT(lyx_view_);
1674 unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1675 BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1676 BOOST_ASSERT(!bm.filename.empty());
1677 string const file = bm.filename.absFilename();
1678 // if the file is not opened, open it.
1679 if (!theBufferList().exists(file))
1680 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
1681 // open may fail, so we need to test it again
1682 if (theBufferList().exists(file)) {
1683 // if the current buffer is not that one, switch to it.
1684 if (lyx_view_->buffer()->fileName() != file)
1685 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
1686 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != file);
1687 view()->moveToPosition(bm.par_id, bm.par_pos);
1692 case LFUN_BOOKMARK_CLEAR:
1693 LyX::ref().session().bookmarks().clear();
1696 case LFUN_TOOLBAR_TOGGLE_STATE:
1697 lyx_view_->toggleToolbarState(argument);
1701 BOOST_ASSERT(lyx_view_);
1702 view()->cursor().dispatch(cmd);
1703 updateFlags = view()->cursor().result().update();
1704 if (!view()->cursor().result().dispatched())
1705 if (view()->dispatch(cmd))
1706 updateFlags = Update::Force | Update::FitCursor;
1711 if (lyx_view_ && view()->buffer()) {
1712 // BufferView::update() updates the ViewMetricsInfo and
1713 // also initializes the position cache for all insets in
1714 // (at least partially) visible top-level paragraphs.
1715 // We will redraw the screen only if needed.
1716 if (view()->update(updateFlags)) {
1717 // Buffer::changed() signals that a repaint is needed.
1718 // The frontend (WorkArea) knows which area to repaint
1719 // thanks to the ViewMetricsInfo updated above.
1720 view()->buffer()->changed();
1723 lyx_view_->updateStatusBar();
1725 // if we executed a mutating lfun, mark the buffer as dirty
1727 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1728 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1729 view()->buffer()->markDirty();
1731 if (view()->cursor().inTexted()) {
1732 lyx_view_->updateLayoutChoice();
1737 lyx_view_->updateMenubar();
1738 lyx_view_->updateToolbars();
1739 sendDispatchMessage(getMessage(), cmd);
1744 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1746 const bool verbose = (cmd.origin == FuncRequest::MENU
1747 || cmd.origin == FuncRequest::TOOLBAR
1748 || cmd.origin == FuncRequest::COMMANDBUFFER);
1750 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1751 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1753 lyx_view_->message(msg);
1757 docstring dispatch_msg = msg;
1758 if (!dispatch_msg.empty())
1759 dispatch_msg += ' ';
1761 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1763 bool argsadded = false;
1765 if (!cmd.argument().empty()) {
1766 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1767 comname += ' ' + cmd.argument();
1772 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1774 if (!shortcuts.empty())
1775 comname += ": " + shortcuts;
1776 else if (!argsadded && !cmd.argument().empty())
1777 comname += ' ' + cmd.argument();
1779 if (!comname.empty()) {
1780 comname = rtrim(comname);
1781 dispatch_msg += '(' + rtrim(comname) + ')';
1784 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1785 << to_utf8(dispatch_msg) << endl;
1786 if (!dispatch_msg.empty())
1787 lyx_view_->message(dispatch_msg);
1791 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1793 // FIXME: initpath is not used. What to do?
1794 string initpath = lyxrc.document_path;
1795 string filename(name);
1797 if (view()->buffer()) {
1798 string const trypath = lyx_view_->buffer()->filePath();
1799 // If directory is writeable, use this as default.
1800 if (isDirWriteable(FileName(trypath)))
1804 static int newfile_number;
1806 if (filename.empty()) {
1807 filename = addName(lyxrc.document_path,
1808 "newfile" + convert<string>(++newfile_number) + ".lyx");
1809 while (theBufferList().exists(filename) ||
1810 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1812 filename = addName(lyxrc.document_path,
1813 "newfile" + convert<string>(newfile_number) +
1818 // The template stuff
1821 FileDialog fileDlg(_("Select template file"),
1822 LFUN_SELECT_FILE_SYNC,
1823 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1824 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1826 FileDialog::Result result =
1827 fileDlg.open(from_utf8(lyxrc.template_path),
1828 FileFilterList(_("LyX Documents (*.lyx)")),
1831 if (result.first == FileDialog::Later)
1833 if (result.second.empty())
1835 templname = to_utf8(result.second);
1838 Buffer * const b = newFile(filename, templname, !name.empty());
1840 lyx_view_->setBuffer(b);
1844 void LyXFunc::open(string const & fname)
1846 string initpath = lyxrc.document_path;
1848 if (view()->buffer()) {
1849 string const trypath = lyx_view_->buffer()->filePath();
1850 // If directory is writeable, use this as default.
1851 if (isDirWriteable(FileName(trypath)))
1857 if (fname.empty()) {
1858 FileDialog fileDlg(_("Select document to open"),
1860 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1861 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1863 FileDialog::Result result =
1864 fileDlg.open(from_utf8(initpath),
1865 FileFilterList(_("LyX Documents (*.lyx)")),
1868 if (result.first == FileDialog::Later)
1871 filename = to_utf8(result.second);
1873 // check selected filename
1874 if (filename.empty()) {
1875 lyx_view_->message(_("Canceled."));
1881 // get absolute path of file and add ".lyx" to the filename if
1883 FileName const fullname = fileSearch(string(), filename, "lyx");
1884 if (!fullname.empty())
1885 filename = fullname.absFilename();
1887 // if the file doesn't exist, let the user create one
1888 if (!fs::exists(fullname.toFilesystemEncoding())) {
1889 // the user specifically chose this name. Believe him.
1890 Buffer * const b = newFile(filename, string(), true);
1892 lyx_view_->setBuffer(b);
1896 docstring const disp_fn = makeDisplayPath(filename);
1897 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1900 if (lyx_view_->loadLyXFile(fullname)) {
1901 str2 = bformat(_("Document %1$s opened."), disp_fn);
1903 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1905 lyx_view_->message(str2);
1909 void LyXFunc::doImport(string const & argument)
1912 string filename = split(argument, format, ' ');
1914 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1915 << " file: " << filename << endl;
1917 // need user interaction
1918 if (filename.empty()) {
1919 string initpath = lyxrc.document_path;
1921 if (view()->buffer()) {
1922 string const trypath = lyx_view_->buffer()->filePath();
1923 // If directory is writeable, use this as default.
1924 if (isDirWriteable(FileName(trypath)))
1928 docstring const text = bformat(_("Select %1$s file to import"),
1929 formats.prettyName(format));
1931 FileDialog fileDlg(text,
1933 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1934 make_pair(_("Examples|#E#e"),
1935 from_utf8(addPath(package().system_support(), "examples"))));
1937 docstring filter = formats.prettyName(format);
1940 filter += from_utf8(formats.extension(format));
1943 FileDialog::Result result =
1944 fileDlg.open(from_utf8(initpath),
1945 FileFilterList(filter),
1948 if (result.first == FileDialog::Later)
1951 filename = to_utf8(result.second);
1953 // check selected filename
1954 if (filename.empty())
1955 lyx_view_->message(_("Canceled."));
1958 if (filename.empty())
1961 // get absolute path of file
1962 FileName const fullname(makeAbsPath(filename));
1964 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
1966 // Check if the document already is open
1967 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
1968 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
1969 lyx_view_->message(_("Canceled."));
1974 // if the file exists already, and we didn't do
1975 // -i lyx thefile.lyx, warn
1976 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
1977 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
1979 docstring text = bformat(_("The document %1$s already exists.\n\n"
1980 "Do you want to over-write that document?"), file);
1981 int const ret = Alert::prompt(_("Over-write document?"),
1982 text, 0, 1, _("&Over-write"), _("&Cancel"));
1985 lyx_view_->message(_("Canceled."));
1990 ErrorList errorList;
1991 Importer::Import(lyx_view_, fullname, format, errorList);
1992 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1996 void LyXFunc::closeBuffer()
1998 // save current cursor position
1999 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2000 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2001 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2002 if (theBufferList().empty()) {
2003 // need this otherwise SEGV may occur while
2004 // trying to set variables that don't exist
2005 // since there's no current buffer
2006 lyx_view_->getDialogs().hideBufferDependent();
2008 lyx_view_->setBuffer(theBufferList().first());
2014 // Each "lyx_view_" should have it's own message method. lyxview and
2015 // the minibuffer would use the minibuffer, but lyxserver would
2016 // send an ERROR signal to its client. Alejandro 970603
2017 // This function is bit problematic when it comes to NLS, to make the
2018 // lyx servers client be language indepenent we must not translate
2019 // strings sent to this func.
2020 void LyXFunc::setErrorMessage(docstring const & m) const
2022 dispatch_buffer = m;
2027 void LyXFunc::setMessage(docstring const & m) const
2029 dispatch_buffer = m;
2033 docstring const LyXFunc::viewStatusMessage()
2035 // When meta-fake key is pressed, show the key sequence so far + "M-".
2037 return keyseq->print() + "M-";
2039 // Else, when a non-complete key sequence is pressed,
2040 // show the available options.
2041 if (keyseq->length() > 0 && !keyseq->deleted())
2042 return keyseq->printOptions();
2044 if (!view()->buffer())
2045 return _("Welcome to LyX!");
2047 return view()->cursor().currentState();
2051 BufferView * LyXFunc::view() const
2053 BOOST_ASSERT(lyx_view_);
2054 return lyx_view_->view();
2058 bool LyXFunc::wasMetaKey() const
2060 return (meta_fake_bit != key_modifier::none);
2066 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2068 // Why the switch you might ask. It is a trick to ensure that all
2069 // the elements in the LyXRCTags enum is handled. As you can see
2070 // there are no breaks at all. So it is just a huge fall-through.
2071 // The nice thing is that we will get a warning from the compiler
2072 // if we forget an element.
2073 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2075 case LyXRC::RC_ACCEPT_COMPOUND:
2076 case LyXRC::RC_ALT_LANG:
2077 case LyXRC::RC_ASCIIROFF_COMMAND:
2078 case LyXRC::RC_ASCII_LINELEN:
2079 case LyXRC::RC_AUTOREGIONDELETE:
2080 case LyXRC::RC_AUTORESET_OPTIONS:
2081 case LyXRC::RC_AUTOSAVE:
2082 case LyXRC::RC_AUTO_NUMBER:
2083 case LyXRC::RC_BACKUPDIR_PATH:
2084 case LyXRC::RC_BIBTEX_COMMAND:
2085 case LyXRC::RC_BINDFILE:
2086 case LyXRC::RC_CHECKLASTFILES:
2087 case LyXRC::RC_USELASTFILEPOS:
2088 case LyXRC::RC_LOADSESSION:
2089 case LyXRC::RC_CHKTEX_COMMAND:
2090 case LyXRC::RC_CONVERTER:
2091 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2092 case LyXRC::RC_COPIER:
2093 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2094 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2095 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2096 case LyXRC::RC_DATE_INSERT_FORMAT:
2097 case LyXRC::RC_DEFAULT_LANGUAGE:
2098 case LyXRC::RC_DEFAULT_PAPERSIZE:
2099 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2100 case LyXRC::RC_DISPLAY_GRAPHICS:
2101 case LyXRC::RC_DOCUMENTPATH:
2102 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2103 string const encoded = FileName(
2104 lyxrc_new.document_path).toFilesystemEncoding();
2105 if (fs::exists(encoded) && fs::is_directory(encoded))
2106 support::package().document_dir() = lyxrc.document_path;
2108 case LyXRC::RC_ESC_CHARS:
2109 case LyXRC::RC_FONT_ENCODING:
2110 case LyXRC::RC_FORMAT:
2111 case LyXRC::RC_INDEX_COMMAND:
2112 case LyXRC::RC_INPUT:
2113 case LyXRC::RC_KBMAP:
2114 case LyXRC::RC_KBMAP_PRIMARY:
2115 case LyXRC::RC_KBMAP_SECONDARY:
2116 case LyXRC::RC_LABEL_INIT_LENGTH:
2117 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2118 case LyXRC::RC_LANGUAGE_AUTO_END:
2119 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2120 case LyXRC::RC_LANGUAGE_COMMAND_END:
2121 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2122 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2123 case LyXRC::RC_LANGUAGE_PACKAGE:
2124 case LyXRC::RC_LANGUAGE_USE_BABEL:
2125 case LyXRC::RC_MAKE_BACKUP:
2126 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2127 case LyXRC::RC_NUMLASTFILES:
2128 case LyXRC::RC_PATH_PREFIX:
2129 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2130 support::prependEnvPath("PATH", lyxrc.path_prefix);
2132 case LyXRC::RC_PERS_DICT:
2133 case LyXRC::RC_POPUP_BOLD_FONT:
2134 case LyXRC::RC_POPUP_FONT_ENCODING:
2135 case LyXRC::RC_POPUP_NORMAL_FONT:
2136 case LyXRC::RC_PREVIEW:
2137 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2138 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2139 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2140 case LyXRC::RC_PRINTCOPIESFLAG:
2141 case LyXRC::RC_PRINTER:
2142 case LyXRC::RC_PRINTEVENPAGEFLAG:
2143 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2144 case LyXRC::RC_PRINTFILEEXTENSION:
2145 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2146 case LyXRC::RC_PRINTODDPAGEFLAG:
2147 case LyXRC::RC_PRINTPAGERANGEFLAG:
2148 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2149 case LyXRC::RC_PRINTPAPERFLAG:
2150 case LyXRC::RC_PRINTREVERSEFLAG:
2151 case LyXRC::RC_PRINTSPOOL_COMMAND:
2152 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2153 case LyXRC::RC_PRINTTOFILE:
2154 case LyXRC::RC_PRINTTOPRINTER:
2155 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2156 case LyXRC::RC_PRINT_COMMAND:
2157 case LyXRC::RC_RTL_SUPPORT:
2158 case LyXRC::RC_SCREEN_DPI:
2159 case LyXRC::RC_SCREEN_FONT_ENCODING:
2160 case LyXRC::RC_SCREEN_FONT_ROMAN:
2161 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2162 case LyXRC::RC_SCREEN_FONT_SANS:
2163 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2164 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2165 case LyXRC::RC_SCREEN_FONT_SIZES:
2166 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2167 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2168 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2169 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2170 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2171 case LyXRC::RC_SCREEN_ZOOM:
2172 case LyXRC::RC_SERVERPIPE:
2173 case LyXRC::RC_SET_COLOR:
2174 case LyXRC::RC_SHOW_BANNER:
2175 case LyXRC::RC_SPELL_COMMAND:
2176 case LyXRC::RC_TEMPDIRPATH:
2177 case LyXRC::RC_TEMPLATEPATH:
2178 case LyXRC::RC_TEX_ALLOWS_SPACES:
2179 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2180 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2181 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2183 case LyXRC::RC_UIFILE:
2184 case LyXRC::RC_USER_EMAIL:
2185 case LyXRC::RC_USER_NAME:
2186 case LyXRC::RC_USETEMPDIR:
2187 case LyXRC::RC_USE_ALT_LANG:
2188 case LyXRC::RC_USE_CONVERTER_CACHE:
2189 case LyXRC::RC_USE_ESC_CHARS:
2190 case LyXRC::RC_USE_INP_ENC:
2191 case LyXRC::RC_USE_PERS_DICT:
2192 case LyXRC::RC_USE_SPELL_LIB:
2193 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2194 case LyXRC::RC_VIEWER:
2195 case LyXRC::RC_LAST: