3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
25 #include "BranchList.h"
27 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
31 #include "bufferview_funcs.h"
33 #include "CutAndPaste.h"
35 #include "DispatchResult.h"
37 #include "ErrorList.h"
40 #include "FuncRequest.h"
41 #include "FuncStatus.h"
44 #include "InsetIterator.h"
52 #include "LyXAction.h"
58 #include "TextClassList.h"
60 #include "Paragraph.h"
61 #include "ParIterator.h"
62 #include "ParagraphParameters.h"
65 #include "insets/InsetBox.h"
66 #include "insets/InsetBranch.h"
67 #include "insets/InsetCommand.h"
68 #include "insets/InsetERT.h"
69 #include "insets/InsetExternal.h"
70 #include "insets/InsetFloat.h"
71 #include "insets/InsetListings.h"
72 #include "insets/InsetGraphics.h"
73 #include "insets/InsetInclude.h"
74 #include "insets/InsetNote.h"
75 #include "insets/InsetTabular.h"
76 #include "insets/InsetVSpace.h"
77 #include "insets/InsetWrap.h"
79 #include "frontends/Application.h"
80 #include "frontends/alert.h"
81 #include "frontends/Dialogs.h"
82 #include "frontends/FileDialog.h"
83 #include "frontends/FontLoader.h"
84 #include "frontends/Gui.h"
85 #include "frontends/KeySymbol.h"
86 #include "frontends/LyXView.h"
87 #include "frontends/Menubar.h"
88 #include "frontends/Toolbars.h"
90 #include "support/environment.h"
91 #include "support/FileFilterList.h"
92 #include "support/filetools.h"
93 #include "support/ForkedcallsController.h"
94 #include "support/fs_extras.h"
95 #include "support/lstrings.h"
96 #include "support/Path.h"
97 #include "support/Package.h"
98 #include "support/Systemcall.h"
99 #include "support/convert.h"
100 #include "support/os.h"
102 #include <boost/current_function.hpp>
103 #include <boost/filesystem/operations.hpp>
110 using bv_funcs::freefont2string;
112 using support::absolutePath;
113 using support::addName;
114 using support::addPath;
115 using support::bformat;
116 using support::changeExtension;
117 using support::contains;
118 using support::FileFilterList;
119 using support::FileName;
120 using support::fileSearch;
121 using support::ForkedcallsController;
122 using support::i18nLibFileSearch;
123 using support::isDirWriteable;
124 using support::isFileReadable;
125 using support::isStrInt;
126 using support::makeAbsPath;
127 using support::makeDisplayPath;
128 using support::package;
129 using support::quoteName;
130 using support::rtrim;
131 using support::split;
132 using support::subst;
133 using support::Systemcall;
134 using support::token;
136 using support::prefixIs;
139 using std::make_pair;
142 using std::istringstream;
143 using std::ostringstream;
145 namespace Alert = frontend::Alert;
146 namespace fs = boost::filesystem;
151 bool getLocalStatus(Cursor cursor,
152 FuncRequest const & cmd, FuncStatus & status)
154 // Try to fix cursor in case it is broken.
155 cursor.fixIfBroken();
157 // This is, of course, a mess. Better create a new doc iterator and use
158 // this in Inset::getStatus. This might require an additional
159 // BufferView * arg, though (which should be avoided)
160 //Cursor safe = *this;
162 for ( ; cursor.depth(); cursor.pop()) {
163 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
164 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
165 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
166 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168 // The inset's getStatus() will return 'true' if it made
169 // a definitive decision on whether it want to handle the
170 // request or not. The result of this decision is put into
171 // the 'status' parameter.
172 if (cursor.inset().getStatus(cursor, cmd, status)) {
181 /** Return the change status at cursor position, taking in account the
182 * status at each level of the document iterator (a table in a deleted
183 * footnote is deleted).
184 * When \param outer is true, the top slice is not looked at.
186 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 size_t const depth = dit.depth() - (outer ? 1 : 0);
190 for (size_t i = 0 ; i < depth ; ++i) {
191 CursorSlice const & slice = dit[i];
192 if (!slice.inset().inMathed()
193 && slice.pos() < slice.paragraph().size()) {
194 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
195 if (ch != Change::UNCHANGED)
199 return Change::UNCHANGED;
207 meta_fake_bit(key_modifier::none)
212 void LyXFunc::initKeySequences(KeyMap * kb)
214 keyseq.reset(new KeySequence(kb, kb));
215 cancel_meta_seq.reset(new KeySequence(kb, kb));
219 void LyXFunc::setLyXView(LyXView * lv)
225 void LyXFunc::handleKeyFunc(kb_action action)
227 char_type c = encoded_last_key;
229 if (keyseq->length())
232 lyx_view_->view()->getIntl().getTransManager().deadkey(
233 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
234 // Need to clear, in case the minibuffer calls these
237 // copied verbatim from do_accent_char
238 view()->cursor().resetAnchor();
243 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
245 BOOST_ASSERT(lyx_view_);
246 if (!LyX::ref().session().bookmarks().isValid(idx))
248 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
249 BOOST_ASSERT(!bm.filename.empty());
250 string const file = bm.filename.absFilename();
251 // if the file is not opened, open it.
252 if (!theBufferList().exists(file)) {
254 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
258 // open may fail, so we need to test it again
259 if (theBufferList().exists(file)) {
260 // if the current buffer is not that one, switch to it.
261 if (lyx_view_->buffer()->fileName() != file) {
263 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
267 // moveToPosition use par_id, and par_pit and return new par_id.
271 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
272 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
273 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
274 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
275 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
280 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
282 LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
284 // Do nothing if we have nothing (JMarc)
285 if (!keysym->isOK()) {
286 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
291 if (keysym->isModifier()) {
292 LYXERR(Debug::KEY) << "isModifier true" << endl;
296 //Encoding const * encoding = view()->cursor().getEncoding();
297 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
298 // FIXME: encoded_last_key shadows the member variable of the same
299 // name. Is that intended?
300 char_type encoded_last_key = keysym->getUCSEncoded();
302 // Do a one-deep top-level lookup for
303 // cancel and meta-fake keys. RVDK_PATCH_5
304 cancel_meta_seq->reset();
306 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
307 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
308 << " action first set to [" << func.action << ']'
311 // When not cancel or meta-fake, do the normal lookup.
312 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
313 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
314 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
315 // remove Caps Lock and Mod2 as a modifiers
316 func = keyseq->addkey(keysym, (state | meta_fake_bit));
317 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
318 << "action now set to ["
319 << func.action << ']' << endl;
322 // Dont remove this unless you know what you are doing.
323 meta_fake_bit = key_modifier::none;
325 // Can this happen now ?
326 if (func.action == LFUN_NOACTION) {
327 func = FuncRequest(LFUN_COMMAND_PREFIX);
330 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
332 << func.action << "]["
333 << to_utf8(keyseq->print(false)) << ']'
336 // already here we know if it any point in going further
337 // why not return already here if action == -1 and
338 // num_bytes == 0? (Lgb)
340 if (keyseq->length() > 1) {
341 lyx_view_->message(keyseq->print(true));
345 // Maybe user can only reach the key via holding down shift.
346 // Let's see. But only if shift is the only modifier
347 if (func.action == LFUN_UNKNOWN_ACTION &&
348 state == key_modifier::shift) {
349 LYXERR(Debug::KEY) << "Trying without shift" << endl;
350 func = keyseq->addkey(keysym, key_modifier::none);
351 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
354 if (func.action == LFUN_UNKNOWN_ACTION) {
355 // Hmm, we didn't match any of the keysequences. See
356 // if it's normal insertable text not already covered
358 if (keysym->isText() && keyseq->length() == 1) {
359 LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
360 func = FuncRequest(LFUN_SELF_INSERT,
361 FuncRequest::KEYBOARD);
363 LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
364 lyx_view_->message(_("Unknown function."));
369 if (func.action == LFUN_SELF_INSERT) {
370 if (encoded_last_key != 0) {
371 docstring const arg(1, encoded_last_key);
372 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
373 FuncRequest::KEYBOARD));
375 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
383 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
385 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
388 Cursor & cur = view()->cursor();
390 /* In LyX/Mac, when a dialog is open, the menus of the
391 application can still be accessed without giving focus to
392 the main window. In this case, we want to disable the menu
393 entries that are buffer-related.
395 Note that this code is not perfect, as bug 1941 attests:
396 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
398 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
399 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
402 if (cmd.action == LFUN_NOACTION) {
403 flag.message(from_utf8(N_("Nothing to do")));
408 switch (cmd.action) {
409 case LFUN_UNKNOWN_ACTION:
410 #ifndef HAVE_LIBAIKSAURUS
411 case LFUN_THESAURUS_ENTRY:
421 if (flag.unknown()) {
422 flag.message(from_utf8(N_("Unknown action")));
426 if (!flag.enabled()) {
427 if (flag.message().empty())
428 flag.message(from_utf8(N_("Command disabled")));
432 // Check whether we need a buffer
433 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
435 flag.message(from_utf8(N_("Command not allowed with"
436 "out any document open")));
441 // I would really like to avoid having this switch and rather try to
442 // encode this in the function itself.
443 // -- And I'd rather let an inset decide which LFUNs it is willing
444 // to handle (Andre')
446 switch (cmd.action) {
447 case LFUN_BUFFER_TOGGLE_READ_ONLY:
448 flag.setOnOff(buf->isReadonly());
451 case LFUN_BUFFER_SWITCH:
452 // toggle on the current buffer, but do not toggle off
453 // the other ones (is that a good idea?)
454 if (to_utf8(cmd.argument()) == buf->fileName())
458 case LFUN_BUFFER_EXPORT:
459 enable = cmd.argument() == "custom"
460 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
463 case LFUN_BUFFER_CHKTEX:
464 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
467 case LFUN_BUILD_PROGRAM:
468 enable = Exporter::isExportable(*buf, "program");
471 case LFUN_LAYOUT_TABULAR:
472 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
476 case LFUN_LAYOUT_PARAGRAPH:
477 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
480 case LFUN_VC_REGISTER:
481 enable = !buf->lyxvc().inUse();
483 case LFUN_VC_CHECK_IN:
484 enable = buf->lyxvc().inUse() && !buf->isReadonly();
486 case LFUN_VC_CHECK_OUT:
487 enable = buf->lyxvc().inUse() && buf->isReadonly();
490 case LFUN_VC_UNDO_LAST:
491 enable = buf->lyxvc().inUse();
493 case LFUN_BUFFER_RELOAD:
494 enable = !buf->isUnnamed() && !buf->isClean();
497 case LFUN_INSET_SETTINGS: {
501 Inset::Code code = cur.inset().lyxCode();
503 case Inset::TABULAR_CODE:
504 enable = cmd.argument() == "tabular";
506 case Inset::ERT_CODE:
507 enable = cmd.argument() == "ert";
509 case Inset::FLOAT_CODE:
510 enable = cmd.argument() == "float";
512 case Inset::WRAP_CODE:
513 enable = cmd.argument() == "wrap";
515 case Inset::NOTE_CODE:
516 enable = cmd.argument() == "note";
518 case Inset::BRANCH_CODE:
519 enable = cmd.argument() == "branch";
521 case Inset::BOX_CODE:
522 enable = cmd.argument() == "box";
524 case Inset::LISTINGS_CODE:
525 enable = cmd.argument() == "listings";
533 case LFUN_INSET_APPLY: {
534 string const name = cmd.getArg(0);
535 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
537 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
539 if (!inset->getStatus(cur, fr, fs)) {
540 // Every inset is supposed to handle this
545 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
546 flag |= getStatus(fr);
548 enable = flag.enabled();
552 case LFUN_DIALOG_TOGGLE:
553 flag.setOnOff(lyx_view_->getDialogs().visible(cmd.getArg(0)));
554 // fall through to set "enable"
555 case LFUN_DIALOG_SHOW: {
556 string const name = cmd.getArg(0);
558 enable = name == "aboutlyx"
559 || name == "file" //FIXME: should be removed.
561 || name == "texinfo";
562 else if (name == "print")
563 enable = Exporter::isExportable(*buf, "dvi")
564 && lyxrc.print_command != "none";
565 else if (name == "character")
566 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
567 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
568 else if (name == "latexlog")
569 enable = isFileReadable(FileName(buf->getLogName().second));
570 else if (name == "spellchecker")
571 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
572 enable = !buf->isReadonly();
576 else if (name == "vclog")
577 enable = buf->lyxvc().inUse();
581 case LFUN_DIALOG_SHOW_NEW_INSET:
582 enable = cur.inset().lyxCode() != Inset::ERT_CODE &&
583 cur.inset().lyxCode() != Inset::LISTINGS_CODE;
584 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
586 if (cur.inset().getStatus(cur, cmd, flag))
591 case LFUN_DIALOG_UPDATE: {
592 string const name = cmd.getArg(0);
594 enable = name == "prefs";
598 case LFUN_CITATION_INSERT: {
599 FuncRequest fr(LFUN_INSET_INSERT, "citation");
600 enable = getStatus(fr).enabled();
604 case LFUN_BUFFER_WRITE: {
605 enable = view()->buffer()->isUnnamed()
606 || !view()->buffer()->isClean();
610 case LFUN_BOOKMARK_GOTO: {
611 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
612 enable = LyX::ref().session().bookmarks().isValid(num);
616 case LFUN_BOOKMARK_CLEAR:
617 enable = LyX::ref().session().bookmarks().size() > 0;
620 case LFUN_TOOLBAR_TOGGLE: {
621 bool const current = lyx_view_->getToolbars().visible(cmd.getArg(0));
622 flag.setOnOff(current);
625 case LFUN_WINDOW_CLOSE: {
626 enable = (theApp()->gui().viewIds().size() > 1);
630 // this one is difficult to get right. As a half-baked
631 // solution, we consider only the first action of the sequence
632 case LFUN_COMMAND_SEQUENCE: {
633 // argument contains ';'-terminated commands
634 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
635 FuncRequest func(lyxaction.lookupFunc(firstcmd));
636 func.origin = cmd.origin;
637 flag = getStatus(func);
640 case LFUN_BUFFER_NEW:
641 case LFUN_BUFFER_NEW_TEMPLATE:
642 case LFUN_WORD_FIND_FORWARD:
643 case LFUN_WORD_FIND_BACKWARD:
644 case LFUN_COMMAND_PREFIX:
645 case LFUN_COMMAND_EXECUTE:
647 case LFUN_META_PREFIX:
648 case LFUN_BUFFER_CLOSE:
649 case LFUN_BUFFER_WRITE_AS:
650 case LFUN_BUFFER_UPDATE:
651 case LFUN_BUFFER_VIEW:
652 case LFUN_BUFFER_IMPORT:
653 case LFUN_BUFFER_AUTO_SAVE:
654 case LFUN_RECONFIGURE:
658 case LFUN_DROP_LAYOUTS_CHOICE:
660 case LFUN_SERVER_GET_NAME:
661 case LFUN_SERVER_NOTIFY:
662 case LFUN_SERVER_GOTO_FILE_ROW:
663 case LFUN_DIALOG_HIDE:
664 case LFUN_DIALOG_DISCONNECT_INSET:
665 case LFUN_BUFFER_CHILD_OPEN:
666 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
667 case LFUN_KEYMAP_OFF:
668 case LFUN_KEYMAP_PRIMARY:
669 case LFUN_KEYMAP_SECONDARY:
670 case LFUN_KEYMAP_TOGGLE:
672 case LFUN_BUFFER_EXPORT_CUSTOM:
673 case LFUN_BUFFER_PRINT:
674 case LFUN_PREFERENCES_SAVE:
675 case LFUN_SCREEN_FONT_UPDATE:
678 case LFUN_EXTERNAL_EDIT:
679 case LFUN_GRAPHICS_EDIT:
680 case LFUN_ALL_INSETS_TOGGLE:
681 case LFUN_BUFFER_LANGUAGE:
682 case LFUN_TEXTCLASS_APPLY:
683 case LFUN_TEXTCLASS_LOAD:
684 case LFUN_BUFFER_SAVE_AS_DEFAULT:
685 case LFUN_BUFFER_PARAMS_APPLY:
686 case LFUN_LYXRC_APPLY:
687 case LFUN_BUFFER_NEXT:
688 case LFUN_BUFFER_PREVIOUS:
689 case LFUN_WINDOW_NEW:
691 // these are handled in our dispatch()
695 if (!getLocalStatus(cur, cmd, flag))
696 flag = view()->getStatus(cmd);
702 // Can we use a readonly buffer?
703 if (buf && buf->isReadonly()
704 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
705 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
706 flag.message(from_utf8(N_("Document is read-only")));
710 // Are we in a DELETED change-tracking region?
711 if (buf && lookupChangeType(cur, true) == Change::DELETED
712 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
713 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
714 flag.message(from_utf8(N_("This portion of the document is deleted.")));
718 // the default error message if we disable the command
719 if (!flag.enabled() && flag.message().empty())
720 flag.message(from_utf8(N_("Command disabled")));
726 bool LyXFunc::ensureBufferClean(BufferView * bv)
728 Buffer & buf = *bv->buffer();
732 docstring const file = makeDisplayPath(buf.fileName(), 30);
733 docstring text = bformat(_("The document %1$s has unsaved "
734 "changes.\n\nDo you want to save "
735 "the document?"), file);
736 int const ret = Alert::prompt(_("Save changed document?"),
737 text, 0, 1, _("&Save"),
741 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
743 return buf.isClean();
749 void showPrintError(string const & name)
751 docstring str = bformat(_("Could not print the document %1$s.\n"
752 "Check that your printer is set up correctly."),
753 makeDisplayPath(name, 50));
754 Alert::error(_("Print document failed"), str);
758 void loadTextclass(string const & name)
760 std::pair<bool, textclass_type> const tc_pair =
761 textclasslist.numberOfClass(name);
763 if (!tc_pair.first) {
764 lyxerr << "Document class \"" << name
765 << "\" does not exist."
770 textclass_type const tc = tc_pair.second;
772 if (!textclasslist[tc].load()) {
773 docstring s = bformat(_("The document could not be converted\n"
774 "into the document class %1$s."),
775 from_utf8(textclasslist[tc].name()));
776 Alert::error(_("Could not change class"), s);
781 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
786 void LyXFunc::dispatch(FuncRequest const & cmd)
788 string const argument = to_utf8(cmd.argument());
789 kb_action const action = cmd.action;
791 LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
792 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
794 // we have not done anything wrong yet.
796 dispatch_buffer.erase();
798 // redraw the screen at the end (first of the two drawing steps).
799 //This is done unless explicitely requested otherwise
800 Update::flags updateFlags = Update::FitCursor;
802 FuncStatus const flag = getStatus(cmd);
803 if (!flag.enabled()) {
804 // We cannot use this function here
805 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
806 << lyxaction.getActionName(action)
807 << " [" << action << "] is disabled at this location"
809 setErrorMessage(flag.message());
813 case LFUN_WORD_FIND_FORWARD:
814 case LFUN_WORD_FIND_BACKWARD: {
815 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
816 static docstring last_search;
817 docstring searched_string;
819 if (!cmd.argument().empty()) {
820 last_search = cmd.argument();
821 searched_string = cmd.argument();
823 searched_string = last_search;
826 if (searched_string.empty())
829 bool const fw = action == LFUN_WORD_FIND_FORWARD;
830 docstring const data =
831 find2string(searched_string, true, false, fw);
832 find(view(), FuncRequest(LFUN_WORD_FIND, data));
836 case LFUN_COMMAND_PREFIX:
837 BOOST_ASSERT(lyx_view_);
838 lyx_view_->message(keyseq->printOptions(true));
841 case LFUN_COMMAND_EXECUTE:
842 BOOST_ASSERT(lyx_view_);
843 lyx_view_->getToolbars().display("minibuffer", true);
844 lyx_view_->focus_command_buffer();
848 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
850 meta_fake_bit = key_modifier::none;
851 if (view()->buffer())
852 // cancel any selection
853 dispatch(FuncRequest(LFUN_MARK_OFF));
854 setMessage(from_ascii(N_("Cancel")));
857 case LFUN_META_PREFIX:
858 meta_fake_bit = key_modifier::alt;
859 setMessage(keyseq->print(true));
862 case LFUN_BUFFER_TOGGLE_READ_ONLY:
863 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
864 if (lyx_view_->buffer()->lyxvc().inUse())
865 lyx_view_->buffer()->lyxvc().toggleReadOnly();
867 lyx_view_->buffer()->setReadonly(
868 !lyx_view_->buffer()->isReadonly());
871 // --- Menus -----------------------------------------------
872 case LFUN_BUFFER_NEW:
873 menuNew(argument, false);
876 case LFUN_BUFFER_NEW_TEMPLATE:
877 menuNew(argument, true);
880 case LFUN_BUFFER_CLOSE:
885 case LFUN_BUFFER_WRITE:
886 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
887 if (!lyx_view_->buffer()->isUnnamed()) {
888 docstring const str = bformat(_("Saving document %1$s..."),
889 makeDisplayPath(lyx_view_->buffer()->fileName()));
890 lyx_view_->message(str);
891 menuWrite(lyx_view_->buffer());
892 lyx_view_->message(str + _(" done."));
894 writeAs(lyx_view_->buffer());
896 updateFlags = Update::None;
899 case LFUN_BUFFER_WRITE_AS:
900 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
901 writeAs(lyx_view_->buffer(), argument);
902 updateFlags = Update::None;
905 case LFUN_BUFFER_RELOAD: {
906 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
907 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
908 docstring text = bformat(_("Any changes will be lost. Are you sure "
909 "you want to revert to the saved version of the document %1$s?"), file);
910 int const ret = Alert::prompt(_("Revert to saved document?"),
911 text, 1, 1, _("&Revert"), _("&Cancel"));
918 case LFUN_BUFFER_UPDATE:
919 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
920 Exporter::Export(lyx_view_->buffer(), argument, true);
923 case LFUN_BUFFER_VIEW:
924 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
925 Exporter::preview(lyx_view_->buffer(), argument);
928 case LFUN_BUILD_PROGRAM:
929 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
930 Exporter::Export(lyx_view_->buffer(), "program", true);
933 case LFUN_BUFFER_CHKTEX:
934 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
935 lyx_view_->buffer()->runChktex();
938 case LFUN_BUFFER_EXPORT:
939 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
940 if (argument == "custom")
941 lyx_view_->getDialogs().show("sendto");
943 Exporter::Export(lyx_view_->buffer(), argument, false);
947 case LFUN_BUFFER_EXPORT_CUSTOM: {
948 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
950 string command = split(argument, format_name, ' ');
951 Format const * format = formats.getFormat(format_name);
953 lyxerr << "Format \"" << format_name
954 << "\" not recognized!"
959 Buffer * buffer = lyx_view_->buffer();
961 // The name of the file created by the conversion process
964 // Output to filename
965 if (format->name() == "lyx") {
966 string const latexname =
967 buffer->getLatexName(false);
968 filename = changeExtension(latexname,
969 format->extension());
970 filename = addName(buffer->temppath(), filename);
972 if (!buffer->writeFile(FileName(filename)))
976 Exporter::Export(buffer, format_name, true, filename);
979 // Substitute $$FName for filename
980 if (!contains(command, "$$FName"))
981 command = "( " + command + " ) < $$FName";
982 command = subst(command, "$$FName", filename);
984 // Execute the command in the background
986 call.startscript(Systemcall::DontWait, command);
990 case LFUN_BUFFER_PRINT: {
991 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
992 // FIXME: cmd.getArg() might fail if one of the arguments
993 // contains double quotes
994 string target = cmd.getArg(0);
995 string target_name = cmd.getArg(1);
996 string command = cmd.getArg(2);
999 || target_name.empty()
1000 || command.empty()) {
1001 lyxerr << "Unable to parse \""
1002 << argument << '"' << endl;
1005 if (target != "printer" && target != "file") {
1006 lyxerr << "Unrecognized target \""
1007 << target << '"' << endl;
1011 Buffer * buffer = lyx_view_->buffer();
1013 if (!Exporter::Export(buffer, "dvi", true)) {
1014 showPrintError(buffer->fileName());
1018 // Push directory path.
1019 string const path(buffer->temppath());
1020 // Prevent the compiler from optimizing away p
1022 support::Path p(pp);
1024 // there are three cases here:
1025 // 1. we print to a file
1026 // 2. we print directly to a printer
1027 // 3. we print using a spool command (print to file first)
1030 string const dviname =
1031 changeExtension(buffer->getLatexName(true),
1034 if (target == "printer") {
1035 if (!lyxrc.print_spool_command.empty()) {
1036 // case 3: print using a spool
1037 string const psname =
1038 changeExtension(dviname,".ps");
1039 command += ' ' + lyxrc.print_to_file
1042 + quoteName(dviname);
1045 lyxrc.print_spool_command + ' ';
1046 if (target_name != "default") {
1047 command2 += lyxrc.print_spool_printerprefix
1051 command2 += quoteName(psname);
1053 // If successful, then spool command
1054 res = one.startscript(
1059 res = one.startscript(
1060 Systemcall::DontWait,
1063 // case 2: print directly to a printer
1064 if (target_name != "default")
1065 command += ' ' + lyxrc.print_to_printer + target_name + ' ';
1066 res = one.startscript(
1067 Systemcall::DontWait,
1068 command + quoteName(dviname));
1072 // case 1: print to a file
1073 FileName const filename(makeAbsPath(target_name,
1074 lyx_view_->buffer()->filePath()));
1075 FileName const dvifile(makeAbsPath(dviname, path));
1076 if (fs::exists(filename.toFilesystemEncoding())) {
1077 docstring text = bformat(
1078 _("The file %1$s already exists.\n\n"
1079 "Do you want to overwrite that file?"),
1080 makeDisplayPath(filename.absFilename()));
1081 if (Alert::prompt(_("Overwrite file?"),
1082 text, 0, 1, _("&Overwrite"), _("&Cancel")) != 0)
1085 command += ' ' + lyxrc.print_to_file
1086 + quoteName(filename.toFilesystemEncoding())
1088 + quoteName(dvifile.toFilesystemEncoding());
1089 res = one.startscript(Systemcall::DontWait,
1094 showPrintError(buffer->fileName());
1098 case LFUN_BUFFER_IMPORT:
1103 // quitting is triggered by the gui code
1104 // (leaving the event loop).
1105 lyx_view_->message(from_utf8(N_("Exiting.")));
1106 if (theBufferList().quitWriteAll())
1107 theApp()->gui().closeAllViews();
1110 case LFUN_BUFFER_AUTO_SAVE:
1114 case LFUN_RECONFIGURE:
1115 BOOST_ASSERT(lyx_view_);
1116 reconfigure(*lyx_view_);
1119 case LFUN_HELP_OPEN: {
1120 BOOST_ASSERT(lyx_view_);
1121 string const arg = argument;
1123 setErrorMessage(from_ascii(N_("Missing argument")));
1126 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1127 if (fname.empty()) {
1128 lyxerr << "LyX: unable to find documentation file `"
1129 << arg << "'. Bad installation?" << endl;
1132 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1133 makeDisplayPath(fname.absFilename())));
1134 lyx_view_->loadLyXFile(fname, false);
1138 // --- version control -------------------------------
1139 case LFUN_VC_REGISTER:
1140 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1141 if (!ensureBufferClean(view()))
1143 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1144 lyx_view_->buffer()->lyxvc().registrer();
1147 updateFlags = Update::Force;
1150 case LFUN_VC_CHECK_IN:
1151 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1152 if (!ensureBufferClean(view()))
1154 if (lyx_view_->buffer()->lyxvc().inUse()
1155 && !lyx_view_->buffer()->isReadonly()) {
1156 lyx_view_->buffer()->lyxvc().checkIn();
1161 case LFUN_VC_CHECK_OUT:
1162 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1163 if (!ensureBufferClean(view()))
1165 if (lyx_view_->buffer()->lyxvc().inUse()
1166 && lyx_view_->buffer()->isReadonly()) {
1167 lyx_view_->buffer()->lyxvc().checkOut();
1172 case LFUN_VC_REVERT:
1173 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1174 lyx_view_->buffer()->lyxvc().revert();
1178 case LFUN_VC_UNDO_LAST:
1179 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1180 lyx_view_->buffer()->lyxvc().undoLast();
1184 // --- buffers ----------------------------------------
1185 case LFUN_BUFFER_SWITCH:
1186 BOOST_ASSERT(lyx_view_);
1187 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1190 case LFUN_BUFFER_NEXT:
1191 BOOST_ASSERT(lyx_view_);
1192 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1195 case LFUN_BUFFER_PREVIOUS:
1196 BOOST_ASSERT(lyx_view_);
1197 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1201 BOOST_ASSERT(lyx_view_);
1202 newFile(view(), argument);
1205 case LFUN_FILE_OPEN:
1206 BOOST_ASSERT(lyx_view_);
1210 case LFUN_DROP_LAYOUTS_CHOICE:
1211 BOOST_ASSERT(lyx_view_);
1212 lyx_view_->getToolbars().openLayoutList();
1215 case LFUN_MENU_OPEN:
1216 BOOST_ASSERT(lyx_view_);
1217 lyx_view_->getMenubar().openByName(from_utf8(argument));
1220 // --- lyxserver commands ----------------------------
1221 case LFUN_SERVER_GET_NAME:
1222 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1223 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1224 LYXERR(Debug::INFO) << "FNAME["
1225 << lyx_view_->buffer()->fileName()
1229 case LFUN_SERVER_NOTIFY:
1230 dispatch_buffer = keyseq->print(false);
1231 theServer().notifyClient(to_utf8(dispatch_buffer));
1234 case LFUN_SERVER_GOTO_FILE_ROW: {
1235 BOOST_ASSERT(lyx_view_);
1238 istringstream is(argument);
1239 is >> file_name >> row;
1240 if (prefixIs(file_name, package().temp_dir().absFilename())) {
1241 // Needed by inverse dvi search. If it is a file
1242 // in tmpdir, call the apropriated function
1243 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1245 // Must replace extension of the file to be .lyx
1246 // and get full path
1247 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1248 // Either change buffer or load the file
1249 if (theBufferList().exists(s.absFilename())) {
1250 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1252 lyx_view_->loadLyXFile(s);
1256 view()->setCursorFromRow(row);
1258 updateFlags = Update::FitCursor;
1262 case LFUN_DIALOG_SHOW: {
1263 BOOST_ASSERT(lyx_view_);
1264 string const name = cmd.getArg(0);
1265 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1267 if (name == "character") {
1268 data = freefont2string();
1270 lyx_view_->getDialogs().show("character", data);
1271 } else if (name == "latexlog") {
1272 pair<Buffer::LogType, string> const logfile =
1273 lyx_view_->buffer()->getLogName();
1274 switch (logfile.first) {
1275 case Buffer::latexlog:
1278 case Buffer::buildlog:
1282 data += Lexer::quoteString(logfile.second);
1283 lyx_view_->getDialogs().show("log", data);
1284 } else if (name == "vclog") {
1285 string const data = "vc " +
1286 Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1287 lyx_view_->getDialogs().show("log", data);
1289 lyx_view_->getDialogs().show(name, data);
1293 case LFUN_DIALOG_SHOW_NEW_INSET: {
1294 BOOST_ASSERT(lyx_view_);
1295 string const name = cmd.getArg(0);
1296 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1297 if (name == "bibitem" ||
1301 name == "nomenclature" ||
1305 InsetCommandParams p(name);
1306 data = InsetCommandMailer::params2string(name, p);
1307 } else if (name == "include") {
1308 // data is the include type: one of "include",
1309 // "input", "verbatiminput" or "verbatiminput*"
1311 // default type is requested
1313 InsetCommandParams p(data);
1314 data = InsetIncludeMailer::params2string(p);
1315 } else if (name == "box") {
1316 // \c data == "Boxed" || "Frameless" etc
1317 InsetBoxParams p(data);
1318 data = InsetBoxMailer::params2string(p);
1319 } else if (name == "branch") {
1320 InsetBranchParams p;
1321 data = InsetBranchMailer::params2string(p);
1322 } else if (name == "citation") {
1323 InsetCommandParams p("cite");
1324 data = InsetCommandMailer::params2string(name, p);
1325 } else if (name == "ert") {
1326 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1327 } else if (name == "external") {
1328 InsetExternalParams p;
1329 Buffer const & buffer = *lyx_view_->buffer();
1330 data = InsetExternalMailer::params2string(p, buffer);
1331 } else if (name == "float") {
1333 data = InsetFloatMailer::params2string(p);
1334 } else if (name == "listings") {
1335 InsetListingsParams p;
1336 data = InsetListingsMailer::params2string(p);
1337 } else if (name == "graphics") {
1338 InsetGraphicsParams p;
1339 Buffer const & buffer = *lyx_view_->buffer();
1340 data = InsetGraphicsMailer::params2string(p, buffer);
1341 } else if (name == "note") {
1343 data = InsetNoteMailer::params2string(p);
1344 } else if (name == "vspace") {
1346 data = InsetVSpaceMailer::params2string(space);
1347 } else if (name == "wrap") {
1349 data = InsetWrapMailer::params2string(p);
1351 lyx_view_->getDialogs().show(name, data, 0);
1355 case LFUN_DIALOG_UPDATE: {
1356 BOOST_ASSERT(lyx_view_);
1357 string const & name = argument;
1358 // Can only update a dialog connected to an existing inset
1359 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1361 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1362 inset->dispatch(view()->cursor(), fr);
1363 } else if (name == "paragraph") {
1364 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1365 } else if (name == "prefs") {
1366 lyx_view_->getDialogs().update(name, string());
1371 case LFUN_DIALOG_HIDE:
1372 Dialogs::hide(argument, 0);
1375 case LFUN_DIALOG_TOGGLE: {
1376 BOOST_ASSERT(lyx_view_);
1377 if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1378 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1380 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1384 case LFUN_DIALOG_DISCONNECT_INSET:
1385 BOOST_ASSERT(lyx_view_);
1386 lyx_view_->getDialogs().disconnect(argument);
1390 case LFUN_CITATION_INSERT: {
1391 BOOST_ASSERT(lyx_view_);
1392 if (!argument.empty()) {
1393 // we can have one optional argument, delimited by '|'
1394 // citation-insert <key>|<text_before>
1395 // this should be enhanced to also support text_after
1396 // and citation style
1397 string arg = argument;
1399 if (contains(argument, "|")) {
1400 arg = token(argument, '|', 0);
1401 opt1 = token(argument, '|', 1);
1403 InsetCommandParams icp("cite");
1404 icp["key"] = from_utf8(arg);
1406 icp["before"] = from_utf8(opt1);
1407 string icstr = InsetCommandMailer::params2string("citation", icp);
1408 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1411 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1415 case LFUN_BUFFER_CHILD_OPEN: {
1416 // takes an optional argument, "|bool", at the end
1417 // indicating whether this file is being opened automatically
1418 // by LyX itself, in which case we will not want to switch
1419 // buffers after opening. The default is false, so in practice
1420 // it is used only when true.
1421 BOOST_ASSERT(lyx_view_);
1422 int const arglength = argument.length();
1424 bool autoOpen = false;
1425 if (argument.substr(arglength - 5, 5) == "|true") {
1427 filename = makeAbsPath(argument.substr(0, arglength - 5),
1428 lyx_view_->buffer()->filePath());
1429 } else if (argument.substr(arglength - 6, 6) == "|false") {
1430 filename = makeAbsPath(argument.substr(0, arglength - 6),
1431 lyx_view_->buffer()->filePath());
1433 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1434 view()->saveBookmark(false);
1435 if (theBufferList().exists(filename.absFilename())) {
1436 Buffer * buf = theBufferList().getBuffer(filename.absFilename());
1438 lyx_view_->setBuffer(buf, true);
1440 buf->setParentName(lyx_view_->buffer()->fileName());
1442 lyx_view_->loadLyXFile(filename, true, true, autoOpen);
1444 // If a screen update is required (in case where auto_open is false),
1445 // loadLyXFile() would have taken care of it already. Otherwise we shall
1446 // reset the update flag because it can cause a circular problem.
1448 updateFlags = Update::None;
1452 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1453 BOOST_ASSERT(lyx_view_);
1454 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1457 case LFUN_KEYMAP_OFF:
1458 BOOST_ASSERT(lyx_view_);
1459 lyx_view_->view()->getIntl().keyMapOn(false);
1462 case LFUN_KEYMAP_PRIMARY:
1463 BOOST_ASSERT(lyx_view_);
1464 lyx_view_->view()->getIntl().keyMapPrim();
1467 case LFUN_KEYMAP_SECONDARY:
1468 BOOST_ASSERT(lyx_view_);
1469 lyx_view_->view()->getIntl().keyMapSec();
1472 case LFUN_KEYMAP_TOGGLE:
1473 BOOST_ASSERT(lyx_view_);
1474 lyx_view_->view()->getIntl().toggleKeyMap();
1480 string rest = split(argument, countstr, ' ');
1481 istringstream is(countstr);
1484 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1485 for (int i = 0; i < count; ++i)
1486 dispatch(lyxaction.lookupFunc(rest));
1490 case LFUN_COMMAND_SEQUENCE: {
1491 // argument contains ';'-terminated commands
1492 string arg = argument;
1493 while (!arg.empty()) {
1495 arg = split(arg, first, ';');
1496 FuncRequest func(lyxaction.lookupFunc(first));
1497 func.origin = cmd.origin;
1503 case LFUN_PREFERENCES_SAVE: {
1504 lyxrc.write(makeAbsPath("preferences",
1505 package().user_support().absFilename()),
1510 case LFUN_SCREEN_FONT_UPDATE:
1511 BOOST_ASSERT(lyx_view_);
1512 // handle the screen font changes.
1513 theFontLoader().update();
1514 /// FIXME: only the current view will be updated. the Gui
1515 /// class is able to furnish the list of views.
1516 updateFlags = Update::Force;
1519 case LFUN_SET_COLOR: {
1521 string const x11_name = split(argument, lyx_name, ' ');
1522 if (lyx_name.empty() || x11_name.empty()) {
1523 setErrorMessage(from_ascii(N_(
1524 "Syntax: set-color <lyx_name>"
1529 bool const graphicsbg_changed =
1530 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1531 x11_name != lcolor.getX11Name(Color::graphicsbg));
1533 if (!lcolor.setColor(lyx_name, x11_name)) {
1535 bformat(_("Set-color \"%1$s\" failed "
1536 "- color is undefined or "
1537 "may not be redefined"),
1538 from_utf8(lyx_name)));
1542 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1544 if (graphicsbg_changed) {
1545 #ifdef WITH_WARNINGS
1546 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1549 graphics::GCache::get().changeDisplay(true);
1556 BOOST_ASSERT(lyx_view_);
1557 lyx_view_->message(from_utf8(argument));
1560 case LFUN_EXTERNAL_EDIT: {
1561 BOOST_ASSERT(lyx_view_);
1562 FuncRequest fr(action, argument);
1563 InsetExternal().dispatch(view()->cursor(), fr);
1567 case LFUN_GRAPHICS_EDIT: {
1568 FuncRequest fr(action, argument);
1569 InsetGraphics().dispatch(view()->cursor(), fr);
1573 case LFUN_INSET_APPLY: {
1574 BOOST_ASSERT(lyx_view_);
1575 string const name = cmd.getArg(0);
1576 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1578 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1579 inset->dispatch(view()->cursor(), fr);
1581 FuncRequest fr(LFUN_INSET_INSERT, argument);
1584 // ideally, the update flag should be set by the insets,
1585 // but this is not possible currently
1586 updateFlags = Update::Force | Update::FitCursor;
1590 case LFUN_ALL_INSETS_TOGGLE: {
1591 BOOST_ASSERT(lyx_view_);
1593 string const name = split(argument, action, ' ');
1594 Inset::Code const inset_code =
1595 Inset::translate(name);
1597 Cursor & cur = view()->cursor();
1598 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1600 Inset & inset = lyx_view_->buffer()->inset();
1601 InsetIterator it = inset_iterator_begin(inset);
1602 InsetIterator const end = inset_iterator_end(inset);
1603 for (; it != end; ++it) {
1604 if (!it->asInsetMath()
1605 && (inset_code == Inset::NO_CODE
1606 || inset_code == it->lyxCode())) {
1607 Cursor tmpcur = cur;
1608 tmpcur.pushLeft(*it);
1609 it->dispatch(tmpcur, fr);
1612 updateFlags = Update::Force | Update::FitCursor;
1616 case LFUN_BUFFER_LANGUAGE: {
1617 BOOST_ASSERT(lyx_view_);
1618 Buffer & buffer = *lyx_view_->buffer();
1619 Language const * oldL = buffer.params().language;
1620 Language const * newL = languages.getLanguage(argument);
1621 if (!newL || oldL == newL)
1624 if (oldL->rightToLeft() == newL->rightToLeft()
1625 && !buffer.isMultiLingual())
1626 buffer.changeLanguage(oldL, newL);
1630 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1631 string const fname =
1632 addName(addPath(package().user_support().absFilename(), "templates/"),
1634 Buffer defaults(fname);
1636 istringstream ss(argument);
1639 int const unknown_tokens = defaults.readHeader(lex);
1641 if (unknown_tokens != 0) {
1642 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1643 << unknown_tokens << " unknown token"
1644 << (unknown_tokens == 1 ? "" : "s")
1648 if (defaults.writeFile(FileName(defaults.fileName())))
1649 setMessage(bformat(_("Document defaults saved in %1$s"),
1650 makeDisplayPath(fname)));
1652 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1656 case LFUN_BUFFER_PARAMS_APPLY: {
1657 BOOST_ASSERT(lyx_view_);
1658 biblio::CiteEngine const engine =
1659 lyx_view_->buffer()->params().getEngine();
1661 istringstream ss(argument);
1664 int const unknown_tokens =
1665 lyx_view_->buffer()->readHeader(lex);
1667 if (unknown_tokens != 0) {
1668 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1669 << unknown_tokens << " unknown token"
1670 << (unknown_tokens == 1 ? "" : "s")
1673 if (engine == lyx_view_->buffer()->params().getEngine())
1676 Cursor & cur = view()->cursor();
1677 FuncRequest fr(LFUN_INSET_REFRESH);
1679 Inset & inset = lyx_view_->buffer()->inset();
1680 InsetIterator it = inset_iterator_begin(inset);
1681 InsetIterator const end = inset_iterator_end(inset);
1682 for (; it != end; ++it)
1683 if (it->lyxCode() == Inset::CITE_CODE)
1684 it->dispatch(cur, fr);
1688 case LFUN_TEXTCLASS_APPLY: {
1689 BOOST_ASSERT(lyx_view_);
1690 Buffer * buffer = lyx_view_->buffer();
1692 textclass_type const old_class =
1693 buffer->params().textclass;
1695 loadTextclass(argument);
1697 std::pair<bool, textclass_type> const tc_pair =
1698 textclasslist.numberOfClass(argument);
1703 textclass_type const new_class = tc_pair.second;
1704 if (old_class == new_class)
1708 lyx_view_->message(_("Converting document to new document class..."));
1709 recordUndoFullDocument(view());
1710 buffer->params().textclass = new_class;
1711 StableDocIterator backcur(view()->cursor());
1712 ErrorList & el = buffer->errorList("Class Switch");
1713 cap::switchBetweenClasses(
1714 old_class, new_class,
1715 static_cast<InsetText &>(buffer->inset()), el);
1717 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1719 buffer->errors("Class Switch");
1720 updateLabels(*buffer);
1721 updateFlags = Update::Force | Update::FitCursor;
1725 case LFUN_TEXTCLASS_LOAD:
1726 loadTextclass(argument);
1729 case LFUN_LYXRC_APPLY: {
1730 LyXRC const lyxrc_orig = lyxrc;
1732 istringstream ss(argument);
1733 bool const success = lyxrc.read(ss) == 0;
1736 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1737 << "Unable to read lyxrc data"
1742 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1744 /// We force the redraw in any case because there might be
1745 /// some screen font changes.
1746 /// FIXME: only the current view will be updated. the Gui
1747 /// class is able to furnish the list of views.
1748 updateFlags = Update::Force;
1752 case LFUN_WINDOW_NEW:
1753 LyX::ref().newLyXView();
1756 case LFUN_WINDOW_CLOSE:
1757 BOOST_ASSERT(lyx_view_);
1758 BOOST_ASSERT(theApp());
1759 // update bookmark pit of the current buffer before window close
1760 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1761 gotoBookmark(i+1, false, false);
1762 // ask the user for saving changes or cancel quit
1763 if (!theBufferList().quitWriteAll())
1768 case LFUN_BOOKMARK_GOTO:
1769 // go to bookmark, open unopened file and switch to buffer if necessary
1770 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1773 case LFUN_BOOKMARK_CLEAR:
1774 LyX::ref().session().bookmarks().clear();
1777 case LFUN_TOOLBAR_TOGGLE: {
1778 BOOST_ASSERT(lyx_view_);
1779 string const name = cmd.getArg(0);
1780 bool const allowauto = cmd.getArg(1) == "allowauto";
1781 lyx_view_->toggleToolbarState(name, allowauto);
1782 ToolbarInfo::Flags const flags =
1783 lyx_view_->getToolbarState(name);
1785 if (flags & ToolbarInfo::ON)
1787 else if (flags & ToolbarInfo::OFF)
1789 else if (flags & ToolbarInfo::AUTO)
1792 setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"),
1793 from_ascii(name), state));
1798 BOOST_ASSERT(lyx_view_);
1799 view()->cursor().dispatch(cmd);
1800 updateFlags = view()->cursor().result().update();
1801 if (!view()->cursor().result().dispatched())
1802 updateFlags = view()->dispatch(cmd);
1807 if (lyx_view_ && view()->buffer()) {
1808 // BufferView::update() updates the ViewMetricsInfo and
1809 // also initializes the position cache for all insets in
1810 // (at least partially) visible top-level paragraphs.
1811 // We will redraw the screen only if needed.
1812 if (view()->update(updateFlags)) {
1813 // Buffer::changed() signals that a repaint is needed.
1814 // The frontend (WorkArea) knows which area to repaint
1815 // thanks to the ViewMetricsInfo updated above.
1816 view()->buffer()->changed();
1819 lyx_view_->updateStatusBar();
1821 // if we executed a mutating lfun, mark the buffer as dirty
1823 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1824 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1825 view()->buffer()->markDirty();
1827 if (view()->cursor().inTexted()) {
1828 lyx_view_->updateLayoutChoice();
1833 lyx_view_->updateMenubar();
1834 lyx_view_->updateToolbars();
1835 // Some messages may already be translated, so we cannot use _()
1836 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1841 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1843 const bool verbose = (cmd.origin == FuncRequest::MENU
1844 || cmd.origin == FuncRequest::TOOLBAR
1845 || cmd.origin == FuncRequest::COMMANDBUFFER);
1847 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1848 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1850 lyx_view_->message(msg);
1854 docstring dispatch_msg = msg;
1855 if (!dispatch_msg.empty())
1856 dispatch_msg += ' ';
1858 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1860 bool argsadded = false;
1862 if (!cmd.argument().empty()) {
1863 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1864 comname += ' ' + cmd.argument();
1869 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1871 if (!shortcuts.empty())
1872 comname += ": " + shortcuts;
1873 else if (!argsadded && !cmd.argument().empty())
1874 comname += ' ' + cmd.argument();
1876 if (!comname.empty()) {
1877 comname = rtrim(comname);
1878 dispatch_msg += '(' + rtrim(comname) + ')';
1881 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1882 << to_utf8(dispatch_msg) << endl;
1883 if (!dispatch_msg.empty())
1884 lyx_view_->message(dispatch_msg);
1888 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1890 // FIXME: initpath is not used. What to do?
1891 string initpath = lyxrc.document_path;
1892 string filename(name);
1894 if (view()->buffer()) {
1895 string const trypath = lyx_view_->buffer()->filePath();
1896 // If directory is writeable, use this as default.
1897 if (isDirWriteable(FileName(trypath)))
1901 static int newfile_number;
1903 if (filename.empty()) {
1904 filename = addName(lyxrc.document_path,
1905 "newfile" + convert<string>(++newfile_number) + ".lyx");
1906 while (theBufferList().exists(filename) ||
1907 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1909 filename = addName(lyxrc.document_path,
1910 "newfile" + convert<string>(newfile_number) +
1915 // The template stuff
1918 FileDialog fileDlg(_("Select template file"),
1919 LFUN_SELECT_FILE_SYNC,
1920 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1921 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1923 FileDialog::Result result =
1924 fileDlg.open(from_utf8(lyxrc.template_path),
1925 FileFilterList(_("LyX Documents (*.lyx)")),
1928 if (result.first == FileDialog::Later)
1930 if (result.second.empty())
1932 templname = to_utf8(result.second);
1935 Buffer * const b = newFile(filename, templname, !name.empty());
1938 lyx_view_->setBuffer(b);
1943 void LyXFunc::open(string const & fname)
1945 string initpath = lyxrc.document_path;
1947 if (view()->buffer()) {
1948 string const trypath = lyx_view_->buffer()->filePath();
1949 // If directory is writeable, use this as default.
1950 if (isDirWriteable(FileName(trypath)))
1956 if (fname.empty()) {
1957 FileDialog fileDlg(_("Select document to open"),
1959 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1960 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1962 FileDialog::Result result =
1963 fileDlg.open(from_utf8(initpath),
1964 FileFilterList(_("LyX Documents (*.lyx)")),
1967 if (result.first == FileDialog::Later)
1970 filename = to_utf8(result.second);
1972 // check selected filename
1973 if (filename.empty()) {
1974 lyx_view_->message(_("Canceled."));
1980 // get absolute path of file and add ".lyx" to the filename if
1982 FileName const fullname = fileSearch(string(), filename, "lyx");
1983 if (!fullname.empty())
1984 filename = fullname.absFilename();
1986 // if the file doesn't exist, let the user create one
1987 if (!fs::exists(fullname.toFilesystemEncoding())) {
1988 // the user specifically chose this name. Believe him.
1989 Buffer * const b = newFile(filename, string(), true);
1991 lyx_view_->setBuffer(b);
1995 docstring const disp_fn = makeDisplayPath(filename);
1996 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1999 if (lyx_view_->loadLyXFile(fullname)) {
2000 str2 = bformat(_("Document %1$s opened."), disp_fn);
2002 str2 = bformat(_("Could not open document %1$s"), disp_fn);
2004 lyx_view_->message(str2);
2008 void LyXFunc::doImport(string const & argument)
2011 string filename = split(argument, format, ' ');
2013 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
2014 << " file: " << filename << endl;
2016 // need user interaction
2017 if (filename.empty()) {
2018 string initpath = lyxrc.document_path;
2020 if (view()->buffer()) {
2021 string const trypath = lyx_view_->buffer()->filePath();
2022 // If directory is writeable, use this as default.
2023 if (isDirWriteable(FileName(trypath)))
2027 docstring const text = bformat(_("Select %1$s file to import"),
2028 formats.prettyName(format));
2030 FileDialog fileDlg(text,
2032 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2033 make_pair(_("Examples|#E#e"),
2034 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2036 docstring filter = formats.prettyName(format);
2039 filter += from_utf8(formats.extension(format));
2042 FileDialog::Result result =
2043 fileDlg.open(from_utf8(initpath),
2044 FileFilterList(filter),
2047 if (result.first == FileDialog::Later)
2050 filename = to_utf8(result.second);
2052 // check selected filename
2053 if (filename.empty())
2054 lyx_view_->message(_("Canceled."));
2057 if (filename.empty())
2060 // get absolute path of file
2061 FileName const fullname(makeAbsPath(filename));
2063 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2065 // Check if the document already is open
2066 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2067 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2068 lyx_view_->message(_("Canceled."));
2073 // if the file exists already, and we didn't do
2074 // -i lyx thefile.lyx, warn
2075 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2076 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2078 docstring text = bformat(_("The document %1$s already exists.\n\n"
2079 "Do you want to overwrite that document?"), file);
2080 int const ret = Alert::prompt(_("Overwrite document?"),
2081 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2084 lyx_view_->message(_("Canceled."));
2089 ErrorList errorList;
2090 Importer::Import(lyx_view_, fullname, format, errorList);
2091 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2095 void LyXFunc::closeBuffer()
2097 // goto bookmark to update bookmark pit.
2098 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2099 gotoBookmark(i+1, false, false);
2101 theBufferList().close(lyx_view_->buffer(), true);
2105 void LyXFunc::reloadBuffer()
2107 FileName filename(lyx_view_->buffer()->fileName());
2109 lyx_view_->loadLyXFile(filename);
2112 // Each "lyx_view_" should have it's own message method. lyxview and
2113 // the minibuffer would use the minibuffer, but lyxserver would
2114 // send an ERROR signal to its client. Alejandro 970603
2115 // This function is bit problematic when it comes to NLS, to make the
2116 // lyx servers client be language indepenent we must not translate
2117 // strings sent to this func.
2118 void LyXFunc::setErrorMessage(docstring const & m) const
2120 dispatch_buffer = m;
2125 void LyXFunc::setMessage(docstring const & m) const
2127 dispatch_buffer = m;
2131 docstring const LyXFunc::viewStatusMessage()
2133 // When meta-fake key is pressed, show the key sequence so far + "M-".
2135 return keyseq->print(true) + "M-";
2137 // Else, when a non-complete key sequence is pressed,
2138 // show the available options.
2139 if (keyseq->length() > 0 && !keyseq->deleted())
2140 return keyseq->printOptions(true);
2142 if (!view()->buffer())
2143 return _("Welcome to LyX!");
2145 return view()->cursor().currentState();
2149 BufferView * LyXFunc::view() const
2151 BOOST_ASSERT(lyx_view_);
2152 return lyx_view_->view();
2156 bool LyXFunc::wasMetaKey() const
2158 return (meta_fake_bit != key_modifier::none);
2164 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2166 // Why the switch you might ask. It is a trick to ensure that all
2167 // the elements in the LyXRCTags enum is handled. As you can see
2168 // there are no breaks at all. So it is just a huge fall-through.
2169 // The nice thing is that we will get a warning from the compiler
2170 // if we forget an element.
2171 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2173 case LyXRC::RC_ACCEPT_COMPOUND:
2174 case LyXRC::RC_ALT_LANG:
2175 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2176 case LyXRC::RC_PLAINTEXT_LINELEN:
2177 case LyXRC::RC_AUTOREGIONDELETE:
2178 case LyXRC::RC_AUTORESET_OPTIONS:
2179 case LyXRC::RC_AUTOSAVE:
2180 case LyXRC::RC_AUTO_NUMBER:
2181 case LyXRC::RC_BACKUPDIR_PATH:
2182 case LyXRC::RC_BIBTEX_COMMAND:
2183 case LyXRC::RC_BINDFILE:
2184 case LyXRC::RC_CHECKLASTFILES:
2185 case LyXRC::RC_USELASTFILEPOS:
2186 case LyXRC::RC_LOADSESSION:
2187 case LyXRC::RC_CHKTEX_COMMAND:
2188 case LyXRC::RC_CONVERTER:
2189 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2190 case LyXRC::RC_COPIER:
2191 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2192 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2193 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2194 case LyXRC::RC_DATE_INSERT_FORMAT:
2195 case LyXRC::RC_DEFAULT_LANGUAGE:
2196 case LyXRC::RC_DEFAULT_PAPERSIZE:
2197 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2198 case LyXRC::RC_DISPLAY_GRAPHICS:
2199 case LyXRC::RC_DOCUMENTPATH:
2200 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2201 string const encoded = FileName(
2202 lyxrc_new.document_path).toFilesystemEncoding();
2203 if (fs::exists(encoded) && fs::is_directory(encoded))
2204 support::package().document_dir() = FileName(lyxrc.document_path);
2206 case LyXRC::RC_ESC_CHARS:
2207 case LyXRC::RC_FONT_ENCODING:
2208 case LyXRC::RC_FORMAT:
2209 case LyXRC::RC_INDEX_COMMAND:
2210 case LyXRC::RC_INPUT:
2211 case LyXRC::RC_KBMAP:
2212 case LyXRC::RC_KBMAP_PRIMARY:
2213 case LyXRC::RC_KBMAP_SECONDARY:
2214 case LyXRC::RC_LABEL_INIT_LENGTH:
2215 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2216 case LyXRC::RC_LANGUAGE_AUTO_END:
2217 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2218 case LyXRC::RC_LANGUAGE_COMMAND_END:
2219 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2220 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2221 case LyXRC::RC_LANGUAGE_PACKAGE:
2222 case LyXRC::RC_LANGUAGE_USE_BABEL:
2223 case LyXRC::RC_MAKE_BACKUP:
2224 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2225 case LyXRC::RC_NUMLASTFILES:
2226 case LyXRC::RC_PATH_PREFIX:
2227 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2228 support::prependEnvPath("PATH", lyxrc.path_prefix);
2230 case LyXRC::RC_PERS_DICT:
2231 case LyXRC::RC_PREVIEW:
2232 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2233 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2234 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2235 case LyXRC::RC_PRINTCOPIESFLAG:
2236 case LyXRC::RC_PRINTER:
2237 case LyXRC::RC_PRINTEVENPAGEFLAG:
2238 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2239 case LyXRC::RC_PRINTFILEEXTENSION:
2240 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2241 case LyXRC::RC_PRINTODDPAGEFLAG:
2242 case LyXRC::RC_PRINTPAGERANGEFLAG:
2243 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2244 case LyXRC::RC_PRINTPAPERFLAG:
2245 case LyXRC::RC_PRINTREVERSEFLAG:
2246 case LyXRC::RC_PRINTSPOOL_COMMAND:
2247 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2248 case LyXRC::RC_PRINTTOFILE:
2249 case LyXRC::RC_PRINTTOPRINTER:
2250 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2251 case LyXRC::RC_PRINT_COMMAND:
2252 case LyXRC::RC_RTL_SUPPORT:
2253 case LyXRC::RC_SCREEN_DPI:
2254 case LyXRC::RC_SCREEN_FONT_ROMAN:
2255 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2256 case LyXRC::RC_SCREEN_FONT_SANS:
2257 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2258 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2259 case LyXRC::RC_SCREEN_FONT_SIZES:
2260 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2261 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2262 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2263 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2264 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2265 case LyXRC::RC_SCREEN_ZOOM:
2266 case LyXRC::RC_SERVERPIPE:
2267 case LyXRC::RC_SET_COLOR:
2268 case LyXRC::RC_SHOW_BANNER:
2269 case LyXRC::RC_SPELL_COMMAND:
2270 case LyXRC::RC_TEMPDIRPATH:
2271 case LyXRC::RC_TEMPLATEPATH:
2272 case LyXRC::RC_TEX_ALLOWS_SPACES:
2273 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2274 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2275 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2277 case LyXRC::RC_UIFILE:
2278 case LyXRC::RC_USER_EMAIL:
2279 case LyXRC::RC_USER_NAME:
2280 case LyXRC::RC_USETEMPDIR:
2281 case LyXRC::RC_USE_ALT_LANG:
2282 case LyXRC::RC_USE_CONVERTER_CACHE:
2283 case LyXRC::RC_USE_ESC_CHARS:
2284 case LyXRC::RC_USE_INP_ENC:
2285 case LyXRC::RC_USE_PERS_DICT:
2286 case LyXRC::RC_USE_SPELL_LIB:
2287 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2288 case LyXRC::RC_VIEWER:
2289 case LyXRC::RC_LAST: