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);
1447 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1448 BOOST_ASSERT(lyx_view_);
1449 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1452 case LFUN_KEYMAP_OFF:
1453 BOOST_ASSERT(lyx_view_);
1454 lyx_view_->view()->getIntl().keyMapOn(false);
1457 case LFUN_KEYMAP_PRIMARY:
1458 BOOST_ASSERT(lyx_view_);
1459 lyx_view_->view()->getIntl().keyMapPrim();
1462 case LFUN_KEYMAP_SECONDARY:
1463 BOOST_ASSERT(lyx_view_);
1464 lyx_view_->view()->getIntl().keyMapSec();
1467 case LFUN_KEYMAP_TOGGLE:
1468 BOOST_ASSERT(lyx_view_);
1469 lyx_view_->view()->getIntl().toggleKeyMap();
1475 string rest = split(argument, countstr, ' ');
1476 istringstream is(countstr);
1479 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1480 for (int i = 0; i < count; ++i)
1481 dispatch(lyxaction.lookupFunc(rest));
1485 case LFUN_COMMAND_SEQUENCE: {
1486 // argument contains ';'-terminated commands
1487 string arg = argument;
1488 while (!arg.empty()) {
1490 arg = split(arg, first, ';');
1491 FuncRequest func(lyxaction.lookupFunc(first));
1492 func.origin = cmd.origin;
1498 case LFUN_PREFERENCES_SAVE: {
1499 lyxrc.write(makeAbsPath("preferences",
1500 package().user_support().absFilename()),
1505 case LFUN_SCREEN_FONT_UPDATE:
1506 BOOST_ASSERT(lyx_view_);
1507 // handle the screen font changes.
1508 theFontLoader().update();
1509 /// FIXME: only the current view will be updated. the Gui
1510 /// class is able to furnish the list of views.
1511 updateFlags = Update::Force;
1514 case LFUN_SET_COLOR: {
1516 string const x11_name = split(argument, lyx_name, ' ');
1517 if (lyx_name.empty() || x11_name.empty()) {
1518 setErrorMessage(from_ascii(N_(
1519 "Syntax: set-color <lyx_name>"
1524 bool const graphicsbg_changed =
1525 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1526 x11_name != lcolor.getX11Name(Color::graphicsbg));
1528 if (!lcolor.setColor(lyx_name, x11_name)) {
1530 bformat(_("Set-color \"%1$s\" failed "
1531 "- color is undefined or "
1532 "may not be redefined"),
1533 from_utf8(lyx_name)));
1537 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1539 if (graphicsbg_changed) {
1540 #ifdef WITH_WARNINGS
1541 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1544 graphics::GCache::get().changeDisplay(true);
1551 BOOST_ASSERT(lyx_view_);
1552 lyx_view_->message(from_utf8(argument));
1555 case LFUN_EXTERNAL_EDIT: {
1556 BOOST_ASSERT(lyx_view_);
1557 FuncRequest fr(action, argument);
1558 InsetExternal().dispatch(view()->cursor(), fr);
1562 case LFUN_GRAPHICS_EDIT: {
1563 FuncRequest fr(action, argument);
1564 InsetGraphics().dispatch(view()->cursor(), fr);
1568 case LFUN_INSET_APPLY: {
1569 BOOST_ASSERT(lyx_view_);
1570 string const name = cmd.getArg(0);
1571 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1573 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1574 inset->dispatch(view()->cursor(), fr);
1576 FuncRequest fr(LFUN_INSET_INSERT, argument);
1579 // ideally, the update flag should be set by the insets,
1580 // but this is not possible currently
1581 updateFlags = Update::Force | Update::FitCursor;
1585 case LFUN_ALL_INSETS_TOGGLE: {
1586 BOOST_ASSERT(lyx_view_);
1588 string const name = split(argument, action, ' ');
1589 Inset::Code const inset_code =
1590 Inset::translate(name);
1592 Cursor & cur = view()->cursor();
1593 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1595 Inset & inset = lyx_view_->buffer()->inset();
1596 InsetIterator it = inset_iterator_begin(inset);
1597 InsetIterator const end = inset_iterator_end(inset);
1598 for (; it != end; ++it) {
1599 if (!it->asInsetMath()
1600 && (inset_code == Inset::NO_CODE
1601 || inset_code == it->lyxCode())) {
1602 Cursor tmpcur = cur;
1603 tmpcur.pushLeft(*it);
1604 it->dispatch(tmpcur, fr);
1607 updateFlags = Update::Force | Update::FitCursor;
1611 case LFUN_BUFFER_LANGUAGE: {
1612 BOOST_ASSERT(lyx_view_);
1613 Buffer & buffer = *lyx_view_->buffer();
1614 Language const * oldL = buffer.params().language;
1615 Language const * newL = languages.getLanguage(argument);
1616 if (!newL || oldL == newL)
1619 if (oldL->rightToLeft() == newL->rightToLeft()
1620 && !buffer.isMultiLingual())
1621 buffer.changeLanguage(oldL, newL);
1625 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1626 string const fname =
1627 addName(addPath(package().user_support().absFilename(), "templates/"),
1629 Buffer defaults(fname);
1631 istringstream ss(argument);
1634 int const unknown_tokens = defaults.readHeader(lex);
1636 if (unknown_tokens != 0) {
1637 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1638 << unknown_tokens << " unknown token"
1639 << (unknown_tokens == 1 ? "" : "s")
1643 if (defaults.writeFile(FileName(defaults.fileName())))
1644 setMessage(bformat(_("Document defaults saved in %1$s"),
1645 makeDisplayPath(fname)));
1647 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1651 case LFUN_BUFFER_PARAMS_APPLY: {
1652 BOOST_ASSERT(lyx_view_);
1653 biblio::CiteEngine const engine =
1654 lyx_view_->buffer()->params().getEngine();
1656 istringstream ss(argument);
1659 int const unknown_tokens =
1660 lyx_view_->buffer()->readHeader(lex);
1662 if (unknown_tokens != 0) {
1663 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1664 << unknown_tokens << " unknown token"
1665 << (unknown_tokens == 1 ? "" : "s")
1668 if (engine == lyx_view_->buffer()->params().getEngine())
1671 Cursor & cur = view()->cursor();
1672 FuncRequest fr(LFUN_INSET_REFRESH);
1674 Inset & inset = lyx_view_->buffer()->inset();
1675 InsetIterator it = inset_iterator_begin(inset);
1676 InsetIterator const end = inset_iterator_end(inset);
1677 for (; it != end; ++it)
1678 if (it->lyxCode() == Inset::CITE_CODE)
1679 it->dispatch(cur, fr);
1683 case LFUN_TEXTCLASS_APPLY: {
1684 BOOST_ASSERT(lyx_view_);
1685 Buffer * buffer = lyx_view_->buffer();
1687 textclass_type const old_class =
1688 buffer->params().textclass;
1690 loadTextclass(argument);
1692 std::pair<bool, textclass_type> const tc_pair =
1693 textclasslist.numberOfClass(argument);
1698 textclass_type const new_class = tc_pair.second;
1699 if (old_class == new_class)
1703 lyx_view_->message(_("Converting document to new document class..."));
1704 recordUndoFullDocument(view());
1705 buffer->params().textclass = new_class;
1706 StableDocIterator backcur(view()->cursor());
1707 ErrorList & el = buffer->errorList("Class Switch");
1708 cap::switchBetweenClasses(
1709 old_class, new_class,
1710 static_cast<InsetText &>(buffer->inset()), el);
1712 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1714 buffer->errors("Class Switch");
1715 updateLabels(*buffer);
1716 updateFlags = Update::Force | Update::FitCursor;
1720 case LFUN_TEXTCLASS_LOAD:
1721 loadTextclass(argument);
1724 case LFUN_LYXRC_APPLY: {
1725 LyXRC const lyxrc_orig = lyxrc;
1727 istringstream ss(argument);
1728 bool const success = lyxrc.read(ss) == 0;
1731 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1732 << "Unable to read lyxrc data"
1737 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1739 /// We force the redraw in any case because there might be
1740 /// some screen font changes.
1741 /// FIXME: only the current view will be updated. the Gui
1742 /// class is able to furnish the list of views.
1743 updateFlags = Update::Force;
1747 case LFUN_WINDOW_NEW:
1748 LyX::ref().newLyXView();
1751 case LFUN_WINDOW_CLOSE:
1752 BOOST_ASSERT(lyx_view_);
1753 BOOST_ASSERT(theApp());
1754 // update bookmark pit of the current buffer before window close
1755 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1756 gotoBookmark(i+1, false, false);
1757 // ask the user for saving changes or cancel quit
1758 if (!theBufferList().quitWriteAll())
1763 case LFUN_BOOKMARK_GOTO:
1764 // go to bookmark, open unopened file and switch to buffer if necessary
1765 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1768 case LFUN_BOOKMARK_CLEAR:
1769 LyX::ref().session().bookmarks().clear();
1772 case LFUN_TOOLBAR_TOGGLE: {
1773 BOOST_ASSERT(lyx_view_);
1774 string const name = cmd.getArg(0);
1775 bool const allowauto = cmd.getArg(1) == "allowauto";
1776 lyx_view_->toggleToolbarState(name, allowauto);
1777 ToolbarInfo::Flags const flags =
1778 lyx_view_->getToolbarState(name);
1780 if (flags & ToolbarInfo::ON)
1782 else if (flags & ToolbarInfo::OFF)
1784 else if (flags & ToolbarInfo::AUTO)
1787 setMessage(bformat(_("Toolbar \"%1$s\" state set to %2$s"),
1788 from_ascii(name), state));
1793 BOOST_ASSERT(lyx_view_);
1794 view()->cursor().dispatch(cmd);
1795 updateFlags = view()->cursor().result().update();
1796 if (!view()->cursor().result().dispatched())
1797 updateFlags = view()->dispatch(cmd);
1802 if (lyx_view_ && view()->buffer()) {
1803 // BufferView::update() updates the ViewMetricsInfo and
1804 // also initializes the position cache for all insets in
1805 // (at least partially) visible top-level paragraphs.
1806 // We will redraw the screen only if needed.
1807 if (view()->update(updateFlags)) {
1808 // Buffer::changed() signals that a repaint is needed.
1809 // The frontend (WorkArea) knows which area to repaint
1810 // thanks to the ViewMetricsInfo updated above.
1811 view()->buffer()->changed();
1814 lyx_view_->updateStatusBar();
1816 // if we executed a mutating lfun, mark the buffer as dirty
1818 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1819 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1820 view()->buffer()->markDirty();
1822 if (view()->cursor().inTexted()) {
1823 lyx_view_->updateLayoutChoice();
1828 lyx_view_->updateMenubar();
1829 lyx_view_->updateToolbars();
1830 // Some messages may already be translated, so we cannot use _()
1831 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1836 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1838 const bool verbose = (cmd.origin == FuncRequest::MENU
1839 || cmd.origin == FuncRequest::TOOLBAR
1840 || cmd.origin == FuncRequest::COMMANDBUFFER);
1842 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1843 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1845 lyx_view_->message(msg);
1849 docstring dispatch_msg = msg;
1850 if (!dispatch_msg.empty())
1851 dispatch_msg += ' ';
1853 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1855 bool argsadded = false;
1857 if (!cmd.argument().empty()) {
1858 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1859 comname += ' ' + cmd.argument();
1864 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1866 if (!shortcuts.empty())
1867 comname += ": " + shortcuts;
1868 else if (!argsadded && !cmd.argument().empty())
1869 comname += ' ' + cmd.argument();
1871 if (!comname.empty()) {
1872 comname = rtrim(comname);
1873 dispatch_msg += '(' + rtrim(comname) + ')';
1876 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1877 << to_utf8(dispatch_msg) << endl;
1878 if (!dispatch_msg.empty())
1879 lyx_view_->message(dispatch_msg);
1883 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1885 // FIXME: initpath is not used. What to do?
1886 string initpath = lyxrc.document_path;
1887 string filename(name);
1889 if (view()->buffer()) {
1890 string const trypath = lyx_view_->buffer()->filePath();
1891 // If directory is writeable, use this as default.
1892 if (isDirWriteable(FileName(trypath)))
1896 static int newfile_number;
1898 if (filename.empty()) {
1899 filename = addName(lyxrc.document_path,
1900 "newfile" + convert<string>(++newfile_number) + ".lyx");
1901 while (theBufferList().exists(filename) ||
1902 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1904 filename = addName(lyxrc.document_path,
1905 "newfile" + convert<string>(newfile_number) +
1910 // The template stuff
1913 FileDialog fileDlg(_("Select template file"),
1914 LFUN_SELECT_FILE_SYNC,
1915 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1916 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1918 FileDialog::Result result =
1919 fileDlg.open(from_utf8(lyxrc.template_path),
1920 FileFilterList(_("LyX Documents (*.lyx)")),
1923 if (result.first == FileDialog::Later)
1925 if (result.second.empty())
1927 templname = to_utf8(result.second);
1930 Buffer * const b = newFile(filename, templname, !name.empty());
1933 lyx_view_->setBuffer(b);
1938 void LyXFunc::open(string const & fname)
1940 string initpath = lyxrc.document_path;
1942 if (view()->buffer()) {
1943 string const trypath = lyx_view_->buffer()->filePath();
1944 // If directory is writeable, use this as default.
1945 if (isDirWriteable(FileName(trypath)))
1951 if (fname.empty()) {
1952 FileDialog fileDlg(_("Select document to open"),
1954 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1955 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1957 FileDialog::Result result =
1958 fileDlg.open(from_utf8(initpath),
1959 FileFilterList(_("LyX Documents (*.lyx)")),
1962 if (result.first == FileDialog::Later)
1965 filename = to_utf8(result.second);
1967 // check selected filename
1968 if (filename.empty()) {
1969 lyx_view_->message(_("Canceled."));
1975 // get absolute path of file and add ".lyx" to the filename if
1977 FileName const fullname = fileSearch(string(), filename, "lyx");
1978 if (!fullname.empty())
1979 filename = fullname.absFilename();
1981 // if the file doesn't exist, let the user create one
1982 if (!fs::exists(fullname.toFilesystemEncoding())) {
1983 // the user specifically chose this name. Believe him.
1984 Buffer * const b = newFile(filename, string(), true);
1986 lyx_view_->setBuffer(b);
1990 docstring const disp_fn = makeDisplayPath(filename);
1991 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1994 if (lyx_view_->loadLyXFile(fullname)) {
1995 str2 = bformat(_("Document %1$s opened."), disp_fn);
1997 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1999 lyx_view_->message(str2);
2003 void LyXFunc::doImport(string const & argument)
2006 string filename = split(argument, format, ' ');
2008 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
2009 << " file: " << filename << endl;
2011 // need user interaction
2012 if (filename.empty()) {
2013 string initpath = lyxrc.document_path;
2015 if (view()->buffer()) {
2016 string const trypath = lyx_view_->buffer()->filePath();
2017 // If directory is writeable, use this as default.
2018 if (isDirWriteable(FileName(trypath)))
2022 docstring const text = bformat(_("Select %1$s file to import"),
2023 formats.prettyName(format));
2025 FileDialog fileDlg(text,
2027 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2028 make_pair(_("Examples|#E#e"),
2029 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2031 docstring filter = formats.prettyName(format);
2034 filter += from_utf8(formats.extension(format));
2037 FileDialog::Result result =
2038 fileDlg.open(from_utf8(initpath),
2039 FileFilterList(filter),
2042 if (result.first == FileDialog::Later)
2045 filename = to_utf8(result.second);
2047 // check selected filename
2048 if (filename.empty())
2049 lyx_view_->message(_("Canceled."));
2052 if (filename.empty())
2055 // get absolute path of file
2056 FileName const fullname(makeAbsPath(filename));
2058 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2060 // Check if the document already is open
2061 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2062 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2063 lyx_view_->message(_("Canceled."));
2068 // if the file exists already, and we didn't do
2069 // -i lyx thefile.lyx, warn
2070 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2071 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2073 docstring text = bformat(_("The document %1$s already exists.\n\n"
2074 "Do you want to overwrite that document?"), file);
2075 int const ret = Alert::prompt(_("Overwrite document?"),
2076 text, 0, 1, _("&Overwrite"), _("&Cancel"));
2079 lyx_view_->message(_("Canceled."));
2084 ErrorList errorList;
2085 Importer::Import(lyx_view_, fullname, format, errorList);
2086 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2090 void LyXFunc::closeBuffer()
2092 // goto bookmark to update bookmark pit.
2093 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2094 gotoBookmark(i+1, false, false);
2096 theBufferList().close(lyx_view_->buffer(), true);
2100 void LyXFunc::reloadBuffer()
2102 FileName filename(lyx_view_->buffer()->fileName());
2104 lyx_view_->loadLyXFile(filename);
2107 // Each "lyx_view_" should have it's own message method. lyxview and
2108 // the minibuffer would use the minibuffer, but lyxserver would
2109 // send an ERROR signal to its client. Alejandro 970603
2110 // This function is bit problematic when it comes to NLS, to make the
2111 // lyx servers client be language indepenent we must not translate
2112 // strings sent to this func.
2113 void LyXFunc::setErrorMessage(docstring const & m) const
2115 dispatch_buffer = m;
2120 void LyXFunc::setMessage(docstring const & m) const
2122 dispatch_buffer = m;
2126 docstring const LyXFunc::viewStatusMessage()
2128 // When meta-fake key is pressed, show the key sequence so far + "M-".
2130 return keyseq->print(true) + "M-";
2132 // Else, when a non-complete key sequence is pressed,
2133 // show the available options.
2134 if (keyseq->length() > 0 && !keyseq->deleted())
2135 return keyseq->printOptions(true);
2137 if (!view()->buffer())
2138 return _("Welcome to LyX!");
2140 return view()->cursor().currentState();
2144 BufferView * LyXFunc::view() const
2146 BOOST_ASSERT(lyx_view_);
2147 return lyx_view_->view();
2151 bool LyXFunc::wasMetaKey() const
2153 return (meta_fake_bit != key_modifier::none);
2159 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2161 // Why the switch you might ask. It is a trick to ensure that all
2162 // the elements in the LyXRCTags enum is handled. As you can see
2163 // there are no breaks at all. So it is just a huge fall-through.
2164 // The nice thing is that we will get a warning from the compiler
2165 // if we forget an element.
2166 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2168 case LyXRC::RC_ACCEPT_COMPOUND:
2169 case LyXRC::RC_ALT_LANG:
2170 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2171 case LyXRC::RC_PLAINTEXT_LINELEN:
2172 case LyXRC::RC_AUTOREGIONDELETE:
2173 case LyXRC::RC_AUTORESET_OPTIONS:
2174 case LyXRC::RC_AUTOSAVE:
2175 case LyXRC::RC_AUTO_NUMBER:
2176 case LyXRC::RC_BACKUPDIR_PATH:
2177 case LyXRC::RC_BIBTEX_COMMAND:
2178 case LyXRC::RC_BINDFILE:
2179 case LyXRC::RC_CHECKLASTFILES:
2180 case LyXRC::RC_USELASTFILEPOS:
2181 case LyXRC::RC_LOADSESSION:
2182 case LyXRC::RC_CHKTEX_COMMAND:
2183 case LyXRC::RC_CONVERTER:
2184 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2185 case LyXRC::RC_COPIER:
2186 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2187 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2188 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2189 case LyXRC::RC_DATE_INSERT_FORMAT:
2190 case LyXRC::RC_DEFAULT_LANGUAGE:
2191 case LyXRC::RC_DEFAULT_PAPERSIZE:
2192 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2193 case LyXRC::RC_DISPLAY_GRAPHICS:
2194 case LyXRC::RC_DOCUMENTPATH:
2195 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2196 string const encoded = FileName(
2197 lyxrc_new.document_path).toFilesystemEncoding();
2198 if (fs::exists(encoded) && fs::is_directory(encoded))
2199 support::package().document_dir() = FileName(lyxrc.document_path);
2201 case LyXRC::RC_ESC_CHARS:
2202 case LyXRC::RC_FONT_ENCODING:
2203 case LyXRC::RC_FORMAT:
2204 case LyXRC::RC_INDEX_COMMAND:
2205 case LyXRC::RC_INPUT:
2206 case LyXRC::RC_KBMAP:
2207 case LyXRC::RC_KBMAP_PRIMARY:
2208 case LyXRC::RC_KBMAP_SECONDARY:
2209 case LyXRC::RC_LABEL_INIT_LENGTH:
2210 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2211 case LyXRC::RC_LANGUAGE_AUTO_END:
2212 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2213 case LyXRC::RC_LANGUAGE_COMMAND_END:
2214 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2215 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2216 case LyXRC::RC_LANGUAGE_PACKAGE:
2217 case LyXRC::RC_LANGUAGE_USE_BABEL:
2218 case LyXRC::RC_MAKE_BACKUP:
2219 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2220 case LyXRC::RC_NUMLASTFILES:
2221 case LyXRC::RC_PATH_PREFIX:
2222 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2223 support::prependEnvPath("PATH", lyxrc.path_prefix);
2225 case LyXRC::RC_PERS_DICT:
2226 case LyXRC::RC_PREVIEW:
2227 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2228 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2229 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2230 case LyXRC::RC_PRINTCOPIESFLAG:
2231 case LyXRC::RC_PRINTER:
2232 case LyXRC::RC_PRINTEVENPAGEFLAG:
2233 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2234 case LyXRC::RC_PRINTFILEEXTENSION:
2235 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2236 case LyXRC::RC_PRINTODDPAGEFLAG:
2237 case LyXRC::RC_PRINTPAGERANGEFLAG:
2238 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2239 case LyXRC::RC_PRINTPAPERFLAG:
2240 case LyXRC::RC_PRINTREVERSEFLAG:
2241 case LyXRC::RC_PRINTSPOOL_COMMAND:
2242 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2243 case LyXRC::RC_PRINTTOFILE:
2244 case LyXRC::RC_PRINTTOPRINTER:
2245 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2246 case LyXRC::RC_PRINT_COMMAND:
2247 case LyXRC::RC_RTL_SUPPORT:
2248 case LyXRC::RC_SCREEN_DPI:
2249 case LyXRC::RC_SCREEN_FONT_ROMAN:
2250 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2251 case LyXRC::RC_SCREEN_FONT_SANS:
2252 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2253 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2254 case LyXRC::RC_SCREEN_FONT_SIZES:
2255 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2256 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2257 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2258 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2259 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2260 case LyXRC::RC_SCREEN_ZOOM:
2261 case LyXRC::RC_SERVERPIPE:
2262 case LyXRC::RC_SET_COLOR:
2263 case LyXRC::RC_SHOW_BANNER:
2264 case LyXRC::RC_SPELL_COMMAND:
2265 case LyXRC::RC_TEMPDIRPATH:
2266 case LyXRC::RC_TEMPLATEPATH:
2267 case LyXRC::RC_TEX_ALLOWS_SPACES:
2268 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2269 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2270 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2272 case LyXRC::RC_UIFILE:
2273 case LyXRC::RC_USER_EMAIL:
2274 case LyXRC::RC_USER_NAME:
2275 case LyXRC::RC_USETEMPDIR:
2276 case LyXRC::RC_USE_ALT_LANG:
2277 case LyXRC::RC_USE_CONVERTER_CACHE:
2278 case LyXRC::RC_USE_ESC_CHARS:
2279 case LyXRC::RC_USE_INP_ENC:
2280 case LyXRC::RC_USE_PERS_DICT:
2281 case LyXRC::RC_USE_SPELL_LIB:
2282 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2283 case LyXRC::RC_VIEWER:
2284 case LyXRC::RC_LAST: