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, 0, 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 setMessage(bformat(_("Opening child document %1$s..."),
1417 makeDisplayPath(filename.absFilename())));
1418 view()->saveBookmark(false);
1419 string const parentfilename = lyx_view_->buffer()->fileName();
1420 if (theBufferList().exists(filename.absFilename()))
1421 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1423 lyx_view_->loadLyXFile(filename);
1424 // Set the parent name of the child document.
1425 // This makes insertion of citations and references in the child work,
1426 // when the target is in the parent or another child document.
1427 lyx_view_->buffer()->setParentName(parentfilename);
1431 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1432 BOOST_ASSERT(lyx_view_);
1433 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1436 case LFUN_KEYMAP_OFF:
1437 BOOST_ASSERT(lyx_view_);
1438 lyx_view_->view()->getIntl().keyMapOn(false);
1441 case LFUN_KEYMAP_PRIMARY:
1442 BOOST_ASSERT(lyx_view_);
1443 lyx_view_->view()->getIntl().keyMapPrim();
1446 case LFUN_KEYMAP_SECONDARY:
1447 BOOST_ASSERT(lyx_view_);
1448 lyx_view_->view()->getIntl().keyMapSec();
1451 case LFUN_KEYMAP_TOGGLE:
1452 BOOST_ASSERT(lyx_view_);
1453 lyx_view_->view()->getIntl().toggleKeyMap();
1459 string rest = split(argument, countstr, ' ');
1460 istringstream is(countstr);
1463 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1464 for (int i = 0; i < count; ++i)
1465 dispatch(lyxaction.lookupFunc(rest));
1469 case LFUN_COMMAND_SEQUENCE: {
1470 // argument contains ';'-terminated commands
1471 string arg = argument;
1472 while (!arg.empty()) {
1474 arg = split(arg, first, ';');
1475 FuncRequest func(lyxaction.lookupFunc(first));
1476 func.origin = cmd.origin;
1482 case LFUN_PREFERENCES_SAVE: {
1483 lyxrc.write(makeAbsPath("preferences",
1484 package().user_support().absFilename()),
1489 case LFUN_SCREEN_FONT_UPDATE:
1490 BOOST_ASSERT(lyx_view_);
1491 // handle the screen font changes.
1492 theFontLoader().update();
1493 /// FIXME: only the current view will be updated. the Gui
1494 /// class is able to furnish the list of views.
1495 updateFlags = Update::Force;
1498 case LFUN_SET_COLOR: {
1500 string const x11_name = split(argument, lyx_name, ' ');
1501 if (lyx_name.empty() || x11_name.empty()) {
1502 setErrorMessage(from_ascii(N_(
1503 "Syntax: set-color <lyx_name>"
1508 bool const graphicsbg_changed =
1509 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1510 x11_name != lcolor.getX11Name(Color::graphicsbg));
1512 if (!lcolor.setColor(lyx_name, x11_name)) {
1514 bformat(_("Set-color \"%1$s\" failed "
1515 "- color is undefined or "
1516 "may not be redefined"),
1517 from_utf8(lyx_name)));
1521 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1523 if (graphicsbg_changed) {
1524 #ifdef WITH_WARNINGS
1525 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1528 graphics::GCache::get().changeDisplay(true);
1535 BOOST_ASSERT(lyx_view_);
1536 lyx_view_->message(from_utf8(argument));
1539 case LFUN_EXTERNAL_EDIT: {
1540 BOOST_ASSERT(lyx_view_);
1541 FuncRequest fr(action, argument);
1542 InsetExternal().dispatch(view()->cursor(), fr);
1546 case LFUN_GRAPHICS_EDIT: {
1547 FuncRequest fr(action, argument);
1548 InsetGraphics().dispatch(view()->cursor(), fr);
1552 case LFUN_INSET_APPLY: {
1553 BOOST_ASSERT(lyx_view_);
1554 string const name = cmd.getArg(0);
1555 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1557 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1558 inset->dispatch(view()->cursor(), fr);
1560 FuncRequest fr(LFUN_INSET_INSERT, argument);
1563 // ideally, the update flag should be set by the insets,
1564 // but this is not possible currently
1565 updateFlags = Update::Force | Update::FitCursor;
1569 case LFUN_ALL_INSETS_TOGGLE: {
1570 BOOST_ASSERT(lyx_view_);
1572 string const name = split(argument, action, ' ');
1573 Inset::Code const inset_code =
1574 Inset::translate(name);
1576 Cursor & cur = view()->cursor();
1577 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1579 Inset & inset = lyx_view_->buffer()->inset();
1580 InsetIterator it = inset_iterator_begin(inset);
1581 InsetIterator const end = inset_iterator_end(inset);
1582 for (; it != end; ++it) {
1583 if (!it->asInsetMath()
1584 && (inset_code == Inset::NO_CODE
1585 || inset_code == it->lyxCode())) {
1586 Cursor tmpcur = cur;
1587 tmpcur.pushLeft(*it);
1588 it->dispatch(tmpcur, fr);
1591 updateFlags = Update::Force | Update::FitCursor;
1595 case LFUN_BUFFER_LANGUAGE: {
1596 BOOST_ASSERT(lyx_view_);
1597 Buffer & buffer = *lyx_view_->buffer();
1598 Language const * oldL = buffer.params().language;
1599 Language const * newL = languages.getLanguage(argument);
1600 if (!newL || oldL == newL)
1603 if (oldL->rightToLeft() == newL->rightToLeft()
1604 && !buffer.isMultiLingual())
1605 buffer.changeLanguage(oldL, newL);
1609 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1610 string const fname =
1611 addName(addPath(package().user_support().absFilename(), "templates/"),
1613 Buffer defaults(fname);
1615 istringstream ss(argument);
1618 int const unknown_tokens = defaults.readHeader(lex);
1620 if (unknown_tokens != 0) {
1621 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1622 << unknown_tokens << " unknown token"
1623 << (unknown_tokens == 1 ? "" : "s")
1627 if (defaults.writeFile(FileName(defaults.fileName())))
1628 setMessage(bformat(_("Document defaults saved in %1$s"),
1629 makeDisplayPath(fname)));
1631 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1635 case LFUN_BUFFER_PARAMS_APPLY: {
1636 BOOST_ASSERT(lyx_view_);
1637 biblio::CiteEngine const engine =
1638 lyx_view_->buffer()->params().getEngine();
1640 istringstream ss(argument);
1643 int const unknown_tokens =
1644 lyx_view_->buffer()->readHeader(lex);
1646 if (unknown_tokens != 0) {
1647 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1648 << unknown_tokens << " unknown token"
1649 << (unknown_tokens == 1 ? "" : "s")
1652 if (engine == lyx_view_->buffer()->params().getEngine())
1655 Cursor & cur = view()->cursor();
1656 FuncRequest fr(LFUN_INSET_REFRESH);
1658 Inset & inset = lyx_view_->buffer()->inset();
1659 InsetIterator it = inset_iterator_begin(inset);
1660 InsetIterator const end = inset_iterator_end(inset);
1661 for (; it != end; ++it)
1662 if (it->lyxCode() == Inset::CITE_CODE)
1663 it->dispatch(cur, fr);
1667 case LFUN_TEXTCLASS_APPLY: {
1668 BOOST_ASSERT(lyx_view_);
1669 Buffer * buffer = lyx_view_->buffer();
1671 textclass_type const old_class =
1672 buffer->params().textclass;
1674 loadTextclass(argument);
1676 std::pair<bool, textclass_type> const tc_pair =
1677 textclasslist.numberOfClass(argument);
1682 textclass_type const new_class = tc_pair.second;
1683 if (old_class == new_class)
1687 lyx_view_->message(_("Converting document to new document class..."));
1688 recordUndoFullDocument(view());
1689 buffer->params().textclass = new_class;
1690 StableDocIterator backcur(view()->cursor());
1691 ErrorList & el = buffer->errorList("Class Switch");
1692 cap::switchBetweenClasses(
1693 old_class, new_class,
1694 static_cast<InsetText &>(buffer->inset()), el);
1696 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1698 buffer->errors("Class Switch");
1699 updateLabels(*buffer);
1700 updateFlags = Update::Force | Update::FitCursor;
1704 case LFUN_TEXTCLASS_LOAD:
1705 loadTextclass(argument);
1708 case LFUN_LYXRC_APPLY: {
1709 LyXRC const lyxrc_orig = lyxrc;
1711 istringstream ss(argument);
1712 bool const success = lyxrc.read(ss) == 0;
1715 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1716 << "Unable to read lyxrc data"
1721 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1723 /// We force the redraw in any case because there might be
1724 /// some screen font changes.
1725 /// FIXME: only the current view will be updated. the Gui
1726 /// class is able to furnish the list of views.
1727 updateFlags = Update::Force;
1731 case LFUN_WINDOW_NEW:
1732 LyX::ref().newLyXView();
1735 case LFUN_WINDOW_CLOSE:
1736 BOOST_ASSERT(lyx_view_);
1737 BOOST_ASSERT(theApp());
1738 // update bookmark pit of the current buffer before window close
1739 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1740 gotoBookmark(i+1, false, false);
1741 // ask the user for saving changes or cancel quit
1742 if (!theBufferList().quitWriteAll())
1747 case LFUN_BOOKMARK_GOTO:
1748 // go to bookmark, open unopened file and switch to buffer if necessary
1749 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1752 case LFUN_BOOKMARK_CLEAR:
1753 LyX::ref().session().bookmarks().clear();
1756 case LFUN_TOOLBAR_TOGGLE_STATE:
1757 lyx_view_->toggleToolbarState(argument);
1760 case LFUN_TOOLBAR_TOGGLE: {
1761 BOOST_ASSERT(lyx_view_);
1762 string const name = to_utf8(cmd.argument());
1763 bool const current = lyx_view_->getToolbars().visible(name);
1764 lyx_view_->getToolbars().display(name, !current);
1769 BOOST_ASSERT(lyx_view_);
1770 view()->cursor().dispatch(cmd);
1771 updateFlags = view()->cursor().result().update();
1772 if (!view()->cursor().result().dispatched())
1773 updateFlags = view()->dispatch(cmd);
1778 if (lyx_view_ && view()->buffer()) {
1779 // BufferView::update() updates the ViewMetricsInfo and
1780 // also initializes the position cache for all insets in
1781 // (at least partially) visible top-level paragraphs.
1782 // We will redraw the screen only if needed.
1783 if (view()->update(updateFlags)) {
1784 // Buffer::changed() signals that a repaint is needed.
1785 // The frontend (WorkArea) knows which area to repaint
1786 // thanks to the ViewMetricsInfo updated above.
1787 view()->buffer()->changed();
1790 lyx_view_->updateStatusBar();
1792 // if we executed a mutating lfun, mark the buffer as dirty
1794 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1795 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1796 view()->buffer()->markDirty();
1798 if (view()->cursor().inTexted()) {
1799 lyx_view_->updateLayoutChoice();
1804 lyx_view_->updateMenubar();
1805 lyx_view_->updateToolbars();
1806 // Some messages may already be translated, so we cannot use _()
1807 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1812 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1814 const bool verbose = (cmd.origin == FuncRequest::MENU
1815 || cmd.origin == FuncRequest::TOOLBAR
1816 || cmd.origin == FuncRequest::COMMANDBUFFER);
1818 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1819 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1821 lyx_view_->message(msg);
1825 docstring dispatch_msg = msg;
1826 if (!dispatch_msg.empty())
1827 dispatch_msg += ' ';
1829 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1831 bool argsadded = false;
1833 if (!cmd.argument().empty()) {
1834 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1835 comname += ' ' + cmd.argument();
1840 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1842 if (!shortcuts.empty())
1843 comname += ": " + shortcuts;
1844 else if (!argsadded && !cmd.argument().empty())
1845 comname += ' ' + cmd.argument();
1847 if (!comname.empty()) {
1848 comname = rtrim(comname);
1849 dispatch_msg += '(' + rtrim(comname) + ')';
1852 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1853 << to_utf8(dispatch_msg) << endl;
1854 if (!dispatch_msg.empty())
1855 lyx_view_->message(dispatch_msg);
1859 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1861 // FIXME: initpath is not used. What to do?
1862 string initpath = lyxrc.document_path;
1863 string filename(name);
1865 if (view()->buffer()) {
1866 string const trypath = lyx_view_->buffer()->filePath();
1867 // If directory is writeable, use this as default.
1868 if (isDirWriteable(FileName(trypath)))
1872 static int newfile_number;
1874 if (filename.empty()) {
1875 filename = addName(lyxrc.document_path,
1876 "newfile" + convert<string>(++newfile_number) + ".lyx");
1877 while (theBufferList().exists(filename) ||
1878 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1880 filename = addName(lyxrc.document_path,
1881 "newfile" + convert<string>(newfile_number) +
1886 // The template stuff
1889 FileDialog fileDlg(_("Select template file"),
1890 LFUN_SELECT_FILE_SYNC,
1891 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1892 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1894 FileDialog::Result result =
1895 fileDlg.open(from_utf8(lyxrc.template_path),
1896 FileFilterList(_("LyX Documents (*.lyx)")),
1899 if (result.first == FileDialog::Later)
1901 if (result.second.empty())
1903 templname = to_utf8(result.second);
1906 Buffer * const b = newFile(filename, templname, !name.empty());
1909 lyx_view_->setBuffer(b);
1914 void LyXFunc::open(string const & fname)
1916 string initpath = lyxrc.document_path;
1918 if (view()->buffer()) {
1919 string const trypath = lyx_view_->buffer()->filePath();
1920 // If directory is writeable, use this as default.
1921 if (isDirWriteable(FileName(trypath)))
1927 if (fname.empty()) {
1928 FileDialog fileDlg(_("Select document to open"),
1930 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1931 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1933 FileDialog::Result result =
1934 fileDlg.open(from_utf8(initpath),
1935 FileFilterList(_("LyX Documents (*.lyx)")),
1938 if (result.first == FileDialog::Later)
1941 filename = to_utf8(result.second);
1943 // check selected filename
1944 if (filename.empty()) {
1945 lyx_view_->message(_("Canceled."));
1951 // get absolute path of file and add ".lyx" to the filename if
1953 FileName const fullname = fileSearch(string(), filename, "lyx");
1954 if (!fullname.empty())
1955 filename = fullname.absFilename();
1957 // if the file doesn't exist, let the user create one
1958 if (!fs::exists(fullname.toFilesystemEncoding())) {
1959 // the user specifically chose this name. Believe him.
1960 Buffer * const b = newFile(filename, string(), true);
1962 lyx_view_->setBuffer(b);
1966 docstring const disp_fn = makeDisplayPath(filename);
1967 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1970 if (lyx_view_->loadLyXFile(fullname)) {
1971 str2 = bformat(_("Document %1$s opened."), disp_fn);
1973 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1975 lyx_view_->message(str2);
1979 void LyXFunc::doImport(string const & argument)
1982 string filename = split(argument, format, ' ');
1984 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1985 << " file: " << filename << endl;
1987 // need user interaction
1988 if (filename.empty()) {
1989 string initpath = lyxrc.document_path;
1991 if (view()->buffer()) {
1992 string const trypath = lyx_view_->buffer()->filePath();
1993 // If directory is writeable, use this as default.
1994 if (isDirWriteable(FileName(trypath)))
1998 docstring const text = bformat(_("Select %1$s file to import"),
1999 formats.prettyName(format));
2001 FileDialog fileDlg(text,
2003 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2004 make_pair(_("Examples|#E#e"),
2005 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2007 docstring filter = formats.prettyName(format);
2010 filter += from_utf8(formats.extension(format));
2013 FileDialog::Result result =
2014 fileDlg.open(from_utf8(initpath),
2015 FileFilterList(filter),
2018 if (result.first == FileDialog::Later)
2021 filename = to_utf8(result.second);
2023 // check selected filename
2024 if (filename.empty())
2025 lyx_view_->message(_("Canceled."));
2028 if (filename.empty())
2031 // get absolute path of file
2032 FileName const fullname(makeAbsPath(filename));
2034 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2036 // Check if the document already is open
2037 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2038 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2039 lyx_view_->message(_("Canceled."));
2044 // if the file exists already, and we didn't do
2045 // -i lyx thefile.lyx, warn
2046 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2047 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2049 docstring text = bformat(_("The document %1$s already exists.\n\n"
2050 "Do you want to over-write that document?"), file);
2051 int const ret = Alert::prompt(_("Over-write document?"),
2052 text, 0, 1, _("&Over-write"), _("&Cancel"));
2055 lyx_view_->message(_("Canceled."));
2060 ErrorList errorList;
2061 Importer::Import(lyx_view_, fullname, format, errorList);
2062 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2066 void LyXFunc::closeBuffer()
2068 // goto bookmark to update bookmark pit.
2069 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2070 gotoBookmark(i+1, false, false);
2071 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2072 if (theBufferList().empty()) {
2073 // need this otherwise SEGV may occur while
2074 // trying to set variables that don't exist
2075 // since there's no current buffer
2076 lyx_view_->getDialogs().hideBufferDependent();
2078 lyx_view_->setBuffer(theBufferList().first());
2084 void LyXFunc::reloadBuffer()
2086 FileName filename(lyx_view_->buffer()->fileName());
2088 lyx_view_->loadLyXFile(filename);
2091 // Each "lyx_view_" should have it's own message method. lyxview and
2092 // the minibuffer would use the minibuffer, but lyxserver would
2093 // send an ERROR signal to its client. Alejandro 970603
2094 // This function is bit problematic when it comes to NLS, to make the
2095 // lyx servers client be language indepenent we must not translate
2096 // strings sent to this func.
2097 void LyXFunc::setErrorMessage(docstring const & m) const
2099 dispatch_buffer = m;
2104 void LyXFunc::setMessage(docstring const & m) const
2106 dispatch_buffer = m;
2110 docstring const LyXFunc::viewStatusMessage()
2112 // When meta-fake key is pressed, show the key sequence so far + "M-".
2114 return keyseq->print(true) + "M-";
2116 // Else, when a non-complete key sequence is pressed,
2117 // show the available options.
2118 if (keyseq->length() > 0 && !keyseq->deleted())
2119 return keyseq->printOptions(true);
2121 if (!view()->buffer())
2122 return _("Welcome to LyX!");
2124 return view()->cursor().currentState();
2128 BufferView * LyXFunc::view() const
2130 BOOST_ASSERT(lyx_view_);
2131 return lyx_view_->view();
2135 bool LyXFunc::wasMetaKey() const
2137 return (meta_fake_bit != key_modifier::none);
2143 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2145 // Why the switch you might ask. It is a trick to ensure that all
2146 // the elements in the LyXRCTags enum is handled. As you can see
2147 // there are no breaks at all. So it is just a huge fall-through.
2148 // The nice thing is that we will get a warning from the compiler
2149 // if we forget an element.
2150 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2152 case LyXRC::RC_ACCEPT_COMPOUND:
2153 case LyXRC::RC_ALT_LANG:
2154 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2155 case LyXRC::RC_PLAINTEXT_LINELEN:
2156 case LyXRC::RC_AUTOREGIONDELETE:
2157 case LyXRC::RC_AUTORESET_OPTIONS:
2158 case LyXRC::RC_AUTOSAVE:
2159 case LyXRC::RC_AUTO_NUMBER:
2160 case LyXRC::RC_BACKUPDIR_PATH:
2161 case LyXRC::RC_BIBTEX_COMMAND:
2162 case LyXRC::RC_BINDFILE:
2163 case LyXRC::RC_CHECKLASTFILES:
2164 case LyXRC::RC_USELASTFILEPOS:
2165 case LyXRC::RC_LOADSESSION:
2166 case LyXRC::RC_CHKTEX_COMMAND:
2167 case LyXRC::RC_CONVERTER:
2168 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2169 case LyXRC::RC_COPIER:
2170 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2171 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2172 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2173 case LyXRC::RC_DATE_INSERT_FORMAT:
2174 case LyXRC::RC_DEFAULT_LANGUAGE:
2175 case LyXRC::RC_DEFAULT_PAPERSIZE:
2176 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2177 case LyXRC::RC_DISPLAY_GRAPHICS:
2178 case LyXRC::RC_DOCUMENTPATH:
2179 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2180 string const encoded = FileName(
2181 lyxrc_new.document_path).toFilesystemEncoding();
2182 if (fs::exists(encoded) && fs::is_directory(encoded))
2183 support::package().document_dir() = FileName(lyxrc.document_path);
2185 case LyXRC::RC_ESC_CHARS:
2186 case LyXRC::RC_FONT_ENCODING:
2187 case LyXRC::RC_FORMAT:
2188 case LyXRC::RC_INDEX_COMMAND:
2189 case LyXRC::RC_INPUT:
2190 case LyXRC::RC_KBMAP:
2191 case LyXRC::RC_KBMAP_PRIMARY:
2192 case LyXRC::RC_KBMAP_SECONDARY:
2193 case LyXRC::RC_LABEL_INIT_LENGTH:
2194 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2195 case LyXRC::RC_LANGUAGE_AUTO_END:
2196 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2197 case LyXRC::RC_LANGUAGE_COMMAND_END:
2198 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2199 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2200 case LyXRC::RC_LANGUAGE_PACKAGE:
2201 case LyXRC::RC_LANGUAGE_USE_BABEL:
2202 case LyXRC::RC_MAKE_BACKUP:
2203 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2204 case LyXRC::RC_NUMLASTFILES:
2205 case LyXRC::RC_PATH_PREFIX:
2206 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2207 support::prependEnvPath("PATH", lyxrc.path_prefix);
2209 case LyXRC::RC_PERS_DICT:
2210 case LyXRC::RC_PREVIEW:
2211 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2212 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2213 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2214 case LyXRC::RC_PRINTCOPIESFLAG:
2215 case LyXRC::RC_PRINTER:
2216 case LyXRC::RC_PRINTEVENPAGEFLAG:
2217 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2218 case LyXRC::RC_PRINTFILEEXTENSION:
2219 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2220 case LyXRC::RC_PRINTODDPAGEFLAG:
2221 case LyXRC::RC_PRINTPAGERANGEFLAG:
2222 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2223 case LyXRC::RC_PRINTPAPERFLAG:
2224 case LyXRC::RC_PRINTREVERSEFLAG:
2225 case LyXRC::RC_PRINTSPOOL_COMMAND:
2226 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2227 case LyXRC::RC_PRINTTOFILE:
2228 case LyXRC::RC_PRINTTOPRINTER:
2229 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2230 case LyXRC::RC_PRINT_COMMAND:
2231 case LyXRC::RC_RTL_SUPPORT:
2232 case LyXRC::RC_SCREEN_DPI:
2233 case LyXRC::RC_SCREEN_FONT_ROMAN:
2234 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2235 case LyXRC::RC_SCREEN_FONT_SANS:
2236 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2237 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2238 case LyXRC::RC_SCREEN_FONT_SIZES:
2239 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2240 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2241 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2242 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2243 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2244 case LyXRC::RC_SCREEN_ZOOM:
2245 case LyXRC::RC_SERVERPIPE:
2246 case LyXRC::RC_SET_COLOR:
2247 case LyXRC::RC_SHOW_BANNER:
2248 case LyXRC::RC_SPELL_COMMAND:
2249 case LyXRC::RC_TEMPDIRPATH:
2250 case LyXRC::RC_TEMPLATEPATH:
2251 case LyXRC::RC_TEX_ALLOWS_SPACES:
2252 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2253 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2254 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2256 case LyXRC::RC_UIFILE:
2257 case LyXRC::RC_USER_EMAIL:
2258 case LyXRC::RC_USER_NAME:
2259 case LyXRC::RC_USETEMPDIR:
2260 case LyXRC::RC_USE_ALT_LANG:
2261 case LyXRC::RC_USE_CONVERTER_CACHE:
2262 case LyXRC::RC_USE_ESC_CHARS:
2263 case LyXRC::RC_USE_INP_ENC:
2264 case LyXRC::RC_USE_PERS_DICT:
2265 case LyXRC::RC_USE_SPELL_LIB:
2266 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2267 case LyXRC::RC_VIEWER:
2268 case LyXRC::RC_LAST: