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.
25 #include "BranchList.h"
27 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
31 #include "bufferview_funcs.h"
33 #include "CutAndPaste.h"
35 #include "DispatchResult.h"
37 #include "ErrorList.h"
40 #include "FuncRequest.h"
41 #include "FuncStatus.h"
44 #include "InsetIterator.h"
52 #include "LyXAction.h"
58 #include "TextClassList.h"
60 #include "Paragraph.h"
61 #include "ParIterator.h"
62 #include "ParagraphParameters.h"
65 #include "insets/InsetBox.h"
66 #include "insets/InsetBranch.h"
67 #include "insets/InsetCommand.h"
68 #include "insets/InsetERT.h"
69 #include "insets/InsetExternal.h"
70 #include "insets/InsetFloat.h"
71 #include "insets/InsetListings.h"
72 #include "insets/InsetGraphics.h"
73 #include "insets/InsetInclude.h"
74 #include "insets/InsetNote.h"
75 #include "insets/InsetTabular.h"
76 #include "insets/InsetVSpace.h"
77 #include "insets/InsetWrap.h"
79 #include "frontends/Application.h"
80 #include "frontends/alert.h"
81 #include "frontends/Dialogs.h"
82 #include "frontends/FileDialog.h"
83 #include "frontends/FontLoader.h"
84 #include "frontends/Gui.h"
85 #include "frontends/KeySymbol.h"
86 #include "frontends/LyXView.h"
87 #include "frontends/Menubar.h"
88 #include "frontends/Toolbars.h"
90 #include "support/environment.h"
91 #include "support/FileFilterList.h"
92 #include "support/filetools.h"
93 #include "support/ForkedcallsController.h"
94 #include "support/fs_extras.h"
95 #include "support/lstrings.h"
96 #include "support/Path.h"
97 #include "support/Package.h"
98 #include "support/Systemcall.h"
99 #include "support/convert.h"
100 #include "support/os.h"
102 #include <boost/current_function.hpp>
103 #include <boost/filesystem/operations.hpp>
110 using bv_funcs::freefont2string;
112 using support::absolutePath;
113 using support::addName;
114 using support::addPath;
115 using support::bformat;
116 using support::changeExtension;
117 using support::contains;
118 using support::FileFilterList;
119 using support::FileName;
120 using support::fileSearch;
121 using support::ForkedcallsController;
122 using support::i18nLibFileSearch;
123 using support::isDirWriteable;
124 using support::isFileReadable;
125 using support::isStrInt;
126 using support::makeAbsPath;
127 using support::makeDisplayPath;
128 using support::package;
129 using support::quoteName;
130 using support::rtrim;
131 using support::split;
132 using support::subst;
133 using support::Systemcall;
134 using support::token;
136 using support::prefixIs;
139 using std::make_pair;
142 using std::istringstream;
143 using std::ostringstream;
145 namespace Alert = frontend::Alert;
146 namespace fs = boost::filesystem;
151 bool getLocalStatus(Cursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //Cursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
207 meta_fake_bit(key_modifier::none)
212 void LyXFunc::initKeySequences(KeyMap * kb)
214 keyseq.reset(new KeySequence(kb, kb));
215 cancel_meta_seq.reset(new KeySequence(kb, kb));
219 void LyXFunc::setLyXView(LyXView * lv)
225 void LyXFunc::handleKeyFunc(kb_action action)
227 char_type c = encoded_last_key;
229 if (keyseq->length())
232 lyx_view_->view()->getIntl().getTransManager().deadkey(
233 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 view()->cursor().resetAnchor();
243 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
245 BOOST_ASSERT(lyx_view_);
246 if (!LyX::ref().session().bookmarks().isValid(idx))
248 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
249 BOOST_ASSERT(!bm.filename.empty());
250 string const file = bm.filename.absFilename();
251 // if the file is not opened, open it.
252 if (!theBufferList().exists(file)) {
254 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
258 // open may fail, so we need to test it again
259 if (theBufferList().exists(file)) {
260 // if the current buffer is not that one, switch to it.
261 if (lyx_view_->buffer()->fileName() != file) {
263 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
267 // moveToPosition use par_id, and par_pit and return new par_id.
271 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
272 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
273 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
274 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
275 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
280 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
282 LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
284 // Do nothing if we have nothing (JMarc)
285 if (!keysym->isOK()) {
286 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
291 if (keysym->isModifier()) {
292 LYXERR(Debug::KEY) << "isModifier true" << endl;
296 //Encoding const * encoding = view()->cursor().getEncoding();
297 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
298 // FIXME: encoded_last_key shadows the member variable of the same
299 // name. Is that intended?
300 char_type encoded_last_key = keysym->getUCSEncoded();
302 // Do a one-deep top-level lookup for
303 // cancel and meta-fake keys. RVDK_PATCH_5
304 cancel_meta_seq->reset();
306 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
307 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
308 << " action first set to [" << func.action << ']'
311 // When not cancel or meta-fake, do the normal lookup.
312 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
313 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
314 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
315 // remove Caps Lock and Mod2 as a modifiers
316 func = keyseq->addkey(keysym, (state | meta_fake_bit));
317 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
318 << "action now set to ["
319 << func.action << ']' << endl;
322 // Dont remove this unless you know what you are doing.
323 meta_fake_bit = key_modifier::none;
325 // Can this happen now ?
326 if (func.action == LFUN_NOACTION) {
327 func = FuncRequest(LFUN_COMMAND_PREFIX);
330 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
332 << func.action << "]["
333 << to_utf8(keyseq->print(false)) << ']'
336 // already here we know if it any point in going further
337 // why not return already here if action == -1 and
338 // num_bytes == 0? (Lgb)
340 if (keyseq->length() > 1) {
341 lyx_view_->message(keyseq->print(true));
345 // Maybe user can only reach the key via holding down shift.
346 // Let's see. But only if shift is the only modifier
347 if (func.action == LFUN_UNKNOWN_ACTION &&
348 state == key_modifier::shift) {
349 LYXERR(Debug::KEY) << "Trying without shift" << endl;
350 func = keyseq->addkey(keysym, key_modifier::none);
351 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
354 if (func.action == LFUN_UNKNOWN_ACTION) {
355 // Hmm, we didn't match any of the keysequences. See
356 // if it's normal insertable text not already covered
358 if (keysym->isText() && keyseq->length() == 1) {
359 LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
360 func = FuncRequest(LFUN_SELF_INSERT,
361 FuncRequest::KEYBOARD);
363 LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
364 lyx_view_->message(_("Unknown function."));
369 if (func.action == LFUN_SELF_INSERT) {
370 if (encoded_last_key != 0) {
371 docstring const arg(1, encoded_last_key);
372 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
373 FuncRequest::KEYBOARD));
375 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
383 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
385 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
388 Cursor & cur = view()->cursor();
390 /* In LyX/Mac, when a dialog is open, the menus of the
391 application can still be accessed without giving focus to
392 the main window. In this case, we want to disable the menu
393 entries that are buffer-related.
395 Note that this code is not perfect, as bug 1941 attests:
396 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
398 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
399 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
402 if (cmd.action == LFUN_NOACTION) {
403 flag.message(from_utf8(N_("Nothing to do")));
408 switch (cmd.action) {
409 case LFUN_UNKNOWN_ACTION:
410 #ifndef HAVE_LIBAIKSAURUS
411 case LFUN_THESAURUS_ENTRY:
421 if (flag.unknown()) {
422 flag.message(from_utf8(N_("Unknown action")));
426 if (!flag.enabled()) {
427 if (flag.message().empty())
428 flag.message(from_utf8(N_("Command disabled")));
432 // Check whether we need a buffer
433 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
435 flag.message(from_utf8(N_("Command not allowed with"
436 "out any document open")));
441 // I would really like to avoid having this switch and rather try to
442 // encode this in the function itself.
443 // -- And I'd rather let an inset decide which LFUNs it is willing
444 // to handle (Andre')
446 switch (cmd.action) {
447 case LFUN_BUFFER_TOGGLE_READ_ONLY:
448 flag.setOnOff(buf->isReadonly());
451 case LFUN_BUFFER_SWITCH:
452 // toggle on the current buffer, but do not toggle off
453 // the other ones (is that a good idea?)
454 if (to_utf8(cmd.argument()) == buf->fileName())
458 case LFUN_BUFFER_EXPORT:
459 enable = cmd.argument() == "custom"
460 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
463 case LFUN_BUFFER_CHKTEX:
464 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
467 case LFUN_BUILD_PROGRAM:
468 enable = Exporter::isExportable(*buf, "program");
471 case LFUN_LAYOUT_TABULAR:
472 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
476 case LFUN_LAYOUT_PARAGRAPH:
477 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
480 case LFUN_VC_REGISTER:
481 enable = !buf->lyxvc().inUse();
483 case LFUN_VC_CHECK_IN:
484 enable = buf->lyxvc().inUse() && !buf->isReadonly();
486 case LFUN_VC_CHECK_OUT:
487 enable = buf->lyxvc().inUse() && buf->isReadonly();
490 case LFUN_VC_UNDO_LAST:
491 enable = buf->lyxvc().inUse();
493 case LFUN_BUFFER_RELOAD:
494 enable = !buf->isUnnamed() && !buf->isClean();
497 case LFUN_INSET_SETTINGS: {
501 Inset::Code code = cur.inset().lyxCode();
503 case Inset::TABULAR_CODE:
504 enable = cmd.argument() == "tabular";
506 case Inset::ERT_CODE:
507 enable = cmd.argument() == "ert";
509 case Inset::FLOAT_CODE:
510 enable = cmd.argument() == "float";
512 case Inset::WRAP_CODE:
513 enable = cmd.argument() == "wrap";
515 case Inset::NOTE_CODE:
516 enable = cmd.argument() == "note";
518 case Inset::BRANCH_CODE:
519 enable = cmd.argument() == "branch";
521 case Inset::BOX_CODE:
522 enable = cmd.argument() == "box";
524 case Inset::LISTINGS_CODE:
525 enable = cmd.argument() == "listings";
533 case LFUN_INSET_APPLY: {
534 string const name = cmd.getArg(0);
535 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
537 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
539 if (!inset->getStatus(cur, fr, fs)) {
540 // Every inset is supposed to handle this
545 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
546 flag |= getStatus(fr);
548 enable = flag.enabled();
552 case LFUN_DIALOG_TOGGLE:
553 flag.setOnOff(lyx_view_->getDialogs().visible(cmd.getArg(0)));
554 // fall through to set "enable"
555 case LFUN_DIALOG_SHOW: {
556 string const name = cmd.getArg(0);
558 enable = name == "aboutlyx"
559 || name == "file" //FIXME: should be removed.
561 || name == "texinfo";
562 else if (name == "print")
563 enable = Exporter::isExportable(*buf, "dvi")
564 && lyxrc.print_command != "none";
565 else if (name == "character")
566 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
567 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
568 else if (name == "latexlog")
569 enable = isFileReadable(FileName(buf->getLogName().second));
570 else if (name == "spellchecker")
571 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
572 enable = !buf->isReadonly();
576 else if (name == "vclog")
577 enable = buf->lyxvc().inUse();
581 case LFUN_DIALOG_SHOW_NEW_INSET:
582 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
583 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
584 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
586 if (cur.inset().getStatus(cur, cmd, flag))
591 case LFUN_DIALOG_UPDATE: {
592 string const name = cmd.getArg(0);
594 enable = name == "prefs";
598 case LFUN_CITATION_INSERT: {
599 FuncRequest fr(LFUN_INSET_INSERT, "citation");
600 enable = getStatus(fr).enabled();
604 case LFUN_BUFFER_WRITE: {
605 enable = view()->buffer()->isUnnamed()
606 || !view()->buffer()->isClean();
610 case LFUN_BOOKMARK_GOTO: {
611 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
612 enable = LyX::ref().session().bookmarks().isValid(num);
616 case LFUN_BOOKMARK_CLEAR:
617 enable = LyX::ref().session().bookmarks().size() > 0;
620 case LFUN_TOOLBAR_TOGGLE: {
621 bool const current = lyx_view_->getToolbars().visible(cmd.getArg(0));
622 flag.setOnOff(current);
625 case LFUN_WINDOW_CLOSE: {
626 enable = (theApp()->gui().viewIds().size() > 1);
630 // this one is difficult to get right. As a half-baked
631 // solution, we consider only the first action of the sequence
632 case LFUN_COMMAND_SEQUENCE: {
633 // argument contains ';'-terminated commands
634 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
635 FuncRequest func(lyxaction.lookupFunc(firstcmd));
636 func.origin = cmd.origin;
637 flag = getStatus(func);
640 case LFUN_BUFFER_NEW:
641 case LFUN_BUFFER_NEW_TEMPLATE:
642 case LFUN_WORD_FIND_FORWARD:
643 case LFUN_WORD_FIND_BACKWARD:
644 case LFUN_COMMAND_PREFIX:
645 case LFUN_COMMAND_EXECUTE:
647 case LFUN_META_PREFIX:
648 case LFUN_BUFFER_CLOSE:
649 case LFUN_BUFFER_WRITE_AS:
650 case LFUN_BUFFER_UPDATE:
651 case LFUN_BUFFER_VIEW:
652 case LFUN_BUFFER_IMPORT:
653 case LFUN_BUFFER_AUTO_SAVE:
654 case LFUN_RECONFIGURE:
658 case LFUN_DROP_LAYOUTS_CHOICE:
660 case LFUN_SERVER_GET_NAME:
661 case LFUN_SERVER_NOTIFY:
662 case LFUN_SERVER_GOTO_FILE_ROW:
663 case LFUN_DIALOG_HIDE:
664 case LFUN_DIALOG_DISCONNECT_INSET:
665 case LFUN_BUFFER_CHILD_OPEN:
666 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
667 case LFUN_KEYMAP_OFF:
668 case LFUN_KEYMAP_PRIMARY:
669 case LFUN_KEYMAP_SECONDARY:
670 case LFUN_KEYMAP_TOGGLE:
672 case LFUN_BUFFER_EXPORT_CUSTOM:
673 case LFUN_BUFFER_PRINT:
674 case LFUN_PREFERENCES_SAVE:
675 case LFUN_SCREEN_FONT_UPDATE:
678 case LFUN_EXTERNAL_EDIT:
679 case LFUN_GRAPHICS_EDIT:
680 case LFUN_ALL_INSETS_TOGGLE:
681 case LFUN_BUFFER_LANGUAGE:
682 case LFUN_TEXTCLASS_APPLY:
683 case LFUN_TEXTCLASS_LOAD:
684 case LFUN_BUFFER_SAVE_AS_DEFAULT:
685 case LFUN_BUFFER_PARAMS_APPLY:
686 case LFUN_LYXRC_APPLY:
687 case LFUN_BUFFER_NEXT:
688 case LFUN_BUFFER_PREVIOUS:
689 case LFUN_WINDOW_NEW:
691 // these are handled in our dispatch()
695 if (!getLocalStatus(cur, cmd, flag))
696 flag = view()->getStatus(cmd);
702 // Can we use a readonly buffer?
703 if (buf && buf->isReadonly()
704 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
705 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
706 flag.message(from_utf8(N_("Document is read-only")));
710 // Are we in a DELETED change-tracking region?
711 if (buf && lookupChangeType(cur, true) == Change::DELETED
712 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
713 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
714 flag.message(from_utf8(N_("This portion of the document is deleted.")));
718 // the default error message if we disable the command
719 if (!flag.enabled() && flag.message().empty())
720 flag.message(from_utf8(N_("Command disabled")));
726 bool LyXFunc::ensureBufferClean(BufferView * bv)
728 Buffer & buf = *bv->buffer();
732 docstring const file = makeDisplayPath(buf.fileName(), 30);
733 docstring text = bformat(_("The document %1$s has unsaved "
734 "changes.\n\nDo you want to save "
735 "the document?"), file);
736 int const ret = Alert::prompt(_("Save changed document?"),
737 text, 0, 1, _("&Save"),
741 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
743 return buf.isClean();
749 void showPrintError(string const & name)
751 docstring str = bformat(_("Could not print the document %1$s.\n"
752 "Check that your printer is set up correctly."),
753 makeDisplayPath(name, 50));
754 Alert::error(_("Print document failed"), str);
758 void loadTextclass(string const & name)
760 std::pair<bool, textclass_type> const tc_pair =
761 textclasslist.numberOfClass(name);
763 if (!tc_pair.first) {
764 lyxerr << "Document class \"" << name
765 << "\" does not exist."
770 textclass_type const tc = tc_pair.second;
772 if (!textclasslist[tc].load()) {
773 docstring s = bformat(_("The document could not be converted\n"
774 "into the document class %1$s."),
775 from_utf8(textclasslist[tc].name()));
776 Alert::error(_("Could not change class"), s);
781 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
786 void LyXFunc::dispatch(FuncRequest const & cmd)
788 string const argument = to_utf8(cmd.argument());
789 kb_action const action = cmd.action;
791 LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
792 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
794 // we have not done anything wrong yet.
796 dispatch_buffer.erase();
798 // redraw the screen at the end (first of the two drawing steps).
799 //This is done unless explicitely requested otherwise
800 Update::flags updateFlags = Update::FitCursor;
802 FuncStatus const flag = getStatus(cmd);
803 if (!flag.enabled()) {
804 // We cannot use this function here
805 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
806 << lyxaction.getActionName(action)
807 << " [" << action << "] is disabled at this location"
809 setErrorMessage(flag.message());
813 case LFUN_WORD_FIND_FORWARD:
814 case LFUN_WORD_FIND_BACKWARD: {
815 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
816 static docstring last_search;
817 docstring searched_string;
819 if (!cmd.argument().empty()) {
820 last_search = cmd.argument();
821 searched_string = cmd.argument();
823 searched_string = last_search;
826 if (searched_string.empty())
829 bool const fw = action == LFUN_WORD_FIND_FORWARD;
830 docstring const data =
831 find2string(searched_string, true, false, fw);
832 find(view(), FuncRequest(LFUN_WORD_FIND, data));
836 case LFUN_COMMAND_PREFIX:
837 BOOST_ASSERT(lyx_view_);
838 lyx_view_->message(keyseq->printOptions(true));
841 case LFUN_COMMAND_EXECUTE:
842 BOOST_ASSERT(lyx_view_);
843 lyx_view_->getToolbars().display("minibuffer", true);
844 lyx_view_->focus_command_buffer();
848 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
850 meta_fake_bit = key_modifier::none;
851 if (view()->buffer())
852 // cancel any selection
853 dispatch(FuncRequest(LFUN_MARK_OFF));
854 setMessage(from_ascii(N_("Cancel")));
857 case LFUN_META_PREFIX:
858 meta_fake_bit = key_modifier::alt;
859 setMessage(keyseq->print(true));
862 case LFUN_BUFFER_TOGGLE_READ_ONLY:
863 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
864 if (lyx_view_->buffer()->lyxvc().inUse())
865 lyx_view_->buffer()->lyxvc().toggleReadOnly();
867 lyx_view_->buffer()->setReadonly(
868 !lyx_view_->buffer()->isReadonly());
871 // --- Menus -----------------------------------------------
872 case LFUN_BUFFER_NEW:
873 menuNew(argument, false);
876 case LFUN_BUFFER_NEW_TEMPLATE:
877 menuNew(argument, true);
880 case LFUN_BUFFER_CLOSE:
885 case LFUN_BUFFER_WRITE:
886 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
887 if (!lyx_view_->buffer()->isUnnamed()) {
888 docstring const str = bformat(_("Saving document %1$s..."),
889 makeDisplayPath(lyx_view_->buffer()->fileName()));
890 lyx_view_->message(str);
891 menuWrite(lyx_view_->buffer());
892 lyx_view_->message(str + _(" done."));
894 writeAs(lyx_view_->buffer());
896 updateFlags = Update::None;
899 case LFUN_BUFFER_WRITE_AS:
900 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
901 writeAs(lyx_view_->buffer(), argument);
902 updateFlags = Update::None;
905 case LFUN_BUFFER_RELOAD: {
906 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
907 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
908 docstring text = bformat(_("Any changes will be lost. Are you sure "
909 "you want to revert to the saved version of the document %1$s?"), file);
910 int const ret = Alert::prompt(_("Revert to saved document?"),
911 text, 1, 1, _("&Revert"), _("&Cancel"));
918 case LFUN_BUFFER_UPDATE:
919 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
920 Exporter::Export(lyx_view_->buffer(), argument, true);
923 case LFUN_BUFFER_VIEW:
924 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
925 Exporter::preview(lyx_view_->buffer(), argument);
928 case LFUN_BUILD_PROGRAM:
929 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
930 Exporter::Export(lyx_view_->buffer(), "program", true);
933 case LFUN_BUFFER_CHKTEX:
934 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
935 lyx_view_->buffer()->runChktex();
938 case LFUN_BUFFER_EXPORT:
939 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
940 if (argument == "custom")
941 lyx_view_->getDialogs().show("sendto");
943 Exporter::Export(lyx_view_->buffer(), argument, false);
947 case LFUN_BUFFER_EXPORT_CUSTOM: {
948 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
950 string command = split(argument, format_name, ' ');
951 Format const * format = formats.getFormat(format_name);
953 lyxerr << "Format \"" << format_name
954 << "\" not recognized!"
959 Buffer * buffer = lyx_view_->buffer();
961 // The name of the file created by the conversion process
964 // Output to filename
965 if (format->name() == "lyx") {
966 string const latexname =
967 buffer->getLatexName(false);
968 filename = changeExtension(latexname,
969 format->extension());
970 filename = addName(buffer->temppath(), filename);
972 if (!buffer->writeFile(FileName(filename)))
976 Exporter::Export(buffer, format_name, true, filename);
979 // Substitute $$FName for filename
980 if (!contains(command, "$$FName"))
981 command = "( " + command + " ) < $$FName";
982 command = subst(command, "$$FName", filename);
984 // Execute the command in the background
986 call.startscript(Systemcall::DontWait, command);
990 case LFUN_BUFFER_PRINT: {
991 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
994 string command = split(split(argument, target, ' '),
998 || target_name.empty()
999 || command.empty()) {
1000 lyxerr << "Unable to parse \""
1001 << argument << '"' << std::endl;
1004 if (target != "printer" && target != "file") {
1005 lyxerr << "Unrecognized target \""
1006 << target << '"' << std::endl;
1010 Buffer * buffer = lyx_view_->buffer();
1012 if (!Exporter::Export(buffer, "dvi", true)) {
1013 showPrintError(buffer->fileName());
1017 // Push directory path.
1018 string const path(buffer->temppath());
1019 // Prevent the compiler from optimizing away p
1021 support::Path p(pp);
1023 // there are three cases here:
1024 // 1. we print to a file
1025 // 2. we print directly to a printer
1026 // 3. we print using a spool command (print to file first)
1029 string const dviname =
1030 changeExtension(buffer->getLatexName(true),
1033 if (target == "printer") {
1034 if (!lyxrc.print_spool_command.empty()) {
1035 // case 3: print using a spool
1036 string const psname =
1037 changeExtension(dviname,".ps");
1038 command += lyxrc.print_to_file
1041 + quoteName(dviname);
1044 lyxrc.print_spool_command +' ';
1045 if (target_name != "default") {
1046 command2 += lyxrc.print_spool_printerprefix
1050 command2 += quoteName(psname);
1052 // If successful, then spool command
1053 res = one.startscript(
1058 res = one.startscript(
1059 Systemcall::DontWait,
1062 // case 2: print directly to a printer
1063 if (target_name != "default")
1064 command += lyxrc.print_to_printer + target_name + ' ';
1065 res = one.startscript(
1066 Systemcall::DontWait,
1067 command + quoteName(dviname));
1071 // case 1: print to a file
1072 FileName const filename(makeAbsPath(target_name, path));
1073 if (fs::exists(filename.toFilesystemEncoding())) {
1074 docstring text = bformat(
1075 _("The file %1$s already exists.\n\n"
1076 "Do you want to over-write that file?"),
1077 makeDisplayPath(filename.absFilename()));
1078 if (Alert::prompt(_("Over-write file?"),
1079 text, 0, 1, _("&Over-write"), _("&Cancel")) != 0)
1082 command += lyxrc.print_to_file
1083 + quoteName(filename.toFilesystemEncoding())
1085 + quoteName(dviname);
1086 res = one.startscript(Systemcall::DontWait,
1091 showPrintError(buffer->fileName());
1095 case LFUN_BUFFER_IMPORT:
1100 // quitting is triggered by the gui code
1101 // (leaving the event loop).
1102 lyx_view_->message(from_utf8(N_("Exiting.")));
1103 if (theBufferList().quitWriteAll())
1104 theApp()->gui().closeAllViews();
1107 case LFUN_BUFFER_AUTO_SAVE:
1111 case LFUN_RECONFIGURE:
1112 BOOST_ASSERT(lyx_view_);
1113 reconfigure(*lyx_view_);
1116 case LFUN_HELP_OPEN: {
1117 BOOST_ASSERT(lyx_view_);
1118 string const arg = argument;
1120 setErrorMessage(from_ascii(N_("Missing argument")));
1123 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1124 if (fname.empty()) {
1125 lyxerr << "LyX: unable to find documentation file `"
1126 << arg << "'. Bad installation?" << endl;
1129 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1130 makeDisplayPath(fname.absFilename())));
1131 lyx_view_->loadLyXFile(fname, false);
1135 // --- version control -------------------------------
1136 case LFUN_VC_REGISTER:
1137 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1138 if (!ensureBufferClean(view()))
1140 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1141 lyx_view_->buffer()->lyxvc().registrer();
1144 updateFlags = Update::Force;
1147 case LFUN_VC_CHECK_IN:
1148 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1149 if (!ensureBufferClean(view()))
1151 if (lyx_view_->buffer()->lyxvc().inUse()
1152 && !lyx_view_->buffer()->isReadonly()) {
1153 lyx_view_->buffer()->lyxvc().checkIn();
1158 case LFUN_VC_CHECK_OUT:
1159 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1160 if (!ensureBufferClean(view()))
1162 if (lyx_view_->buffer()->lyxvc().inUse()
1163 && lyx_view_->buffer()->isReadonly()) {
1164 lyx_view_->buffer()->lyxvc().checkOut();
1169 case LFUN_VC_REVERT:
1170 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1171 lyx_view_->buffer()->lyxvc().revert();
1175 case LFUN_VC_UNDO_LAST:
1176 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1177 lyx_view_->buffer()->lyxvc().undoLast();
1181 // --- buffers ----------------------------------------
1182 case LFUN_BUFFER_SWITCH:
1183 BOOST_ASSERT(lyx_view_);
1184 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1187 case LFUN_BUFFER_NEXT:
1188 BOOST_ASSERT(lyx_view_);
1189 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1192 case LFUN_BUFFER_PREVIOUS:
1193 BOOST_ASSERT(lyx_view_);
1194 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1198 BOOST_ASSERT(lyx_view_);
1199 newFile(view(), argument);
1202 case LFUN_FILE_OPEN:
1203 BOOST_ASSERT(lyx_view_);
1207 case LFUN_DROP_LAYOUTS_CHOICE:
1208 BOOST_ASSERT(lyx_view_);
1209 lyx_view_->getToolbars().openLayoutList();
1212 case LFUN_MENU_OPEN:
1213 BOOST_ASSERT(lyx_view_);
1214 lyx_view_->getMenubar().openByName(from_utf8(argument));
1217 // --- lyxserver commands ----------------------------
1218 case LFUN_SERVER_GET_NAME:
1219 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1220 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1221 LYXERR(Debug::INFO) << "FNAME["
1222 << lyx_view_->buffer()->fileName()
1226 case LFUN_SERVER_NOTIFY:
1227 dispatch_buffer = keyseq->print(false);
1228 theServer().notifyClient(to_utf8(dispatch_buffer));
1231 case LFUN_SERVER_GOTO_FILE_ROW: {
1232 BOOST_ASSERT(lyx_view_);
1235 istringstream is(argument);
1236 is >> file_name >> row;
1237 if (prefixIs(file_name, package().temp_dir().absFilename())) {
1238 // Needed by inverse dvi search. If it is a file
1239 // in tmpdir, call the apropriated function
1240 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1242 // Must replace extension of the file to be .lyx
1243 // and get full path
1244 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1245 // Either change buffer or load the file
1246 if (theBufferList().exists(s.absFilename())) {
1247 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1249 lyx_view_->loadLyXFile(s);
1253 view()->setCursorFromRow(row);
1255 updateFlags = Update::FitCursor;
1259 case LFUN_DIALOG_SHOW: {
1260 BOOST_ASSERT(lyx_view_);
1261 string const name = cmd.getArg(0);
1262 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1264 if (name == "character") {
1265 data = freefont2string();
1267 lyx_view_->getDialogs().show("character", data);
1268 } else if (name == "latexlog") {
1269 pair<Buffer::LogType, string> const logfile =
1270 lyx_view_->buffer()->getLogName();
1271 switch (logfile.first) {
1272 case Buffer::latexlog:
1275 case Buffer::buildlog:
1279 data += Lexer::quoteString(logfile.second);
1280 lyx_view_->getDialogs().show("log", data);
1281 } else if (name == "vclog") {
1282 string const data = "vc " +
1283 Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1284 lyx_view_->getDialogs().show("log", data);
1286 lyx_view_->getDialogs().show(name, data);
1290 case LFUN_DIALOG_SHOW_NEW_INSET: {
1291 BOOST_ASSERT(lyx_view_);
1292 string const name = cmd.getArg(0);
1293 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1294 if (name == "bibitem" ||
1298 name == "nomenclature" ||
1302 InsetCommandParams p(name);
1303 data = InsetCommandMailer::params2string(name, p);
1304 } else if (name == "include") {
1305 // data is the include type: one of "include",
1306 // "input", "verbatiminput" or "verbatiminput*"
1308 // default type is requested
1310 InsetCommandParams p(data);
1311 data = InsetIncludeMailer::params2string(p);
1312 } else if (name == "box") {
1313 // \c data == "Boxed" || "Frameless" etc
1314 InsetBoxParams p(data);
1315 data = InsetBoxMailer::params2string(p);
1316 } else if (name == "branch") {
1317 InsetBranchParams p;
1318 data = InsetBranchMailer::params2string(p);
1319 } else if (name == "citation") {
1320 InsetCommandParams p("cite");
1321 data = InsetCommandMailer::params2string(name, p);
1322 } else if (name == "ert") {
1323 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1324 } else if (name == "external") {
1325 InsetExternalParams p;
1326 Buffer const & buffer = *lyx_view_->buffer();
1327 data = InsetExternalMailer::params2string(p, buffer);
1328 } else if (name == "float") {
1330 data = InsetFloatMailer::params2string(p);
1331 } else if (name == "listings") {
1332 InsetListingsParams p;
1333 data = InsetListingsMailer::params2string(p);
1334 } else if (name == "graphics") {
1335 InsetGraphicsParams p;
1336 Buffer const & buffer = *lyx_view_->buffer();
1337 data = InsetGraphicsMailer::params2string(p, buffer);
1338 } else if (name == "note") {
1340 data = InsetNoteMailer::params2string(p);
1341 } else if (name == "vspace") {
1343 data = InsetVSpaceMailer::params2string(space);
1344 } else if (name == "wrap") {
1346 data = InsetWrapMailer::params2string(p);
1348 lyx_view_->getDialogs().show(name, data, 0);
1352 case LFUN_DIALOG_UPDATE: {
1353 BOOST_ASSERT(lyx_view_);
1354 string const & name = argument;
1355 // Can only update a dialog connected to an existing inset
1356 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1358 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1359 inset->dispatch(view()->cursor(), fr);
1360 } else if (name == "paragraph") {
1361 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1362 } else if (name == "prefs") {
1363 lyx_view_->getDialogs().update(name, string());
1368 case LFUN_DIALOG_HIDE:
1369 Dialogs::hide(argument, 0);
1372 case LFUN_DIALOG_TOGGLE: {
1373 BOOST_ASSERT(lyx_view_);
1374 if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1375 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1377 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1381 case LFUN_DIALOG_DISCONNECT_INSET:
1382 BOOST_ASSERT(lyx_view_);
1383 lyx_view_->getDialogs().disconnect(argument);
1387 case LFUN_CITATION_INSERT: {
1388 BOOST_ASSERT(lyx_view_);
1389 if (!argument.empty()) {
1390 // we can have one optional argument, delimited by '|'
1391 // citation-insert <key>|<text_before>
1392 // this should be enhanced to also support text_after
1393 // and citation style
1394 string arg = argument;
1396 if (contains(argument, "|")) {
1397 arg = token(argument, '|', 0);
1398 opt1 = token(argument, '|', 1);
1400 InsetCommandParams icp("cite");
1401 icp["key"] = from_utf8(arg);
1403 icp["before"] = from_utf8(opt1);
1404 string icstr = InsetCommandMailer::params2string("citation", icp);
1405 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1408 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1412 case LFUN_BUFFER_CHILD_OPEN: {
1413 BOOST_ASSERT(lyx_view_);
1414 FileName const filename =
1415 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1416 view()->saveBookmark(false);
1417 string const parentfilename = lyx_view_->buffer()->fileName();
1418 if (theBufferList().exists(filename.absFilename()))
1419 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1421 if (lyx_view_->loadLyXFile(filename)) {
1422 // Set the parent name of the child document.
1423 // This makes insertion of citations and references in the child work,
1424 // when the target is in the parent or another child document.
1425 lyx_view_->buffer()->setParentName(parentfilename);
1426 setMessage(bformat(_("Opening child document %1$s..."),
1427 makeDisplayPath(filename.absFilename())));
1429 setMessage(_("Document not loaded."));
1433 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1434 BOOST_ASSERT(lyx_view_);
1435 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1438 case LFUN_KEYMAP_OFF:
1439 BOOST_ASSERT(lyx_view_);
1440 lyx_view_->view()->getIntl().keyMapOn(false);
1443 case LFUN_KEYMAP_PRIMARY:
1444 BOOST_ASSERT(lyx_view_);
1445 lyx_view_->view()->getIntl().keyMapPrim();
1448 case LFUN_KEYMAP_SECONDARY:
1449 BOOST_ASSERT(lyx_view_);
1450 lyx_view_->view()->getIntl().keyMapSec();
1453 case LFUN_KEYMAP_TOGGLE:
1454 BOOST_ASSERT(lyx_view_);
1455 lyx_view_->view()->getIntl().toggleKeyMap();
1461 string rest = split(argument, countstr, ' ');
1462 istringstream is(countstr);
1465 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1466 for (int i = 0; i < count; ++i)
1467 dispatch(lyxaction.lookupFunc(rest));
1471 case LFUN_COMMAND_SEQUENCE: {
1472 // argument contains ';'-terminated commands
1473 string arg = argument;
1474 while (!arg.empty()) {
1476 arg = split(arg, first, ';');
1477 FuncRequest func(lyxaction.lookupFunc(first));
1478 func.origin = cmd.origin;
1484 case LFUN_PREFERENCES_SAVE: {
1485 lyxrc.write(makeAbsPath("preferences",
1486 package().user_support().absFilename()),
1491 case LFUN_SCREEN_FONT_UPDATE:
1492 BOOST_ASSERT(lyx_view_);
1493 // handle the screen font changes.
1494 theFontLoader().update();
1495 /// FIXME: only the current view will be updated. the Gui
1496 /// class is able to furnish the list of views.
1497 updateFlags = Update::Force;
1500 case LFUN_SET_COLOR: {
1502 string const x11_name = split(argument, lyx_name, ' ');
1503 if (lyx_name.empty() || x11_name.empty()) {
1504 setErrorMessage(from_ascii(N_(
1505 "Syntax: set-color <lyx_name>"
1510 bool const graphicsbg_changed =
1511 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1512 x11_name != lcolor.getX11Name(Color::graphicsbg));
1514 if (!lcolor.setColor(lyx_name, x11_name)) {
1516 bformat(_("Set-color \"%1$s\" failed "
1517 "- color is undefined or "
1518 "may not be redefined"),
1519 from_utf8(lyx_name)));
1523 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1525 if (graphicsbg_changed) {
1526 #ifdef WITH_WARNINGS
1527 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1530 graphics::GCache::get().changeDisplay(true);
1537 BOOST_ASSERT(lyx_view_);
1538 lyx_view_->message(from_utf8(argument));
1541 case LFUN_EXTERNAL_EDIT: {
1542 BOOST_ASSERT(lyx_view_);
1543 FuncRequest fr(action, argument);
1544 InsetExternal().dispatch(view()->cursor(), fr);
1548 case LFUN_GRAPHICS_EDIT: {
1549 FuncRequest fr(action, argument);
1550 InsetGraphics().dispatch(view()->cursor(), fr);
1554 case LFUN_INSET_APPLY: {
1555 BOOST_ASSERT(lyx_view_);
1556 string const name = cmd.getArg(0);
1557 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1559 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1560 inset->dispatch(view()->cursor(), fr);
1562 FuncRequest fr(LFUN_INSET_INSERT, argument);
1565 // ideally, the update flag should be set by the insets,
1566 // but this is not possible currently
1567 updateFlags = Update::Force | Update::FitCursor;
1571 case LFUN_ALL_INSETS_TOGGLE: {
1572 BOOST_ASSERT(lyx_view_);
1574 string const name = split(argument, action, ' ');
1575 Inset::Code const inset_code =
1576 Inset::translate(name);
1578 Cursor & cur = view()->cursor();
1579 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1581 Inset & inset = lyx_view_->buffer()->inset();
1582 InsetIterator it = inset_iterator_begin(inset);
1583 InsetIterator const end = inset_iterator_end(inset);
1584 for (; it != end; ++it) {
1585 if (!it->asInsetMath()
1586 && (inset_code == Inset::NO_CODE
1587 || inset_code == it->lyxCode())) {
1588 Cursor tmpcur = cur;
1589 tmpcur.pushLeft(*it);
1590 it->dispatch(tmpcur, fr);
1593 updateFlags = Update::Force | Update::FitCursor;
1597 case LFUN_BUFFER_LANGUAGE: {
1598 BOOST_ASSERT(lyx_view_);
1599 Buffer & buffer = *lyx_view_->buffer();
1600 Language const * oldL = buffer.params().language;
1601 Language const * newL = languages.getLanguage(argument);
1602 if (!newL || oldL == newL)
1605 if (oldL->rightToLeft() == newL->rightToLeft()
1606 && !buffer.isMultiLingual())
1607 buffer.changeLanguage(oldL, newL);
1611 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1612 string const fname =
1613 addName(addPath(package().user_support().absFilename(), "templates/"),
1615 Buffer defaults(fname);
1617 istringstream ss(argument);
1620 int const unknown_tokens = defaults.readHeader(lex);
1622 if (unknown_tokens != 0) {
1623 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1624 << unknown_tokens << " unknown token"
1625 << (unknown_tokens == 1 ? "" : "s")
1629 if (defaults.writeFile(FileName(defaults.fileName())))
1630 setMessage(bformat(_("Document defaults saved in %1$s"),
1631 makeDisplayPath(fname)));
1633 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1637 case LFUN_BUFFER_PARAMS_APPLY: {
1638 BOOST_ASSERT(lyx_view_);
1639 biblio::CiteEngine const engine =
1640 lyx_view_->buffer()->params().getEngine();
1642 istringstream ss(argument);
1645 int const unknown_tokens =
1646 lyx_view_->buffer()->readHeader(lex);
1648 if (unknown_tokens != 0) {
1649 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1650 << unknown_tokens << " unknown token"
1651 << (unknown_tokens == 1 ? "" : "s")
1654 if (engine == lyx_view_->buffer()->params().getEngine())
1657 Cursor & cur = view()->cursor();
1658 FuncRequest fr(LFUN_INSET_REFRESH);
1660 Inset & inset = lyx_view_->buffer()->inset();
1661 InsetIterator it = inset_iterator_begin(inset);
1662 InsetIterator const end = inset_iterator_end(inset);
1663 for (; it != end; ++it)
1664 if (it->lyxCode() == Inset::CITE_CODE)
1665 it->dispatch(cur, fr);
1669 case LFUN_TEXTCLASS_APPLY: {
1670 BOOST_ASSERT(lyx_view_);
1671 Buffer * buffer = lyx_view_->buffer();
1673 textclass_type const old_class =
1674 buffer->params().textclass;
1676 loadTextclass(argument);
1678 std::pair<bool, textclass_type> const tc_pair =
1679 textclasslist.numberOfClass(argument);
1684 textclass_type const new_class = tc_pair.second;
1685 if (old_class == new_class)
1689 lyx_view_->message(_("Converting document to new document class..."));
1690 recordUndoFullDocument(view());
1691 buffer->params().textclass = new_class;
1692 StableDocIterator backcur(view()->cursor());
1693 ErrorList & el = buffer->errorList("Class Switch");
1694 cap::switchBetweenClasses(
1695 old_class, new_class,
1696 static_cast<InsetText &>(buffer->inset()), el);
1698 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1700 buffer->errors("Class Switch");
1701 updateLabels(*buffer);
1702 updateFlags = Update::Force | Update::FitCursor;
1706 case LFUN_TEXTCLASS_LOAD:
1707 loadTextclass(argument);
1710 case LFUN_LYXRC_APPLY: {
1711 LyXRC const lyxrc_orig = lyxrc;
1713 istringstream ss(argument);
1714 bool const success = lyxrc.read(ss) == 0;
1717 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1718 << "Unable to read lyxrc data"
1723 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1725 /// We force the redraw in any case because there might be
1726 /// some screen font changes.
1727 /// FIXME: only the current view will be updated. the Gui
1728 /// class is able to furnish the list of views.
1729 updateFlags = Update::Force;
1733 case LFUN_WINDOW_NEW:
1734 LyX::ref().newLyXView();
1737 case LFUN_WINDOW_CLOSE:
1738 BOOST_ASSERT(lyx_view_);
1739 BOOST_ASSERT(theApp());
1740 // update bookmark pit of the current buffer before window close
1741 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1742 gotoBookmark(i+1, false, false);
1743 // ask the user for saving changes or cancel quit
1744 if (!theBufferList().quitWriteAll())
1749 case LFUN_BOOKMARK_GOTO:
1750 // go to bookmark, open unopened file and switch to buffer if necessary
1751 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1754 case LFUN_BOOKMARK_CLEAR:
1755 LyX::ref().session().bookmarks().clear();
1758 case LFUN_TOOLBAR_TOGGLE: {
1759 BOOST_ASSERT(lyx_view_);
1760 string const name = cmd.getArg(0);
1761 bool const allowauto = cmd.getArg(1) == "allowauto";
1762 lyx_view_->toggleToolbarState(name, allowauto);
1763 ToolbarInfo::Flags const flags =
1764 lyx_view_->getToolbarState(name);
1766 if (flags & ToolbarInfo::ON)
1768 else if (flags & ToolbarInfo::OFF)
1770 else if (flags & ToolbarInfo::AUTO)
1773 setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"),
1774 from_ascii(name), state));
1779 BOOST_ASSERT(lyx_view_);
1780 view()->cursor().dispatch(cmd);
1781 updateFlags = view()->cursor().result().update();
1782 if (!view()->cursor().result().dispatched())
1783 updateFlags = view()->dispatch(cmd);
1788 if (lyx_view_ && view()->buffer()) {
1789 // BufferView::update() updates the ViewMetricsInfo and
1790 // also initializes the position cache for all insets in
1791 // (at least partially) visible top-level paragraphs.
1792 // We will redraw the screen only if needed.
1793 if (view()->update(updateFlags)) {
1794 // Buffer::changed() signals that a repaint is needed.
1795 // The frontend (WorkArea) knows which area to repaint
1796 // thanks to the ViewMetricsInfo updated above.
1797 view()->buffer()->changed();
1800 lyx_view_->updateStatusBar();
1802 // if we executed a mutating lfun, mark the buffer as dirty
1804 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1805 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1806 view()->buffer()->markDirty();
1808 if (view()->cursor().inTexted()) {
1809 lyx_view_->updateLayoutChoice();
1814 lyx_view_->updateMenubar();
1815 lyx_view_->updateToolbars();
1816 // Some messages may already be translated, so we cannot use _()
1817 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1822 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1824 const bool verbose = (cmd.origin == FuncRequest::MENU
1825 || cmd.origin == FuncRequest::TOOLBAR
1826 || cmd.origin == FuncRequest::COMMANDBUFFER);
1828 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1829 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1831 lyx_view_->message(msg);
1835 docstring dispatch_msg = msg;
1836 if (!dispatch_msg.empty())
1837 dispatch_msg += ' ';
1839 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1841 bool argsadded = false;
1843 if (!cmd.argument().empty()) {
1844 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1845 comname += ' ' + cmd.argument();
1850 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1852 if (!shortcuts.empty())
1853 comname += ": " + shortcuts;
1854 else if (!argsadded && !cmd.argument().empty())
1855 comname += ' ' + cmd.argument();
1857 if (!comname.empty()) {
1858 comname = rtrim(comname);
1859 dispatch_msg += '(' + rtrim(comname) + ')';
1862 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1863 << to_utf8(dispatch_msg) << endl;
1864 if (!dispatch_msg.empty())
1865 lyx_view_->message(dispatch_msg);
1869 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1871 // FIXME: initpath is not used. What to do?
1872 string initpath = lyxrc.document_path;
1873 string filename(name);
1875 if (view()->buffer()) {
1876 string const trypath = lyx_view_->buffer()->filePath();
1877 // If directory is writeable, use this as default.
1878 if (isDirWriteable(FileName(trypath)))
1882 static int newfile_number;
1884 if (filename.empty()) {
1885 filename = addName(lyxrc.document_path,
1886 "newfile" + convert<string>(++newfile_number) + ".lyx");
1887 while (theBufferList().exists(filename) ||
1888 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1890 filename = addName(lyxrc.document_path,
1891 "newfile" + convert<string>(newfile_number) +
1896 // The template stuff
1899 FileDialog fileDlg(_("Select template file"),
1900 LFUN_SELECT_FILE_SYNC,
1901 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1902 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1904 FileDialog::Result result =
1905 fileDlg.open(from_utf8(lyxrc.template_path),
1906 FileFilterList(_("LyX Documents (*.lyx)")),
1909 if (result.first == FileDialog::Later)
1911 if (result.second.empty())
1913 templname = to_utf8(result.second);
1916 Buffer * const b = newFile(filename, templname, !name.empty());
1919 lyx_view_->setBuffer(b);
1924 void LyXFunc::open(string const & fname)
1926 string initpath = lyxrc.document_path;
1928 if (view()->buffer()) {
1929 string const trypath = lyx_view_->buffer()->filePath();
1930 // If directory is writeable, use this as default.
1931 if (isDirWriteable(FileName(trypath)))
1937 if (fname.empty()) {
1938 FileDialog fileDlg(_("Select document to open"),
1940 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1941 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1943 FileDialog::Result result =
1944 fileDlg.open(from_utf8(initpath),
1945 FileFilterList(_("LyX Documents (*.lyx)")),
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."));
1961 // get absolute path of file and add ".lyx" to the filename if
1963 FileName const fullname = fileSearch(string(), filename, "lyx");
1964 if (!fullname.empty())
1965 filename = fullname.absFilename();
1967 // if the file doesn't exist, let the user create one
1968 if (!fs::exists(fullname.toFilesystemEncoding())) {
1969 // the user specifically chose this name. Believe him.
1970 Buffer * const b = newFile(filename, string(), true);
1972 lyx_view_->setBuffer(b);
1976 docstring const disp_fn = makeDisplayPath(filename);
1977 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1980 if (lyx_view_->loadLyXFile(fullname)) {
1981 str2 = bformat(_("Document %1$s opened."), disp_fn);
1983 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1985 lyx_view_->message(str2);
1989 void LyXFunc::doImport(string const & argument)
1992 string filename = split(argument, format, ' ');
1994 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1995 << " file: " << filename << endl;
1997 // need user interaction
1998 if (filename.empty()) {
1999 string initpath = lyxrc.document_path;
2001 if (view()->buffer()) {
2002 string const trypath = lyx_view_->buffer()->filePath();
2003 // If directory is writeable, use this as default.
2004 if (isDirWriteable(FileName(trypath)))
2008 docstring const text = bformat(_("Select %1$s file to import"),
2009 formats.prettyName(format));
2011 FileDialog fileDlg(text,
2013 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2014 make_pair(_("Examples|#E#e"),
2015 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2017 docstring filter = formats.prettyName(format);
2020 filter += from_utf8(formats.extension(format));
2023 FileDialog::Result result =
2024 fileDlg.open(from_utf8(initpath),
2025 FileFilterList(filter),
2028 if (result.first == FileDialog::Later)
2031 filename = to_utf8(result.second);
2033 // check selected filename
2034 if (filename.empty())
2035 lyx_view_->message(_("Canceled."));
2038 if (filename.empty())
2041 // get absolute path of file
2042 FileName const fullname(makeAbsPath(filename));
2044 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2046 // Check if the document already is open
2047 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2048 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2049 lyx_view_->message(_("Canceled."));
2054 // if the file exists already, and we didn't do
2055 // -i lyx thefile.lyx, warn
2056 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2057 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2059 docstring text = bformat(_("The document %1$s already exists.\n\n"
2060 "Do you want to over-write that document?"), file);
2061 int const ret = Alert::prompt(_("Over-write document?"),
2062 text, 0, 1, _("&Over-write"), _("&Cancel"));
2065 lyx_view_->message(_("Canceled."));
2070 ErrorList errorList;
2071 Importer::Import(lyx_view_, fullname, format, errorList);
2072 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2076 void LyXFunc::closeBuffer()
2078 // goto bookmark to update bookmark pit.
2079 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2080 gotoBookmark(i+1, false, false);
2081 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2082 if (theBufferList().empty()) {
2083 // need this otherwise SEGV may occur while
2084 // trying to set variables that don't exist
2085 // since there's no current buffer
2086 lyx_view_->getDialogs().hideBufferDependent();
2088 lyx_view_->setBuffer(theBufferList().first());
2094 void LyXFunc::reloadBuffer()
2096 FileName filename(lyx_view_->buffer()->fileName());
2098 lyx_view_->loadLyXFile(filename);
2101 // Each "lyx_view_" should have it's own message method. lyxview and
2102 // the minibuffer would use the minibuffer, but lyxserver would
2103 // send an ERROR signal to its client. Alejandro 970603
2104 // This function is bit problematic when it comes to NLS, to make the
2105 // lyx servers client be language indepenent we must not translate
2106 // strings sent to this func.
2107 void LyXFunc::setErrorMessage(docstring const & m) const
2109 dispatch_buffer = m;
2114 void LyXFunc::setMessage(docstring const & m) const
2116 dispatch_buffer = m;
2120 docstring const LyXFunc::viewStatusMessage()
2122 // When meta-fake key is pressed, show the key sequence so far + "M-".
2124 return keyseq->print(true) + "M-";
2126 // Else, when a non-complete key sequence is pressed,
2127 // show the available options.
2128 if (keyseq->length() > 0 && !keyseq->deleted())
2129 return keyseq->printOptions(true);
2131 if (!view()->buffer())
2132 return _("Welcome to LyX!");
2134 return view()->cursor().currentState();
2138 BufferView * LyXFunc::view() const
2140 BOOST_ASSERT(lyx_view_);
2141 return lyx_view_->view();
2145 bool LyXFunc::wasMetaKey() const
2147 return (meta_fake_bit != key_modifier::none);
2153 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2155 // Why the switch you might ask. It is a trick to ensure that all
2156 // the elements in the LyXRCTags enum is handled. As you can see
2157 // there are no breaks at all. So it is just a huge fall-through.
2158 // The nice thing is that we will get a warning from the compiler
2159 // if we forget an element.
2160 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2162 case LyXRC::RC_ACCEPT_COMPOUND:
2163 case LyXRC::RC_ALT_LANG:
2164 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2165 case LyXRC::RC_PLAINTEXT_LINELEN:
2166 case LyXRC::RC_AUTOREGIONDELETE:
2167 case LyXRC::RC_AUTORESET_OPTIONS:
2168 case LyXRC::RC_AUTOSAVE:
2169 case LyXRC::RC_AUTO_NUMBER:
2170 case LyXRC::RC_BACKUPDIR_PATH:
2171 case LyXRC::RC_BIBTEX_COMMAND:
2172 case LyXRC::RC_BINDFILE:
2173 case LyXRC::RC_CHECKLASTFILES:
2174 case LyXRC::RC_USELASTFILEPOS:
2175 case LyXRC::RC_LOADSESSION:
2176 case LyXRC::RC_CHKTEX_COMMAND:
2177 case LyXRC::RC_CONVERTER:
2178 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2179 case LyXRC::RC_COPIER:
2180 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2181 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2182 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2183 case LyXRC::RC_DATE_INSERT_FORMAT:
2184 case LyXRC::RC_DEFAULT_LANGUAGE:
2185 case LyXRC::RC_DEFAULT_PAPERSIZE:
2186 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2187 case LyXRC::RC_DISPLAY_GRAPHICS:
2188 case LyXRC::RC_DOCUMENTPATH:
2189 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2190 string const encoded = FileName(
2191 lyxrc_new.document_path).toFilesystemEncoding();
2192 if (fs::exists(encoded) && fs::is_directory(encoded))
2193 support::package().document_dir() = FileName(lyxrc.document_path);
2195 case LyXRC::RC_ESC_CHARS:
2196 case LyXRC::RC_FONT_ENCODING:
2197 case LyXRC::RC_FORMAT:
2198 case LyXRC::RC_INDEX_COMMAND:
2199 case LyXRC::RC_INPUT:
2200 case LyXRC::RC_KBMAP:
2201 case LyXRC::RC_KBMAP_PRIMARY:
2202 case LyXRC::RC_KBMAP_SECONDARY:
2203 case LyXRC::RC_LABEL_INIT_LENGTH:
2204 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2205 case LyXRC::RC_LANGUAGE_AUTO_END:
2206 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2207 case LyXRC::RC_LANGUAGE_COMMAND_END:
2208 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2209 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2210 case LyXRC::RC_LANGUAGE_PACKAGE:
2211 case LyXRC::RC_LANGUAGE_USE_BABEL:
2212 case LyXRC::RC_MAKE_BACKUP:
2213 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2214 case LyXRC::RC_NUMLASTFILES:
2215 case LyXRC::RC_PATH_PREFIX:
2216 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2217 support::prependEnvPath("PATH", lyxrc.path_prefix);
2219 case LyXRC::RC_PERS_DICT:
2220 case LyXRC::RC_PREVIEW:
2221 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2222 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2223 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2224 case LyXRC::RC_PRINTCOPIESFLAG:
2225 case LyXRC::RC_PRINTER:
2226 case LyXRC::RC_PRINTEVENPAGEFLAG:
2227 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2228 case LyXRC::RC_PRINTFILEEXTENSION:
2229 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2230 case LyXRC::RC_PRINTODDPAGEFLAG:
2231 case LyXRC::RC_PRINTPAGERANGEFLAG:
2232 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2233 case LyXRC::RC_PRINTPAPERFLAG:
2234 case LyXRC::RC_PRINTREVERSEFLAG:
2235 case LyXRC::RC_PRINTSPOOL_COMMAND:
2236 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2237 case LyXRC::RC_PRINTTOFILE:
2238 case LyXRC::RC_PRINTTOPRINTER:
2239 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2240 case LyXRC::RC_PRINT_COMMAND:
2241 case LyXRC::RC_RTL_SUPPORT:
2242 case LyXRC::RC_SCREEN_DPI:
2243 case LyXRC::RC_SCREEN_FONT_ROMAN:
2244 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2245 case LyXRC::RC_SCREEN_FONT_SANS:
2246 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2247 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2248 case LyXRC::RC_SCREEN_FONT_SIZES:
2249 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2250 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2251 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2252 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2253 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2254 case LyXRC::RC_SCREEN_ZOOM:
2255 case LyXRC::RC_SERVERPIPE:
2256 case LyXRC::RC_SET_COLOR:
2257 case LyXRC::RC_SHOW_BANNER:
2258 case LyXRC::RC_SPELL_COMMAND:
2259 case LyXRC::RC_TEMPDIRPATH:
2260 case LyXRC::RC_TEMPLATEPATH:
2261 case LyXRC::RC_TEX_ALLOWS_SPACES:
2262 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2263 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2264 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2266 case LyXRC::RC_UIFILE:
2267 case LyXRC::RC_USER_EMAIL:
2268 case LyXRC::RC_USER_NAME:
2269 case LyXRC::RC_USETEMPDIR:
2270 case LyXRC::RC_USE_ALT_LANG:
2271 case LyXRC::RC_USE_CONVERTER_CACHE:
2272 case LyXRC::RC_USE_ESC_CHARS:
2273 case LyXRC::RC_USE_INP_ENC:
2274 case LyXRC::RC_USE_PERS_DICT:
2275 case LyXRC::RC_USE_SPELL_LIB:
2276 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2277 case LyXRC::RC_VIEWER:
2278 case LyXRC::RC_LAST: