3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
24 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "BufferList.h"
28 #include "BufferParams.h"
29 #include "BufferView.h"
30 #include "bufferview_funcs.h"
32 #include "CutAndPaste.h"
34 #include "DispatchResult.h"
36 #include "ErrorList.h"
39 #include "FuncRequest.h"
40 #include "FuncStatus.h"
43 #include "InsetIterator.h"
51 #include "LyXAction.h"
57 #include "TextClassList.h"
59 #include "Paragraph.h"
60 #include "ParIterator.h"
61 #include "ParagraphParameters.h"
64 #include "insets/InsetBox.h"
65 #include "insets/InsetBranch.h"
66 #include "insets/InsetCommand.h"
67 #include "insets/InsetERT.h"
68 #include "insets/InsetExternal.h"
69 #include "insets/InsetFloat.h"
70 #include "insets/InsetListings.h"
71 #include "insets/InsetGraphics.h"
72 #include "insets/InsetInclude.h"
73 #include "insets/InsetNote.h"
74 #include "insets/InsetTabular.h"
75 #include "insets/InsetVSpace.h"
76 #include "insets/InsetWrap.h"
78 #include "frontends/Application.h"
79 #include "frontends/alert.h"
80 #include "frontends/Dialogs.h"
81 #include "frontends/FileDialog.h"
82 #include "frontends/FontLoader.h"
83 #include "frontends/Gui.h"
84 #include "frontends/KeySymbol.h"
85 #include "frontends/LyXView.h"
86 #include "frontends/Menubar.h"
87 #include "frontends/Toolbars.h"
89 #include "support/environment.h"
90 #include "support/FileFilterList.h"
91 #include "support/filetools.h"
92 #include "support/ForkedcallsController.h"
93 #include "support/fs_extras.h"
94 #include "support/lstrings.h"
95 #include "support/Path.h"
96 #include "support/Package.h"
97 #include "support/Systemcall.h"
98 #include "support/convert.h"
99 #include "support/os.h"
101 #include <boost/current_function.hpp>
102 #include <boost/filesystem/operations.hpp>
109 using bv_funcs::freefont2string;
111 using support::absolutePath;
112 using support::addName;
113 using support::addPath;
114 using support::bformat;
115 using support::changeExtension;
116 using support::contains;
117 using support::FileFilterList;
118 using support::FileName;
119 using support::fileSearch;
120 using support::ForkedcallsController;
121 using support::i18nLibFileSearch;
122 using support::isDirWriteable;
123 using support::isFileReadable;
124 using support::isStrInt;
125 using support::makeAbsPath;
126 using support::makeDisplayPath;
127 using support::package;
128 using support::quoteName;
129 using support::rtrim;
130 using support::split;
131 using support::subst;
132 using support::Systemcall;
133 using support::token;
135 using support::prefixIs;
138 using std::make_pair;
141 using std::istringstream;
142 using std::ostringstream;
144 namespace Alert = frontend::Alert;
145 namespace fs = boost::filesystem;
150 bool getLocalStatus(Cursor cursor,
151 FuncRequest const & cmd, FuncStatus & status)
153 // Try to fix cursor in case it is broken.
154 cursor.fixIfBroken();
156 // This is, of course, a mess. Better create a new doc iterator and use
157 // this in Inset::getStatus. This might require an additional
158 // BufferView * arg, though (which should be avoided)
159 //Cursor safe = *this;
161 for ( ; cursor.depth(); cursor.pop()) {
162 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
163 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
164 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
165 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
167 // The inset's getStatus() will return 'true' if it made
168 // a definitive decision on whether it want to handle the
169 // request or not. The result of this decision is put into
170 // the 'status' parameter.
171 if (cursor.inset().getStatus(cursor, cmd, status)) {
180 /** Return the change status at cursor position, taking in account the
181 * status at each level of the document iterator (a table in a deleted
182 * footnote is deleted).
183 * When \param outer is true, the top slice is not looked at.
185 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
187 size_t const depth = dit.depth() - (outer ? 1 : 0);
189 for (size_t i = 0 ; i < depth ; ++i) {
190 CursorSlice const & slice = dit[i];
191 if (!slice.inset().inMathed()
192 && slice.pos() < slice.paragraph().size()) {
193 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
194 if (ch != Change::UNCHANGED)
198 return Change::UNCHANGED;
206 meta_fake_bit(key_modifier::none)
211 void LyXFunc::initKeySequences(KeyMap * kb)
213 keyseq.reset(new KeySequence(kb, kb));
214 cancel_meta_seq.reset(new KeySequence(kb, kb));
218 void LyXFunc::setLyXView(LyXView * lv)
224 void LyXFunc::handleKeyFunc(kb_action action)
226 char_type c = encoded_last_key;
228 if (keyseq->length())
231 lyx_view_->view()->getIntl().getTransManager().deadkey(
232 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
233 // Need to clear, in case the minibuffer calls these
236 // copied verbatim from do_accent_char
237 view()->cursor().resetAnchor();
242 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
244 BOOST_ASSERT(lyx_view_);
245 if (!LyX::ref().session().bookmarks().isValid(idx))
247 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
248 BOOST_ASSERT(!bm.filename.empty());
249 string const file = bm.filename.absFilename();
250 // if the file is not opened, open it.
251 if (!theBufferList().exists(file)) {
253 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
257 // open may fail, so we need to test it again
258 if (theBufferList().exists(file)) {
259 // if the current buffer is not that one, switch to it.
260 if (lyx_view_->buffer()->fileName() != file) {
262 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
266 // moveToPosition use par_id, and par_pit and return new par_id.
270 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
271 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
272 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
273 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
274 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
279 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
281 LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
283 // Do nothing if we have nothing (JMarc)
284 if (!keysym->isOK()) {
285 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
290 if (keysym->isModifier()) {
291 LYXERR(Debug::KEY) << "isModifier true" << endl;
295 //Encoding const * encoding = view()->cursor().getEncoding();
296 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
297 // FIXME: encoded_last_key shadows the member variable of the same
298 // name. Is that intended?
299 char_type encoded_last_key = keysym->getUCSEncoded();
301 // Do a one-deep top-level lookup for
302 // cancel and meta-fake keys. RVDK_PATCH_5
303 cancel_meta_seq->reset();
305 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
306 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
307 << " action first set to [" << func.action << ']'
310 // When not cancel or meta-fake, do the normal lookup.
311 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
312 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
313 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
314 // remove Caps Lock and Mod2 as a modifiers
315 func = keyseq->addkey(keysym, (state | meta_fake_bit));
316 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
317 << "action now set to ["
318 << func.action << ']' << endl;
321 // Dont remove this unless you know what you are doing.
322 meta_fake_bit = key_modifier::none;
324 // Can this happen now ?
325 if (func.action == LFUN_NOACTION) {
326 func = FuncRequest(LFUN_COMMAND_PREFIX);
329 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
331 << func.action << "]["
332 << to_utf8(keyseq->print(false)) << ']'
335 // already here we know if it any point in going further
336 // why not return already here if action == -1 and
337 // num_bytes == 0? (Lgb)
339 if (keyseq->length() > 1) {
340 lyx_view_->message(keyseq->print(true));
344 // Maybe user can only reach the key via holding down shift.
345 // Let's see. But only if shift is the only modifier
346 if (func.action == LFUN_UNKNOWN_ACTION &&
347 state == key_modifier::shift) {
348 LYXERR(Debug::KEY) << "Trying without shift" << endl;
349 func = keyseq->addkey(keysym, key_modifier::none);
350 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
353 if (func.action == LFUN_UNKNOWN_ACTION) {
354 // Hmm, we didn't match any of the keysequences. See
355 // if it's normal insertable text not already covered
357 if (keysym->isText() && keyseq->length() == 1) {
358 LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
359 func = FuncRequest(LFUN_SELF_INSERT,
360 FuncRequest::KEYBOARD);
362 LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
363 lyx_view_->message(_("Unknown function."));
368 if (func.action == LFUN_SELF_INSERT) {
369 if (encoded_last_key != 0) {
370 docstring const arg(1, encoded_last_key);
371 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
372 FuncRequest::KEYBOARD));
374 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
382 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
384 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
387 Cursor & cur = view()->cursor();
389 /* In LyX/Mac, when a dialog is open, the menus of the
390 application can still be accessed without giving focus to
391 the main window. In this case, we want to disable the menu
392 entries that are buffer-related.
394 Note that this code is not perfect, as bug 1941 attests:
395 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
397 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
398 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
401 if (cmd.action == LFUN_NOACTION) {
402 flag.message(from_utf8(N_("Nothing to do")));
407 switch (cmd.action) {
408 case LFUN_UNKNOWN_ACTION:
409 #ifndef HAVE_LIBAIKSAURUS
410 case LFUN_THESAURUS_ENTRY:
420 if (flag.unknown()) {
421 flag.message(from_utf8(N_("Unknown action")));
425 if (!flag.enabled()) {
426 if (flag.message().empty())
427 flag.message(from_utf8(N_("Command disabled")));
431 // Check whether we need a buffer
432 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
434 flag.message(from_utf8(N_("Command not allowed with"
435 "out any document open")));
440 // I would really like to avoid having this switch and rather try to
441 // encode this in the function itself.
442 // -- And I'd rather let an inset decide which LFUNs it is willing
443 // to handle (Andre')
445 switch (cmd.action) {
446 case LFUN_BUFFER_TOGGLE_READ_ONLY:
447 flag.setOnOff(buf->isReadonly());
450 case LFUN_BUFFER_SWITCH:
451 // toggle on the current buffer, but do not toggle off
452 // the other ones (is that a good idea?)
453 if (to_utf8(cmd.argument()) == buf->fileName())
457 case LFUN_BUFFER_EXPORT:
458 enable = cmd.argument() == "custom"
459 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
462 case LFUN_BUFFER_CHKTEX:
463 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
466 case LFUN_BUILD_PROGRAM:
467 enable = Exporter::isExportable(*buf, "program");
470 case LFUN_LAYOUT_TABULAR:
471 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
475 case LFUN_LAYOUT_PARAGRAPH:
476 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
479 case LFUN_VC_REGISTER:
480 enable = !buf->lyxvc().inUse();
482 case LFUN_VC_CHECK_IN:
483 enable = buf->lyxvc().inUse() && !buf->isReadonly();
485 case LFUN_VC_CHECK_OUT:
486 enable = buf->lyxvc().inUse() && buf->isReadonly();
489 case LFUN_VC_UNDO_LAST:
490 enable = buf->lyxvc().inUse();
492 case LFUN_BUFFER_RELOAD:
493 enable = !buf->isUnnamed() && !buf->isClean();
496 case LFUN_INSET_SETTINGS: {
500 Inset::Code code = cur.inset().lyxCode();
502 case Inset::TABULAR_CODE:
503 enable = cmd.argument() == "tabular";
505 case Inset::ERT_CODE:
506 enable = cmd.argument() == "ert";
508 case Inset::FLOAT_CODE:
509 enable = cmd.argument() == "float";
511 case Inset::WRAP_CODE:
512 enable = cmd.argument() == "wrap";
514 case Inset::NOTE_CODE:
515 enable = cmd.argument() == "note";
517 case Inset::BRANCH_CODE:
518 enable = cmd.argument() == "branch";
520 case Inset::BOX_CODE:
521 enable = cmd.argument() == "box";
523 case Inset::LISTINGS_CODE:
524 enable = cmd.argument() == "listings";
532 case LFUN_INSET_APPLY: {
533 string const name = cmd.getArg(0);
534 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
536 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
538 if (!inset->getStatus(cur, fr, fs)) {
539 // Every inset is supposed to handle this
544 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
545 flag |= getStatus(fr);
547 enable = flag.enabled();
551 case LFUN_DIALOG_TOGGLE:
552 flag.setOnOff(lyx_view_->getDialogs().visible(cmd.getArg(0)));
553 // fall through to set "enable"
554 case LFUN_DIALOG_SHOW: {
555 string const name = cmd.getArg(0);
557 enable = name == "aboutlyx"
558 || name == "file" //FIXME: should be removed.
560 || name == "texinfo";
561 else if (name == "print")
562 enable = Exporter::isExportable(*buf, "dvi")
563 && lyxrc.print_command != "none";
564 else if (name == "character")
565 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
566 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
567 else if (name == "latexlog")
568 enable = isFileReadable(FileName(buf->getLogName().second));
569 else if (name == "spellchecker")
570 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
571 enable = !buf->isReadonly();
575 else if (name == "vclog")
576 enable = buf->lyxvc().inUse();
580 case LFUN_DIALOG_SHOW_NEW_INSET:
581 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
582 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
583 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
585 if (cur.inset().getStatus(cur, cmd, flag))
590 case LFUN_DIALOG_UPDATE: {
591 string const name = cmd.getArg(0);
593 enable = name == "prefs";
597 case LFUN_CITATION_INSERT: {
598 FuncRequest fr(LFUN_INSET_INSERT, "citation");
599 enable = getStatus(fr).enabled();
603 case LFUN_BUFFER_WRITE: {
604 enable = view()->buffer()->isUnnamed()
605 || !view()->buffer()->isClean();
609 case LFUN_BOOKMARK_GOTO: {
610 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
611 enable = LyX::ref().session().bookmarks().isValid(num);
615 case LFUN_BOOKMARK_CLEAR:
616 enable = LyX::ref().session().bookmarks().size() > 0;
619 case LFUN_TOOLBAR_TOGGLE_STATE: {
620 ToolbarInfo::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
621 if (!(flags & ToolbarInfo::AUTO))
622 flag.setOnOff(flags & ToolbarInfo::ON);
626 case LFUN_TOOLBAR_TOGGLE: {
627 bool const current = lyx_view_->getToolbars().visible(cmd.getArg(0));
628 flag.setOnOff(current);
631 // this one is difficult to get right. As a half-baked
632 // solution, we consider only the first action of the sequence
633 case LFUN_COMMAND_SEQUENCE: {
634 // argument contains ';'-terminated commands
635 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
636 FuncRequest func(lyxaction.lookupFunc(firstcmd));
637 func.origin = cmd.origin;
638 flag = getStatus(func);
641 case LFUN_BUFFER_NEW:
642 case LFUN_BUFFER_NEW_TEMPLATE:
643 case LFUN_WORD_FIND_FORWARD:
644 case LFUN_WORD_FIND_BACKWARD:
645 case LFUN_COMMAND_PREFIX:
646 case LFUN_COMMAND_EXECUTE:
648 case LFUN_META_PREFIX:
649 case LFUN_BUFFER_CLOSE:
650 case LFUN_BUFFER_WRITE_AS:
651 case LFUN_BUFFER_UPDATE:
652 case LFUN_BUFFER_VIEW:
653 case LFUN_BUFFER_IMPORT:
654 case LFUN_BUFFER_AUTO_SAVE:
655 case LFUN_RECONFIGURE:
659 case LFUN_DROP_LAYOUTS_CHOICE:
661 case LFUN_SERVER_GET_NAME:
662 case LFUN_SERVER_NOTIFY:
663 case LFUN_SERVER_GOTO_FILE_ROW:
664 case LFUN_DIALOG_HIDE:
665 case LFUN_DIALOG_DISCONNECT_INSET:
666 case LFUN_BUFFER_CHILD_OPEN:
667 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
668 case LFUN_KEYMAP_OFF:
669 case LFUN_KEYMAP_PRIMARY:
670 case LFUN_KEYMAP_SECONDARY:
671 case LFUN_KEYMAP_TOGGLE:
673 case LFUN_BUFFER_EXPORT_CUSTOM:
674 case LFUN_BUFFER_PRINT:
675 case LFUN_PREFERENCES_SAVE:
676 case LFUN_SCREEN_FONT_UPDATE:
679 case LFUN_EXTERNAL_EDIT:
680 case LFUN_GRAPHICS_EDIT:
681 case LFUN_ALL_INSETS_TOGGLE:
682 case LFUN_BUFFER_LANGUAGE:
683 case LFUN_TEXTCLASS_APPLY:
684 case LFUN_TEXTCLASS_LOAD:
685 case LFUN_BUFFER_SAVE_AS_DEFAULT:
686 case LFUN_BUFFER_PARAMS_APPLY:
687 case LFUN_LYXRC_APPLY:
688 case LFUN_BUFFER_NEXT:
689 case LFUN_BUFFER_PREVIOUS:
690 case LFUN_WINDOW_NEW:
691 case LFUN_WINDOW_CLOSE:
693 // these are handled in our dispatch()
697 if (!getLocalStatus(cur, cmd, flag))
698 flag = view()->getStatus(cmd);
704 // Can we use a readonly buffer?
705 if (buf && buf->isReadonly()
706 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
707 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
708 flag.message(from_utf8(N_("Document is read-only")));
712 // Are we in a DELETED change-tracking region?
713 if (buf && lookupChangeType(cur, true) == Change::DELETED
714 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
715 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
716 flag.message(from_utf8(N_("This portion of the document is deleted.")));
720 // the default error message if we disable the command
721 if (!flag.enabled() && flag.message().empty())
722 flag.message(from_utf8(N_("Command disabled")));
728 bool LyXFunc::ensureBufferClean(BufferView * bv)
730 Buffer & buf = *bv->buffer();
734 docstring const file = makeDisplayPath(buf.fileName(), 30);
735 docstring text = bformat(_("The document %1$s has unsaved "
736 "changes.\n\nDo you want to save "
737 "the document?"), file);
738 int const ret = Alert::prompt(_("Save changed document?"),
739 text, 0, 1, _("&Save"),
743 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
745 return buf.isClean();
751 void showPrintError(string const & name)
753 docstring str = bformat(_("Could not print the document %1$s.\n"
754 "Check that your printer is set up correctly."),
755 makeDisplayPath(name, 50));
756 Alert::error(_("Print document failed"), str);
760 void loadTextclass(string const & name)
762 std::pair<bool, textclass_type> const tc_pair =
763 textclasslist.numberOfClass(name);
765 if (!tc_pair.first) {
766 lyxerr << "Document class \"" << name
767 << "\" does not exist."
772 textclass_type const tc = tc_pair.second;
774 if (!textclasslist[tc].load()) {
775 docstring s = bformat(_("The document could not be converted\n"
776 "into the document class %1$s."),
777 from_utf8(textclasslist[tc].name()));
778 Alert::error(_("Could not change class"), s);
783 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
788 void LyXFunc::dispatch(FuncRequest const & cmd)
790 string const argument = to_utf8(cmd.argument());
791 kb_action const action = cmd.action;
793 LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
794 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
796 // we have not done anything wrong yet.
798 dispatch_buffer.erase();
800 // redraw the screen at the end (first of the two drawing steps).
801 //This is done unless explicitely requested otherwise
802 Update::flags updateFlags = Update::FitCursor;
804 FuncStatus const flag = getStatus(cmd);
805 if (!flag.enabled()) {
806 // We cannot use this function here
807 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
808 << lyxaction.getActionName(action)
809 << " [" << action << "] is disabled at this location"
811 setErrorMessage(flag.message());
815 case LFUN_WORD_FIND_FORWARD:
816 case LFUN_WORD_FIND_BACKWARD: {
817 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
818 static docstring last_search;
819 docstring searched_string;
821 if (!cmd.argument().empty()) {
822 last_search = cmd.argument();
823 searched_string = cmd.argument();
825 searched_string = last_search;
828 if (searched_string.empty())
831 bool const fw = action == LFUN_WORD_FIND_FORWARD;
832 docstring const data =
833 find2string(searched_string, true, false, fw);
834 find(view(), FuncRequest(LFUN_WORD_FIND, data));
838 case LFUN_COMMAND_PREFIX:
839 BOOST_ASSERT(lyx_view_);
840 lyx_view_->message(keyseq->printOptions(true));
843 case LFUN_COMMAND_EXECUTE:
844 BOOST_ASSERT(lyx_view_);
845 lyx_view_->getToolbars().display("minibuffer", true);
846 lyx_view_->focus_command_buffer();
850 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
852 meta_fake_bit = key_modifier::none;
853 if (view()->buffer())
854 // cancel any selection
855 dispatch(FuncRequest(LFUN_MARK_OFF));
856 setMessage(from_ascii(N_("Cancel")));
859 case LFUN_META_PREFIX:
860 meta_fake_bit = key_modifier::alt;
861 setMessage(keyseq->print(true));
864 case LFUN_BUFFER_TOGGLE_READ_ONLY:
865 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
866 if (lyx_view_->buffer()->lyxvc().inUse())
867 lyx_view_->buffer()->lyxvc().toggleReadOnly();
869 lyx_view_->buffer()->setReadonly(
870 !lyx_view_->buffer()->isReadonly());
873 // --- Menus -----------------------------------------------
874 case LFUN_BUFFER_NEW:
875 menuNew(argument, false);
878 case LFUN_BUFFER_NEW_TEMPLATE:
879 menuNew(argument, true);
882 case LFUN_BUFFER_CLOSE:
887 case LFUN_BUFFER_WRITE:
888 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
889 if (!lyx_view_->buffer()->isUnnamed()) {
890 docstring const str = bformat(_("Saving document %1$s..."),
891 makeDisplayPath(lyx_view_->buffer()->fileName()));
892 lyx_view_->message(str);
893 menuWrite(lyx_view_->buffer());
894 lyx_view_->message(str + _(" done."));
896 writeAs(lyx_view_->buffer());
898 updateFlags = Update::None;
901 case LFUN_BUFFER_WRITE_AS:
902 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
903 writeAs(lyx_view_->buffer(), argument);
904 updateFlags = Update::None;
907 case LFUN_BUFFER_RELOAD: {
908 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
909 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
910 docstring text = bformat(_("Any changes will be lost. Are you sure "
911 "you want to revert to the saved version of the document %1$s?"), file);
912 int const ret = Alert::prompt(_("Revert to saved document?"),
913 text, 1, 1, _("&Revert"), _("&Cancel"));
920 case LFUN_BUFFER_UPDATE:
921 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
922 Exporter::Export(lyx_view_->buffer(), argument, true);
925 case LFUN_BUFFER_VIEW:
926 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
927 Exporter::preview(lyx_view_->buffer(), argument);
930 case LFUN_BUILD_PROGRAM:
931 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
932 Exporter::Export(lyx_view_->buffer(), "program", true);
935 case LFUN_BUFFER_CHKTEX:
936 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
937 lyx_view_->buffer()->runChktex();
940 case LFUN_BUFFER_EXPORT:
941 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
942 if (argument == "custom")
943 lyx_view_->getDialogs().show("sendto");
945 Exporter::Export(lyx_view_->buffer(), argument, false);
949 case LFUN_BUFFER_EXPORT_CUSTOM: {
950 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
952 string command = split(argument, format_name, ' ');
953 Format const * format = formats.getFormat(format_name);
955 lyxerr << "Format \"" << format_name
956 << "\" not recognized!"
961 Buffer * buffer = lyx_view_->buffer();
963 // The name of the file created by the conversion process
966 // Output to filename
967 if (format->name() == "lyx") {
968 string const latexname =
969 buffer->getLatexName(false);
970 filename = changeExtension(latexname,
971 format->extension());
972 filename = addName(buffer->temppath(), filename);
974 if (!buffer->writeFile(FileName(filename)))
978 Exporter::Export(buffer, format_name, true, filename);
981 // Substitute $$FName for filename
982 if (!contains(command, "$$FName"))
983 command = "( " + command + " ) < $$FName";
984 command = subst(command, "$$FName", filename);
986 // Execute the command in the background
988 call.startscript(Systemcall::DontWait, command);
992 case LFUN_BUFFER_PRINT: {
993 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
996 string command = split(split(argument, target, ' '),
1000 || target_name.empty()
1001 || command.empty()) {
1002 lyxerr << "Unable to parse \""
1003 << argument << '"' << std::endl;
1006 if (target != "printer" && target != "file") {
1007 lyxerr << "Unrecognized target \""
1008 << target << '"' << std::endl;
1012 Buffer * buffer = lyx_view_->buffer();
1014 if (!Exporter::Export(buffer, "dvi", true)) {
1015 showPrintError(buffer->fileName());
1019 // Push directory path.
1020 string const path(buffer->temppath());
1021 // Prevent the compiler from optimizing away p
1023 support::Path p(pp);
1025 // there are three cases here:
1026 // 1. we print to a file
1027 // 2. we print directly to a printer
1028 // 3. we print using a spool command (print to file first)
1031 string const dviname =
1032 changeExtension(buffer->getLatexName(true),
1035 if (target == "printer") {
1036 if (!lyxrc.print_spool_command.empty()) {
1037 // case 3: print using a spool
1038 string const psname =
1039 changeExtension(dviname,".ps");
1040 command += lyxrc.print_to_file
1043 + quoteName(dviname);
1046 lyxrc.print_spool_command +' ';
1047 if (target_name != "default") {
1048 command2 += lyxrc.print_spool_printerprefix
1052 command2 += quoteName(psname);
1054 // If successful, then spool command
1055 res = one.startscript(
1060 res = one.startscript(
1061 Systemcall::DontWait,
1064 // case 2: print directly to a printer
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_STATE:
1759 lyx_view_->toggleToolbarState(argument);
1762 case LFUN_TOOLBAR_TOGGLE: {
1763 BOOST_ASSERT(lyx_view_);
1764 string const name = to_utf8(cmd.argument());
1765 bool const current = lyx_view_->getToolbars().visible(name);
1766 lyx_view_->getToolbars().display(name, !current);
1771 BOOST_ASSERT(lyx_view_);
1772 view()->cursor().dispatch(cmd);
1773 updateFlags = view()->cursor().result().update();
1774 if (!view()->cursor().result().dispatched())
1775 updateFlags = view()->dispatch(cmd);
1780 if (lyx_view_ && view()->buffer()) {
1781 // BufferView::update() updates the ViewMetricsInfo and
1782 // also initializes the position cache for all insets in
1783 // (at least partially) visible top-level paragraphs.
1784 // We will redraw the screen only if needed.
1785 if (view()->update(updateFlags)) {
1786 // Buffer::changed() signals that a repaint is needed.
1787 // The frontend (WorkArea) knows which area to repaint
1788 // thanks to the ViewMetricsInfo updated above.
1789 view()->buffer()->changed();
1792 lyx_view_->updateStatusBar();
1794 // if we executed a mutating lfun, mark the buffer as dirty
1796 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1797 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1798 view()->buffer()->markDirty();
1800 if (view()->cursor().inTexted()) {
1801 lyx_view_->updateLayoutChoice();
1806 lyx_view_->updateMenubar();
1807 lyx_view_->updateToolbars();
1808 // Some messages may already be translated, so we cannot use _()
1809 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1814 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1816 const bool verbose = (cmd.origin == FuncRequest::MENU
1817 || cmd.origin == FuncRequest::TOOLBAR
1818 || cmd.origin == FuncRequest::COMMANDBUFFER);
1820 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1821 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1823 lyx_view_->message(msg);
1827 docstring dispatch_msg = msg;
1828 if (!dispatch_msg.empty())
1829 dispatch_msg += ' ';
1831 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1833 bool argsadded = false;
1835 if (!cmd.argument().empty()) {
1836 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1837 comname += ' ' + cmd.argument();
1842 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1844 if (!shortcuts.empty())
1845 comname += ": " + shortcuts;
1846 else if (!argsadded && !cmd.argument().empty())
1847 comname += ' ' + cmd.argument();
1849 if (!comname.empty()) {
1850 comname = rtrim(comname);
1851 dispatch_msg += '(' + rtrim(comname) + ')';
1854 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1855 << to_utf8(dispatch_msg) << endl;
1856 if (!dispatch_msg.empty())
1857 lyx_view_->message(dispatch_msg);
1861 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1863 // FIXME: initpath is not used. What to do?
1864 string initpath = lyxrc.document_path;
1865 string filename(name);
1867 if (view()->buffer()) {
1868 string const trypath = lyx_view_->buffer()->filePath();
1869 // If directory is writeable, use this as default.
1870 if (isDirWriteable(FileName(trypath)))
1874 static int newfile_number;
1876 if (filename.empty()) {
1877 filename = addName(lyxrc.document_path,
1878 "newfile" + convert<string>(++newfile_number) + ".lyx");
1879 while (theBufferList().exists(filename) ||
1880 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1882 filename = addName(lyxrc.document_path,
1883 "newfile" + convert<string>(newfile_number) +
1888 // The template stuff
1891 FileDialog fileDlg(_("Select template file"),
1892 LFUN_SELECT_FILE_SYNC,
1893 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1894 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1896 FileDialog::Result result =
1897 fileDlg.open(from_utf8(lyxrc.template_path),
1898 FileFilterList(_("LyX Documents (*.lyx)")),
1901 if (result.first == FileDialog::Later)
1903 if (result.second.empty())
1905 templname = to_utf8(result.second);
1908 Buffer * const b = newFile(filename, templname, !name.empty());
1911 lyx_view_->setBuffer(b);
1916 void LyXFunc::open(string const & fname)
1918 string initpath = lyxrc.document_path;
1920 if (view()->buffer()) {
1921 string const trypath = lyx_view_->buffer()->filePath();
1922 // If directory is writeable, use this as default.
1923 if (isDirWriteable(FileName(trypath)))
1929 if (fname.empty()) {
1930 FileDialog fileDlg(_("Select document to open"),
1932 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1933 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1935 FileDialog::Result result =
1936 fileDlg.open(from_utf8(initpath),
1937 FileFilterList(_("LyX Documents (*.lyx)")),
1940 if (result.first == FileDialog::Later)
1943 filename = to_utf8(result.second);
1945 // check selected filename
1946 if (filename.empty()) {
1947 lyx_view_->message(_("Canceled."));
1953 // get absolute path of file and add ".lyx" to the filename if
1955 FileName const fullname = fileSearch(string(), filename, "lyx");
1956 if (!fullname.empty())
1957 filename = fullname.absFilename();
1959 // if the file doesn't exist, let the user create one
1960 if (!fs::exists(fullname.toFilesystemEncoding())) {
1961 // the user specifically chose this name. Believe him.
1962 Buffer * const b = newFile(filename, string(), true);
1964 lyx_view_->setBuffer(b);
1968 docstring const disp_fn = makeDisplayPath(filename);
1969 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1972 if (lyx_view_->loadLyXFile(fullname)) {
1973 str2 = bformat(_("Document %1$s opened."), disp_fn);
1975 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1977 lyx_view_->message(str2);
1981 void LyXFunc::doImport(string const & argument)
1984 string filename = split(argument, format, ' ');
1986 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1987 << " file: " << filename << endl;
1989 // need user interaction
1990 if (filename.empty()) {
1991 string initpath = lyxrc.document_path;
1993 if (view()->buffer()) {
1994 string const trypath = lyx_view_->buffer()->filePath();
1995 // If directory is writeable, use this as default.
1996 if (isDirWriteable(FileName(trypath)))
2000 docstring const text = bformat(_("Select %1$s file to import"),
2001 formats.prettyName(format));
2003 FileDialog fileDlg(text,
2005 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2006 make_pair(_("Examples|#E#e"),
2007 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2009 docstring filter = formats.prettyName(format);
2012 filter += from_utf8(formats.extension(format));
2015 FileDialog::Result result =
2016 fileDlg.open(from_utf8(initpath),
2017 FileFilterList(filter),
2020 if (result.first == FileDialog::Later)
2023 filename = to_utf8(result.second);
2025 // check selected filename
2026 if (filename.empty())
2027 lyx_view_->message(_("Canceled."));
2030 if (filename.empty())
2033 // get absolute path of file
2034 FileName const fullname(makeAbsPath(filename));
2036 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2038 // Check if the document already is open
2039 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2040 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2041 lyx_view_->message(_("Canceled."));
2046 // if the file exists already, and we didn't do
2047 // -i lyx thefile.lyx, warn
2048 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2049 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2051 docstring text = bformat(_("The document %1$s already exists.\n\n"
2052 "Do you want to over-write that document?"), file);
2053 int const ret = Alert::prompt(_("Over-write document?"),
2054 text, 0, 1, _("&Over-write"), _("&Cancel"));
2057 lyx_view_->message(_("Canceled."));
2062 ErrorList errorList;
2063 Importer::Import(lyx_view_, fullname, format, errorList);
2064 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2068 void LyXFunc::closeBuffer()
2070 // goto bookmark to update bookmark pit.
2071 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2072 gotoBookmark(i+1, false, false);
2073 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2074 if (theBufferList().empty()) {
2075 // need this otherwise SEGV may occur while
2076 // trying to set variables that don't exist
2077 // since there's no current buffer
2078 lyx_view_->getDialogs().hideBufferDependent();
2080 lyx_view_->setBuffer(theBufferList().first());
2086 void LyXFunc::reloadBuffer()
2088 FileName filename(lyx_view_->buffer()->fileName());
2090 lyx_view_->loadLyXFile(filename);
2093 // Each "lyx_view_" should have it's own message method. lyxview and
2094 // the minibuffer would use the minibuffer, but lyxserver would
2095 // send an ERROR signal to its client. Alejandro 970603
2096 // This function is bit problematic when it comes to NLS, to make the
2097 // lyx servers client be language indepenent we must not translate
2098 // strings sent to this func.
2099 void LyXFunc::setErrorMessage(docstring const & m) const
2101 dispatch_buffer = m;
2106 void LyXFunc::setMessage(docstring const & m) const
2108 dispatch_buffer = m;
2112 docstring const LyXFunc::viewStatusMessage()
2114 // When meta-fake key is pressed, show the key sequence so far + "M-".
2116 return keyseq->print(true) + "M-";
2118 // Else, when a non-complete key sequence is pressed,
2119 // show the available options.
2120 if (keyseq->length() > 0 && !keyseq->deleted())
2121 return keyseq->printOptions(true);
2123 if (!view()->buffer())
2124 return _("Welcome to LyX!");
2126 return view()->cursor().currentState();
2130 BufferView * LyXFunc::view() const
2132 BOOST_ASSERT(lyx_view_);
2133 return lyx_view_->view();
2137 bool LyXFunc::wasMetaKey() const
2139 return (meta_fake_bit != key_modifier::none);
2145 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2147 // Why the switch you might ask. It is a trick to ensure that all
2148 // the elements in the LyXRCTags enum is handled. As you can see
2149 // there are no breaks at all. So it is just a huge fall-through.
2150 // The nice thing is that we will get a warning from the compiler
2151 // if we forget an element.
2152 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2154 case LyXRC::RC_ACCEPT_COMPOUND:
2155 case LyXRC::RC_ALT_LANG:
2156 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2157 case LyXRC::RC_PLAINTEXT_LINELEN:
2158 case LyXRC::RC_AUTOREGIONDELETE:
2159 case LyXRC::RC_AUTORESET_OPTIONS:
2160 case LyXRC::RC_AUTOSAVE:
2161 case LyXRC::RC_AUTO_NUMBER:
2162 case LyXRC::RC_BACKUPDIR_PATH:
2163 case LyXRC::RC_BIBTEX_COMMAND:
2164 case LyXRC::RC_BINDFILE:
2165 case LyXRC::RC_CHECKLASTFILES:
2166 case LyXRC::RC_USELASTFILEPOS:
2167 case LyXRC::RC_LOADSESSION:
2168 case LyXRC::RC_CHKTEX_COMMAND:
2169 case LyXRC::RC_CONVERTER:
2170 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2171 case LyXRC::RC_COPIER:
2172 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2173 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2174 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2175 case LyXRC::RC_DATE_INSERT_FORMAT:
2176 case LyXRC::RC_DEFAULT_LANGUAGE:
2177 case LyXRC::RC_DEFAULT_PAPERSIZE:
2178 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2179 case LyXRC::RC_DISPLAY_GRAPHICS:
2180 case LyXRC::RC_DOCUMENTPATH:
2181 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2182 string const encoded = FileName(
2183 lyxrc_new.document_path).toFilesystemEncoding();
2184 if (fs::exists(encoded) && fs::is_directory(encoded))
2185 support::package().document_dir() = FileName(lyxrc.document_path);
2187 case LyXRC::RC_ESC_CHARS:
2188 case LyXRC::RC_FONT_ENCODING:
2189 case LyXRC::RC_FORMAT:
2190 case LyXRC::RC_INDEX_COMMAND:
2191 case LyXRC::RC_INPUT:
2192 case LyXRC::RC_KBMAP:
2193 case LyXRC::RC_KBMAP_PRIMARY:
2194 case LyXRC::RC_KBMAP_SECONDARY:
2195 case LyXRC::RC_LABEL_INIT_LENGTH:
2196 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2197 case LyXRC::RC_LANGUAGE_AUTO_END:
2198 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2199 case LyXRC::RC_LANGUAGE_COMMAND_END:
2200 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2201 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2202 case LyXRC::RC_LANGUAGE_PACKAGE:
2203 case LyXRC::RC_LANGUAGE_USE_BABEL:
2204 case LyXRC::RC_MAKE_BACKUP:
2205 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2206 case LyXRC::RC_NUMLASTFILES:
2207 case LyXRC::RC_PATH_PREFIX:
2208 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2209 support::prependEnvPath("PATH", lyxrc.path_prefix);
2211 case LyXRC::RC_PERS_DICT:
2212 case LyXRC::RC_PREVIEW:
2213 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2214 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2215 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2216 case LyXRC::RC_PRINTCOPIESFLAG:
2217 case LyXRC::RC_PRINTER:
2218 case LyXRC::RC_PRINTEVENPAGEFLAG:
2219 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2220 case LyXRC::RC_PRINTFILEEXTENSION:
2221 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2222 case LyXRC::RC_PRINTODDPAGEFLAG:
2223 case LyXRC::RC_PRINTPAGERANGEFLAG:
2224 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2225 case LyXRC::RC_PRINTPAPERFLAG:
2226 case LyXRC::RC_PRINTREVERSEFLAG:
2227 case LyXRC::RC_PRINTSPOOL_COMMAND:
2228 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2229 case LyXRC::RC_PRINTTOFILE:
2230 case LyXRC::RC_PRINTTOPRINTER:
2231 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2232 case LyXRC::RC_PRINT_COMMAND:
2233 case LyXRC::RC_RTL_SUPPORT:
2234 case LyXRC::RC_SCREEN_DPI:
2235 case LyXRC::RC_SCREEN_FONT_ROMAN:
2236 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2237 case LyXRC::RC_SCREEN_FONT_SANS:
2238 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2239 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2240 case LyXRC::RC_SCREEN_FONT_SIZES:
2241 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2242 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2243 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2244 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2245 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2246 case LyXRC::RC_SCREEN_ZOOM:
2247 case LyXRC::RC_SERVERPIPE:
2248 case LyXRC::RC_SET_COLOR:
2249 case LyXRC::RC_SHOW_BANNER:
2250 case LyXRC::RC_SPELL_COMMAND:
2251 case LyXRC::RC_TEMPDIRPATH:
2252 case LyXRC::RC_TEMPLATEPATH:
2253 case LyXRC::RC_TEX_ALLOWS_SPACES:
2254 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2255 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2256 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2258 case LyXRC::RC_UIFILE:
2259 case LyXRC::RC_USER_EMAIL:
2260 case LyXRC::RC_USER_NAME:
2261 case LyXRC::RC_USETEMPDIR:
2262 case LyXRC::RC_USE_ALT_LANG:
2263 case LyXRC::RC_USE_CONVERTER_CACHE:
2264 case LyXRC::RC_USE_ESC_CHARS:
2265 case LyXRC::RC_USE_INP_ENC:
2266 case LyXRC::RC_USE_PERS_DICT:
2267 case LyXRC::RC_USE_SPELL_LIB:
2268 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2269 case LyXRC::RC_VIEWER:
2270 case LyXRC::RC_LAST: