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 overwrite that file?"),
1077 makeDisplayPath(filename.absFilename()));
1078 if (Alert::prompt(_("Overwrite file?"),
1079 text, 0, 1, _("&Overwrite"), _("&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 // takes an optional argument, "|bool", at the end
1414 // indicating whether this file is being opened automatically
1415 // by LyX itself, in which case we will not want to switch
1416 // buffers after opening. The default is false, so in practice
1417 // it is used only when true.
1418 BOOST_ASSERT(lyx_view_);
1419 int const arglength = argument.length();
1421 bool autoOpen = false;
1422 if (argument.substr(arglength - 5, 5) == "|true") {
1424 filename = makeAbsPath(argument.substr(0, arglength - 5),
1425 lyx_view_->buffer()->filePath());
1426 } else if (argument.substr(arglength - 6, 6) == "|false") {
1427 filename = makeAbsPath(argument.substr(0, arglength - 6),
1428 lyx_view_->buffer()->filePath());
1430 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1431 view()->saveBookmark(false);
1432 if (theBufferList().exists(filename.absFilename())) {
1433 Buffer * buf = theBufferList().getBuffer(filename.absFilename());
1435 lyx_view_->setBuffer(buf, true);
1437 buf->setParentName(lyx_view_->buffer()->fileName());
1439 lyx_view_->loadLyXFile(filename, true, true, autoOpen);
1444 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1445 BOOST_ASSERT(lyx_view_);
1446 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1449 case LFUN_KEYMAP_OFF:
1450 BOOST_ASSERT(lyx_view_);
1451 lyx_view_->view()->getIntl().keyMapOn(false);
1454 case LFUN_KEYMAP_PRIMARY:
1455 BOOST_ASSERT(lyx_view_);
1456 lyx_view_->view()->getIntl().keyMapPrim();
1459 case LFUN_KEYMAP_SECONDARY:
1460 BOOST_ASSERT(lyx_view_);
1461 lyx_view_->view()->getIntl().keyMapSec();
1464 case LFUN_KEYMAP_TOGGLE:
1465 BOOST_ASSERT(lyx_view_);
1466 lyx_view_->view()->getIntl().toggleKeyMap();
1472 string rest = split(argument, countstr, ' ');
1473 istringstream is(countstr);
1476 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1477 for (int i = 0; i < count; ++i)
1478 dispatch(lyxaction.lookupFunc(rest));
1482 case LFUN_COMMAND_SEQUENCE: {
1483 // argument contains ';'-terminated commands
1484 string arg = argument;
1485 while (!arg.empty()) {
1487 arg = split(arg, first, ';');
1488 FuncRequest func(lyxaction.lookupFunc(first));
1489 func.origin = cmd.origin;
1495 case LFUN_PREFERENCES_SAVE: {
1496 lyxrc.write(makeAbsPath("preferences",
1497 package().user_support().absFilename()),
1502 case LFUN_SCREEN_FONT_UPDATE:
1503 BOOST_ASSERT(lyx_view_);
1504 // handle the screen font changes.
1505 theFontLoader().update();
1506 /// FIXME: only the current view will be updated. the Gui
1507 /// class is able to furnish the list of views.
1508 updateFlags = Update::Force;
1511 case LFUN_SET_COLOR: {
1513 string const x11_name = split(argument, lyx_name, ' ');
1514 if (lyx_name.empty() || x11_name.empty()) {
1515 setErrorMessage(from_ascii(N_(
1516 "Syntax: set-color <lyx_name>"
1521 bool const graphicsbg_changed =
1522 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1523 x11_name != lcolor.getX11Name(Color::graphicsbg));
1525 if (!lcolor.setColor(lyx_name, x11_name)) {
1527 bformat(_("Set-color \"%1$s\" failed "
1528 "- color is undefined or "
1529 "may not be redefined"),
1530 from_utf8(lyx_name)));
1534 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1536 if (graphicsbg_changed) {
1537 #ifdef WITH_WARNINGS
1538 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1541 graphics::GCache::get().changeDisplay(true);
1548 BOOST_ASSERT(lyx_view_);
1549 lyx_view_->message(from_utf8(argument));
1552 case LFUN_EXTERNAL_EDIT: {
1553 BOOST_ASSERT(lyx_view_);
1554 FuncRequest fr(action, argument);
1555 InsetExternal().dispatch(view()->cursor(), fr);
1559 case LFUN_GRAPHICS_EDIT: {
1560 FuncRequest fr(action, argument);
1561 InsetGraphics().dispatch(view()->cursor(), fr);
1565 case LFUN_INSET_APPLY: {
1566 BOOST_ASSERT(lyx_view_);
1567 string const name = cmd.getArg(0);
1568 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1570 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1571 inset->dispatch(view()->cursor(), fr);
1573 FuncRequest fr(LFUN_INSET_INSERT, argument);
1576 // ideally, the update flag should be set by the insets,
1577 // but this is not possible currently
1578 updateFlags = Update::Force | Update::FitCursor;
1582 case LFUN_ALL_INSETS_TOGGLE: {
1583 BOOST_ASSERT(lyx_view_);
1585 string const name = split(argument, action, ' ');
1586 Inset::Code const inset_code =
1587 Inset::translate(name);
1589 Cursor & cur = view()->cursor();
1590 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1592 Inset & inset = lyx_view_->buffer()->inset();
1593 InsetIterator it = inset_iterator_begin(inset);
1594 InsetIterator const end = inset_iterator_end(inset);
1595 for (; it != end; ++it) {
1596 if (!it->asInsetMath()
1597 && (inset_code == Inset::NO_CODE
1598 || inset_code == it->lyxCode())) {
1599 Cursor tmpcur = cur;
1600 tmpcur.pushLeft(*it);
1601 it->dispatch(tmpcur, fr);
1604 updateFlags = Update::Force | Update::FitCursor;
1608 case LFUN_BUFFER_LANGUAGE: {
1609 BOOST_ASSERT(lyx_view_);
1610 Buffer & buffer = *lyx_view_->buffer();
1611 Language const * oldL = buffer.params().language;
1612 Language const * newL = languages.getLanguage(argument);
1613 if (!newL || oldL == newL)
1616 if (oldL->rightToLeft() == newL->rightToLeft()
1617 && !buffer.isMultiLingual())
1618 buffer.changeLanguage(oldL, newL);
1622 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1623 string const fname =
1624 addName(addPath(package().user_support().absFilename(), "templates/"),
1626 Buffer defaults(fname);
1628 istringstream ss(argument);
1631 int const unknown_tokens = defaults.readHeader(lex);
1633 if (unknown_tokens != 0) {
1634 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1635 << unknown_tokens << " unknown token"
1636 << (unknown_tokens == 1 ? "" : "s")
1640 if (defaults.writeFile(FileName(defaults.fileName())))
1641 setMessage(bformat(_("Document defaults saved in %1$s"),
1642 makeDisplayPath(fname)));
1644 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1648 case LFUN_BUFFER_PARAMS_APPLY: {
1649 BOOST_ASSERT(lyx_view_);
1650 biblio::CiteEngine const engine =
1651 lyx_view_->buffer()->params().getEngine();
1653 istringstream ss(argument);
1656 int const unknown_tokens =
1657 lyx_view_->buffer()->readHeader(lex);
1659 if (unknown_tokens != 0) {
1660 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1661 << unknown_tokens << " unknown token"
1662 << (unknown_tokens == 1 ? "" : "s")
1665 if (engine == lyx_view_->buffer()->params().getEngine())
1668 Cursor & cur = view()->cursor();
1669 FuncRequest fr(LFUN_INSET_REFRESH);
1671 Inset & inset = lyx_view_->buffer()->inset();
1672 InsetIterator it = inset_iterator_begin(inset);
1673 InsetIterator const end = inset_iterator_end(inset);
1674 for (; it != end; ++it)
1675 if (it->lyxCode() == Inset::CITE_CODE)
1676 it->dispatch(cur, fr);
1680 case LFUN_TEXTCLASS_APPLY: {
1681 BOOST_ASSERT(lyx_view_);
1682 Buffer * buffer = lyx_view_->buffer();
1684 textclass_type const old_class =
1685 buffer->params().textclass;
1687 loadTextclass(argument);
1689 std::pair<bool, textclass_type> const tc_pair =
1690 textclasslist.numberOfClass(argument);
1695 textclass_type const new_class = tc_pair.second;
1696 if (old_class == new_class)
1700 lyx_view_->message(_("Converting document to new document class..."));
1701 recordUndoFullDocument(view());
1702 buffer->params().textclass = new_class;
1703 StableDocIterator backcur(view()->cursor());
1704 ErrorList & el = buffer->errorList("Class Switch");
1705 cap::switchBetweenClasses(
1706 old_class, new_class,
1707 static_cast<InsetText &>(buffer->inset()), el);
1709 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1711 buffer->errors("Class Switch");
1712 updateLabels(*buffer);
1713 updateFlags = Update::Force | Update::FitCursor;
1717 case LFUN_TEXTCLASS_LOAD:
1718 loadTextclass(argument);
1721 case LFUN_LYXRC_APPLY: {
1722 LyXRC const lyxrc_orig = lyxrc;
1724 istringstream ss(argument);
1725 bool const success = lyxrc.read(ss) == 0;
1728 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1729 << "Unable to read lyxrc data"
1734 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1736 /// We force the redraw in any case because there might be
1737 /// some screen font changes.
1738 /// FIXME: only the current view will be updated. the Gui
1739 /// class is able to furnish the list of views.
1740 updateFlags = Update::Force;
1744 case LFUN_WINDOW_NEW:
1745 LyX::ref().newLyXView();
1748 case LFUN_WINDOW_CLOSE:
1749 BOOST_ASSERT(lyx_view_);
1750 BOOST_ASSERT(theApp());
1751 // update bookmark pit of the current buffer before window close
1752 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1753 gotoBookmark(i+1, false, false);
1754 // ask the user for saving changes or cancel quit
1755 if (!theBufferList().quitWriteAll())
1760 case LFUN_BOOKMARK_GOTO:
1761 // go to bookmark, open unopened file and switch to buffer if necessary
1762 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1765 case LFUN_BOOKMARK_CLEAR:
1766 LyX::ref().session().bookmarks().clear();
1769 case LFUN_TOOLBAR_TOGGLE: {
1770 BOOST_ASSERT(lyx_view_);
1771 string const name = cmd.getArg(0);
1772 bool const allowauto = cmd.getArg(1) == "allowauto";
1773 lyx_view_->toggleToolbarState(name, allowauto);
1774 ToolbarInfo::Flags const flags =
1775 lyx_view_->getToolbarState(name);
1777 if (flags & ToolbarInfo::ON)
1779 else if (flags & ToolbarInfo::OFF)
1781 else if (flags & ToolbarInfo::AUTO)
1784 setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"),
1785 from_ascii(name), state));
1790 BOOST_ASSERT(lyx_view_);
1791 view()->cursor().dispatch(cmd);
1792 updateFlags = view()->cursor().result().update();
1793 if (!view()->cursor().result().dispatched())
1794 updateFlags = view()->dispatch(cmd);
1799 if (lyx_view_ && view()->buffer()) {
1800 // BufferView::update() updates the ViewMetricsInfo and
1801 // also initializes the position cache for all insets in
1802 // (at least partially) visible top-level paragraphs.
1803 // We will redraw the screen only if needed.
1804 if (view()->update(updateFlags)) {
1805 // Buffer::changed() signals that a repaint is needed.
1806 // The frontend (WorkArea) knows which area to repaint
1807 // thanks to the ViewMetricsInfo updated above.
1808 view()->buffer()->changed();
1811 lyx_view_->updateStatusBar();
1813 // if we executed a mutating lfun, mark the buffer as dirty
1815 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1816 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1817 view()->buffer()->markDirty();
1819 if (view()->cursor().inTexted()) {
1820 lyx_view_->updateLayoutChoice();
1825 lyx_view_->updateMenubar();
1826 lyx_view_->updateToolbars();
1827 // Some messages may already be translated, so we cannot use _()
1828 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1833 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1835 const bool verbose = (cmd.origin == FuncRequest::MENU
1836 || cmd.origin == FuncRequest::TOOLBAR
1837 || cmd.origin == FuncRequest::COMMANDBUFFER);
1839 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1840 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1842 lyx_view_->message(msg);
1846 docstring dispatch_msg = msg;
1847 if (!dispatch_msg.empty())
1848 dispatch_msg += ' ';
1850 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1852 bool argsadded = false;
1854 if (!cmd.argument().empty()) {
1855 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1856 comname += ' ' + cmd.argument();
1861 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1863 if (!shortcuts.empty())
1864 comname += ": " + shortcuts;
1865 else if (!argsadded && !cmd.argument().empty())
1866 comname += ' ' + cmd.argument();
1868 if (!comname.empty()) {
1869 comname = rtrim(comname);
1870 dispatch_msg += '(' + rtrim(comname) + ')';
1873 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1874 << to_utf8(dispatch_msg) << endl;
1875 if (!dispatch_msg.empty())
1876 lyx_view_->message(dispatch_msg);
1880 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1882 // FIXME: initpath is not used. What to do?
1883 string initpath = lyxrc.document_path;
1884 string filename(name);
1886 if (view()->buffer()) {
1887 string const trypath = lyx_view_->buffer()->filePath();
1888 // If directory is writeable, use this as default.
1889 if (isDirWriteable(FileName(trypath)))
1893 static int newfile_number;
1895 if (filename.empty()) {
1896 filename = addName(lyxrc.document_path,
1897 "newfile" + convert<string>(++newfile_number) + ".lyx");
1898 while (theBufferList().exists(filename) ||
1899 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1901 filename = addName(lyxrc.document_path,
1902 "newfile" + convert<string>(newfile_number) +
1907 // The template stuff
1910 FileDialog fileDlg(_("Select template file"),
1911 LFUN_SELECT_FILE_SYNC,
1912 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1913 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1915 FileDialog::Result result =
1916 fileDlg.open(from_utf8(lyxrc.template_path),
1917 FileFilterList(_("LyX Documents (*.lyx)")),
1920 if (result.first == FileDialog::Later)
1922 if (result.second.empty())
1924 templname = to_utf8(result.second);
1927 Buffer * const b = newFile(filename, templname, !name.empty());
1930 lyx_view_->setBuffer(b);
1935 void LyXFunc::open(string const & fname)
1937 string initpath = lyxrc.document_path;
1939 if (view()->buffer()) {
1940 string const trypath = lyx_view_->buffer()->filePath();
1941 // If directory is writeable, use this as default.
1942 if (isDirWriteable(FileName(trypath)))
1948 if (fname.empty()) {
1949 FileDialog fileDlg(_("Select document to open"),
1951 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1952 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1954 FileDialog::Result result =
1955 fileDlg.open(from_utf8(initpath),
1956 FileFilterList(_("LyX Documents (*.lyx)")),
1959 if (result.first == FileDialog::Later)
1962 filename = to_utf8(result.second);
1964 // check selected filename
1965 if (filename.empty()) {
1966 lyx_view_->message(_("Canceled."));
1972 // get absolute path of file and add ".lyx" to the filename if
1974 FileName const fullname = fileSearch(string(), filename, "lyx");
1975 if (!fullname.empty())
1976 filename = fullname.absFilename();
1978 // if the file doesn't exist, let the user create one
1979 if (!fs::exists(fullname.toFilesystemEncoding())) {
1980 // the user specifically chose this name. Believe him.
1981 Buffer * const b = newFile(filename, string(), true);
1983 lyx_view_->setBuffer(b);
1987 docstring const disp_fn = makeDisplayPath(filename);
1988 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1991 if (lyx_view_->loadLyXFile(fullname)) {
1992 str2 = bformat(_("Document %1$s opened."), disp_fn);
1994 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1996 lyx_view_->message(str2);
2000 void LyXFunc::doImport(string const & argument)
2003 string filename = split(argument, format, ' ');
2005 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
2006 << " file: " << filename << endl;
2008 // need user interaction
2009 if (filename.empty()) {
2010 string initpath = lyxrc.document_path;
2012 if (view()->buffer()) {
2013 string const trypath = lyx_view_->buffer()->filePath();
2014 // If directory is writeable, use this as default.
2015 if (isDirWriteable(FileName(trypath)))
2019 docstring const text = bformat(_("Select %1$s file to import"),
2020 formats.prettyName(format));
2022 FileDialog fileDlg(text,
2024 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2025 make_pair(_("Examples|#E#e"),
2026 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2028 docstring filter = formats.prettyName(format);
2031 filter += from_utf8(formats.extension(format));
2034 FileDialog::Result result =
2035 fileDlg.open(from_utf8(initpath),
2036 FileFilterList(filter),
2039 if (result.first == FileDialog::Later)
2042 filename = to_utf8(result.second);
2044 // check selected filename
2045 if (filename.empty())
2046 lyx_view_->message(_("Canceled."));
2049 if (filename.empty())
2052 // get absolute path of file
2053 FileName const fullname(makeAbsPath(filename));
2055 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2057 // Check if the document already is open
2058 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2059 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2060 lyx_view_->message(_("Canceled."));
2065 // if the file exists already, and we didn't do
2066 // -i lyx thefile.lyx, warn
2067 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2068 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2070 docstring text = bformat(_("The document %1$s already exists.\n\n"
2071 "Do you want to overwrite that document?"), file);
2072 int const ret = Alert::prompt(_("Overwrite document?"),
2073 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2076 lyx_view_->message(_("Canceled."));
2081 ErrorList errorList;
2082 Importer::Import(lyx_view_, fullname, format, errorList);
2083 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2087 void LyXFunc::closeBuffer()
2089 // goto bookmark to update bookmark pit.
2090 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2091 gotoBookmark(i+1, false, false);
2092 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2093 if (theBufferList().empty()) {
2094 // need this otherwise SEGV may occur while
2095 // trying to set variables that don't exist
2096 // since there's no current buffer
2097 lyx_view_->getDialogs().hideBufferDependent();
2099 lyx_view_->setBuffer(theBufferList().first());
2105 void LyXFunc::reloadBuffer()
2107 FileName filename(lyx_view_->buffer()->fileName());
2109 lyx_view_->loadLyXFile(filename);
2112 // Each "lyx_view_" should have it's own message method. lyxview and
2113 // the minibuffer would use the minibuffer, but lyxserver would
2114 // send an ERROR signal to its client. Alejandro 970603
2115 // This function is bit problematic when it comes to NLS, to make the
2116 // lyx servers client be language indepenent we must not translate
2117 // strings sent to this func.
2118 void LyXFunc::setErrorMessage(docstring const & m) const
2120 dispatch_buffer = m;
2125 void LyXFunc::setMessage(docstring const & m) const
2127 dispatch_buffer = m;
2131 docstring const LyXFunc::viewStatusMessage()
2133 // When meta-fake key is pressed, show the key sequence so far + "M-".
2135 return keyseq->print(true) + "M-";
2137 // Else, when a non-complete key sequence is pressed,
2138 // show the available options.
2139 if (keyseq->length() > 0 && !keyseq->deleted())
2140 return keyseq->printOptions(true);
2142 if (!view()->buffer())
2143 return _("Welcome to LyX!");
2145 return view()->cursor().currentState();
2149 BufferView * LyXFunc::view() const
2151 BOOST_ASSERT(lyx_view_);
2152 return lyx_view_->view();
2156 bool LyXFunc::wasMetaKey() const
2158 return (meta_fake_bit != key_modifier::none);
2164 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2166 // Why the switch you might ask. It is a trick to ensure that all
2167 // the elements in the LyXRCTags enum is handled. As you can see
2168 // there are no breaks at all. So it is just a huge fall-through.
2169 // The nice thing is that we will get a warning from the compiler
2170 // if we forget an element.
2171 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2173 case LyXRC::RC_ACCEPT_COMPOUND:
2174 case LyXRC::RC_ALT_LANG:
2175 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2176 case LyXRC::RC_PLAINTEXT_LINELEN:
2177 case LyXRC::RC_AUTOREGIONDELETE:
2178 case LyXRC::RC_AUTORESET_OPTIONS:
2179 case LyXRC::RC_AUTOSAVE:
2180 case LyXRC::RC_AUTO_NUMBER:
2181 case LyXRC::RC_BACKUPDIR_PATH:
2182 case LyXRC::RC_BIBTEX_COMMAND:
2183 case LyXRC::RC_BINDFILE:
2184 case LyXRC::RC_CHECKLASTFILES:
2185 case LyXRC::RC_USELASTFILEPOS:
2186 case LyXRC::RC_LOADSESSION:
2187 case LyXRC::RC_CHKTEX_COMMAND:
2188 case LyXRC::RC_CONVERTER:
2189 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2190 case LyXRC::RC_COPIER:
2191 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2192 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2193 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2194 case LyXRC::RC_DATE_INSERT_FORMAT:
2195 case LyXRC::RC_DEFAULT_LANGUAGE:
2196 case LyXRC::RC_DEFAULT_PAPERSIZE:
2197 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2198 case LyXRC::RC_DISPLAY_GRAPHICS:
2199 case LyXRC::RC_DOCUMENTPATH:
2200 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2201 string const encoded = FileName(
2202 lyxrc_new.document_path).toFilesystemEncoding();
2203 if (fs::exists(encoded) && fs::is_directory(encoded))
2204 support::package().document_dir() = FileName(lyxrc.document_path);
2206 case LyXRC::RC_ESC_CHARS:
2207 case LyXRC::RC_FONT_ENCODING:
2208 case LyXRC::RC_FORMAT:
2209 case LyXRC::RC_INDEX_COMMAND:
2210 case LyXRC::RC_INPUT:
2211 case LyXRC::RC_KBMAP:
2212 case LyXRC::RC_KBMAP_PRIMARY:
2213 case LyXRC::RC_KBMAP_SECONDARY:
2214 case LyXRC::RC_LABEL_INIT_LENGTH:
2215 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2216 case LyXRC::RC_LANGUAGE_AUTO_END:
2217 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2218 case LyXRC::RC_LANGUAGE_COMMAND_END:
2219 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2220 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2221 case LyXRC::RC_LANGUAGE_PACKAGE:
2222 case LyXRC::RC_LANGUAGE_USE_BABEL:
2223 case LyXRC::RC_MAKE_BACKUP:
2224 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2225 case LyXRC::RC_NUMLASTFILES:
2226 case LyXRC::RC_PATH_PREFIX:
2227 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2228 support::prependEnvPath("PATH", lyxrc.path_prefix);
2230 case LyXRC::RC_PERS_DICT:
2231 case LyXRC::RC_PREVIEW:
2232 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2233 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2234 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2235 case LyXRC::RC_PRINTCOPIESFLAG:
2236 case LyXRC::RC_PRINTER:
2237 case LyXRC::RC_PRINTEVENPAGEFLAG:
2238 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2239 case LyXRC::RC_PRINTFILEEXTENSION:
2240 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2241 case LyXRC::RC_PRINTODDPAGEFLAG:
2242 case LyXRC::RC_PRINTPAGERANGEFLAG:
2243 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2244 case LyXRC::RC_PRINTPAPERFLAG:
2245 case LyXRC::RC_PRINTREVERSEFLAG:
2246 case LyXRC::RC_PRINTSPOOL_COMMAND:
2247 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2248 case LyXRC::RC_PRINTTOFILE:
2249 case LyXRC::RC_PRINTTOPRINTER:
2250 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2251 case LyXRC::RC_PRINT_COMMAND:
2252 case LyXRC::RC_RTL_SUPPORT:
2253 case LyXRC::RC_SCREEN_DPI:
2254 case LyXRC::RC_SCREEN_FONT_ROMAN:
2255 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2256 case LyXRC::RC_SCREEN_FONT_SANS:
2257 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2258 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2259 case LyXRC::RC_SCREEN_FONT_SIZES:
2260 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2261 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2262 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2263 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2264 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2265 case LyXRC::RC_SCREEN_ZOOM:
2266 case LyXRC::RC_SERVERPIPE:
2267 case LyXRC::RC_SET_COLOR:
2268 case LyXRC::RC_SHOW_BANNER:
2269 case LyXRC::RC_SPELL_COMMAND:
2270 case LyXRC::RC_TEMPDIRPATH:
2271 case LyXRC::RC_TEMPLATEPATH:
2272 case LyXRC::RC_TEX_ALLOWS_SPACES:
2273 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2274 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2275 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2277 case LyXRC::RC_UIFILE:
2278 case LyXRC::RC_USER_EMAIL:
2279 case LyXRC::RC_USER_NAME:
2280 case LyXRC::RC_USETEMPDIR:
2281 case LyXRC::RC_USE_ALT_LANG:
2282 case LyXRC::RC_USE_CONVERTER_CACHE:
2283 case LyXRC::RC_USE_ESC_CHARS:
2284 case LyXRC::RC_USE_INP_ENC:
2285 case LyXRC::RC_USE_PERS_DICT:
2286 case LyXRC::RC_USE_SPELL_LIB:
2287 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2288 case LyXRC::RC_VIEWER:
2289 case LyXRC::RC_LAST: