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 // this one is difficult to get right. As a half-baked
627 // solution, we consider only the first action of the sequence
628 case LFUN_COMMAND_SEQUENCE: {
629 // argument contains ';'-terminated commands
630 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
631 FuncRequest func(lyxaction.lookupFunc(firstcmd));
632 func.origin = cmd.origin;
633 flag = getStatus(func);
636 case LFUN_BUFFER_NEW:
637 case LFUN_BUFFER_NEW_TEMPLATE:
638 case LFUN_WORD_FIND_FORWARD:
639 case LFUN_WORD_FIND_BACKWARD:
640 case LFUN_COMMAND_PREFIX:
641 case LFUN_COMMAND_EXECUTE:
643 case LFUN_META_PREFIX:
644 case LFUN_BUFFER_CLOSE:
645 case LFUN_BUFFER_WRITE_AS:
646 case LFUN_BUFFER_UPDATE:
647 case LFUN_BUFFER_VIEW:
648 case LFUN_BUFFER_IMPORT:
649 case LFUN_BUFFER_AUTO_SAVE:
650 case LFUN_RECONFIGURE:
654 case LFUN_DROP_LAYOUTS_CHOICE:
656 case LFUN_SERVER_GET_NAME:
657 case LFUN_SERVER_NOTIFY:
658 case LFUN_SERVER_GOTO_FILE_ROW:
659 case LFUN_DIALOG_HIDE:
660 case LFUN_DIALOG_DISCONNECT_INSET:
661 case LFUN_BUFFER_CHILD_OPEN:
662 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
663 case LFUN_KEYMAP_OFF:
664 case LFUN_KEYMAP_PRIMARY:
665 case LFUN_KEYMAP_SECONDARY:
666 case LFUN_KEYMAP_TOGGLE:
668 case LFUN_BUFFER_EXPORT_CUSTOM:
669 case LFUN_BUFFER_PRINT:
670 case LFUN_PREFERENCES_SAVE:
671 case LFUN_SCREEN_FONT_UPDATE:
674 case LFUN_EXTERNAL_EDIT:
675 case LFUN_GRAPHICS_EDIT:
676 case LFUN_ALL_INSETS_TOGGLE:
677 case LFUN_BUFFER_LANGUAGE:
678 case LFUN_TEXTCLASS_APPLY:
679 case LFUN_TEXTCLASS_LOAD:
680 case LFUN_BUFFER_SAVE_AS_DEFAULT:
681 case LFUN_BUFFER_PARAMS_APPLY:
682 case LFUN_LYXRC_APPLY:
683 case LFUN_BUFFER_NEXT:
684 case LFUN_BUFFER_PREVIOUS:
685 case LFUN_WINDOW_NEW:
686 case LFUN_WINDOW_CLOSE:
688 // these are handled in our dispatch()
692 if (!getLocalStatus(cur, cmd, flag))
693 flag = view()->getStatus(cmd);
699 // Can we use a readonly buffer?
700 if (buf && buf->isReadonly()
701 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
702 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
703 flag.message(from_utf8(N_("Document is read-only")));
707 // Are we in a DELETED change-tracking region?
708 if (buf && lookupChangeType(cur, true) == Change::DELETED
709 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
710 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
711 flag.message(from_utf8(N_("This portion of the document is deleted.")));
715 // the default error message if we disable the command
716 if (!flag.enabled() && flag.message().empty())
717 flag.message(from_utf8(N_("Command disabled")));
723 bool LyXFunc::ensureBufferClean(BufferView * bv)
725 Buffer & buf = *bv->buffer();
729 docstring const file = makeDisplayPath(buf.fileName(), 30);
730 docstring text = bformat(_("The document %1$s has unsaved "
731 "changes.\n\nDo you want to save "
732 "the document?"), file);
733 int const ret = Alert::prompt(_("Save changed document?"),
734 text, 0, 1, _("&Save"),
738 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
740 return buf.isClean();
746 void showPrintError(string const & name)
748 docstring str = bformat(_("Could not print the document %1$s.\n"
749 "Check that your printer is set up correctly."),
750 makeDisplayPath(name, 50));
751 Alert::error(_("Print document failed"), str);
755 void loadTextclass(string const & name)
757 std::pair<bool, textclass_type> const tc_pair =
758 textclasslist.numberOfClass(name);
760 if (!tc_pair.first) {
761 lyxerr << "Document class \"" << name
762 << "\" does not exist."
767 textclass_type const tc = tc_pair.second;
769 if (!textclasslist[tc].load()) {
770 docstring s = bformat(_("The document could not be converted\n"
771 "into the document class %1$s."),
772 from_utf8(textclasslist[tc].name()));
773 Alert::error(_("Could not change class"), s);
778 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
783 void LyXFunc::dispatch(FuncRequest const & cmd)
785 string const argument = to_utf8(cmd.argument());
786 kb_action const action = cmd.action;
788 LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
789 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
791 // we have not done anything wrong yet.
793 dispatch_buffer.erase();
795 // redraw the screen at the end (first of the two drawing steps).
796 //This is done unless explicitely requested otherwise
797 Update::flags updateFlags = Update::FitCursor;
799 FuncStatus const flag = getStatus(cmd);
800 if (!flag.enabled()) {
801 // We cannot use this function here
802 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
803 << lyxaction.getActionName(action)
804 << " [" << action << "] is disabled at this location"
806 setErrorMessage(flag.message());
810 case LFUN_WORD_FIND_FORWARD:
811 case LFUN_WORD_FIND_BACKWARD: {
812 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
813 static docstring last_search;
814 docstring searched_string;
816 if (!cmd.argument().empty()) {
817 last_search = cmd.argument();
818 searched_string = cmd.argument();
820 searched_string = last_search;
823 if (searched_string.empty())
826 bool const fw = action == LFUN_WORD_FIND_FORWARD;
827 docstring const data =
828 find2string(searched_string, true, false, fw);
829 find(view(), FuncRequest(LFUN_WORD_FIND, data));
833 case LFUN_COMMAND_PREFIX:
834 BOOST_ASSERT(lyx_view_);
835 lyx_view_->message(keyseq->printOptions(true));
838 case LFUN_COMMAND_EXECUTE:
839 BOOST_ASSERT(lyx_view_);
840 lyx_view_->getToolbars().display("minibuffer", true);
841 lyx_view_->focus_command_buffer();
845 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
847 meta_fake_bit = key_modifier::none;
848 if (view()->buffer())
849 // cancel any selection
850 dispatch(FuncRequest(LFUN_MARK_OFF));
851 setMessage(from_ascii(N_("Cancel")));
854 case LFUN_META_PREFIX:
855 meta_fake_bit = key_modifier::alt;
856 setMessage(keyseq->print(true));
859 case LFUN_BUFFER_TOGGLE_READ_ONLY:
860 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
861 if (lyx_view_->buffer()->lyxvc().inUse())
862 lyx_view_->buffer()->lyxvc().toggleReadOnly();
864 lyx_view_->buffer()->setReadonly(
865 !lyx_view_->buffer()->isReadonly());
868 // --- Menus -----------------------------------------------
869 case LFUN_BUFFER_NEW:
870 menuNew(argument, false);
873 case LFUN_BUFFER_NEW_TEMPLATE:
874 menuNew(argument, true);
877 case LFUN_BUFFER_CLOSE:
882 case LFUN_BUFFER_WRITE:
883 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
884 if (!lyx_view_->buffer()->isUnnamed()) {
885 docstring const str = bformat(_("Saving document %1$s..."),
886 makeDisplayPath(lyx_view_->buffer()->fileName()));
887 lyx_view_->message(str);
888 menuWrite(lyx_view_->buffer());
889 lyx_view_->message(str + _(" done."));
891 writeAs(lyx_view_->buffer());
893 updateFlags = Update::None;
896 case LFUN_BUFFER_WRITE_AS:
897 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
898 writeAs(lyx_view_->buffer(), argument);
899 updateFlags = Update::None;
902 case LFUN_BUFFER_RELOAD: {
903 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
904 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
905 docstring text = bformat(_("Any changes will be lost. Are you sure "
906 "you want to revert to the saved version of the document %1$s?"), file);
907 int const ret = Alert::prompt(_("Revert to saved document?"),
908 text, 0, 1, _("&Revert"), _("&Cancel"));
915 case LFUN_BUFFER_UPDATE:
916 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
917 Exporter::Export(lyx_view_->buffer(), argument, true);
920 case LFUN_BUFFER_VIEW:
921 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
922 Exporter::preview(lyx_view_->buffer(), argument);
925 case LFUN_BUILD_PROGRAM:
926 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
927 Exporter::Export(lyx_view_->buffer(), "program", true);
930 case LFUN_BUFFER_CHKTEX:
931 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
932 lyx_view_->buffer()->runChktex();
935 case LFUN_BUFFER_EXPORT:
936 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
937 if (argument == "custom")
938 lyx_view_->getDialogs().show("sendto");
940 Exporter::Export(lyx_view_->buffer(), argument, false);
944 case LFUN_BUFFER_EXPORT_CUSTOM: {
945 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
947 string command = split(argument, format_name, ' ');
948 Format const * format = formats.getFormat(format_name);
950 lyxerr << "Format \"" << format_name
951 << "\" not recognized!"
956 Buffer * buffer = lyx_view_->buffer();
958 // The name of the file created by the conversion process
961 // Output to filename
962 if (format->name() == "lyx") {
963 string const latexname =
964 buffer->getLatexName(false);
965 filename = changeExtension(latexname,
966 format->extension());
967 filename = addName(buffer->temppath(), filename);
969 if (!buffer->writeFile(FileName(filename)))
973 Exporter::Export(buffer, format_name, true, filename);
976 // Substitute $$FName for filename
977 if (!contains(command, "$$FName"))
978 command = "( " + command + " ) < $$FName";
979 command = subst(command, "$$FName", filename);
981 // Execute the command in the background
983 call.startscript(Systemcall::DontWait, command);
987 case LFUN_BUFFER_PRINT: {
988 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
991 string command = split(split(argument, target, ' '),
995 || target_name.empty()
996 || command.empty()) {
997 lyxerr << "Unable to parse \""
998 << argument << '"' << std::endl;
1001 if (target != "printer" && target != "file") {
1002 lyxerr << "Unrecognized target \""
1003 << target << '"' << std::endl;
1007 Buffer * buffer = lyx_view_->buffer();
1009 if (!Exporter::Export(buffer, "dvi", true)) {
1010 showPrintError(buffer->fileName());
1014 // Push directory path.
1015 string const path(buffer->temppath());
1016 // Prevent the compiler from optimizing away p
1018 support::Path p(pp);
1020 // there are three cases here:
1021 // 1. we print to a file
1022 // 2. we print directly to a printer
1023 // 3. we print using a spool command (print to file first)
1026 string const dviname =
1027 changeExtension(buffer->getLatexName(true),
1030 if (target == "printer") {
1031 if (!lyxrc.print_spool_command.empty()) {
1032 // case 3: print using a spool
1033 string const psname =
1034 changeExtension(dviname,".ps");
1035 command += lyxrc.print_to_file
1038 + quoteName(dviname);
1041 lyxrc.print_spool_command +' ';
1042 if (target_name != "default") {
1043 command2 += lyxrc.print_spool_printerprefix
1047 command2 += quoteName(psname);
1049 // If successful, then spool command
1050 res = one.startscript(
1055 res = one.startscript(
1056 Systemcall::DontWait,
1059 // case 2: print directly to a printer
1060 res = one.startscript(
1061 Systemcall::DontWait,
1062 command + quoteName(dviname));
1066 // case 1: print to a file
1067 FileName const filename(makeAbsPath(target_name, path));
1068 if (fs::exists(filename.toFilesystemEncoding())) {
1069 docstring text = bformat(
1070 _("The file %1$s already exists.\n\n"
1071 "Do you want to over-write that file?"),
1072 makeDisplayPath(filename.absFilename()));
1073 if (Alert::prompt(_("Over-write file?"),
1074 text, 0, 1, _("&Over-write"), _("&Cancel")) != 0)
1077 command += lyxrc.print_to_file
1078 + quoteName(filename.toFilesystemEncoding())
1080 + quoteName(dviname);
1081 res = one.startscript(Systemcall::DontWait,
1086 showPrintError(buffer->fileName());
1090 case LFUN_BUFFER_IMPORT:
1095 // quitting is triggered by the gui code
1096 // (leaving the event loop).
1097 lyx_view_->message(from_utf8(N_("Exiting.")));
1098 if (theBufferList().quitWriteAll())
1099 theApp()->gui().closeAllViews();
1102 case LFUN_BUFFER_AUTO_SAVE:
1106 case LFUN_RECONFIGURE:
1107 BOOST_ASSERT(lyx_view_);
1108 reconfigure(*lyx_view_);
1111 case LFUN_HELP_OPEN: {
1112 BOOST_ASSERT(lyx_view_);
1113 string const arg = argument;
1115 setErrorMessage(from_ascii(N_("Missing argument")));
1118 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1119 if (fname.empty()) {
1120 lyxerr << "LyX: unable to find documentation file `"
1121 << arg << "'. Bad installation?" << endl;
1124 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1125 makeDisplayPath(fname.absFilename())));
1126 lyx_view_->loadLyXFile(fname, false);
1130 // --- version control -------------------------------
1131 case LFUN_VC_REGISTER:
1132 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1133 if (!ensureBufferClean(view()))
1135 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1136 lyx_view_->buffer()->lyxvc().registrer();
1139 updateFlags = Update::Force;
1142 case LFUN_VC_CHECK_IN:
1143 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1144 if (!ensureBufferClean(view()))
1146 if (lyx_view_->buffer()->lyxvc().inUse()
1147 && !lyx_view_->buffer()->isReadonly()) {
1148 lyx_view_->buffer()->lyxvc().checkIn();
1153 case LFUN_VC_CHECK_OUT:
1154 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1155 if (!ensureBufferClean(view()))
1157 if (lyx_view_->buffer()->lyxvc().inUse()
1158 && lyx_view_->buffer()->isReadonly()) {
1159 lyx_view_->buffer()->lyxvc().checkOut();
1164 case LFUN_VC_REVERT:
1165 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1166 lyx_view_->buffer()->lyxvc().revert();
1170 case LFUN_VC_UNDO_LAST:
1171 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1172 lyx_view_->buffer()->lyxvc().undoLast();
1176 // --- buffers ----------------------------------------
1177 case LFUN_BUFFER_SWITCH:
1178 BOOST_ASSERT(lyx_view_);
1179 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1182 case LFUN_BUFFER_NEXT:
1183 BOOST_ASSERT(lyx_view_);
1184 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1187 case LFUN_BUFFER_PREVIOUS:
1188 BOOST_ASSERT(lyx_view_);
1189 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1193 BOOST_ASSERT(lyx_view_);
1194 newFile(view(), argument);
1197 case LFUN_FILE_OPEN:
1198 BOOST_ASSERT(lyx_view_);
1202 case LFUN_DROP_LAYOUTS_CHOICE:
1203 BOOST_ASSERT(lyx_view_);
1204 lyx_view_->getToolbars().openLayoutList();
1207 case LFUN_MENU_OPEN:
1208 BOOST_ASSERT(lyx_view_);
1209 lyx_view_->getMenubar().openByName(from_utf8(argument));
1212 // --- lyxserver commands ----------------------------
1213 case LFUN_SERVER_GET_NAME:
1214 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1215 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1216 LYXERR(Debug::INFO) << "FNAME["
1217 << lyx_view_->buffer()->fileName()
1221 case LFUN_SERVER_NOTIFY:
1222 dispatch_buffer = keyseq->print(false);
1223 theServer().notifyClient(to_utf8(dispatch_buffer));
1226 case LFUN_SERVER_GOTO_FILE_ROW: {
1227 BOOST_ASSERT(lyx_view_);
1230 istringstream is(argument);
1231 is >> file_name >> row;
1232 if (prefixIs(file_name, package().temp_dir().absFilename())) {
1233 // Needed by inverse dvi search. If it is a file
1234 // in tmpdir, call the apropriated function
1235 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1237 // Must replace extension of the file to be .lyx
1238 // and get full path
1239 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1240 // Either change buffer or load the file
1241 if (theBufferList().exists(s.absFilename())) {
1242 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1244 lyx_view_->loadLyXFile(s);
1248 view()->setCursorFromRow(row);
1250 updateFlags = Update::FitCursor;
1254 case LFUN_DIALOG_SHOW: {
1255 BOOST_ASSERT(lyx_view_);
1256 string const name = cmd.getArg(0);
1257 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1259 if (name == "character") {
1260 data = freefont2string();
1262 lyx_view_->getDialogs().show("character", data);
1263 } else if (name == "latexlog") {
1264 pair<Buffer::LogType, string> const logfile =
1265 lyx_view_->buffer()->getLogName();
1266 switch (logfile.first) {
1267 case Buffer::latexlog:
1270 case Buffer::buildlog:
1274 data += Lexer::quoteString(logfile.second);
1275 lyx_view_->getDialogs().show("log", data);
1276 } else if (name == "vclog") {
1277 string const data = "vc " +
1278 Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1279 lyx_view_->getDialogs().show("log", data);
1281 lyx_view_->getDialogs().show(name, data);
1285 case LFUN_DIALOG_SHOW_NEW_INSET: {
1286 BOOST_ASSERT(lyx_view_);
1287 string const name = cmd.getArg(0);
1288 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1289 if (name == "bibitem" ||
1293 name == "nomenclature" ||
1297 InsetCommandParams p(name);
1298 data = InsetCommandMailer::params2string(name, p);
1299 } else if (name == "include") {
1300 // data is the include type: one of "include",
1301 // "input", "verbatiminput" or "verbatiminput*"
1303 // default type is requested
1305 InsetCommandParams p(data);
1306 data = InsetIncludeMailer::params2string(p);
1307 } else if (name == "box") {
1308 // \c data == "Boxed" || "Frameless" etc
1309 InsetBoxParams p(data);
1310 data = InsetBoxMailer::params2string(p);
1311 } else if (name == "branch") {
1312 InsetBranchParams p;
1313 data = InsetBranchMailer::params2string(p);
1314 } else if (name == "citation") {
1315 InsetCommandParams p("cite");
1316 data = InsetCommandMailer::params2string(name, p);
1317 } else if (name == "ert") {
1318 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1319 } else if (name == "external") {
1320 InsetExternalParams p;
1321 Buffer const & buffer = *lyx_view_->buffer();
1322 data = InsetExternalMailer::params2string(p, buffer);
1323 } else if (name == "float") {
1325 data = InsetFloatMailer::params2string(p);
1326 } else if (name == "listings") {
1327 InsetListingsParams p;
1328 data = InsetListingsMailer::params2string(p);
1329 } else if (name == "graphics") {
1330 InsetGraphicsParams p;
1331 Buffer const & buffer = *lyx_view_->buffer();
1332 data = InsetGraphicsMailer::params2string(p, buffer);
1333 } else if (name == "note") {
1335 data = InsetNoteMailer::params2string(p);
1336 } else if (name == "vspace") {
1338 data = InsetVSpaceMailer::params2string(space);
1339 } else if (name == "wrap") {
1341 data = InsetWrapMailer::params2string(p);
1343 lyx_view_->getDialogs().show(name, data, 0);
1347 case LFUN_DIALOG_UPDATE: {
1348 BOOST_ASSERT(lyx_view_);
1349 string const & name = argument;
1350 // Can only update a dialog connected to an existing inset
1351 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1353 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1354 inset->dispatch(view()->cursor(), fr);
1355 } else if (name == "paragraph") {
1356 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1357 } else if (name == "prefs") {
1358 lyx_view_->getDialogs().update(name, string());
1363 case LFUN_DIALOG_HIDE:
1364 Dialogs::hide(argument, 0);
1367 case LFUN_DIALOG_TOGGLE: {
1368 BOOST_ASSERT(lyx_view_);
1369 if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1370 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1372 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1376 case LFUN_DIALOG_DISCONNECT_INSET:
1377 BOOST_ASSERT(lyx_view_);
1378 lyx_view_->getDialogs().disconnect(argument);
1382 case LFUN_CITATION_INSERT: {
1383 BOOST_ASSERT(lyx_view_);
1384 if (!argument.empty()) {
1385 // we can have one optional argument, delimited by '|'
1386 // citation-insert <key>|<text_before>
1387 // this should be enhanced to also support text_after
1388 // and citation style
1389 string arg = argument;
1391 if (contains(argument, "|")) {
1392 arg = token(argument, '|', 0);
1393 opt1 = token(argument, '|', 1);
1395 InsetCommandParams icp("cite");
1396 icp["key"] = from_utf8(arg);
1398 icp["before"] = from_utf8(opt1);
1399 string icstr = InsetCommandMailer::params2string("citation", icp);
1400 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1403 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1407 case LFUN_BUFFER_CHILD_OPEN: {
1408 BOOST_ASSERT(lyx_view_);
1409 FileName const filename =
1410 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1411 setMessage(bformat(_("Opening child document %1$s..."),
1412 makeDisplayPath(filename.absFilename())));
1413 view()->saveBookmark(false);
1414 string const parentfilename = lyx_view_->buffer()->fileName();
1415 if (theBufferList().exists(filename.absFilename()))
1416 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1418 lyx_view_->loadLyXFile(filename);
1419 // Set the parent name of the child document.
1420 // This makes insertion of citations and references in the child work,
1421 // when the target is in the parent or another child document.
1422 lyx_view_->buffer()->setParentName(parentfilename);
1426 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1427 BOOST_ASSERT(lyx_view_);
1428 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1431 case LFUN_KEYMAP_OFF:
1432 BOOST_ASSERT(lyx_view_);
1433 lyx_view_->view()->getIntl().keyMapOn(false);
1436 case LFUN_KEYMAP_PRIMARY:
1437 BOOST_ASSERT(lyx_view_);
1438 lyx_view_->view()->getIntl().keyMapPrim();
1441 case LFUN_KEYMAP_SECONDARY:
1442 BOOST_ASSERT(lyx_view_);
1443 lyx_view_->view()->getIntl().keyMapSec();
1446 case LFUN_KEYMAP_TOGGLE:
1447 BOOST_ASSERT(lyx_view_);
1448 lyx_view_->view()->getIntl().toggleKeyMap();
1454 string rest = split(argument, countstr, ' ');
1455 istringstream is(countstr);
1458 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1459 for (int i = 0; i < count; ++i)
1460 dispatch(lyxaction.lookupFunc(rest));
1464 case LFUN_COMMAND_SEQUENCE: {
1465 // argument contains ';'-terminated commands
1466 string arg = argument;
1467 while (!arg.empty()) {
1469 arg = split(arg, first, ';');
1470 FuncRequest func(lyxaction.lookupFunc(first));
1471 func.origin = cmd.origin;
1477 case LFUN_PREFERENCES_SAVE: {
1478 lyxrc.write(makeAbsPath("preferences",
1479 package().user_support().absFilename()),
1484 case LFUN_SCREEN_FONT_UPDATE:
1485 BOOST_ASSERT(lyx_view_);
1486 // handle the screen font changes.
1487 theFontLoader().update();
1488 /// FIXME: only the current view will be updated. the Gui
1489 /// class is able to furnish the list of views.
1490 updateFlags = Update::Force;
1493 case LFUN_SET_COLOR: {
1495 string const x11_name = split(argument, lyx_name, ' ');
1496 if (lyx_name.empty() || x11_name.empty()) {
1497 setErrorMessage(from_ascii(N_(
1498 "Syntax: set-color <lyx_name>"
1503 bool const graphicsbg_changed =
1504 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1505 x11_name != lcolor.getX11Name(Color::graphicsbg));
1507 if (!lcolor.setColor(lyx_name, x11_name)) {
1509 bformat(_("Set-color \"%1$s\" failed "
1510 "- color is undefined or "
1511 "may not be redefined"),
1512 from_utf8(lyx_name)));
1516 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1518 if (graphicsbg_changed) {
1519 #ifdef WITH_WARNINGS
1520 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1523 graphics::GCache::get().changeDisplay(true);
1530 BOOST_ASSERT(lyx_view_);
1531 lyx_view_->message(from_utf8(argument));
1534 case LFUN_EXTERNAL_EDIT: {
1535 BOOST_ASSERT(lyx_view_);
1536 FuncRequest fr(action, argument);
1537 InsetExternal().dispatch(view()->cursor(), fr);
1541 case LFUN_GRAPHICS_EDIT: {
1542 FuncRequest fr(action, argument);
1543 InsetGraphics().dispatch(view()->cursor(), fr);
1547 case LFUN_INSET_APPLY: {
1548 BOOST_ASSERT(lyx_view_);
1549 string const name = cmd.getArg(0);
1550 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1552 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1553 inset->dispatch(view()->cursor(), fr);
1555 FuncRequest fr(LFUN_INSET_INSERT, argument);
1558 // ideally, the update flag should be set by the insets,
1559 // but this is not possible currently
1560 updateFlags = Update::Force | Update::FitCursor;
1564 case LFUN_ALL_INSETS_TOGGLE: {
1565 BOOST_ASSERT(lyx_view_);
1567 string const name = split(argument, action, ' ');
1568 Inset::Code const inset_code =
1569 Inset::translate(name);
1571 Cursor & cur = view()->cursor();
1572 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1574 Inset & inset = lyx_view_->buffer()->inset();
1575 InsetIterator it = inset_iterator_begin(inset);
1576 InsetIterator const end = inset_iterator_end(inset);
1577 for (; it != end; ++it) {
1578 if (!it->asInsetMath()
1579 && (inset_code == Inset::NO_CODE
1580 || inset_code == it->lyxCode())) {
1581 Cursor tmpcur = cur;
1582 tmpcur.pushLeft(*it);
1583 it->dispatch(tmpcur, fr);
1586 updateFlags = Update::Force | Update::FitCursor;
1590 case LFUN_BUFFER_LANGUAGE: {
1591 BOOST_ASSERT(lyx_view_);
1592 Buffer & buffer = *lyx_view_->buffer();
1593 Language const * oldL = buffer.params().language;
1594 Language const * newL = languages.getLanguage(argument);
1595 if (!newL || oldL == newL)
1598 if (oldL->rightToLeft() == newL->rightToLeft()
1599 && !buffer.isMultiLingual())
1600 buffer.changeLanguage(oldL, newL);
1604 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1605 string const fname =
1606 addName(addPath(package().user_support().absFilename(), "templates/"),
1608 Buffer defaults(fname);
1610 istringstream ss(argument);
1613 int const unknown_tokens = defaults.readHeader(lex);
1615 if (unknown_tokens != 0) {
1616 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1617 << unknown_tokens << " unknown token"
1618 << (unknown_tokens == 1 ? "" : "s")
1622 if (defaults.writeFile(FileName(defaults.fileName())))
1623 setMessage(bformat(_("Document defaults saved in %1$s"),
1624 makeDisplayPath(fname)));
1626 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1630 case LFUN_BUFFER_PARAMS_APPLY: {
1631 BOOST_ASSERT(lyx_view_);
1632 biblio::CiteEngine const engine =
1633 lyx_view_->buffer()->params().getEngine();
1635 istringstream ss(argument);
1638 int const unknown_tokens =
1639 lyx_view_->buffer()->readHeader(lex);
1641 if (unknown_tokens != 0) {
1642 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1643 << unknown_tokens << " unknown token"
1644 << (unknown_tokens == 1 ? "" : "s")
1647 if (engine == lyx_view_->buffer()->params().getEngine())
1650 Cursor & cur = view()->cursor();
1651 FuncRequest fr(LFUN_INSET_REFRESH);
1653 Inset & inset = lyx_view_->buffer()->inset();
1654 InsetIterator it = inset_iterator_begin(inset);
1655 InsetIterator const end = inset_iterator_end(inset);
1656 for (; it != end; ++it)
1657 if (it->lyxCode() == Inset::CITE_CODE)
1658 it->dispatch(cur, fr);
1662 case LFUN_TEXTCLASS_APPLY: {
1663 BOOST_ASSERT(lyx_view_);
1664 Buffer * buffer = lyx_view_->buffer();
1666 textclass_type const old_class =
1667 buffer->params().textclass;
1669 loadTextclass(argument);
1671 std::pair<bool, textclass_type> const tc_pair =
1672 textclasslist.numberOfClass(argument);
1677 textclass_type const new_class = tc_pair.second;
1678 if (old_class == new_class)
1682 lyx_view_->message(_("Converting document to new document class..."));
1683 recordUndoFullDocument(view());
1684 buffer->params().textclass = new_class;
1685 StableDocIterator backcur(view()->cursor());
1686 ErrorList & el = buffer->errorList("Class Switch");
1687 cap::switchBetweenClasses(
1688 old_class, new_class,
1689 static_cast<InsetText &>(buffer->inset()), el);
1691 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1693 buffer->errors("Class Switch");
1694 updateLabels(*buffer);
1695 updateFlags = Update::Force | Update::FitCursor;
1699 case LFUN_TEXTCLASS_LOAD:
1700 loadTextclass(argument);
1703 case LFUN_LYXRC_APPLY: {
1704 LyXRC const lyxrc_orig = lyxrc;
1706 istringstream ss(argument);
1707 bool const success = lyxrc.read(ss) == 0;
1710 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1711 << "Unable to read lyxrc data"
1716 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1718 /// We force the redraw in any case because there might be
1719 /// some screen font changes.
1720 /// FIXME: only the current view will be updated. the Gui
1721 /// class is able to furnish the list of views.
1722 updateFlags = Update::Force;
1726 case LFUN_WINDOW_NEW:
1727 LyX::ref().newLyXView();
1730 case LFUN_WINDOW_CLOSE:
1731 BOOST_ASSERT(lyx_view_);
1732 BOOST_ASSERT(theApp());
1733 // update bookmark pit of the current buffer before window close
1734 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1735 gotoBookmark(i+1, false, false);
1736 // ask the user for saving changes or cancel quit
1737 if (!theBufferList().quitWriteAll())
1742 case LFUN_BOOKMARK_GOTO:
1743 // go to bookmark, open unopened file and switch to buffer if necessary
1744 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1747 case LFUN_BOOKMARK_CLEAR:
1748 LyX::ref().session().bookmarks().clear();
1751 case LFUN_TOOLBAR_TOGGLE_STATE:
1752 lyx_view_->toggleToolbarState(argument);
1756 BOOST_ASSERT(lyx_view_);
1757 view()->cursor().dispatch(cmd);
1758 updateFlags = view()->cursor().result().update();
1759 if (!view()->cursor().result().dispatched())
1760 updateFlags = view()->dispatch(cmd);
1765 if (lyx_view_ && view()->buffer()) {
1766 // BufferView::update() updates the ViewMetricsInfo and
1767 // also initializes the position cache for all insets in
1768 // (at least partially) visible top-level paragraphs.
1769 // We will redraw the screen only if needed.
1770 if (view()->update(updateFlags)) {
1771 // Buffer::changed() signals that a repaint is needed.
1772 // The frontend (WorkArea) knows which area to repaint
1773 // thanks to the ViewMetricsInfo updated above.
1774 view()->buffer()->changed();
1777 lyx_view_->updateStatusBar();
1779 // if we executed a mutating lfun, mark the buffer as dirty
1781 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1782 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1783 view()->buffer()->markDirty();
1785 if (view()->cursor().inTexted()) {
1786 lyx_view_->updateLayoutChoice();
1791 lyx_view_->updateMenubar();
1792 lyx_view_->updateToolbars();
1793 // Some messages may already be translated, so we cannot use _()
1794 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1799 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1801 const bool verbose = (cmd.origin == FuncRequest::MENU
1802 || cmd.origin == FuncRequest::TOOLBAR
1803 || cmd.origin == FuncRequest::COMMANDBUFFER);
1805 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1806 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1808 lyx_view_->message(msg);
1812 docstring dispatch_msg = msg;
1813 if (!dispatch_msg.empty())
1814 dispatch_msg += ' ';
1816 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1818 bool argsadded = false;
1820 if (!cmd.argument().empty()) {
1821 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1822 comname += ' ' + cmd.argument();
1827 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1829 if (!shortcuts.empty())
1830 comname += ": " + shortcuts;
1831 else if (!argsadded && !cmd.argument().empty())
1832 comname += ' ' + cmd.argument();
1834 if (!comname.empty()) {
1835 comname = rtrim(comname);
1836 dispatch_msg += '(' + rtrim(comname) + ')';
1839 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1840 << to_utf8(dispatch_msg) << endl;
1841 if (!dispatch_msg.empty())
1842 lyx_view_->message(dispatch_msg);
1846 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1848 // FIXME: initpath is not used. What to do?
1849 string initpath = lyxrc.document_path;
1850 string filename(name);
1852 if (view()->buffer()) {
1853 string const trypath = lyx_view_->buffer()->filePath();
1854 // If directory is writeable, use this as default.
1855 if (isDirWriteable(FileName(trypath)))
1859 static int newfile_number;
1861 if (filename.empty()) {
1862 filename = addName(lyxrc.document_path,
1863 "newfile" + convert<string>(++newfile_number) + ".lyx");
1864 while (theBufferList().exists(filename) ||
1865 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1867 filename = addName(lyxrc.document_path,
1868 "newfile" + convert<string>(newfile_number) +
1873 // The template stuff
1876 FileDialog fileDlg(_("Select template file"),
1877 LFUN_SELECT_FILE_SYNC,
1878 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1879 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1881 FileDialog::Result result =
1882 fileDlg.open(from_utf8(lyxrc.template_path),
1883 FileFilterList(_("LyX Documents (*.lyx)")),
1886 if (result.first == FileDialog::Later)
1888 if (result.second.empty())
1890 templname = to_utf8(result.second);
1893 Buffer * const b = newFile(filename, templname, !name.empty());
1896 lyx_view_->setBuffer(b);
1901 void LyXFunc::open(string const & fname)
1903 string initpath = lyxrc.document_path;
1905 if (view()->buffer()) {
1906 string const trypath = lyx_view_->buffer()->filePath();
1907 // If directory is writeable, use this as default.
1908 if (isDirWriteable(FileName(trypath)))
1914 if (fname.empty()) {
1915 FileDialog fileDlg(_("Select document to open"),
1917 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1918 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1920 FileDialog::Result result =
1921 fileDlg.open(from_utf8(initpath),
1922 FileFilterList(_("LyX Documents (*.lyx)")),
1925 if (result.first == FileDialog::Later)
1928 filename = to_utf8(result.second);
1930 // check selected filename
1931 if (filename.empty()) {
1932 lyx_view_->message(_("Canceled."));
1938 // get absolute path of file and add ".lyx" to the filename if
1940 FileName const fullname = fileSearch(string(), filename, "lyx");
1941 if (!fullname.empty())
1942 filename = fullname.absFilename();
1944 // if the file doesn't exist, let the user create one
1945 if (!fs::exists(fullname.toFilesystemEncoding())) {
1946 // the user specifically chose this name. Believe him.
1947 Buffer * const b = newFile(filename, string(), true);
1949 lyx_view_->setBuffer(b);
1953 docstring const disp_fn = makeDisplayPath(filename);
1954 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1957 if (lyx_view_->loadLyXFile(fullname)) {
1958 str2 = bformat(_("Document %1$s opened."), disp_fn);
1960 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1962 lyx_view_->message(str2);
1966 void LyXFunc::doImport(string const & argument)
1969 string filename = split(argument, format, ' ');
1971 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1972 << " file: " << filename << endl;
1974 // need user interaction
1975 if (filename.empty()) {
1976 string initpath = lyxrc.document_path;
1978 if (view()->buffer()) {
1979 string const trypath = lyx_view_->buffer()->filePath();
1980 // If directory is writeable, use this as default.
1981 if (isDirWriteable(FileName(trypath)))
1985 docstring const text = bformat(_("Select %1$s file to import"),
1986 formats.prettyName(format));
1988 FileDialog fileDlg(text,
1990 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1991 make_pair(_("Examples|#E#e"),
1992 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1994 docstring filter = formats.prettyName(format);
1997 filter += from_utf8(formats.extension(format));
2000 FileDialog::Result result =
2001 fileDlg.open(from_utf8(initpath),
2002 FileFilterList(filter),
2005 if (result.first == FileDialog::Later)
2008 filename = to_utf8(result.second);
2010 // check selected filename
2011 if (filename.empty())
2012 lyx_view_->message(_("Canceled."));
2015 if (filename.empty())
2018 // get absolute path of file
2019 FileName const fullname(makeAbsPath(filename));
2021 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2023 // Check if the document already is open
2024 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2025 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2026 lyx_view_->message(_("Canceled."));
2031 // if the file exists already, and we didn't do
2032 // -i lyx thefile.lyx, warn
2033 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2034 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2036 docstring text = bformat(_("The document %1$s already exists.\n\n"
2037 "Do you want to over-write that document?"), file);
2038 int const ret = Alert::prompt(_("Over-write document?"),
2039 text, 0, 1, _("&Over-write"), _("&Cancel"));
2042 lyx_view_->message(_("Canceled."));
2047 ErrorList errorList;
2048 Importer::Import(lyx_view_, fullname, format, errorList);
2049 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2053 void LyXFunc::closeBuffer()
2055 // goto bookmark to update bookmark pit.
2056 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2057 gotoBookmark(i+1, false, false);
2058 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2059 if (theBufferList().empty()) {
2060 // need this otherwise SEGV may occur while
2061 // trying to set variables that don't exist
2062 // since there's no current buffer
2063 lyx_view_->getDialogs().hideBufferDependent();
2065 lyx_view_->setBuffer(theBufferList().first());
2071 void LyXFunc::reloadBuffer()
2073 FileName filename(lyx_view_->buffer()->fileName());
2075 lyx_view_->loadLyXFile(filename);
2078 // Each "lyx_view_" should have it's own message method. lyxview and
2079 // the minibuffer would use the minibuffer, but lyxserver would
2080 // send an ERROR signal to its client. Alejandro 970603
2081 // This function is bit problematic when it comes to NLS, to make the
2082 // lyx servers client be language indepenent we must not translate
2083 // strings sent to this func.
2084 void LyXFunc::setErrorMessage(docstring const & m) const
2086 dispatch_buffer = m;
2091 void LyXFunc::setMessage(docstring const & m) const
2093 dispatch_buffer = m;
2097 docstring const LyXFunc::viewStatusMessage()
2099 // When meta-fake key is pressed, show the key sequence so far + "M-".
2101 return keyseq->print(true) + "M-";
2103 // Else, when a non-complete key sequence is pressed,
2104 // show the available options.
2105 if (keyseq->length() > 0 && !keyseq->deleted())
2106 return keyseq->printOptions(true);
2108 if (!view()->buffer())
2109 return _("Welcome to LyX!");
2111 return view()->cursor().currentState();
2115 BufferView * LyXFunc::view() const
2117 BOOST_ASSERT(lyx_view_);
2118 return lyx_view_->view();
2122 bool LyXFunc::wasMetaKey() const
2124 return (meta_fake_bit != key_modifier::none);
2130 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2132 // Why the switch you might ask. It is a trick to ensure that all
2133 // the elements in the LyXRCTags enum is handled. As you can see
2134 // there are no breaks at all. So it is just a huge fall-through.
2135 // The nice thing is that we will get a warning from the compiler
2136 // if we forget an element.
2137 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2139 case LyXRC::RC_ACCEPT_COMPOUND:
2140 case LyXRC::RC_ALT_LANG:
2141 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2142 case LyXRC::RC_PLAINTEXT_LINELEN:
2143 case LyXRC::RC_AUTOREGIONDELETE:
2144 case LyXRC::RC_AUTORESET_OPTIONS:
2145 case LyXRC::RC_AUTOSAVE:
2146 case LyXRC::RC_AUTO_NUMBER:
2147 case LyXRC::RC_BACKUPDIR_PATH:
2148 case LyXRC::RC_BIBTEX_COMMAND:
2149 case LyXRC::RC_BINDFILE:
2150 case LyXRC::RC_CHECKLASTFILES:
2151 case LyXRC::RC_USELASTFILEPOS:
2152 case LyXRC::RC_LOADSESSION:
2153 case LyXRC::RC_CHKTEX_COMMAND:
2154 case LyXRC::RC_CONVERTER:
2155 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2156 case LyXRC::RC_COPIER:
2157 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2158 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2159 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2160 case LyXRC::RC_DATE_INSERT_FORMAT:
2161 case LyXRC::RC_DEFAULT_LANGUAGE:
2162 case LyXRC::RC_DEFAULT_PAPERSIZE:
2163 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2164 case LyXRC::RC_DISPLAY_GRAPHICS:
2165 case LyXRC::RC_DOCUMENTPATH:
2166 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2167 string const encoded = FileName(
2168 lyxrc_new.document_path).toFilesystemEncoding();
2169 if (fs::exists(encoded) && fs::is_directory(encoded))
2170 support::package().document_dir() = FileName(lyxrc.document_path);
2172 case LyXRC::RC_ESC_CHARS:
2173 case LyXRC::RC_FONT_ENCODING:
2174 case LyXRC::RC_FORMAT:
2175 case LyXRC::RC_INDEX_COMMAND:
2176 case LyXRC::RC_INPUT:
2177 case LyXRC::RC_KBMAP:
2178 case LyXRC::RC_KBMAP_PRIMARY:
2179 case LyXRC::RC_KBMAP_SECONDARY:
2180 case LyXRC::RC_LABEL_INIT_LENGTH:
2181 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2182 case LyXRC::RC_LANGUAGE_AUTO_END:
2183 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2184 case LyXRC::RC_LANGUAGE_COMMAND_END:
2185 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2186 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2187 case LyXRC::RC_LANGUAGE_PACKAGE:
2188 case LyXRC::RC_LANGUAGE_USE_BABEL:
2189 case LyXRC::RC_MAKE_BACKUP:
2190 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2191 case LyXRC::RC_NUMLASTFILES:
2192 case LyXRC::RC_PATH_PREFIX:
2193 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2194 support::prependEnvPath("PATH", lyxrc.path_prefix);
2196 case LyXRC::RC_PERS_DICT:
2197 case LyXRC::RC_PREVIEW:
2198 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2199 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2200 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2201 case LyXRC::RC_PRINTCOPIESFLAG:
2202 case LyXRC::RC_PRINTER:
2203 case LyXRC::RC_PRINTEVENPAGEFLAG:
2204 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2205 case LyXRC::RC_PRINTFILEEXTENSION:
2206 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2207 case LyXRC::RC_PRINTODDPAGEFLAG:
2208 case LyXRC::RC_PRINTPAGERANGEFLAG:
2209 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2210 case LyXRC::RC_PRINTPAPERFLAG:
2211 case LyXRC::RC_PRINTREVERSEFLAG:
2212 case LyXRC::RC_PRINTSPOOL_COMMAND:
2213 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2214 case LyXRC::RC_PRINTTOFILE:
2215 case LyXRC::RC_PRINTTOPRINTER:
2216 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2217 case LyXRC::RC_PRINT_COMMAND:
2218 case LyXRC::RC_RTL_SUPPORT:
2219 case LyXRC::RC_SCREEN_DPI:
2220 case LyXRC::RC_SCREEN_FONT_ROMAN:
2221 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2222 case LyXRC::RC_SCREEN_FONT_SANS:
2223 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2224 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2225 case LyXRC::RC_SCREEN_FONT_SIZES:
2226 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2227 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2228 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2229 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2230 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2231 case LyXRC::RC_SCREEN_ZOOM:
2232 case LyXRC::RC_SERVERPIPE:
2233 case LyXRC::RC_SET_COLOR:
2234 case LyXRC::RC_SHOW_BANNER:
2235 case LyXRC::RC_SPELL_COMMAND:
2236 case LyXRC::RC_TEMPDIRPATH:
2237 case LyXRC::RC_TEMPLATEPATH:
2238 case LyXRC::RC_TEX_ALLOWS_SPACES:
2239 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2240 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2241 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2243 case LyXRC::RC_UIFILE:
2244 case LyXRC::RC_USER_EMAIL:
2245 case LyXRC::RC_USER_NAME:
2246 case LyXRC::RC_USETEMPDIR:
2247 case LyXRC::RC_USE_ALT_LANG:
2248 case LyXRC::RC_USE_CONVERTER_CACHE:
2249 case LyXRC::RC_USE_ESC_CHARS:
2250 case LyXRC::RC_USE_INP_ENC:
2251 case LyXRC::RC_USE_PERS_DICT:
2252 case LyXRC::RC_USE_SPELL_LIB:
2253 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2254 case LyXRC::RC_VIEWER:
2255 case LyXRC::RC_LAST: