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::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
247 BOOST_ASSERT(lyx_view_);
248 if (!LyX::ref().session().bookmarks().isValid(idx))
250 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
251 BOOST_ASSERT(!bm.filename.empty());
252 string const file = bm.filename.absFilename();
253 // if the file is not opened, open it.
254 if (!theBufferList().exists(file)) {
256 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
260 // open may fail, so we need to test it again
261 if (theBufferList().exists(file)) {
262 // if the current buffer is not that one, switch to it.
263 if (lyx_view_->buffer()->fileName() != file) {
265 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
269 // moveToPosition use par_id, and par_pit and return new par_id.
272 boost::tie(new_pit, new_id) = view()->moveToPosition(bm.par_pit, bm.par_id, bm.par_pos);
273 // if par_id or pit has been changed, reset par_pit and par_id
274 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
275 if (bm.par_pit != new_pit || bm.par_id != new_id)
276 const_cast<BookmarksSection::Bookmark &>(bm).setPos(new_pit, new_id);
281 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
283 lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
285 // Do nothing if we have nothing (JMarc)
286 if (!keysym->isOK()) {
287 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
292 if (keysym->isModifier()) {
293 lyxerr[Debug::KEY] << "isModifier true" << endl;
297 //Encoding const * encoding = view()->cursor().getEncoding();
298 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
299 size_t encoded_last_key = keysym->getUCSEncoded();
301 // Do a one-deep top-level lookup for
302 // cancel and meta-fake keys. RVDK_PATCH_5
303 cancel_meta_seq->reset();
305 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
306 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
307 << " action first set to [" << func.action << ']'
310 // When not cancel or meta-fake, do the normal lookup.
311 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
312 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
313 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
314 // remove Caps Lock and Mod2 as a modifiers
315 func = keyseq->addkey(keysym, (state | meta_fake_bit));
316 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
317 << "action now set to ["
318 << func.action << ']' << endl;
321 // Dont remove this unless you know what you are doing.
322 meta_fake_bit = key_modifier::none;
324 // Can this happen now ?
325 if (func.action == LFUN_NOACTION) {
326 func = FuncRequest(LFUN_COMMAND_PREFIX);
329 if (lyxerr.debugging(Debug::KEY)) {
330 lyxerr << BOOST_CURRENT_FUNCTION
332 << func.action << "]["
333 << to_utf8(keyseq->print(false)) << ']'
337 // already here we know if it any point in going further
338 // why not return already here if action == -1 and
339 // num_bytes == 0? (Lgb)
341 if (keyseq->length() > 1) {
342 lyx_view_->message(keyseq->print(true));
346 // Maybe user can only reach the key via holding down shift.
347 // Let's see. But only if shift is the only modifier
348 if (func.action == LFUN_UNKNOWN_ACTION &&
349 state == key_modifier::shift) {
350 lyxerr[Debug::KEY] << "Trying without shift" << endl;
351 func = keyseq->addkey(keysym, key_modifier::none);
352 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
355 if (func.action == LFUN_UNKNOWN_ACTION) {
356 // Hmm, we didn't match any of the keysequences. See
357 // if it's normal insertable text not already covered
359 if (keysym->isText() && keyseq->length() == 1) {
360 lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
361 func = FuncRequest(LFUN_SELF_INSERT,
362 FuncRequest::KEYBOARD);
364 lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
365 lyx_view_->message(_("Unknown function."));
370 if (func.action == LFUN_SELF_INSERT) {
371 if (encoded_last_key != 0) {
372 docstring const arg(1, encoded_last_key);
373 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
374 FuncRequest::KEYBOARD));
376 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
384 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
386 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
389 if (cmd.action == LFUN_LYX_QUIT) {
390 flag.message(from_utf8(N_("Exiting")));
393 } else if (cmd.action == LFUN_BOOKMARK_GOTO) {
394 // bookmarks can be valid even if there is no opened buffer
395 flag.enabled(LyX::ref().session().bookmarks().isValid(convert<unsigned int>(to_utf8(cmd.argument()))));
397 } else if (cmd.action == LFUN_BOOKMARK_CLEAR) {
398 flag.enabled(LyX::ref().session().bookmarks().size() > 0);
400 } else if (cmd.action == LFUN_TOOLBAR_TOGGLE_STATE) {
401 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
402 if (!(flags & ToolbarBackend::AUTO))
403 flag.setOnOff(flags & ToolbarBackend::ON);
407 LCursor & cur = view()->cursor();
409 /* In LyX/Mac, when a dialog is open, the menus of the
410 application can still be accessed without giving focus to
411 the main window. In this case, we want to disable the menu
412 entries that are buffer-related.
414 Note that this code is not perfect, as bug 1941 attests:
415 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
417 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
418 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
421 if (cmd.action == LFUN_NOACTION) {
422 flag.message(from_utf8(N_("Nothing to do")));
427 switch (cmd.action) {
428 case LFUN_UNKNOWN_ACTION:
429 #ifndef HAVE_LIBAIKSAURUS
430 case LFUN_THESAURUS_ENTRY:
440 if (flag.unknown()) {
441 flag.message(from_utf8(N_("Unknown action")));
445 if (!flag.enabled()) {
446 if (flag.message().empty())
447 flag.message(from_utf8(N_("Command disabled")));
451 // Check whether we need a buffer
452 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
454 flag.message(from_utf8(N_("Command not allowed with"
455 "out any document open")));
460 // I would really like to avoid having this switch and rather try to
461 // encode this in the function itself.
462 // -- And I'd rather let an inset decide which LFUNs it is willing
463 // to handle (Andre')
465 switch (cmd.action) {
466 case LFUN_BUFFER_TOGGLE_READ_ONLY:
467 flag.setOnOff(buf->isReadonly());
470 case LFUN_BUFFER_SWITCH:
471 // toggle on the current buffer, but do not toggle off
472 // the other ones (is that a good idea?)
473 if (to_utf8(cmd.argument()) == buf->fileName())
477 case LFUN_BUFFER_EXPORT:
478 enable = cmd.argument() == "custom"
479 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
482 case LFUN_BUFFER_CHKTEX:
483 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
486 case LFUN_BUILD_PROGRAM:
487 enable = Exporter::isExportable(*buf, "program");
490 case LFUN_LAYOUT_TABULAR:
491 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
495 case LFUN_LAYOUT_PARAGRAPH:
496 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
499 case LFUN_VC_REGISTER:
500 enable = !buf->lyxvc().inUse();
502 case LFUN_VC_CHECK_IN:
503 enable = buf->lyxvc().inUse() && !buf->isReadonly();
505 case LFUN_VC_CHECK_OUT:
506 enable = buf->lyxvc().inUse() && buf->isReadonly();
509 case LFUN_VC_UNDO_LAST:
510 enable = buf->lyxvc().inUse();
512 case LFUN_BUFFER_RELOAD:
513 enable = !buf->isUnnamed() && !buf->isClean();
516 case LFUN_INSET_SETTINGS: {
520 InsetBase::Code code = cur.inset().lyxCode();
522 case InsetBase::TABULAR_CODE:
523 enable = cmd.argument() == "tabular";
525 case InsetBase::ERT_CODE:
526 enable = cmd.argument() == "ert";
528 case InsetBase::FLOAT_CODE:
529 enable = cmd.argument() == "float";
531 case InsetBase::WRAP_CODE:
532 enable = cmd.argument() == "wrap";
534 case InsetBase::NOTE_CODE:
535 enable = cmd.argument() == "note";
537 case InsetBase::BRANCH_CODE:
538 enable = cmd.argument() == "branch";
540 case InsetBase::BOX_CODE:
541 enable = cmd.argument() == "box";
549 case LFUN_INSET_APPLY: {
550 string const name = cmd.getArg(0);
551 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
553 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
555 if (!inset->getStatus(cur, fr, fs)) {
556 // Every inset is supposed to handle this
561 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
562 flag |= getStatus(fr);
564 enable = flag.enabled();
568 case LFUN_DIALOG_SHOW: {
569 string const name = cmd.getArg(0);
571 enable = name == "aboutlyx"
575 || name == "texinfo";
576 else if (name == "print")
577 enable = Exporter::isExportable(*buf, "dvi")
578 && lyxrc.print_command != "none";
579 else if (name == "character" || name == "mathpanel")
580 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
581 else if (name == "latexlog")
582 enable = isFileReadable(FileName(buf->getLogName().second));
583 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
584 else if (name == "spellchecker")
587 else if (name == "vclog")
588 enable = buf->lyxvc().inUse();
589 else if (name == "view-source")
594 case LFUN_DIALOG_SHOW_NEW_INSET:
595 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
598 case LFUN_DIALOG_UPDATE: {
599 string const name = cmd.getArg(0);
601 enable = name == "prefs";
605 case LFUN_CITATION_INSERT: {
606 FuncRequest fr(LFUN_INSET_INSERT, "citation");
607 enable = getStatus(fr).enabled();
611 case LFUN_BUFFER_WRITE: {
612 enable = view()->buffer()->isUnnamed()
613 || !view()->buffer()->isClean();
618 // this one is difficult to get right. As a half-baked
619 // solution, we consider only the first action of the sequence
620 case LFUN_COMMAND_SEQUENCE: {
621 // argument contains ';'-terminated commands
622 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
623 FuncRequest func(lyxaction.lookupFunc(firstcmd));
624 func.origin = cmd.origin;
625 flag = getStatus(func);
628 case LFUN_BUFFER_NEW:
629 case LFUN_BUFFER_NEW_TEMPLATE:
630 case LFUN_WORD_FIND_FORWARD:
631 case LFUN_WORD_FIND_BACKWARD:
632 case LFUN_COMMAND_PREFIX:
633 case LFUN_COMMAND_EXECUTE:
635 case LFUN_META_PREFIX:
636 case LFUN_BUFFER_CLOSE:
637 case LFUN_BUFFER_WRITE_AS:
638 case LFUN_BUFFER_UPDATE:
639 case LFUN_BUFFER_VIEW:
640 case LFUN_BUFFER_IMPORT:
642 case LFUN_BUFFER_AUTO_SAVE:
643 case LFUN_RECONFIGURE:
647 case LFUN_DROP_LAYOUTS_CHOICE:
649 case LFUN_SERVER_GET_NAME:
650 case LFUN_SERVER_NOTIFY:
651 case LFUN_SERVER_GOTO_FILE_ROW:
652 case LFUN_DIALOG_HIDE:
653 case LFUN_DIALOG_DISCONNECT_INSET:
654 case LFUN_BUFFER_CHILD_OPEN:
655 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
656 case LFUN_KEYMAP_OFF:
657 case LFUN_KEYMAP_PRIMARY:
658 case LFUN_KEYMAP_SECONDARY:
659 case LFUN_KEYMAP_TOGGLE:
661 case LFUN_BUFFER_EXPORT_CUSTOM:
662 case LFUN_BUFFER_PRINT:
663 case LFUN_PREFERENCES_SAVE:
664 case LFUN_SCREEN_FONT_UPDATE:
667 case LFUN_EXTERNAL_EDIT:
668 case LFUN_GRAPHICS_EDIT:
669 case LFUN_ALL_INSETS_TOGGLE:
670 case LFUN_BUFFER_LANGUAGE:
671 case LFUN_TEXTCLASS_APPLY:
672 case LFUN_TEXTCLASS_LOAD:
673 case LFUN_BUFFER_SAVE_AS_DEFAULT:
674 case LFUN_BUFFER_PARAMS_APPLY:
675 case LFUN_LYXRC_APPLY:
676 case LFUN_BUFFER_NEXT:
677 case LFUN_BUFFER_PREVIOUS:
678 case LFUN_WINDOW_NEW:
679 case LFUN_WINDOW_CLOSE:
680 // these are handled in our dispatch()
684 if (!getLocalStatus(cur, cmd, flag))
685 flag = view()->getStatus(cmd);
691 // Can we use a readonly buffer?
692 if (buf && buf->isReadonly()
693 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
694 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
695 flag.message(from_utf8(N_("Document is read-only")));
699 // Are we in a DELETED change-tracking region?
700 if (buf && lookupChangeType(cur, true) == Change::DELETED
701 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
702 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
703 flag.message(from_utf8(N_("This portion of the document is deleted.")));
707 // the default error message if we disable the command
708 if (!flag.enabled() && flag.message().empty())
709 flag.message(from_utf8(N_("Command disabled")));
715 bool LyXFunc::ensureBufferClean(BufferView * bv)
717 Buffer & buf = *bv->buffer();
721 docstring const file = makeDisplayPath(buf.fileName(), 30);
722 docstring text = bformat(_("The document %1$s has unsaved "
723 "changes.\n\nDo you want to save "
724 "the document?"), file);
725 int const ret = Alert::prompt(_("Save changed document?"),
726 text, 0, 1, _("&Save"),
730 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
732 return buf.isClean();
738 void showPrintError(string const & name)
740 docstring str = bformat(_("Could not print the document %1$s.\n"
741 "Check that your printer is set up correctly."),
742 makeDisplayPath(name, 50));
743 Alert::error(_("Print document failed"), str);
747 void loadTextclass(string const & name)
749 std::pair<bool, textclass_type> const tc_pair =
750 textclasslist.numberOfClass(name);
752 if (!tc_pair.first) {
753 lyxerr << "Document class \"" << name
754 << "\" does not exist."
759 textclass_type const tc = tc_pair.second;
761 if (!textclasslist[tc].load()) {
762 docstring s = bformat(_("The document could not be converted\n"
763 "into the document class %1$s."),
764 from_utf8(textclasslist[tc].name()));
765 Alert::error(_("Could not change class"), s);
770 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
775 void LyXFunc::dispatch(FuncRequest const & cmd)
777 string const argument = to_utf8(cmd.argument());
778 kb_action const action = cmd.action;
780 lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
781 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
783 // we have not done anything wrong yet.
785 dispatch_buffer.erase();
787 // redraw the screen at the end (first of the two drawing steps).
788 //This is done unless explicitely requested otherwise
789 Update::flags updateFlags = Update::FitCursor;
791 FuncStatus const flag = getStatus(cmd);
792 if (!flag.enabled()) {
793 // We cannot use this function here
794 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
795 << lyxaction.getActionName(action)
796 << " [" << action << "] is disabled at this location"
798 setErrorMessage(flag.message());
802 case LFUN_WORD_FIND_FORWARD:
803 case LFUN_WORD_FIND_BACKWARD: {
804 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
805 static docstring last_search;
806 docstring searched_string;
808 if (!cmd.argument().empty()) {
809 last_search = cmd.argument();
810 searched_string = cmd.argument();
812 searched_string = last_search;
815 if (searched_string.empty())
818 bool const fw = action == LFUN_WORD_FIND_FORWARD;
819 docstring const data =
820 find2string(searched_string, true, false, fw);
821 find(view(), FuncRequest(LFUN_WORD_FIND, data));
825 case LFUN_COMMAND_PREFIX:
826 BOOST_ASSERT(lyx_view_);
827 lyx_view_->message(keyseq->printOptions(true));
830 case LFUN_COMMAND_EXECUTE:
831 BOOST_ASSERT(lyx_view_);
832 lyx_view_->getToolbars().display("minibuffer", true);
833 lyx_view_->focus_command_buffer();
837 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
839 meta_fake_bit = key_modifier::none;
840 if (view()->buffer())
841 // cancel any selection
842 dispatch(FuncRequest(LFUN_MARK_OFF));
843 setMessage(_("Cancel"));
846 case LFUN_META_PREFIX:
847 meta_fake_bit = key_modifier::alt;
848 setMessage(keyseq->print(true));
851 case LFUN_BUFFER_TOGGLE_READ_ONLY:
852 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
853 if (lyx_view_->buffer()->lyxvc().inUse())
854 lyx_view_->buffer()->lyxvc().toggleReadOnly();
856 lyx_view_->buffer()->setReadonly(
857 !lyx_view_->buffer()->isReadonly());
860 // --- Menus -----------------------------------------------
861 case LFUN_BUFFER_NEW:
862 menuNew(argument, false);
865 case LFUN_BUFFER_NEW_TEMPLATE:
866 menuNew(argument, true);
869 case LFUN_BUFFER_CLOSE:
874 case LFUN_BUFFER_WRITE:
875 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
876 if (!lyx_view_->buffer()->isUnnamed()) {
877 docstring const str = bformat(_("Saving document %1$s..."),
878 makeDisplayPath(lyx_view_->buffer()->fileName()));
879 lyx_view_->message(str);
880 menuWrite(lyx_view_->buffer());
881 lyx_view_->message(str + _(" done."));
883 writeAs(lyx_view_->buffer());
884 updateFlags = Update::None;
887 case LFUN_BUFFER_WRITE_AS:
888 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
889 writeAs(lyx_view_->buffer(), argument);
890 updateFlags = Update::None;
893 case LFUN_BUFFER_RELOAD: {
894 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
895 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
896 docstring text = bformat(_("Any changes will be lost. Are you sure "
897 "you want to revert to the saved version of the document %1$s?"), file);
898 int const ret = Alert::prompt(_("Revert to saved document?"),
899 text, 0, 1, _("&Revert"), _("&Cancel"));
906 case LFUN_BUFFER_UPDATE:
907 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
908 Exporter::Export(lyx_view_->buffer(), argument, true);
911 case LFUN_BUFFER_VIEW:
912 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
913 Exporter::preview(lyx_view_->buffer(), argument);
916 case LFUN_BUILD_PROGRAM:
917 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
918 Exporter::Export(lyx_view_->buffer(), "program", true);
921 case LFUN_BUFFER_CHKTEX:
922 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
923 lyx_view_->buffer()->runChktex();
926 case LFUN_BUFFER_EXPORT:
927 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
928 if (argument == "custom")
929 lyx_view_->getDialogs().show("sendto");
931 Exporter::Export(lyx_view_->buffer(), argument, false);
935 case LFUN_BUFFER_EXPORT_CUSTOM: {
936 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
938 string command = split(argument, format_name, ' ');
939 Format const * format = formats.getFormat(format_name);
941 lyxerr << "Format \"" << format_name
942 << "\" not recognized!"
947 Buffer * buffer = lyx_view_->buffer();
949 // The name of the file created by the conversion process
952 // Output to filename
953 if (format->name() == "lyx") {
954 string const latexname =
955 buffer->getLatexName(false);
956 filename = changeExtension(latexname,
957 format->extension());
958 filename = addName(buffer->temppath(), filename);
960 if (!buffer->writeFile(FileName(filename)))
964 Exporter::Export(buffer, format_name, true, filename);
967 // Substitute $$FName for filename
968 if (!contains(command, "$$FName"))
969 command = "( " + command + " ) < $$FName";
970 command = subst(command, "$$FName", filename);
972 // Execute the command in the background
974 call.startscript(Systemcall::DontWait, command);
978 case LFUN_BUFFER_PRINT: {
979 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
982 string command = split(split(argument, target, ' '),
986 || target_name.empty()
987 || command.empty()) {
988 lyxerr << "Unable to parse \""
989 << argument << '"' << std::endl;
992 if (target != "printer" && target != "file") {
993 lyxerr << "Unrecognized target \""
994 << target << '"' << std::endl;
998 Buffer * buffer = lyx_view_->buffer();
1000 if (!Exporter::Export(buffer, "dvi", true)) {
1001 showPrintError(buffer->fileName());
1005 // Push directory path.
1006 string const path = buffer->temppath();
1007 support::Path p(path);
1009 // there are three cases here:
1010 // 1. we print to a file
1011 // 2. we print directly to a printer
1012 // 3. we print using a spool command (print to file first)
1015 string const dviname =
1016 changeExtension(buffer->getLatexName(true),
1019 if (target == "printer") {
1020 if (!lyxrc.print_spool_command.empty()) {
1021 // case 3: print using a spool
1022 string const psname =
1023 changeExtension(dviname,".ps");
1024 command += lyxrc.print_to_file
1027 + quoteName(dviname);
1030 lyxrc.print_spool_command +' ';
1031 if (target_name != "default") {
1032 command2 += lyxrc.print_spool_printerprefix
1036 command2 += quoteName(psname);
1038 // If successful, then spool command
1039 res = one.startscript(
1044 res = one.startscript(
1045 Systemcall::DontWait,
1048 // case 2: print directly to a printer
1049 res = one.startscript(
1050 Systemcall::DontWait,
1051 command + quoteName(dviname));
1055 // case 1: print to a file
1056 command += lyxrc.print_to_file
1057 + quoteName(makeAbsPath(target_name,
1058 path).toFilesystemEncoding())
1060 + quoteName(dviname);
1061 res = one.startscript(Systemcall::DontWait,
1066 showPrintError(buffer->fileName());
1070 case LFUN_BUFFER_IMPORT:
1075 // quitting is triggered by the gui code
1076 // (leaving the event loop).
1077 if (theBufferList().quitWriteAll())
1078 theApp()->gui().closeAllViews();
1081 case LFUN_TOC_VIEW: {
1082 BOOST_ASSERT(lyx_view_);
1083 InsetCommandParams p("tableofcontents");
1084 string const data = InsetCommandMailer::params2string("toc", p);
1085 lyx_view_->getDialogs().show("toc", data, 0);
1089 case LFUN_BUFFER_AUTO_SAVE:
1093 case LFUN_RECONFIGURE:
1094 BOOST_ASSERT(lyx_view_);
1095 reconfigure(*lyx_view_);
1098 case LFUN_HELP_OPEN: {
1099 BOOST_ASSERT(lyx_view_);
1100 string const arg = argument;
1102 setErrorMessage(_("Missing argument"));
1105 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1106 if (fname.empty()) {
1107 lyxerr << "LyX: unable to find documentation file `"
1108 << arg << "'. Bad installation?" << endl;
1111 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1112 makeDisplayPath(fname.absFilename())));
1113 lyx_view_->loadLyXFile(fname, false);
1117 // --- version control -------------------------------
1118 case LFUN_VC_REGISTER:
1119 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1120 if (!ensureBufferClean(view()))
1122 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1123 lyx_view_->buffer()->lyxvc().registrer();
1128 case LFUN_VC_CHECK_IN:
1129 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1130 if (!ensureBufferClean(view()))
1132 if (lyx_view_->buffer()->lyxvc().inUse()
1133 && !lyx_view_->buffer()->isReadonly()) {
1134 lyx_view_->buffer()->lyxvc().checkIn();
1139 case LFUN_VC_CHECK_OUT:
1140 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1141 if (!ensureBufferClean(view()))
1143 if (lyx_view_->buffer()->lyxvc().inUse()
1144 && lyx_view_->buffer()->isReadonly()) {
1145 lyx_view_->buffer()->lyxvc().checkOut();
1150 case LFUN_VC_REVERT:
1151 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1152 lyx_view_->buffer()->lyxvc().revert();
1156 case LFUN_VC_UNDO_LAST:
1157 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1158 lyx_view_->buffer()->lyxvc().undoLast();
1162 // --- buffers ----------------------------------------
1163 case LFUN_BUFFER_SWITCH:
1164 BOOST_ASSERT(lyx_view_);
1165 // update bookmark pit of the current buffer before switch
1166 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1167 gotoBookmark(i+1, false, false);
1168 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1171 case LFUN_BUFFER_NEXT:
1172 BOOST_ASSERT(lyx_view_);
1173 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1176 case LFUN_BUFFER_PREVIOUS:
1177 BOOST_ASSERT(lyx_view_);
1178 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1182 BOOST_ASSERT(lyx_view_);
1183 newFile(view(), argument);
1186 case LFUN_FILE_OPEN:
1187 BOOST_ASSERT(lyx_view_);
1191 case LFUN_DROP_LAYOUTS_CHOICE:
1192 BOOST_ASSERT(lyx_view_);
1193 lyx_view_->getToolbars().openLayoutList();
1196 case LFUN_MENU_OPEN:
1197 BOOST_ASSERT(lyx_view_);
1198 lyx_view_->getMenubar().openByName(from_utf8(argument));
1201 // --- lyxserver commands ----------------------------
1202 case LFUN_SERVER_GET_NAME:
1203 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1204 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1205 lyxerr[Debug::INFO] << "FNAME["
1206 << lyx_view_->buffer()->fileName()
1210 case LFUN_SERVER_NOTIFY:
1211 dispatch_buffer = keyseq->print(false);
1212 theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1215 case LFUN_SERVER_GOTO_FILE_ROW: {
1216 BOOST_ASSERT(lyx_view_);
1219 istringstream is(argument);
1220 is >> file_name >> row;
1221 if (prefixIs(file_name, package().temp_dir())) {
1222 // Needed by inverse dvi search. If it is a file
1223 // in tmpdir, call the apropriated function
1224 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1226 // Must replace extension of the file to be .lyx
1227 // and get full path
1228 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1229 // Either change buffer or load the file
1230 if (theBufferList().exists(s.absFilename())) {
1231 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1233 lyx_view_->loadLyXFile(s);
1237 view()->setCursorFromRow(row);
1240 // see BufferView::center()
1244 case LFUN_DIALOG_SHOW: {
1245 BOOST_ASSERT(lyx_view_);
1246 string const name = cmd.getArg(0);
1247 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1249 if (name == "character") {
1250 data = freefont2string();
1252 lyx_view_->getDialogs().show("character", data);
1253 } else if (name == "latexlog") {
1254 pair<Buffer::LogType, string> const logfile =
1255 lyx_view_->buffer()->getLogName();
1256 switch (logfile.first) {
1257 case Buffer::latexlog:
1260 case Buffer::buildlog:
1264 data += LyXLex::quoteString(logfile.second);
1265 lyx_view_->getDialogs().show("log", data);
1266 } else if (name == "vclog") {
1267 string const data = "vc " +
1268 LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1269 lyx_view_->getDialogs().show("log", data);
1271 lyx_view_->getDialogs().show(name, data);
1275 case LFUN_DIALOG_SHOW_NEW_INSET: {
1276 BOOST_ASSERT(lyx_view_);
1277 string const name = cmd.getArg(0);
1278 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1279 if (name == "bibitem" ||
1283 name == "nomenclature" ||
1287 InsetCommandParams p(name);
1288 data = InsetCommandMailer::params2string(name, p);
1289 } else if (name == "include") {
1290 // data is the include type: one of "include",
1291 // "input", "verbatiminput" or "verbatiminput*"
1293 // default type is requested
1295 InsetCommandParams p(data);
1296 data = InsetIncludeMailer::params2string(p);
1297 } else if (name == "box") {
1298 // \c data == "Boxed" || "Frameless" etc
1299 InsetBoxParams p(data);
1300 data = InsetBoxMailer::params2string(p);
1301 } else if (name == "branch") {
1302 InsetBranchParams p;
1303 data = InsetBranchMailer::params2string(p);
1304 } else if (name == "citation") {
1305 InsetCommandParams p("cite");
1306 data = InsetCommandMailer::params2string(name, p);
1307 } else if (name == "ert") {
1308 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1309 } else if (name == "external") {
1310 InsetExternalParams p;
1311 Buffer const & buffer = *lyx_view_->buffer();
1312 data = InsetExternalMailer::params2string(p, buffer);
1313 } else if (name == "float") {
1315 data = InsetFloatMailer::params2string(p);
1316 } else if (name == "graphics") {
1317 InsetGraphicsParams p;
1318 Buffer const & buffer = *lyx_view_->buffer();
1319 data = InsetGraphicsMailer::params2string(p, buffer);
1320 } else if (name == "note") {
1322 data = InsetNoteMailer::params2string(p);
1323 } else if (name == "vspace") {
1325 data = InsetVSpaceMailer::params2string(space);
1326 } else if (name == "wrap") {
1328 data = InsetWrapMailer::params2string(p);
1330 lyx_view_->getDialogs().show(name, data, 0);
1334 case LFUN_DIALOG_UPDATE: {
1335 BOOST_ASSERT(lyx_view_);
1336 string const & name = argument;
1337 // Can only update a dialog connected to an existing inset
1338 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1340 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1341 inset->dispatch(view()->cursor(), fr);
1342 } else if (name == "paragraph") {
1343 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1344 } else if (name == "prefs") {
1345 lyx_view_->getDialogs().update(name, string());
1350 case LFUN_DIALOG_HIDE:
1351 Dialogs::hide(argument, 0);
1354 case LFUN_DIALOG_DISCONNECT_INSET:
1355 BOOST_ASSERT(lyx_view_);
1356 lyx_view_->getDialogs().disconnect(argument);
1360 case LFUN_CITATION_INSERT: {
1361 BOOST_ASSERT(lyx_view_);
1362 if (!argument.empty()) {
1363 // we can have one optional argument, delimited by '|'
1364 // citation-insert <key>|<text_before>
1365 // this should be enhanced to also support text_after
1366 // and citation style
1367 string arg = argument;
1369 if (contains(argument, "|")) {
1370 arg = token(argument, '|', 0);
1371 opt1 = '[' + token(argument, '|', 1) + ']';
1373 std::ostringstream os;
1374 os << "citation LatexCommand\n"
1375 << "\\cite" << opt1 << "{" << arg << "}\n"
1377 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1380 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1384 case LFUN_BUFFER_CHILD_OPEN: {
1385 BOOST_ASSERT(lyx_view_);
1386 FileName const filename =
1387 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1388 // FIXME Should use bformat
1389 setMessage(_("Opening child document ") +
1390 makeDisplayPath(filename.absFilename()) + "...");
1391 view()->saveBookmark(false);
1392 string const parentfilename = lyx_view_->buffer()->fileName();
1393 if (theBufferList().exists(filename.absFilename()))
1394 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1396 lyx_view_->loadLyXFile(filename);
1397 // Set the parent name of the child document.
1398 // This makes insertion of citations and references in the child work,
1399 // when the target is in the parent or another child document.
1400 lyx_view_->buffer()->setParentName(parentfilename);
1404 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1405 BOOST_ASSERT(lyx_view_);
1406 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1409 case LFUN_KEYMAP_OFF:
1410 BOOST_ASSERT(lyx_view_);
1411 lyx_view_->view()->getIntl().keyMapOn(false);
1414 case LFUN_KEYMAP_PRIMARY:
1415 BOOST_ASSERT(lyx_view_);
1416 lyx_view_->view()->getIntl().keyMapPrim();
1419 case LFUN_KEYMAP_SECONDARY:
1420 BOOST_ASSERT(lyx_view_);
1421 lyx_view_->view()->getIntl().keyMapSec();
1424 case LFUN_KEYMAP_TOGGLE:
1425 BOOST_ASSERT(lyx_view_);
1426 lyx_view_->view()->getIntl().toggleKeyMap();
1432 string rest = split(argument, countstr, ' ');
1433 istringstream is(countstr);
1436 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1437 for (int i = 0; i < count; ++i)
1438 dispatch(lyxaction.lookupFunc(rest));
1442 case LFUN_COMMAND_SEQUENCE: {
1443 // argument contains ';'-terminated commands
1444 string arg = argument;
1445 while (!arg.empty()) {
1447 arg = split(arg, first, ';');
1448 FuncRequest func(lyxaction.lookupFunc(first));
1449 func.origin = cmd.origin;
1455 case LFUN_PREFERENCES_SAVE: {
1456 lyxrc.write(makeAbsPath("preferences",
1457 package().user_support()),
1462 case LFUN_SCREEN_FONT_UPDATE:
1463 BOOST_ASSERT(lyx_view_);
1464 // handle the screen font changes.
1465 lyxrc.set_font_norm_type();
1466 theFontLoader().update();
1467 // All visible buffers will need resize
1471 case LFUN_SET_COLOR: {
1473 string const x11_name = split(argument, lyx_name, ' ');
1474 if (lyx_name.empty() || x11_name.empty()) {
1475 setErrorMessage(_("Syntax: set-color <lyx_name>"
1480 bool const graphicsbg_changed =
1481 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1482 x11_name != lcolor.getX11Name(LColor::graphicsbg));
1484 if (!lcolor.setColor(lyx_name, x11_name)) {
1486 bformat(_("Set-color \"%1$s\" failed "
1487 "- color is undefined or "
1488 "may not be redefined"),
1489 from_utf8(lyx_name)));
1493 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1495 if (graphicsbg_changed) {
1496 #ifdef WITH_WARNINGS
1497 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1500 graphics::GCache::get().changeDisplay(true);
1507 BOOST_ASSERT(lyx_view_);
1508 lyx_view_->message(from_utf8(argument));
1511 case LFUN_EXTERNAL_EDIT: {
1512 BOOST_ASSERT(lyx_view_);
1513 FuncRequest fr(action, argument);
1514 InsetExternal().dispatch(view()->cursor(), fr);
1518 case LFUN_GRAPHICS_EDIT: {
1519 FuncRequest fr(action, argument);
1520 InsetGraphics().dispatch(view()->cursor(), fr);
1524 case LFUN_INSET_APPLY: {
1525 BOOST_ASSERT(lyx_view_);
1526 string const name = cmd.getArg(0);
1527 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1529 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1530 inset->dispatch(view()->cursor(), fr);
1532 FuncRequest fr(LFUN_INSET_INSERT, argument);
1535 // ideally, the update flag should be set by the insets,
1536 // but this is not possible currently
1537 updateFlags = Update::Force | Update::FitCursor;
1541 case LFUN_ALL_INSETS_TOGGLE: {
1542 BOOST_ASSERT(lyx_view_);
1544 string const name = split(argument, action, ' ');
1545 InsetBase::Code const inset_code =
1546 InsetBase::translate(name);
1548 LCursor & cur = view()->cursor();
1549 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1551 InsetBase & inset = lyx_view_->buffer()->inset();
1552 InsetIterator it = inset_iterator_begin(inset);
1553 InsetIterator const end = inset_iterator_end(inset);
1554 for (; it != end; ++it) {
1555 if (inset_code == InsetBase::NO_CODE
1556 || inset_code == it->lyxCode()) {
1557 LCursor tmpcur = cur;
1558 tmpcur.pushLeft(*it);
1559 it->dispatch(tmpcur, fr);
1562 updateFlags = Update::Force | Update::FitCursor;
1566 case LFUN_BUFFER_LANGUAGE: {
1567 BOOST_ASSERT(lyx_view_);
1568 Buffer & buffer = *lyx_view_->buffer();
1569 Language const * oldL = buffer.params().language;
1570 Language const * newL = languages.getLanguage(argument);
1571 if (!newL || oldL == newL)
1574 if (oldL->rightToLeft() == newL->rightToLeft()
1575 && !buffer.isMultiLingual())
1576 buffer.changeLanguage(oldL, newL);
1578 buffer.updateDocLang(newL);
1582 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1583 string const fname =
1584 addName(addPath(package().user_support(), "templates/"),
1586 Buffer defaults(fname);
1588 istringstream ss(argument);
1591 int const unknown_tokens = defaults.readHeader(lex);
1593 if (unknown_tokens != 0) {
1594 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1595 << unknown_tokens << " unknown token"
1596 << (unknown_tokens == 1 ? "" : "s")
1600 if (defaults.writeFile(FileName(defaults.fileName())))
1601 // FIXME Should use bformat
1602 setMessage(_("Document defaults saved in ")
1603 + makeDisplayPath(fname));
1605 setErrorMessage(_("Unable to save document defaults"));
1609 case LFUN_BUFFER_PARAMS_APPLY: {
1610 BOOST_ASSERT(lyx_view_);
1611 biblio::CiteEngine const engine =
1612 lyx_view_->buffer()->params().cite_engine;
1614 istringstream ss(argument);
1617 int const unknown_tokens =
1618 lyx_view_->buffer()->readHeader(lex);
1620 if (unknown_tokens != 0) {
1621 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1622 << unknown_tokens << " unknown token"
1623 << (unknown_tokens == 1 ? "" : "s")
1626 if (engine == lyx_view_->buffer()->params().cite_engine)
1629 LCursor & cur = view()->cursor();
1630 FuncRequest fr(LFUN_INSET_REFRESH);
1632 InsetBase & inset = lyx_view_->buffer()->inset();
1633 InsetIterator it = inset_iterator_begin(inset);
1634 InsetIterator const end = inset_iterator_end(inset);
1635 for (; it != end; ++it)
1636 if (it->lyxCode() == InsetBase::CITE_CODE)
1637 it->dispatch(cur, fr);
1641 case LFUN_TEXTCLASS_APPLY: {
1642 BOOST_ASSERT(lyx_view_);
1643 Buffer * buffer = lyx_view_->buffer();
1645 textclass_type const old_class =
1646 buffer->params().textclass;
1648 loadTextclass(argument);
1650 std::pair<bool, textclass_type> const tc_pair =
1651 textclasslist.numberOfClass(argument);
1656 textclass_type const new_class = tc_pair.second;
1657 if (old_class == new_class)
1661 lyx_view_->message(_("Converting document to new document class..."));
1662 recordUndoFullDocument(view());
1663 buffer->params().textclass = new_class;
1664 StableDocIterator backcur(view()->cursor());
1665 ErrorList & el = buffer->errorList("Class Switch");
1666 cap::switchBetweenClasses(
1667 old_class, new_class,
1668 static_cast<InsetText &>(buffer->inset()), el);
1670 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1672 buffer->errors("Class Switch");
1673 updateLabels(*buffer);
1674 updateFlags = Update::Force | Update::FitCursor;
1678 case LFUN_TEXTCLASS_LOAD:
1679 loadTextclass(argument);
1682 case LFUN_LYXRC_APPLY: {
1683 LyXRC const lyxrc_orig = lyxrc;
1685 istringstream ss(argument);
1686 bool const success = lyxrc.read(ss) == 0;
1689 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1690 << "Unable to read lyxrc data"
1695 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1699 case LFUN_WINDOW_NEW:
1700 LyX::ref().newLyXView();
1703 case LFUN_WINDOW_CLOSE:
1704 BOOST_ASSERT(lyx_view_);
1705 BOOST_ASSERT(theApp());
1706 // update bookmark pit of the current buffer before window close
1707 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1708 gotoBookmark(i+1, false, false);
1709 // ask the user for saving changes or cancel quit
1710 if (!theBufferList().quitWriteAll())
1715 case LFUN_BOOKMARK_GOTO:
1716 // go to bookmark, open unopened file and switch to buffer if necessary
1717 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1720 case LFUN_BOOKMARK_CLEAR:
1721 LyX::ref().session().bookmarks().clear();
1724 case LFUN_TOOLBAR_TOGGLE_STATE:
1725 lyx_view_->toggleToolbarState(argument);
1729 BOOST_ASSERT(lyx_view_);
1730 view()->cursor().dispatch(cmd);
1731 updateFlags = view()->cursor().result().update();
1732 if (!view()->cursor().result().dispatched())
1733 if (view()->dispatch(cmd))
1734 updateFlags = Update::Force | Update::FitCursor;
1739 if (lyx_view_ && view()->buffer()) {
1740 // BufferView::update() updates the ViewMetricsInfo and
1741 // also initializes the position cache for all insets in
1742 // (at least partially) visible top-level paragraphs.
1743 // We will redraw the screen only if needed.
1744 if (view()->update(updateFlags)) {
1745 // Buffer::changed() signals that a repaint is needed.
1746 // The frontend (WorkArea) knows which area to repaint
1747 // thanks to the ViewMetricsInfo updated above.
1748 view()->buffer()->changed();
1751 lyx_view_->updateStatusBar();
1753 // if we executed a mutating lfun, mark the buffer as dirty
1755 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1756 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1757 view()->buffer()->markDirty();
1759 if (view()->cursor().inTexted()) {
1760 lyx_view_->updateLayoutChoice();
1765 lyx_view_->updateMenubar();
1766 lyx_view_->updateToolbars();
1767 sendDispatchMessage(getMessage(), cmd);
1772 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1774 const bool verbose = (cmd.origin == FuncRequest::MENU
1775 || cmd.origin == FuncRequest::TOOLBAR
1776 || cmd.origin == FuncRequest::COMMANDBUFFER);
1778 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1779 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1781 lyx_view_->message(msg);
1785 docstring dispatch_msg = msg;
1786 if (!dispatch_msg.empty())
1787 dispatch_msg += ' ';
1789 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1791 bool argsadded = false;
1793 if (!cmd.argument().empty()) {
1794 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1795 comname += ' ' + cmd.argument();
1800 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1802 if (!shortcuts.empty())
1803 comname += ": " + shortcuts;
1804 else if (!argsadded && !cmd.argument().empty())
1805 comname += ' ' + cmd.argument();
1807 if (!comname.empty()) {
1808 comname = rtrim(comname);
1809 dispatch_msg += '(' + rtrim(comname) + ')';
1812 lyxerr[Debug::ACTION] << "verbose dispatch msg "
1813 << to_utf8(dispatch_msg) << endl;
1814 if (!dispatch_msg.empty())
1815 lyx_view_->message(dispatch_msg);
1819 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1821 // FIXME: initpath is not used. What to do?
1822 string initpath = lyxrc.document_path;
1823 string filename(name);
1825 if (view()->buffer()) {
1826 string const trypath = lyx_view_->buffer()->filePath();
1827 // If directory is writeable, use this as default.
1828 if (isDirWriteable(FileName(trypath)))
1832 static int newfile_number;
1834 if (filename.empty()) {
1835 filename = addName(lyxrc.document_path,
1836 "newfile" + convert<string>(++newfile_number) + ".lyx");
1837 while (theBufferList().exists(filename) ||
1838 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1840 filename = addName(lyxrc.document_path,
1841 "newfile" + convert<string>(newfile_number) +
1846 // The template stuff
1849 FileDialog fileDlg(_("Select template file"),
1850 LFUN_SELECT_FILE_SYNC,
1851 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1852 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1854 FileDialog::Result result =
1855 fileDlg.open(from_utf8(lyxrc.template_path),
1856 FileFilterList(_("LyX Documents (*.lyx)")),
1859 if (result.first == FileDialog::Later)
1861 if (result.second.empty())
1863 templname = to_utf8(result.second);
1866 Buffer * const b = newFile(filename, templname, !name.empty());
1868 lyx_view_->setBuffer(b);
1872 void LyXFunc::open(string const & fname)
1874 string initpath = lyxrc.document_path;
1876 if (view()->buffer()) {
1877 string const trypath = lyx_view_->buffer()->filePath();
1878 // If directory is writeable, use this as default.
1879 if (isDirWriteable(FileName(trypath)))
1885 if (fname.empty()) {
1886 FileDialog fileDlg(_("Select document to open"),
1888 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1889 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1891 FileDialog::Result result =
1892 fileDlg.open(from_utf8(initpath),
1893 FileFilterList(_("LyX Documents (*.lyx)")),
1896 if (result.first == FileDialog::Later)
1899 filename = to_utf8(result.second);
1901 // check selected filename
1902 if (filename.empty()) {
1903 lyx_view_->message(_("Canceled."));
1909 // get absolute path of file and add ".lyx" to the filename if
1911 FileName const fullname = fileSearch(string(), filename, "lyx");
1912 if (!fullname.empty())
1913 filename = fullname.absFilename();
1915 // if the file doesn't exist, let the user create one
1916 if (!fs::exists(fullname.toFilesystemEncoding())) {
1917 // the user specifically chose this name. Believe him.
1918 Buffer * const b = newFile(filename, string(), true);
1920 lyx_view_->setBuffer(b);
1924 docstring const disp_fn = makeDisplayPath(filename);
1925 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1928 if (lyx_view_->loadLyXFile(fullname)) {
1929 str2 = bformat(_("Document %1$s opened."), disp_fn);
1931 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1933 lyx_view_->message(str2);
1937 void LyXFunc::doImport(string const & argument)
1940 string filename = split(argument, format, ' ');
1942 lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1943 << " file: " << filename << endl;
1945 // need user interaction
1946 if (filename.empty()) {
1947 string initpath = lyxrc.document_path;
1949 if (view()->buffer()) {
1950 string const trypath = lyx_view_->buffer()->filePath();
1951 // If directory is writeable, use this as default.
1952 if (isDirWriteable(FileName(trypath)))
1956 docstring const text = bformat(_("Select %1$s file to import"),
1957 formats.prettyName(format));
1959 FileDialog fileDlg(text,
1961 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1962 make_pair(_("Examples|#E#e"),
1963 from_utf8(addPath(package().system_support(), "examples"))));
1965 docstring filter = formats.prettyName(format);
1968 filter += from_utf8(formats.extension(format));
1971 FileDialog::Result result =
1972 fileDlg.open(from_utf8(initpath),
1973 FileFilterList(filter),
1976 if (result.first == FileDialog::Later)
1979 filename = to_utf8(result.second);
1981 // check selected filename
1982 if (filename.empty())
1983 lyx_view_->message(_("Canceled."));
1986 if (filename.empty())
1989 // get absolute path of file
1990 FileName const fullname(makeAbsPath(filename));
1992 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
1994 // Check if the document already is open
1995 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
1996 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
1997 lyx_view_->message(_("Canceled."));
2002 // if the file exists already, and we didn't do
2003 // -i lyx thefile.lyx, warn
2004 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2005 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2007 docstring text = bformat(_("The document %1$s already exists.\n\n"
2008 "Do you want to over-write that document?"), file);
2009 int const ret = Alert::prompt(_("Over-write document?"),
2010 text, 0, 1, _("&Over-write"), _("&Cancel"));
2013 lyx_view_->message(_("Canceled."));
2018 ErrorList errorList;
2019 Importer::Import(lyx_view_, fullname, format, errorList);
2020 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2024 void LyXFunc::closeBuffer()
2026 // save current cursor position
2027 LyX::ref().session().lastFilePos().save(FileName(lyx_view_->buffer()->fileName()),
2028 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2029 // goto bookmark to update bookmark pit.
2030 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2031 gotoBookmark(i+1, false, false);
2032 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2033 if (theBufferList().empty()) {
2034 // need this otherwise SEGV may occur while
2035 // trying to set variables that don't exist
2036 // since there's no current buffer
2037 lyx_view_->getDialogs().hideBufferDependent();
2039 lyx_view_->setBuffer(theBufferList().first());
2045 // Each "lyx_view_" should have it's own message method. lyxview and
2046 // the minibuffer would use the minibuffer, but lyxserver would
2047 // send an ERROR signal to its client. Alejandro 970603
2048 // This function is bit problematic when it comes to NLS, to make the
2049 // lyx servers client be language indepenent we must not translate
2050 // strings sent to this func.
2051 void LyXFunc::setErrorMessage(docstring const & m) const
2053 dispatch_buffer = m;
2058 void LyXFunc::setMessage(docstring const & m) const
2060 dispatch_buffer = m;
2064 docstring const LyXFunc::viewStatusMessage()
2066 // When meta-fake key is pressed, show the key sequence so far + "M-".
2068 return keyseq->print(true) + "M-";
2070 // Else, when a non-complete key sequence is pressed,
2071 // show the available options.
2072 if (keyseq->length() > 0 && !keyseq->deleted())
2073 return keyseq->printOptions(true);
2075 if (!view()->buffer())
2076 return _("Welcome to LyX!");
2078 return view()->cursor().currentState();
2082 BufferView * LyXFunc::view() const
2084 BOOST_ASSERT(lyx_view_);
2085 return lyx_view_->view();
2089 bool LyXFunc::wasMetaKey() const
2091 return (meta_fake_bit != key_modifier::none);
2097 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2099 // Why the switch you might ask. It is a trick to ensure that all
2100 // the elements in the LyXRCTags enum is handled. As you can see
2101 // there are no breaks at all. So it is just a huge fall-through.
2102 // The nice thing is that we will get a warning from the compiler
2103 // if we forget an element.
2104 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2106 case LyXRC::RC_ACCEPT_COMPOUND:
2107 case LyXRC::RC_ALT_LANG:
2108 case LyXRC::RC_ASCIIROFF_COMMAND:
2109 case LyXRC::RC_ASCII_LINELEN:
2110 case LyXRC::RC_AUTOREGIONDELETE:
2111 case LyXRC::RC_AUTORESET_OPTIONS:
2112 case LyXRC::RC_AUTOSAVE:
2113 case LyXRC::RC_AUTO_NUMBER:
2114 case LyXRC::RC_BACKUPDIR_PATH:
2115 case LyXRC::RC_BIBTEX_COMMAND:
2116 case LyXRC::RC_BINDFILE:
2117 case LyXRC::RC_CHECKLASTFILES:
2118 case LyXRC::RC_USELASTFILEPOS:
2119 case LyXRC::RC_LOADSESSION:
2120 case LyXRC::RC_CHKTEX_COMMAND:
2121 case LyXRC::RC_CONVERTER:
2122 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2123 case LyXRC::RC_COPIER:
2124 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2125 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2126 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2127 case LyXRC::RC_DATE_INSERT_FORMAT:
2128 case LyXRC::RC_DEFAULT_LANGUAGE:
2129 case LyXRC::RC_DEFAULT_PAPERSIZE:
2130 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2131 case LyXRC::RC_DISPLAY_GRAPHICS:
2132 case LyXRC::RC_DOCUMENTPATH:
2133 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2134 string const encoded = FileName(
2135 lyxrc_new.document_path).toFilesystemEncoding();
2136 if (fs::exists(encoded) && fs::is_directory(encoded))
2137 support::package().document_dir() = lyxrc.document_path;
2139 case LyXRC::RC_ESC_CHARS:
2140 case LyXRC::RC_FONT_ENCODING:
2141 case LyXRC::RC_FORMAT:
2142 case LyXRC::RC_INDEX_COMMAND:
2143 case LyXRC::RC_INPUT:
2144 case LyXRC::RC_KBMAP:
2145 case LyXRC::RC_KBMAP_PRIMARY:
2146 case LyXRC::RC_KBMAP_SECONDARY:
2147 case LyXRC::RC_LABEL_INIT_LENGTH:
2148 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2149 case LyXRC::RC_LANGUAGE_AUTO_END:
2150 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2151 case LyXRC::RC_LANGUAGE_COMMAND_END:
2152 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2153 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2154 case LyXRC::RC_LANGUAGE_PACKAGE:
2155 case LyXRC::RC_LANGUAGE_USE_BABEL:
2156 case LyXRC::RC_MAKE_BACKUP:
2157 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2158 case LyXRC::RC_NUMLASTFILES:
2159 case LyXRC::RC_PATH_PREFIX:
2160 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2161 support::prependEnvPath("PATH", lyxrc.path_prefix);
2163 case LyXRC::RC_PERS_DICT:
2164 case LyXRC::RC_POPUP_BOLD_FONT:
2165 case LyXRC::RC_POPUP_FONT_ENCODING:
2166 case LyXRC::RC_POPUP_NORMAL_FONT:
2167 case LyXRC::RC_PREVIEW:
2168 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2169 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2170 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2171 case LyXRC::RC_PRINTCOPIESFLAG:
2172 case LyXRC::RC_PRINTER:
2173 case LyXRC::RC_PRINTEVENPAGEFLAG:
2174 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2175 case LyXRC::RC_PRINTFILEEXTENSION:
2176 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2177 case LyXRC::RC_PRINTODDPAGEFLAG:
2178 case LyXRC::RC_PRINTPAGERANGEFLAG:
2179 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2180 case LyXRC::RC_PRINTPAPERFLAG:
2181 case LyXRC::RC_PRINTREVERSEFLAG:
2182 case LyXRC::RC_PRINTSPOOL_COMMAND:
2183 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2184 case LyXRC::RC_PRINTTOFILE:
2185 case LyXRC::RC_PRINTTOPRINTER:
2186 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2187 case LyXRC::RC_PRINT_COMMAND:
2188 case LyXRC::RC_RTL_SUPPORT:
2189 case LyXRC::RC_SCREEN_DPI:
2190 case LyXRC::RC_SCREEN_FONT_ENCODING:
2191 case LyXRC::RC_SCREEN_FONT_ROMAN:
2192 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2193 case LyXRC::RC_SCREEN_FONT_SANS:
2194 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2195 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2196 case LyXRC::RC_SCREEN_FONT_SIZES:
2197 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2198 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2199 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2200 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2201 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2202 case LyXRC::RC_SCREEN_ZOOM:
2203 case LyXRC::RC_SERVERPIPE:
2204 case LyXRC::RC_SET_COLOR:
2205 case LyXRC::RC_SHOW_BANNER:
2206 case LyXRC::RC_SPELL_COMMAND:
2207 case LyXRC::RC_TEMPDIRPATH:
2208 case LyXRC::RC_TEMPLATEPATH:
2209 case LyXRC::RC_TEX_ALLOWS_SPACES:
2210 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2211 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2212 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2214 case LyXRC::RC_UIFILE:
2215 case LyXRC::RC_USER_EMAIL:
2216 case LyXRC::RC_USER_NAME:
2217 case LyXRC::RC_USETEMPDIR:
2218 case LyXRC::RC_USE_ALT_LANG:
2219 case LyXRC::RC_USE_CONVERTER_CACHE:
2220 case LyXRC::RC_USE_ESC_CHARS:
2221 case LyXRC::RC_USE_INP_ENC:
2222 case LyXRC::RC_USE_PERS_DICT:
2223 case LyXRC::RC_USE_SPELL_LIB:
2224 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2225 case LyXRC::RC_VIEWER:
2226 case LyXRC::RC_LAST: