3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alfredo Braunstein
7 * \author Lars Gullik Bjønnes
8 * \author Jean-Marc Lasgouttes
9 * \author Angus Leeming
11 * \author André Pönitz
14 * \author Martin Vermeer
15 * \author Jürgen Vigna
17 * Full author contact details are available in file CREDITS.
24 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "BufferList.h"
28 #include "BufferParams.h"
29 #include "BufferView.h"
30 #include "bufferview_funcs.h"
32 #include "CutAndPaste.h"
34 #include "DispatchResult.h"
36 #include "ErrorList.h"
39 #include "FuncRequest.h"
40 #include "FuncStatus.h"
43 #include "InsetIterator.h"
51 #include "LyXAction.h"
57 #include "TextClassList.h"
59 #include "Paragraph.h"
60 #include "ParIterator.h"
61 #include "ParagraphParameters.h"
64 #include "insets/InsetBox.h"
65 #include "insets/InsetBranch.h"
66 #include "insets/InsetCommand.h"
67 #include "insets/InsetERT.h"
68 #include "insets/InsetExternal.h"
69 #include "insets/InsetFloat.h"
70 #include "insets/InsetGraphics.h"
71 #include "insets/InsetInclude.h"
72 #include "insets/InsetNote.h"
73 #include "insets/InsetTabular.h"
74 #include "insets/InsetVSpace.h"
75 #include "insets/InsetWrap.h"
77 #include "frontends/Application.h"
78 #include "frontends/alert.h"
79 #include "frontends/Dialogs.h"
80 #include "frontends/FileDialog.h"
81 #include "frontends/FontLoader.h"
82 #include "frontends/Gui.h"
83 #include "frontends/KeySymbol.h"
84 #include "frontends/LyXView.h"
85 #include "frontends/Menubar.h"
86 #include "frontends/Toolbars.h"
88 #include "support/environment.h"
89 #include "support/FileFilterList.h"
90 #include "support/filetools.h"
91 #include "support/ForkedcallsController.h"
92 #include "support/fs_extras.h"
93 #include "support/lstrings.h"
94 #include "support/Path.h"
95 #include "support/Package.h"
96 #include "support/Systemcall.h"
97 #include "support/convert.h"
98 #include "support/os.h"
100 #include <boost/current_function.hpp>
101 #include <boost/filesystem/operations.hpp>
108 using bv_funcs::freefont2string;
110 using support::absolutePath;
111 using support::addName;
112 using support::addPath;
113 using support::bformat;
114 using support::changeExtension;
115 using support::contains;
116 using support::FileFilterList;
117 using support::FileName;
118 using support::fileSearch;
119 using support::ForkedcallsController;
120 using support::i18nLibFileSearch;
121 using support::isDirWriteable;
122 using support::isFileReadable;
123 using support::isStrInt;
124 using support::makeAbsPath;
125 using support::makeDisplayPath;
126 using support::package;
127 using support::quoteName;
128 using support::rtrim;
129 using support::split;
130 using support::subst;
131 using support::Systemcall;
132 using support::token;
134 using support::prefixIs;
137 using std::make_pair;
140 using std::istringstream;
141 using std::ostringstream;
143 namespace Alert = frontend::Alert;
144 namespace fs = boost::filesystem;
149 bool getLocalStatus(Cursor cursor,
150 FuncRequest const & cmd, FuncStatus & status)
152 // Try to fix cursor in case it is broken.
153 cursor.fixIfBroken();
155 // This is, of course, a mess. Better create a new doc iterator and use
156 // this in Inset::getStatus. This might require an additional
157 // BufferView * arg, though (which should be avoided)
158 //Cursor safe = *this;
160 for ( ; cursor.depth(); cursor.pop()) {
161 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
162 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
163 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
164 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
166 // The inset's getStatus() will return 'true' if it made
167 // a definitive decision on whether it want to handle the
168 // request or not. The result of this decision is put into
169 // the 'status' parameter.
170 if (cursor.inset().getStatus(cursor, cmd, status)) {
179 /** Return the change status at cursor position, taking in account the
180 * status at each level of the document iterator (a table in a deleted
181 * footnote is deleted).
182 * When \param outer is true, the top slice is not looked at.
184 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
186 size_t const depth = dit.depth() - (outer ? 1 : 0);
188 for (size_t i = 0 ; i < depth ; ++i) {
189 CursorSlice const & slice = dit[i];
190 if (!slice.inset().inMathed()
191 && slice.pos() < slice.paragraph().size()) {
192 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
193 if (ch != Change::UNCHANGED)
197 return Change::UNCHANGED;
205 meta_fake_bit(key_modifier::none)
210 void LyXFunc::initKeySequences(KeyMap * kb)
212 keyseq.reset(new KeySequence(kb, kb));
213 cancel_meta_seq.reset(new KeySequence(kb, kb));
217 void LyXFunc::setLyXView(LyXView * lv)
223 void LyXFunc::handleKeyFunc(kb_action action)
225 char_type c = encoded_last_key;
227 if (keyseq->length())
230 lyx_view_->view()->getIntl().getTransManager().deadkey(
231 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
232 // Need to clear, in case the minibuffer calls these
235 // copied verbatim from do_accent_char
236 view()->cursor().resetAnchor();
241 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
243 BOOST_ASSERT(lyx_view_);
244 if (!LyX::ref().session().bookmarks().isValid(idx))
246 BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
247 BOOST_ASSERT(!bm.filename.empty());
248 string const file = bm.filename.absFilename();
249 // if the file is not opened, open it.
250 if (!theBufferList().exists(file)) {
252 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
256 // open may fail, so we need to test it again
257 if (theBufferList().exists(file)) {
258 // if the current buffer is not that one, switch to it.
259 if (lyx_view_->buffer()->fileName() != file) {
261 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
265 // moveToPosition use par_id, and par_pit and return new par_id.
269 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
270 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
271 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
272 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
273 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
278 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
280 LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
282 // Do nothing if we have nothing (JMarc)
283 if (!keysym->isOK()) {
284 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
289 if (keysym->isModifier()) {
290 LYXERR(Debug::KEY) << "isModifier true" << endl;
294 //Encoding const * encoding = view()->cursor().getEncoding();
295 //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
296 // FIXME: encoded_last_key shadows the member variable of the same
297 // name. Is that intended?
298 char_type encoded_last_key = keysym->getUCSEncoded();
300 // Do a one-deep top-level lookup for
301 // cancel and meta-fake keys. RVDK_PATCH_5
302 cancel_meta_seq->reset();
304 FuncRequest func = cancel_meta_seq->addkey(keysym, state);
305 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
306 << " action first set to [" << func.action << ']'
309 // When not cancel or meta-fake, do the normal lookup.
310 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
311 // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
312 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
313 // remove Caps Lock and Mod2 as a modifiers
314 func = keyseq->addkey(keysym, (state | meta_fake_bit));
315 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
316 << "action now set to ["
317 << func.action << ']' << endl;
320 // Dont remove this unless you know what you are doing.
321 meta_fake_bit = key_modifier::none;
323 // Can this happen now ?
324 if (func.action == LFUN_NOACTION) {
325 func = FuncRequest(LFUN_COMMAND_PREFIX);
328 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
330 << func.action << "]["
331 << to_utf8(keyseq->print(false)) << ']'
334 // already here we know if it any point in going further
335 // why not return already here if action == -1 and
336 // num_bytes == 0? (Lgb)
338 if (keyseq->length() > 1) {
339 lyx_view_->message(keyseq->print(true));
343 // Maybe user can only reach the key via holding down shift.
344 // Let's see. But only if shift is the only modifier
345 if (func.action == LFUN_UNKNOWN_ACTION &&
346 state == key_modifier::shift) {
347 LYXERR(Debug::KEY) << "Trying without shift" << endl;
348 func = keyseq->addkey(keysym, key_modifier::none);
349 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
352 if (func.action == LFUN_UNKNOWN_ACTION) {
353 // Hmm, we didn't match any of the keysequences. See
354 // if it's normal insertable text not already covered
356 if (keysym->isText() && keyseq->length() == 1) {
357 LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
358 func = FuncRequest(LFUN_SELF_INSERT,
359 FuncRequest::KEYBOARD);
361 LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
362 lyx_view_->message(_("Unknown function."));
367 if (func.action == LFUN_SELF_INSERT) {
368 if (encoded_last_key != 0) {
369 docstring const arg(1, encoded_last_key);
370 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
371 FuncRequest::KEYBOARD));
373 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
381 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
383 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
386 Cursor & cur = view()->cursor();
388 /* In LyX/Mac, when a dialog is open, the menus of the
389 application can still be accessed without giving focus to
390 the main window. In this case, we want to disable the menu
391 entries that are buffer-related.
393 Note that this code is not perfect, as bug 1941 attests:
394 http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
396 Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
397 if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
400 if (cmd.action == LFUN_NOACTION) {
401 flag.message(from_utf8(N_("Nothing to do")));
406 switch (cmd.action) {
407 case LFUN_UNKNOWN_ACTION:
408 #ifndef HAVE_LIBAIKSAURUS
409 case LFUN_THESAURUS_ENTRY:
419 if (flag.unknown()) {
420 flag.message(from_utf8(N_("Unknown action")));
424 if (!flag.enabled()) {
425 if (flag.message().empty())
426 flag.message(from_utf8(N_("Command disabled")));
430 // Check whether we need a buffer
431 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
433 flag.message(from_utf8(N_("Command not allowed with"
434 "out any document open")));
439 // I would really like to avoid having this switch and rather try to
440 // encode this in the function itself.
441 // -- And I'd rather let an inset decide which LFUNs it is willing
442 // to handle (Andre')
444 switch (cmd.action) {
445 case LFUN_BUFFER_TOGGLE_READ_ONLY:
446 flag.setOnOff(buf->isReadonly());
449 case LFUN_BUFFER_SWITCH:
450 // toggle on the current buffer, but do not toggle off
451 // the other ones (is that a good idea?)
452 if (to_utf8(cmd.argument()) == buf->fileName())
456 case LFUN_BUFFER_EXPORT:
457 enable = cmd.argument() == "custom"
458 || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
461 case LFUN_BUFFER_CHKTEX:
462 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
465 case LFUN_BUILD_PROGRAM:
466 enable = Exporter::isExportable(*buf, "program");
469 case LFUN_LAYOUT_TABULAR:
470 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
474 case LFUN_LAYOUT_PARAGRAPH:
475 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
478 case LFUN_VC_REGISTER:
479 enable = !buf->lyxvc().inUse();
481 case LFUN_VC_CHECK_IN:
482 enable = buf->lyxvc().inUse() && !buf->isReadonly();
484 case LFUN_VC_CHECK_OUT:
485 enable = buf->lyxvc().inUse() && buf->isReadonly();
488 case LFUN_VC_UNDO_LAST:
489 enable = buf->lyxvc().inUse();
491 case LFUN_BUFFER_RELOAD:
492 enable = !buf->isUnnamed() && !buf->isClean();
495 case LFUN_INSET_SETTINGS: {
499 Inset::Code code = cur.inset().lyxCode();
501 case Inset::TABULAR_CODE:
502 enable = cmd.argument() == "tabular";
504 case Inset::ERT_CODE:
505 enable = cmd.argument() == "ert";
507 case Inset::FLOAT_CODE:
508 enable = cmd.argument() == "float";
510 case Inset::WRAP_CODE:
511 enable = cmd.argument() == "wrap";
513 case Inset::NOTE_CODE:
514 enable = cmd.argument() == "note";
516 case Inset::BRANCH_CODE:
517 enable = cmd.argument() == "branch";
519 case Inset::BOX_CODE:
520 enable = cmd.argument() == "box";
528 case LFUN_INSET_APPLY: {
529 string const name = cmd.getArg(0);
530 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
532 FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
534 if (!inset->getStatus(cur, fr, fs)) {
535 // Every inset is supposed to handle this
540 FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
541 flag |= getStatus(fr);
543 enable = flag.enabled();
547 case LFUN_DIALOG_TOGGLE:
548 flag.setOnOff(lyx_view_->getDialogs().visible(cmd.getArg(0)));
549 // fall through to set "enable"
550 case LFUN_DIALOG_SHOW: {
551 string const name = cmd.getArg(0);
553 enable = name == "aboutlyx"
554 || name == "file" //FIXME: should be removed.
556 || name == "texinfo";
557 else if (name == "print")
558 enable = Exporter::isExportable(*buf, "dvi")
559 && lyxrc.print_command != "none";
560 else if (name == "character")
561 enable = cur.inset().lyxCode() != Inset::ERT_CODE;
562 else if (name == "latexlog")
563 enable = isFileReadable(FileName(buf->getLogName().second));
564 else if (name == "spellchecker")
565 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
566 enable = !buf->isReadonly();
570 else if (name == "vclog")
571 enable = buf->lyxvc().inUse();
575 case LFUN_DIALOG_SHOW_NEW_INSET:
576 enable = cur.inset().lyxCode() != Inset::ERT_CODE;
577 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
579 if (cur.inset().getStatus(cur, cmd, flag))
584 case LFUN_DIALOG_UPDATE: {
585 string const name = cmd.getArg(0);
587 enable = name == "prefs";
591 case LFUN_CITATION_INSERT: {
592 FuncRequest fr(LFUN_INSET_INSERT, "citation");
593 enable = getStatus(fr).enabled();
597 case LFUN_BUFFER_WRITE: {
598 enable = view()->buffer()->isUnnamed()
599 || !view()->buffer()->isClean();
603 case LFUN_BOOKMARK_GOTO: {
604 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
605 enable = LyX::ref().session().bookmarks().isValid(num);
609 case LFUN_BOOKMARK_CLEAR:
610 enable = LyX::ref().session().bookmarks().size() > 0;
613 case LFUN_TOOLBAR_TOGGLE_STATE: {
614 ToolbarInfo::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
615 if (!(flags & ToolbarInfo::AUTO))
616 flag.setOnOff(flags & ToolbarInfo::ON);
620 // this one is difficult to get right. As a half-baked
621 // solution, we consider only the first action of the sequence
622 case LFUN_COMMAND_SEQUENCE: {
623 // argument contains ';'-terminated commands
624 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
625 FuncRequest func(lyxaction.lookupFunc(firstcmd));
626 func.origin = cmd.origin;
627 flag = getStatus(func);
630 case LFUN_BUFFER_NEW:
631 case LFUN_BUFFER_NEW_TEMPLATE:
632 case LFUN_WORD_FIND_FORWARD:
633 case LFUN_WORD_FIND_BACKWARD:
634 case LFUN_COMMAND_PREFIX:
635 case LFUN_COMMAND_EXECUTE:
637 case LFUN_META_PREFIX:
638 case LFUN_BUFFER_CLOSE:
639 case LFUN_BUFFER_WRITE_AS:
640 case LFUN_BUFFER_UPDATE:
641 case LFUN_BUFFER_VIEW:
642 case LFUN_BUFFER_IMPORT:
643 case LFUN_BUFFER_AUTO_SAVE:
644 case LFUN_RECONFIGURE:
648 case LFUN_DROP_LAYOUTS_CHOICE:
650 case LFUN_SERVER_GET_NAME:
651 case LFUN_SERVER_NOTIFY:
652 case LFUN_SERVER_GOTO_FILE_ROW:
653 case LFUN_DIALOG_HIDE:
654 case LFUN_DIALOG_DISCONNECT_INSET:
655 case LFUN_BUFFER_CHILD_OPEN:
656 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
657 case LFUN_KEYMAP_OFF:
658 case LFUN_KEYMAP_PRIMARY:
659 case LFUN_KEYMAP_SECONDARY:
660 case LFUN_KEYMAP_TOGGLE:
662 case LFUN_BUFFER_EXPORT_CUSTOM:
663 case LFUN_BUFFER_PRINT:
664 case LFUN_PREFERENCES_SAVE:
665 case LFUN_SCREEN_FONT_UPDATE:
668 case LFUN_EXTERNAL_EDIT:
669 case LFUN_GRAPHICS_EDIT:
670 case LFUN_ALL_INSETS_TOGGLE:
671 case LFUN_BUFFER_LANGUAGE:
672 case LFUN_TEXTCLASS_APPLY:
673 case LFUN_TEXTCLASS_LOAD:
674 case LFUN_BUFFER_SAVE_AS_DEFAULT:
675 case LFUN_BUFFER_PARAMS_APPLY:
676 case LFUN_LYXRC_APPLY:
677 case LFUN_BUFFER_NEXT:
678 case LFUN_BUFFER_PREVIOUS:
679 case LFUN_WINDOW_NEW:
680 case LFUN_WINDOW_CLOSE:
682 // these are handled in our dispatch()
686 if (!getLocalStatus(cur, cmd, flag))
687 flag = view()->getStatus(cmd);
693 // Can we use a readonly buffer?
694 if (buf && buf->isReadonly()
695 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
696 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
697 flag.message(from_utf8(N_("Document is read-only")));
701 // Are we in a DELETED change-tracking region?
702 if (buf && lookupChangeType(cur, true) == Change::DELETED
703 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
704 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
705 flag.message(from_utf8(N_("This portion of the document is deleted.")));
709 // the default error message if we disable the command
710 if (!flag.enabled() && flag.message().empty())
711 flag.message(from_utf8(N_("Command disabled")));
717 bool LyXFunc::ensureBufferClean(BufferView * bv)
719 Buffer & buf = *bv->buffer();
723 docstring const file = makeDisplayPath(buf.fileName(), 30);
724 docstring text = bformat(_("The document %1$s has unsaved "
725 "changes.\n\nDo you want to save "
726 "the document?"), file);
727 int const ret = Alert::prompt(_("Save changed document?"),
728 text, 0, 1, _("&Save"),
732 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
734 return buf.isClean();
740 void showPrintError(string const & name)
742 docstring str = bformat(_("Could not print the document %1$s.\n"
743 "Check that your printer is set up correctly."),
744 makeDisplayPath(name, 50));
745 Alert::error(_("Print document failed"), str);
749 void loadTextclass(string const & name)
751 std::pair<bool, textclass_type> const tc_pair =
752 textclasslist.numberOfClass(name);
754 if (!tc_pair.first) {
755 lyxerr << "Document class \"" << name
756 << "\" does not exist."
761 textclass_type const tc = tc_pair.second;
763 if (!textclasslist[tc].load()) {
764 docstring s = bformat(_("The document could not be converted\n"
765 "into the document class %1$s."),
766 from_utf8(textclasslist[tc].name()));
767 Alert::error(_("Could not change class"), s);
772 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
777 void LyXFunc::dispatch(FuncRequest const & cmd)
779 string const argument = to_utf8(cmd.argument());
780 kb_action const action = cmd.action;
782 LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
783 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
785 // we have not done anything wrong yet.
787 dispatch_buffer.erase();
789 // redraw the screen at the end (first of the two drawing steps).
790 //This is done unless explicitely requested otherwise
791 Update::flags updateFlags = Update::FitCursor;
793 FuncStatus const flag = getStatus(cmd);
794 if (!flag.enabled()) {
795 // We cannot use this function here
796 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
797 << lyxaction.getActionName(action)
798 << " [" << action << "] is disabled at this location"
800 setErrorMessage(flag.message());
804 case LFUN_WORD_FIND_FORWARD:
805 case LFUN_WORD_FIND_BACKWARD: {
806 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
807 static docstring last_search;
808 docstring searched_string;
810 if (!cmd.argument().empty()) {
811 last_search = cmd.argument();
812 searched_string = cmd.argument();
814 searched_string = last_search;
817 if (searched_string.empty())
820 bool const fw = action == LFUN_WORD_FIND_FORWARD;
821 docstring const data =
822 find2string(searched_string, true, false, fw);
823 find(view(), FuncRequest(LFUN_WORD_FIND, data));
827 case LFUN_COMMAND_PREFIX:
828 BOOST_ASSERT(lyx_view_);
829 lyx_view_->message(keyseq->printOptions(true));
832 case LFUN_COMMAND_EXECUTE:
833 BOOST_ASSERT(lyx_view_);
834 lyx_view_->getToolbars().display("minibuffer", true);
835 lyx_view_->focus_command_buffer();
839 BOOST_ASSERT(lyx_view_ && lyx_view_->view());
841 meta_fake_bit = key_modifier::none;
842 if (view()->buffer())
843 // cancel any selection
844 dispatch(FuncRequest(LFUN_MARK_OFF));
845 setMessage(from_ascii(N_("Cancel")));
848 case LFUN_META_PREFIX:
849 meta_fake_bit = key_modifier::alt;
850 setMessage(keyseq->print(true));
853 case LFUN_BUFFER_TOGGLE_READ_ONLY:
854 BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
855 if (lyx_view_->buffer()->lyxvc().inUse())
856 lyx_view_->buffer()->lyxvc().toggleReadOnly();
858 lyx_view_->buffer()->setReadonly(
859 !lyx_view_->buffer()->isReadonly());
862 // --- Menus -----------------------------------------------
863 case LFUN_BUFFER_NEW:
864 menuNew(argument, false);
867 case LFUN_BUFFER_NEW_TEMPLATE:
868 menuNew(argument, true);
871 case LFUN_BUFFER_CLOSE:
876 case LFUN_BUFFER_WRITE:
877 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
878 if (!lyx_view_->buffer()->isUnnamed()) {
879 docstring const str = bformat(_("Saving document %1$s..."),
880 makeDisplayPath(lyx_view_->buffer()->fileName()));
881 lyx_view_->message(str);
882 menuWrite(lyx_view_->buffer());
883 lyx_view_->message(str + _(" done."));
885 writeAs(lyx_view_->buffer());
887 updateFlags = Update::None;
890 case LFUN_BUFFER_WRITE_AS:
891 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
892 writeAs(lyx_view_->buffer(), argument);
893 updateFlags = Update::None;
896 case LFUN_BUFFER_RELOAD: {
897 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
898 docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
899 docstring text = bformat(_("Any changes will be lost. Are you sure "
900 "you want to revert to the saved version of the document %1$s?"), file);
901 int const ret = Alert::prompt(_("Revert to saved document?"),
902 text, 0, 1, _("&Revert"), _("&Cancel"));
909 case LFUN_BUFFER_UPDATE:
910 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
911 Exporter::Export(lyx_view_->buffer(), argument, true);
914 case LFUN_BUFFER_VIEW:
915 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
916 Exporter::preview(lyx_view_->buffer(), argument);
919 case LFUN_BUILD_PROGRAM:
920 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
921 Exporter::Export(lyx_view_->buffer(), "program", true);
924 case LFUN_BUFFER_CHKTEX:
925 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
926 lyx_view_->buffer()->runChktex();
929 case LFUN_BUFFER_EXPORT:
930 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
931 if (argument == "custom")
932 lyx_view_->getDialogs().show("sendto");
934 Exporter::Export(lyx_view_->buffer(), argument, false);
938 case LFUN_BUFFER_EXPORT_CUSTOM: {
939 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
941 string command = split(argument, format_name, ' ');
942 Format const * format = formats.getFormat(format_name);
944 lyxerr << "Format \"" << format_name
945 << "\" not recognized!"
950 Buffer * buffer = lyx_view_->buffer();
952 // The name of the file created by the conversion process
955 // Output to filename
956 if (format->name() == "lyx") {
957 string const latexname =
958 buffer->getLatexName(false);
959 filename = changeExtension(latexname,
960 format->extension());
961 filename = addName(buffer->temppath(), filename);
963 if (!buffer->writeFile(FileName(filename)))
967 Exporter::Export(buffer, format_name, true, filename);
970 // Substitute $$FName for filename
971 if (!contains(command, "$$FName"))
972 command = "( " + command + " ) < $$FName";
973 command = subst(command, "$$FName", filename);
975 // Execute the command in the background
977 call.startscript(Systemcall::DontWait, command);
981 case LFUN_BUFFER_PRINT: {
982 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
985 string command = split(split(argument, target, ' '),
989 || target_name.empty()
990 || command.empty()) {
991 lyxerr << "Unable to parse \""
992 << argument << '"' << std::endl;
995 if (target != "printer" && target != "file") {
996 lyxerr << "Unrecognized target \""
997 << target << '"' << std::endl;
1001 Buffer * buffer = lyx_view_->buffer();
1003 if (!Exporter::Export(buffer, "dvi", true)) {
1004 showPrintError(buffer->fileName());
1008 // Push directory path.
1009 string const path(buffer->temppath());
1010 // Prevent the compiler from optimizing away p
1012 support::Path p(pp);
1014 // there are three cases here:
1015 // 1. we print to a file
1016 // 2. we print directly to a printer
1017 // 3. we print using a spool command (print to file first)
1020 string const dviname =
1021 changeExtension(buffer->getLatexName(true),
1024 if (target == "printer") {
1025 if (!lyxrc.print_spool_command.empty()) {
1026 // case 3: print using a spool
1027 string const psname =
1028 changeExtension(dviname,".ps");
1029 command += lyxrc.print_to_file
1032 + quoteName(dviname);
1035 lyxrc.print_spool_command +' ';
1036 if (target_name != "default") {
1037 command2 += lyxrc.print_spool_printerprefix
1041 command2 += quoteName(psname);
1043 // If successful, then spool command
1044 res = one.startscript(
1049 res = one.startscript(
1050 Systemcall::DontWait,
1053 // case 2: print directly to a printer
1054 res = one.startscript(
1055 Systemcall::DontWait,
1056 command + quoteName(dviname));
1060 // case 1: print to a file
1061 FileName const filename(makeAbsPath(target_name, path));
1062 if (fs::exists(filename.toFilesystemEncoding())) {
1063 docstring text = bformat(
1064 _("The file %1$s already exists.\n\n"
1065 "Do you want to over-write that file?"),
1066 makeDisplayPath(filename.absFilename()));
1067 if (Alert::prompt(_("Over-write file?"),
1068 text, 0, 1, _("&Over-write"), _("&Cancel")) != 0)
1071 command += lyxrc.print_to_file
1072 + quoteName(filename.toFilesystemEncoding())
1074 + quoteName(dviname);
1075 res = one.startscript(Systemcall::DontWait,
1080 showPrintError(buffer->fileName());
1084 case LFUN_BUFFER_IMPORT:
1089 // quitting is triggered by the gui code
1090 // (leaving the event loop).
1091 lyx_view_->message(from_utf8(N_("Exiting.")));
1092 if (theBufferList().quitWriteAll())
1093 theApp()->gui().closeAllViews();
1096 case LFUN_BUFFER_AUTO_SAVE:
1100 case LFUN_RECONFIGURE:
1101 BOOST_ASSERT(lyx_view_);
1102 reconfigure(*lyx_view_);
1105 case LFUN_HELP_OPEN: {
1106 BOOST_ASSERT(lyx_view_);
1107 string const arg = argument;
1109 setErrorMessage(from_ascii(N_("Missing argument")));
1112 FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1113 if (fname.empty()) {
1114 lyxerr << "LyX: unable to find documentation file `"
1115 << arg << "'. Bad installation?" << endl;
1118 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1119 makeDisplayPath(fname.absFilename())));
1120 lyx_view_->loadLyXFile(fname, false);
1124 // --- version control -------------------------------
1125 case LFUN_VC_REGISTER:
1126 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1127 if (!ensureBufferClean(view()))
1129 if (!lyx_view_->buffer()->lyxvc().inUse()) {
1130 lyx_view_->buffer()->lyxvc().registrer();
1133 updateFlags = Update::Force;
1136 case LFUN_VC_CHECK_IN:
1137 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1138 if (!ensureBufferClean(view()))
1140 if (lyx_view_->buffer()->lyxvc().inUse()
1141 && !lyx_view_->buffer()->isReadonly()) {
1142 lyx_view_->buffer()->lyxvc().checkIn();
1147 case LFUN_VC_CHECK_OUT:
1148 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1149 if (!ensureBufferClean(view()))
1151 if (lyx_view_->buffer()->lyxvc().inUse()
1152 && lyx_view_->buffer()->isReadonly()) {
1153 lyx_view_->buffer()->lyxvc().checkOut();
1158 case LFUN_VC_REVERT:
1159 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1160 lyx_view_->buffer()->lyxvc().revert();
1164 case LFUN_VC_UNDO_LAST:
1165 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1166 lyx_view_->buffer()->lyxvc().undoLast();
1170 // --- buffers ----------------------------------------
1171 case LFUN_BUFFER_SWITCH:
1172 BOOST_ASSERT(lyx_view_);
1173 lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1176 case LFUN_BUFFER_NEXT:
1177 BOOST_ASSERT(lyx_view_);
1178 lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1181 case LFUN_BUFFER_PREVIOUS:
1182 BOOST_ASSERT(lyx_view_);
1183 lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1187 BOOST_ASSERT(lyx_view_);
1188 newFile(view(), argument);
1191 case LFUN_FILE_OPEN:
1192 BOOST_ASSERT(lyx_view_);
1196 case LFUN_DROP_LAYOUTS_CHOICE:
1197 BOOST_ASSERT(lyx_view_);
1198 lyx_view_->getToolbars().openLayoutList();
1201 case LFUN_MENU_OPEN:
1202 BOOST_ASSERT(lyx_view_);
1203 lyx_view_->getMenubar().openByName(from_utf8(argument));
1206 // --- lyxserver commands ----------------------------
1207 case LFUN_SERVER_GET_NAME:
1208 BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1209 setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1210 LYXERR(Debug::INFO) << "FNAME["
1211 << lyx_view_->buffer()->fileName()
1215 case LFUN_SERVER_NOTIFY:
1216 dispatch_buffer = keyseq->print(false);
1217 theServer().notifyClient(to_utf8(dispatch_buffer));
1220 case LFUN_SERVER_GOTO_FILE_ROW: {
1221 BOOST_ASSERT(lyx_view_);
1224 istringstream is(argument);
1225 is >> file_name >> row;
1226 if (prefixIs(file_name, package().temp_dir().absFilename())) {
1227 // Needed by inverse dvi search. If it is a file
1228 // in tmpdir, call the apropriated function
1229 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1231 // Must replace extension of the file to be .lyx
1232 // and get full path
1233 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1234 // Either change buffer or load the file
1235 if (theBufferList().exists(s.absFilename())) {
1236 lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1238 lyx_view_->loadLyXFile(s);
1242 view()->setCursorFromRow(row);
1244 updateFlags = Update::FitCursor;
1248 case LFUN_DIALOG_SHOW: {
1249 BOOST_ASSERT(lyx_view_);
1250 string const name = cmd.getArg(0);
1251 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1253 if (name == "character") {
1254 data = freefont2string();
1256 lyx_view_->getDialogs().show("character", data);
1257 } else if (name == "latexlog") {
1258 pair<Buffer::LogType, string> const logfile =
1259 lyx_view_->buffer()->getLogName();
1260 switch (logfile.first) {
1261 case Buffer::latexlog:
1264 case Buffer::buildlog:
1268 data += Lexer::quoteString(logfile.second);
1269 lyx_view_->getDialogs().show("log", data);
1270 } else if (name == "vclog") {
1271 string const data = "vc " +
1272 Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1273 lyx_view_->getDialogs().show("log", data);
1275 lyx_view_->getDialogs().show(name, data);
1279 case LFUN_DIALOG_SHOW_NEW_INSET: {
1280 BOOST_ASSERT(lyx_view_);
1281 string const name = cmd.getArg(0);
1282 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1283 if (name == "bibitem" ||
1287 name == "nomenclature" ||
1291 InsetCommandParams p(name);
1292 data = InsetCommandMailer::params2string(name, p);
1293 } else if (name == "include") {
1294 // data is the include type: one of "include",
1295 // "input", "verbatiminput" or "verbatiminput*"
1297 // default type is requested
1299 InsetCommandParams p(data);
1300 data = InsetIncludeMailer::params2string(p);
1301 } else if (name == "box") {
1302 // \c data == "Boxed" || "Frameless" etc
1303 InsetBoxParams p(data);
1304 data = InsetBoxMailer::params2string(p);
1305 } else if (name == "branch") {
1306 InsetBranchParams p;
1307 data = InsetBranchMailer::params2string(p);
1308 } else if (name == "citation") {
1309 InsetCommandParams p("cite");
1310 data = InsetCommandMailer::params2string(name, p);
1311 } else if (name == "ert") {
1312 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1313 } else if (name == "external") {
1314 InsetExternalParams p;
1315 Buffer const & buffer = *lyx_view_->buffer();
1316 data = InsetExternalMailer::params2string(p, buffer);
1317 } else if (name == "float") {
1319 data = InsetFloatMailer::params2string(p);
1320 } else if (name == "graphics") {
1321 InsetGraphicsParams p;
1322 Buffer const & buffer = *lyx_view_->buffer();
1323 data = InsetGraphicsMailer::params2string(p, buffer);
1324 } else if (name == "note") {
1326 data = InsetNoteMailer::params2string(p);
1327 } else if (name == "vspace") {
1329 data = InsetVSpaceMailer::params2string(space);
1330 } else if (name == "wrap") {
1332 data = InsetWrapMailer::params2string(p);
1334 lyx_view_->getDialogs().show(name, data, 0);
1338 case LFUN_DIALOG_UPDATE: {
1339 BOOST_ASSERT(lyx_view_);
1340 string const & name = argument;
1341 // Can only update a dialog connected to an existing inset
1342 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1344 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1345 inset->dispatch(view()->cursor(), fr);
1346 } else if (name == "paragraph") {
1347 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1348 } else if (name == "prefs") {
1349 lyx_view_->getDialogs().update(name, string());
1354 case LFUN_DIALOG_HIDE:
1355 Dialogs::hide(argument, 0);
1358 case LFUN_DIALOG_TOGGLE: {
1359 BOOST_ASSERT(lyx_view_);
1360 if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1361 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1363 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1367 case LFUN_DIALOG_DISCONNECT_INSET:
1368 BOOST_ASSERT(lyx_view_);
1369 lyx_view_->getDialogs().disconnect(argument);
1373 case LFUN_CITATION_INSERT: {
1374 BOOST_ASSERT(lyx_view_);
1375 if (!argument.empty()) {
1376 // we can have one optional argument, delimited by '|'
1377 // citation-insert <key>|<text_before>
1378 // this should be enhanced to also support text_after
1379 // and citation style
1380 string arg = argument;
1382 if (contains(argument, "|")) {
1383 arg = token(argument, '|', 0);
1384 opt1 = token(argument, '|', 1);
1386 InsetCommandParams icp("cite");
1387 icp["key"] = from_utf8(arg);
1389 icp["before"] = from_utf8(opt1);
1390 string icstr = InsetCommandMailer::params2string("citation", icp);
1391 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1394 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1398 case LFUN_BUFFER_CHILD_OPEN: {
1399 BOOST_ASSERT(lyx_view_);
1400 FileName const filename =
1401 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1402 setMessage(bformat(_("Opening child document %1$s..."),
1403 makeDisplayPath(filename.absFilename())));
1404 view()->saveBookmark(false);
1405 string const parentfilename = lyx_view_->buffer()->fileName();
1406 if (theBufferList().exists(filename.absFilename()))
1407 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1409 lyx_view_->loadLyXFile(filename);
1410 // Set the parent name of the child document.
1411 // This makes insertion of citations and references in the child work,
1412 // when the target is in the parent or another child document.
1413 lyx_view_->buffer()->setParentName(parentfilename);
1417 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1418 BOOST_ASSERT(lyx_view_);
1419 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1422 case LFUN_KEYMAP_OFF:
1423 BOOST_ASSERT(lyx_view_);
1424 lyx_view_->view()->getIntl().keyMapOn(false);
1427 case LFUN_KEYMAP_PRIMARY:
1428 BOOST_ASSERT(lyx_view_);
1429 lyx_view_->view()->getIntl().keyMapPrim();
1432 case LFUN_KEYMAP_SECONDARY:
1433 BOOST_ASSERT(lyx_view_);
1434 lyx_view_->view()->getIntl().keyMapSec();
1437 case LFUN_KEYMAP_TOGGLE:
1438 BOOST_ASSERT(lyx_view_);
1439 lyx_view_->view()->getIntl().toggleKeyMap();
1445 string rest = split(argument, countstr, ' ');
1446 istringstream is(countstr);
1449 lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1450 for (int i = 0; i < count; ++i)
1451 dispatch(lyxaction.lookupFunc(rest));
1455 case LFUN_COMMAND_SEQUENCE: {
1456 // argument contains ';'-terminated commands
1457 string arg = argument;
1458 while (!arg.empty()) {
1460 arg = split(arg, first, ';');
1461 FuncRequest func(lyxaction.lookupFunc(first));
1462 func.origin = cmd.origin;
1468 case LFUN_PREFERENCES_SAVE: {
1469 lyxrc.write(makeAbsPath("preferences",
1470 package().user_support().absFilename()),
1475 case LFUN_SCREEN_FONT_UPDATE:
1476 BOOST_ASSERT(lyx_view_);
1477 // handle the screen font changes.
1478 theFontLoader().update();
1479 /// FIXME: only the current view will be updated. the Gui
1480 /// class is able to furnish the list of views.
1481 updateFlags = Update::Force;
1484 case LFUN_SET_COLOR: {
1486 string const x11_name = split(argument, lyx_name, ' ');
1487 if (lyx_name.empty() || x11_name.empty()) {
1488 setErrorMessage(from_ascii(N_(
1489 "Syntax: set-color <lyx_name>"
1494 bool const graphicsbg_changed =
1495 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1496 x11_name != lcolor.getX11Name(Color::graphicsbg));
1498 if (!lcolor.setColor(lyx_name, x11_name)) {
1500 bformat(_("Set-color \"%1$s\" failed "
1501 "- color is undefined or "
1502 "may not be redefined"),
1503 from_utf8(lyx_name)));
1507 theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1509 if (graphicsbg_changed) {
1510 #ifdef WITH_WARNINGS
1511 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1514 graphics::GCache::get().changeDisplay(true);
1521 BOOST_ASSERT(lyx_view_);
1522 lyx_view_->message(from_utf8(argument));
1525 case LFUN_EXTERNAL_EDIT: {
1526 BOOST_ASSERT(lyx_view_);
1527 FuncRequest fr(action, argument);
1528 InsetExternal().dispatch(view()->cursor(), fr);
1532 case LFUN_GRAPHICS_EDIT: {
1533 FuncRequest fr(action, argument);
1534 InsetGraphics().dispatch(view()->cursor(), fr);
1538 case LFUN_INSET_APPLY: {
1539 BOOST_ASSERT(lyx_view_);
1540 string const name = cmd.getArg(0);
1541 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1543 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1544 inset->dispatch(view()->cursor(), fr);
1546 FuncRequest fr(LFUN_INSET_INSERT, argument);
1549 // ideally, the update flag should be set by the insets,
1550 // but this is not possible currently
1551 updateFlags = Update::Force | Update::FitCursor;
1555 case LFUN_ALL_INSETS_TOGGLE: {
1556 BOOST_ASSERT(lyx_view_);
1558 string const name = split(argument, action, ' ');
1559 Inset::Code const inset_code =
1560 Inset::translate(name);
1562 Cursor & cur = view()->cursor();
1563 FuncRequest fr(LFUN_INSET_TOGGLE, action);
1565 Inset & inset = lyx_view_->buffer()->inset();
1566 InsetIterator it = inset_iterator_begin(inset);
1567 InsetIterator const end = inset_iterator_end(inset);
1568 for (; it != end; ++it) {
1569 if (!it->asInsetMath()
1570 && (inset_code == Inset::NO_CODE
1571 || inset_code == it->lyxCode())) {
1572 Cursor tmpcur = cur;
1573 tmpcur.pushLeft(*it);
1574 it->dispatch(tmpcur, fr);
1577 updateFlags = Update::Force | Update::FitCursor;
1581 case LFUN_BUFFER_LANGUAGE: {
1582 BOOST_ASSERT(lyx_view_);
1583 Buffer & buffer = *lyx_view_->buffer();
1584 Language const * oldL = buffer.params().language;
1585 Language const * newL = languages.getLanguage(argument);
1586 if (!newL || oldL == newL)
1589 if (oldL->rightToLeft() == newL->rightToLeft()
1590 && !buffer.isMultiLingual())
1591 buffer.changeLanguage(oldL, newL);
1595 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1596 string const fname =
1597 addName(addPath(package().user_support().absFilename(), "templates/"),
1599 Buffer defaults(fname);
1601 istringstream ss(argument);
1604 int const unknown_tokens = defaults.readHeader(lex);
1606 if (unknown_tokens != 0) {
1607 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1608 << unknown_tokens << " unknown token"
1609 << (unknown_tokens == 1 ? "" : "s")
1613 if (defaults.writeFile(FileName(defaults.fileName())))
1614 setMessage(bformat(_("Document defaults saved in %1$s"),
1615 makeDisplayPath(fname)));
1617 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1621 case LFUN_BUFFER_PARAMS_APPLY: {
1622 BOOST_ASSERT(lyx_view_);
1623 biblio::CiteEngine const engine =
1624 lyx_view_->buffer()->params().getEngine();
1626 istringstream ss(argument);
1629 int const unknown_tokens =
1630 lyx_view_->buffer()->readHeader(lex);
1632 if (unknown_tokens != 0) {
1633 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1634 << unknown_tokens << " unknown token"
1635 << (unknown_tokens == 1 ? "" : "s")
1638 if (engine == lyx_view_->buffer()->params().getEngine())
1641 Cursor & cur = view()->cursor();
1642 FuncRequest fr(LFUN_INSET_REFRESH);
1644 Inset & inset = lyx_view_->buffer()->inset();
1645 InsetIterator it = inset_iterator_begin(inset);
1646 InsetIterator const end = inset_iterator_end(inset);
1647 for (; it != end; ++it)
1648 if (it->lyxCode() == Inset::CITE_CODE)
1649 it->dispatch(cur, fr);
1653 case LFUN_TEXTCLASS_APPLY: {
1654 BOOST_ASSERT(lyx_view_);
1655 Buffer * buffer = lyx_view_->buffer();
1657 textclass_type const old_class =
1658 buffer->params().textclass;
1660 loadTextclass(argument);
1662 std::pair<bool, textclass_type> const tc_pair =
1663 textclasslist.numberOfClass(argument);
1668 textclass_type const new_class = tc_pair.second;
1669 if (old_class == new_class)
1673 lyx_view_->message(_("Converting document to new document class..."));
1674 recordUndoFullDocument(view());
1675 buffer->params().textclass = new_class;
1676 StableDocIterator backcur(view()->cursor());
1677 ErrorList & el = buffer->errorList("Class Switch");
1678 cap::switchBetweenClasses(
1679 old_class, new_class,
1680 static_cast<InsetText &>(buffer->inset()), el);
1682 view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1684 buffer->errors("Class Switch");
1685 updateLabels(*buffer);
1686 updateFlags = Update::Force | Update::FitCursor;
1690 case LFUN_TEXTCLASS_LOAD:
1691 loadTextclass(argument);
1694 case LFUN_LYXRC_APPLY: {
1695 LyXRC const lyxrc_orig = lyxrc;
1697 istringstream ss(argument);
1698 bool const success = lyxrc.read(ss) == 0;
1701 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1702 << "Unable to read lyxrc data"
1707 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1709 /// We force the redraw in any case because there might be
1710 /// some screen font changes.
1711 /// FIXME: only the current view will be updated. the Gui
1712 /// class is able to furnish the list of views.
1713 updateFlags = Update::Force;
1717 case LFUN_WINDOW_NEW:
1718 LyX::ref().newLyXView();
1721 case LFUN_WINDOW_CLOSE:
1722 BOOST_ASSERT(lyx_view_);
1723 BOOST_ASSERT(theApp());
1724 // update bookmark pit of the current buffer before window close
1725 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1726 gotoBookmark(i+1, false, false);
1727 // ask the user for saving changes or cancel quit
1728 if (!theBufferList().quitWriteAll())
1733 case LFUN_BOOKMARK_GOTO:
1734 // go to bookmark, open unopened file and switch to buffer if necessary
1735 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1738 case LFUN_BOOKMARK_CLEAR:
1739 LyX::ref().session().bookmarks().clear();
1742 case LFUN_TOOLBAR_TOGGLE_STATE:
1743 lyx_view_->toggleToolbarState(argument);
1747 BOOST_ASSERT(lyx_view_);
1748 view()->cursor().dispatch(cmd);
1749 updateFlags = view()->cursor().result().update();
1750 if (!view()->cursor().result().dispatched())
1751 updateFlags = view()->dispatch(cmd);
1756 if (lyx_view_ && view()->buffer()) {
1757 // BufferView::update() updates the ViewMetricsInfo and
1758 // also initializes the position cache for all insets in
1759 // (at least partially) visible top-level paragraphs.
1760 // We will redraw the screen only if needed.
1761 if (view()->update(updateFlags)) {
1762 // Buffer::changed() signals that a repaint is needed.
1763 // The frontend (WorkArea) knows which area to repaint
1764 // thanks to the ViewMetricsInfo updated above.
1765 view()->buffer()->changed();
1768 lyx_view_->updateStatusBar();
1770 // if we executed a mutating lfun, mark the buffer as dirty
1772 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1773 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1774 view()->buffer()->markDirty();
1776 if (view()->cursor().inTexted()) {
1777 lyx_view_->updateLayoutChoice();
1782 lyx_view_->updateMenubar();
1783 lyx_view_->updateToolbars();
1784 // Some messages may already be translated, so we cannot use _()
1785 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1790 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1792 const bool verbose = (cmd.origin == FuncRequest::MENU
1793 || cmd.origin == FuncRequest::TOOLBAR
1794 || cmd.origin == FuncRequest::COMMANDBUFFER);
1796 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1797 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1799 lyx_view_->message(msg);
1803 docstring dispatch_msg = msg;
1804 if (!dispatch_msg.empty())
1805 dispatch_msg += ' ';
1807 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1809 bool argsadded = false;
1811 if (!cmd.argument().empty()) {
1812 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1813 comname += ' ' + cmd.argument();
1818 docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1820 if (!shortcuts.empty())
1821 comname += ": " + shortcuts;
1822 else if (!argsadded && !cmd.argument().empty())
1823 comname += ' ' + cmd.argument();
1825 if (!comname.empty()) {
1826 comname = rtrim(comname);
1827 dispatch_msg += '(' + rtrim(comname) + ')';
1830 LYXERR(Debug::ACTION) << "verbose dispatch msg "
1831 << to_utf8(dispatch_msg) << endl;
1832 if (!dispatch_msg.empty())
1833 lyx_view_->message(dispatch_msg);
1837 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1839 // FIXME: initpath is not used. What to do?
1840 string initpath = lyxrc.document_path;
1841 string filename(name);
1843 if (view()->buffer()) {
1844 string const trypath = lyx_view_->buffer()->filePath();
1845 // If directory is writeable, use this as default.
1846 if (isDirWriteable(FileName(trypath)))
1850 static int newfile_number;
1852 if (filename.empty()) {
1853 filename = addName(lyxrc.document_path,
1854 "newfile" + convert<string>(++newfile_number) + ".lyx");
1855 while (theBufferList().exists(filename) ||
1856 fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1858 filename = addName(lyxrc.document_path,
1859 "newfile" + convert<string>(newfile_number) +
1864 // The template stuff
1867 FileDialog fileDlg(_("Select template file"),
1868 LFUN_SELECT_FILE_SYNC,
1869 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1870 make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1872 FileDialog::Result result =
1873 fileDlg.open(from_utf8(lyxrc.template_path),
1874 FileFilterList(_("LyX Documents (*.lyx)")),
1877 if (result.first == FileDialog::Later)
1879 if (result.second.empty())
1881 templname = to_utf8(result.second);
1884 Buffer * const b = newFile(filename, templname, !name.empty());
1887 lyx_view_->setBuffer(b);
1892 void LyXFunc::open(string const & fname)
1894 string initpath = lyxrc.document_path;
1896 if (view()->buffer()) {
1897 string const trypath = lyx_view_->buffer()->filePath();
1898 // If directory is writeable, use this as default.
1899 if (isDirWriteable(FileName(trypath)))
1905 if (fname.empty()) {
1906 FileDialog fileDlg(_("Select document to open"),
1908 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1909 make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1911 FileDialog::Result result =
1912 fileDlg.open(from_utf8(initpath),
1913 FileFilterList(_("LyX Documents (*.lyx)")),
1916 if (result.first == FileDialog::Later)
1919 filename = to_utf8(result.second);
1921 // check selected filename
1922 if (filename.empty()) {
1923 lyx_view_->message(_("Canceled."));
1929 // get absolute path of file and add ".lyx" to the filename if
1931 FileName const fullname = fileSearch(string(), filename, "lyx");
1932 if (!fullname.empty())
1933 filename = fullname.absFilename();
1935 // if the file doesn't exist, let the user create one
1936 if (!fs::exists(fullname.toFilesystemEncoding())) {
1937 // the user specifically chose this name. Believe him.
1938 Buffer * const b = newFile(filename, string(), true);
1940 lyx_view_->setBuffer(b);
1944 docstring const disp_fn = makeDisplayPath(filename);
1945 lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1948 if (lyx_view_->loadLyXFile(fullname)) {
1949 str2 = bformat(_("Document %1$s opened."), disp_fn);
1951 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1953 lyx_view_->message(str2);
1957 void LyXFunc::doImport(string const & argument)
1960 string filename = split(argument, format, ' ');
1962 LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1963 << " file: " << filename << endl;
1965 // need user interaction
1966 if (filename.empty()) {
1967 string initpath = lyxrc.document_path;
1969 if (view()->buffer()) {
1970 string const trypath = lyx_view_->buffer()->filePath();
1971 // If directory is writeable, use this as default.
1972 if (isDirWriteable(FileName(trypath)))
1976 docstring const text = bformat(_("Select %1$s file to import"),
1977 formats.prettyName(format));
1979 FileDialog fileDlg(text,
1981 make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1982 make_pair(_("Examples|#E#e"),
1983 from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1985 docstring filter = formats.prettyName(format);
1988 filter += from_utf8(formats.extension(format));
1991 FileDialog::Result result =
1992 fileDlg.open(from_utf8(initpath),
1993 FileFilterList(filter),
1996 if (result.first == FileDialog::Later)
1999 filename = to_utf8(result.second);
2001 // check selected filename
2002 if (filename.empty())
2003 lyx_view_->message(_("Canceled."));
2006 if (filename.empty())
2009 // get absolute path of file
2010 FileName const fullname(makeAbsPath(filename));
2012 FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2014 // Check if the document already is open
2015 if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2016 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2017 lyx_view_->message(_("Canceled."));
2022 // if the file exists already, and we didn't do
2023 // -i lyx thefile.lyx, warn
2024 if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2025 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2027 docstring text = bformat(_("The document %1$s already exists.\n\n"
2028 "Do you want to over-write that document?"), file);
2029 int const ret = Alert::prompt(_("Over-write document?"),
2030 text, 0, 1, _("&Over-write"), _("&Cancel"));
2033 lyx_view_->message(_("Canceled."));
2038 ErrorList errorList;
2039 Importer::Import(lyx_view_, fullname, format, errorList);
2040 // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2044 void LyXFunc::closeBuffer()
2046 // goto bookmark to update bookmark pit.
2047 for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2048 gotoBookmark(i+1, false, false);
2049 if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2050 if (theBufferList().empty()) {
2051 // need this otherwise SEGV may occur while
2052 // trying to set variables that don't exist
2053 // since there's no current buffer
2054 lyx_view_->getDialogs().hideBufferDependent();
2056 lyx_view_->setBuffer(theBufferList().first());
2062 void LyXFunc::reloadBuffer()
2064 FileName filename(lyx_view_->buffer()->fileName());
2066 lyx_view_->loadLyXFile(filename);
2069 // Each "lyx_view_" should have it's own message method. lyxview and
2070 // the minibuffer would use the minibuffer, but lyxserver would
2071 // send an ERROR signal to its client. Alejandro 970603
2072 // This function is bit problematic when it comes to NLS, to make the
2073 // lyx servers client be language indepenent we must not translate
2074 // strings sent to this func.
2075 void LyXFunc::setErrorMessage(docstring const & m) const
2077 dispatch_buffer = m;
2082 void LyXFunc::setMessage(docstring const & m) const
2084 dispatch_buffer = m;
2088 docstring const LyXFunc::viewStatusMessage()
2090 // When meta-fake key is pressed, show the key sequence so far + "M-".
2092 return keyseq->print(true) + "M-";
2094 // Else, when a non-complete key sequence is pressed,
2095 // show the available options.
2096 if (keyseq->length() > 0 && !keyseq->deleted())
2097 return keyseq->printOptions(true);
2099 if (!view()->buffer())
2100 return _("Welcome to LyX!");
2102 return view()->cursor().currentState();
2106 BufferView * LyXFunc::view() const
2108 BOOST_ASSERT(lyx_view_);
2109 return lyx_view_->view();
2113 bool LyXFunc::wasMetaKey() const
2115 return (meta_fake_bit != key_modifier::none);
2121 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2123 // Why the switch you might ask. It is a trick to ensure that all
2124 // the elements in the LyXRCTags enum is handled. As you can see
2125 // there are no breaks at all. So it is just a huge fall-through.
2126 // The nice thing is that we will get a warning from the compiler
2127 // if we forget an element.
2128 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2130 case LyXRC::RC_ACCEPT_COMPOUND:
2131 case LyXRC::RC_ALT_LANG:
2132 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2133 case LyXRC::RC_PLAINTEXT_LINELEN:
2134 case LyXRC::RC_AUTOREGIONDELETE:
2135 case LyXRC::RC_AUTORESET_OPTIONS:
2136 case LyXRC::RC_AUTOSAVE:
2137 case LyXRC::RC_AUTO_NUMBER:
2138 case LyXRC::RC_BACKUPDIR_PATH:
2139 case LyXRC::RC_BIBTEX_COMMAND:
2140 case LyXRC::RC_BINDFILE:
2141 case LyXRC::RC_CHECKLASTFILES:
2142 case LyXRC::RC_USELASTFILEPOS:
2143 case LyXRC::RC_LOADSESSION:
2144 case LyXRC::RC_CHKTEX_COMMAND:
2145 case LyXRC::RC_CONVERTER:
2146 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2147 case LyXRC::RC_COPIER:
2148 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2149 case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2150 case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2151 case LyXRC::RC_DATE_INSERT_FORMAT:
2152 case LyXRC::RC_DEFAULT_LANGUAGE:
2153 case LyXRC::RC_DEFAULT_PAPERSIZE:
2154 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2155 case LyXRC::RC_DISPLAY_GRAPHICS:
2156 case LyXRC::RC_DOCUMENTPATH:
2157 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2158 string const encoded = FileName(
2159 lyxrc_new.document_path).toFilesystemEncoding();
2160 if (fs::exists(encoded) && fs::is_directory(encoded))
2161 support::package().document_dir() = FileName(lyxrc.document_path);
2163 case LyXRC::RC_ESC_CHARS:
2164 case LyXRC::RC_FONT_ENCODING:
2165 case LyXRC::RC_FORMAT:
2166 case LyXRC::RC_INDEX_COMMAND:
2167 case LyXRC::RC_INPUT:
2168 case LyXRC::RC_KBMAP:
2169 case LyXRC::RC_KBMAP_PRIMARY:
2170 case LyXRC::RC_KBMAP_SECONDARY:
2171 case LyXRC::RC_LABEL_INIT_LENGTH:
2172 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2173 case LyXRC::RC_LANGUAGE_AUTO_END:
2174 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2175 case LyXRC::RC_LANGUAGE_COMMAND_END:
2176 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2177 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2178 case LyXRC::RC_LANGUAGE_PACKAGE:
2179 case LyXRC::RC_LANGUAGE_USE_BABEL:
2180 case LyXRC::RC_MAKE_BACKUP:
2181 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2182 case LyXRC::RC_NUMLASTFILES:
2183 case LyXRC::RC_PATH_PREFIX:
2184 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2185 support::prependEnvPath("PATH", lyxrc.path_prefix);
2187 case LyXRC::RC_PERS_DICT:
2188 case LyXRC::RC_PREVIEW:
2189 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2190 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2191 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2192 case LyXRC::RC_PRINTCOPIESFLAG:
2193 case LyXRC::RC_PRINTER:
2194 case LyXRC::RC_PRINTEVENPAGEFLAG:
2195 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2196 case LyXRC::RC_PRINTFILEEXTENSION:
2197 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2198 case LyXRC::RC_PRINTODDPAGEFLAG:
2199 case LyXRC::RC_PRINTPAGERANGEFLAG:
2200 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2201 case LyXRC::RC_PRINTPAPERFLAG:
2202 case LyXRC::RC_PRINTREVERSEFLAG:
2203 case LyXRC::RC_PRINTSPOOL_COMMAND:
2204 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2205 case LyXRC::RC_PRINTTOFILE:
2206 case LyXRC::RC_PRINTTOPRINTER:
2207 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2208 case LyXRC::RC_PRINT_COMMAND:
2209 case LyXRC::RC_RTL_SUPPORT:
2210 case LyXRC::RC_SCREEN_DPI:
2211 case LyXRC::RC_SCREEN_FONT_ROMAN:
2212 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2213 case LyXRC::RC_SCREEN_FONT_SANS:
2214 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2215 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2216 case LyXRC::RC_SCREEN_FONT_SIZES:
2217 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2218 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2219 case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2220 case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2221 case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2222 case LyXRC::RC_SCREEN_ZOOM:
2223 case LyXRC::RC_SERVERPIPE:
2224 case LyXRC::RC_SET_COLOR:
2225 case LyXRC::RC_SHOW_BANNER:
2226 case LyXRC::RC_SPELL_COMMAND:
2227 case LyXRC::RC_TEMPDIRPATH:
2228 case LyXRC::RC_TEMPLATEPATH:
2229 case LyXRC::RC_TEX_ALLOWS_SPACES:
2230 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2231 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2232 support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2234 case LyXRC::RC_UIFILE:
2235 case LyXRC::RC_USER_EMAIL:
2236 case LyXRC::RC_USER_NAME:
2237 case LyXRC::RC_USETEMPDIR:
2238 case LyXRC::RC_USE_ALT_LANG:
2239 case LyXRC::RC_USE_CONVERTER_CACHE:
2240 case LyXRC::RC_USE_ESC_CHARS:
2241 case LyXRC::RC_USE_INP_ENC:
2242 case LyXRC::RC_USE_PERS_DICT:
2243 case LyXRC::RC_USE_SPELL_LIB:
2244 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2245 case LyXRC::RC_VIEWER:
2246 case LyXRC::RC_LAST: