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 "LayoutFile.h"
25 #include "BranchList.h"
26 #include "buffer_funcs.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
33 #include "Converter.h"
35 #include "CutAndPaste.h"
36 #include "DispatchResult.h"
38 #include "ErrorList.h"
40 #include "FuncRequest.h"
41 #include "FuncStatus.h"
42 #include "InsetIterator.h"
47 #include "LyXAction.h"
52 #include "Paragraph.h"
53 #include "ParagraphParameters.h"
54 #include "ParIterator.h"
59 #include "insets/InsetBox.h"
60 #include "insets/InsetBranch.h"
61 #include "insets/InsetCommand.h"
62 #include "insets/InsetERT.h"
63 #include "insets/InsetExternal.h"
64 #include "insets/InsetFloat.h"
65 #include "insets/InsetGraphics.h"
66 #include "insets/InsetInclude.h"
67 #include "insets/InsetListings.h"
68 #include "insets/InsetNote.h"
69 #include "insets/InsetPhantom.h"
70 #include "insets/InsetSpace.h"
71 #include "insets/InsetTabular.h"
72 #include "insets/InsetVSpace.h"
73 #include "insets/InsetWrap.h"
75 #include "frontends/alert.h"
76 #include "frontends/Application.h"
77 #include "frontends/KeySymbol.h"
78 #include "frontends/LyXView.h"
79 #include "frontends/Selection.h"
81 #include "support/debug.h"
82 #include "support/environment.h"
83 #include "support/FileName.h"
84 #include "support/filetools.h"
85 #include "support/gettext.h"
86 #include "support/lstrings.h"
87 #include "support/Path.h"
88 #include "support/Package.h"
89 #include "support/Systemcall.h"
90 #include "support/convert.h"
91 #include "support/os.h"
97 using namespace lyx::support;
101 using frontend::LyXView;
103 namespace Alert = frontend::Alert;
108 // This function runs "configure" and then rereads lyx.defaults to
109 // reconfigure the automatic settings.
110 void reconfigure(LyXView * lv, string const & option)
112 // emit message signal.
114 lv->message(_("Running configure..."));
116 // Run configure in user lyx directory
117 PathChanger p(package().user_support());
118 string configure_command = package().configure_command();
119 configure_command += option;
121 int ret = one.startscript(Systemcall::Wait, configure_command);
123 // emit message signal.
125 lv->message(_("Reloading configuration..."));
126 lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
127 // Re-read packages.lst
128 LaTeXFeatures::getAvailable();
131 Alert::information(_("System reconfiguration failed"),
132 _("The system reconfiguration has failed.\n"
133 "Default textclass is used but LyX may "
134 "not be able to work properly.\n"
135 "Please reconfigure again if needed."));
138 Alert::information(_("System reconfigured"),
139 _("The system has been reconfigured.\n"
140 "You need to restart LyX to make use of any\n"
141 "updated document class specifications."));
145 bool getLocalStatus(Cursor cursor, FuncRequest const & cmd, FuncStatus & status)
147 // Try to fix cursor in case it is broken.
148 cursor.fixIfBroken();
150 // This is, of course, a mess. Better create a new doc iterator and use
151 // this in Inset::getStatus. This might require an additional
152 // BufferView * arg, though (which should be avoided)
153 //Cursor safe = *this;
155 for ( ; cursor.depth(); cursor.pop()) {
156 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
157 LASSERT(cursor.idx() <= cursor.lastidx(), /**/);
158 LASSERT(cursor.pit() <= cursor.lastpit(), /**/);
159 LASSERT(cursor.pos() <= cursor.lastpos(), /**/);
161 // The inset's getStatus() will return 'true' if it made
162 // a definitive decision on whether it want to handle the
163 // request or not. The result of this decision is put into
164 // the 'status' parameter.
165 if (cursor.inset().getStatus(cursor, cmd, status)) {
174 /** Return the change status at cursor position, taking in account the
175 * status at each level of the document iterator (a table in a deleted
176 * footnote is deleted).
177 * When \param outer is true, the top slice is not looked at.
179 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
181 size_t const depth = dit.depth() - (outer ? 1 : 0);
183 for (size_t i = 0 ; i < depth ; ++i) {
184 CursorSlice const & slice = dit[i];
185 if (!slice.inset().inMathed()
186 && slice.pos() < slice.paragraph().size()) {
187 Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
188 if (ch != Change::UNCHANGED)
192 return Change::UNCHANGED;
199 : lyx_view_(0), encoded_last_key(0), meta_fake_bit(NoModifier)
204 void LyXFunc::initKeySequences(KeyMap * kb)
206 keyseq = KeySequence(kb, kb);
207 cancel_meta_seq = KeySequence(kb, kb);
211 void LyXFunc::setLyXView(LyXView * lv)
213 if (lyx_view_ && lyx_view_->view() && lyx_view_ != lv)
214 // save current selection to the selection buffer to allow
215 // middle-button paste in another window
216 cap::saveSelection(lyx_view_->view()->cursor());
221 void LyXFunc::handleKeyFunc(FuncCode action)
223 char_type c = encoded_last_key;
228 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
229 lyx_view_->view()->getIntl().getTransManager().deadkey(
230 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
231 // Need to clear, in case the minibuffer calls these
234 // copied verbatim from do_accent_char
235 view()->cursor().resetAnchor();
236 view()->processUpdateFlags(Update::FitCursor);
239 //FIXME: bookmark handling is a frontend issue. This code should be transferred
240 // to GuiView and be GuiView and be window dependent.
241 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
243 LASSERT(lyx_view_, /**/);
244 if (!theSession().bookmarks().isValid(idx))
246 BookmarksSection::Bookmark const & bm = theSession().bookmarks().bookmark(idx);
247 LASSERT(!bm.filename.empty(), /**/);
248 string const file = bm.filename.absFilename();
249 // if the file is not opened, open it.
250 if (!theBufferList().exists(bm.filename)) {
252 dispatch(FuncRequest(LFUN_FILE_OPEN, file));
256 // open may fail, so we need to test it again
257 if (!theBufferList().exists(bm.filename))
260 // bm can be changed when saving
261 BookmarksSection::Bookmark tmp = bm;
263 // Special case idx == 0 used for back-from-back jump navigation
265 dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
267 // if the current buffer is not that one, switch to it.
268 if (!lyx_view_->buffer() || lyx_view_->buffer()->fileName() != tmp.filename) {
271 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
274 // moveToPosition try paragraph id first and then paragraph (pit, pos).
275 if (!view()->moveToPosition(tmp.bottom_pit, tmp.bottom_pos,
276 tmp.top_id, tmp.top_pos))
283 // Cursor jump succeeded!
284 Cursor const & cur = view()->cursor();
285 pit_type new_pit = cur.pit();
286 pos_type new_pos = cur.pos();
287 int new_id = cur.paragraph().id();
289 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
290 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
291 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos
292 || bm.top_id != new_id) {
293 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
294 new_pit, new_pos, new_id);
299 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
301 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
303 // Do nothing if we have nothing (JMarc)
304 if (!keysym.isOK()) {
305 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
306 lyx_view_->restartCursor();
310 if (keysym.isModifier()) {
311 LYXERR(Debug::KEY, "isModifier true");
313 lyx_view_->restartCursor();
317 //Encoding const * encoding = view()->cursor().getEncoding();
318 //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
319 // FIXME: encoded_last_key shadows the member variable of the same
320 // name. Is that intended?
321 char_type encoded_last_key = keysym.getUCSEncoded();
323 // Do a one-deep top-level lookup for
324 // cancel and meta-fake keys. RVDK_PATCH_5
325 cancel_meta_seq.reset();
327 FuncRequest func = cancel_meta_seq.addkey(keysym, state);
328 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
330 // When not cancel or meta-fake, do the normal lookup.
331 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
332 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
333 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
334 // remove Caps Lock and Mod2 as a modifiers
335 func = keyseq.addkey(keysym, (state | meta_fake_bit));
336 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
339 // Dont remove this unless you know what you are doing.
340 meta_fake_bit = NoModifier;
342 // Can this happen now ?
343 if (func.action == LFUN_NOACTION)
344 func = FuncRequest(LFUN_COMMAND_PREFIX);
346 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
347 << keyseq.print(KeySequence::Portable) << ']');
349 // already here we know if it any point in going further
350 // why not return already here if action == -1 and
351 // num_bytes == 0? (Lgb)
353 if (keyseq.length() > 1)
354 lyx_view_->message(keyseq.print(KeySequence::ForGui));
357 // Maybe user can only reach the key via holding down shift.
358 // Let's see. But only if shift is the only modifier
359 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
360 LYXERR(Debug::KEY, "Trying without shift");
361 func = keyseq.addkey(keysym, NoModifier);
362 LYXERR(Debug::KEY, "Action now " << func.action);
365 if (func.action == LFUN_UNKNOWN_ACTION) {
366 // Hmm, we didn't match any of the keysequences. See
367 // if it's normal insertable text not already covered
369 if (keysym.isText() && keyseq.length() == 1) {
370 LYXERR(Debug::KEY, "isText() is true, inserting.");
371 func = FuncRequest(LFUN_SELF_INSERT,
372 FuncRequest::KEYBOARD);
374 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
375 lyx_view_->message(_("Unknown function."));
376 lyx_view_->restartCursor();
381 if (func.action == LFUN_SELF_INSERT) {
382 if (encoded_last_key != 0) {
383 docstring const arg(1, encoded_last_key);
384 dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
385 FuncRequest::KEYBOARD));
386 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
396 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
398 //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
401 /* In LyX/Mac, when a dialog is open, the menus of the
402 application can still be accessed without giving focus to
403 the main window. In this case, we want to disable the menu
404 entries that are buffer or view-related.
406 If this code is moved somewhere else (like in
407 GuiView::getStatus), then several functions will not be
410 frontend::LyXView * lv = 0;
413 && (cmd.origin != FuncRequest::MENU || lyx_view_->hasFocus())) {
415 buf = lyx_view_->buffer();
418 if (cmd.action == LFUN_NOACTION) {
419 flag.message(from_utf8(N_("Nothing to do")));
420 flag.setEnabled(false);
424 switch (cmd.action) {
425 case LFUN_UNKNOWN_ACTION:
427 flag.setEnabled(false);
434 if (flag.unknown()) {
435 flag.message(from_utf8(N_("Unknown action")));
439 if (!flag.enabled()) {
440 if (flag.message().empty())
441 flag.message(from_utf8(N_("Command disabled")));
445 // Check whether we need a buffer
446 if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
448 flag.message(from_utf8(N_("Command not allowed with"
449 "out any document open")));
450 flag.setEnabled(false);
454 // I would really like to avoid having this switch and rather try to
455 // encode this in the function itself.
456 // -- And I'd rather let an inset decide which LFUNs it is willing
457 // to handle (Andre')
459 switch (cmd.action) {
461 case LFUN_BUFFER_TOGGLE_READ_ONLY:
462 flag.setOnOff(buf->isReadonly());
465 case LFUN_BUFFER_SWITCH:
466 // toggle on the current buffer, but do not toggle off
467 // the other ones (is that a good idea?)
468 if (buf && to_utf8(cmd.argument()) == buf->absFileName())
472 case LFUN_BUFFER_CHKTEX:
473 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
476 case LFUN_BUILD_PROGRAM:
477 enable = buf->isExportable("program");
480 case LFUN_VC_REGISTER:
481 enable = !buf->lyxvc().inUse();
483 case LFUN_VC_CHECK_IN:
484 enable = buf->lyxvc().checkInEnabled();
486 case LFUN_VC_CHECK_OUT:
487 enable = buf->lyxvc().checkOutEnabled();
489 case LFUN_VC_LOCKING_TOGGLE:
490 enable = !buf->isReadonly() && buf->lyxvc().lockingToggleEnabled();
491 flag.setOnOff(enable && !buf->lyxvc().locker().empty());
494 enable = buf->lyxvc().inUse();
496 case LFUN_VC_UNDO_LAST:
497 enable = buf->lyxvc().undoLastEnabled();
499 case LFUN_BUFFER_RELOAD:
500 enable = !buf->isUnnamed() && buf->fileName().exists()
501 && (!buf->isClean() || buf->isExternallyModified(Buffer::timestamp_method));
504 case LFUN_CITATION_INSERT: {
505 FuncRequest fr(LFUN_INSET_INSERT, "citation");
506 enable = getStatus(fr).enabled();
510 // This could be used for the no-GUI version. The GUI version is handled in
511 // LyXView::getStatus(). See above.
513 case LFUN_BUFFER_WRITE:
514 case LFUN_BUFFER_WRITE_AS: {
515 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
516 enable = b && (b->isUnnamed() || !b->isClean());
521 case LFUN_BUFFER_WRITE_ALL: {
522 // We enable the command only if there are some modified buffers
523 Buffer * first = theBufferList().first();
528 // We cannot use a for loop as the buffer list is a cycle.
534 b = theBufferList().next(b);
535 } while (b != first);
539 case LFUN_BOOKMARK_GOTO: {
540 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
541 enable = theSession().bookmarks().isValid(num);
545 case LFUN_BOOKMARK_CLEAR:
546 enable = theSession().bookmarks().hasValid();
549 // this one is difficult to get right. As a half-baked
550 // solution, we consider only the first action of the sequence
551 case LFUN_COMMAND_SEQUENCE: {
552 // argument contains ';'-terminated commands
553 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
554 FuncRequest func(lyxaction.lookupFunc(firstcmd));
555 func.origin = cmd.origin;
556 flag = getStatus(func);
560 // we want to check if at least one of these is enabled
561 case LFUN_COMMAND_ALTERNATIVES: {
562 // argument contains ';'-terminated commands
563 string arg = to_utf8(cmd.argument());
564 while (!arg.empty()) {
566 arg = split(arg, first, ';');
567 FuncRequest func(lyxaction.lookupFunc(first));
568 func.origin = cmd.origin;
569 flag = getStatus(func);
570 // if this one is enabled, the whole thing is
579 string name = to_utf8(cmd.argument());
580 if (theTopLevelCmdDef().lock(name, func)) {
581 func.origin = cmd.origin;
582 flag = getStatus(func);
583 theTopLevelCmdDef().release(name);
585 // catch recursion or unknown command
586 // definition. all operations until the
587 // recursion or unknown command definition
588 // occurs are performed, so set the state to
595 case LFUN_VC_COMMAND: {
596 if (cmd.argument().empty())
599 if (!buf && contains(cmd.getArg(0), 'D'))
604 case LFUN_MASTER_BUFFER_UPDATE:
605 case LFUN_MASTER_BUFFER_VIEW:
606 if (!buf->parent()) {
610 case LFUN_BUFFER_UPDATE:
611 case LFUN_BUFFER_VIEW: {
612 string format = to_utf8(cmd.argument());
613 if (cmd.argument().empty())
614 format = buf->getDefaultOutputFormat();
615 typedef vector<Format const *> Formats;
617 formats = buf->exportableFormats(true);
618 Formats::const_iterator fit = formats.begin();
619 Formats::const_iterator end = formats.end();
621 for (; fit != end ; ++fit) {
622 if ((*fit)->name() == format)
628 case LFUN_WORD_FIND_FORWARD:
629 case LFUN_WORD_FIND_BACKWARD:
630 case LFUN_WORD_FINDADV:
631 case LFUN_COMMAND_PREFIX:
632 case LFUN_COMMAND_EXECUTE:
634 case LFUN_META_PREFIX:
635 case LFUN_BUFFER_CLOSE:
636 case LFUN_BUFFER_IMPORT:
637 case LFUN_BUFFER_AUTO_SAVE:
638 case LFUN_RECONFIGURE:
640 case LFUN_DROP_LAYOUTS_CHOICE:
642 case LFUN_SERVER_GET_FILENAME:
643 case LFUN_SERVER_NOTIFY:
644 case LFUN_SERVER_GOTO_FILE_ROW:
645 case LFUN_DIALOG_HIDE:
646 case LFUN_DIALOG_DISCONNECT_INSET:
647 case LFUN_BUFFER_CHILD_OPEN:
648 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
649 case LFUN_KEYMAP_OFF:
650 case LFUN_KEYMAP_PRIMARY:
651 case LFUN_KEYMAP_SECONDARY:
652 case LFUN_KEYMAP_TOGGLE:
654 case LFUN_BUFFER_EXPORT_CUSTOM:
655 case LFUN_PREFERENCES_SAVE:
657 case LFUN_INSET_EDIT:
658 case LFUN_BUFFER_LANGUAGE:
659 case LFUN_TEXTCLASS_APPLY:
660 case LFUN_TEXTCLASS_LOAD:
661 case LFUN_BUFFER_SAVE_AS_DEFAULT:
662 case LFUN_BUFFER_PARAMS_APPLY:
663 case LFUN_LAYOUT_MODULES_CLEAR:
664 case LFUN_LAYOUT_MODULE_ADD:
665 case LFUN_LAYOUT_RELOAD:
666 case LFUN_LYXRC_APPLY:
667 case LFUN_BUFFER_NEXT:
668 case LFUN_BUFFER_PREVIOUS:
669 // these are handled in our dispatch()
677 if (theApp()->getStatus(cmd, flag))
680 // Does the view know something?
685 if (lv->getStatus(cmd, flag))
688 // If we do not have a BufferView, then other functions are disabled
694 // Is this a function that acts on inset at point?
695 Inset * inset = view()->cursor().nextInset();
696 if (lyxaction.funcHasFlag(cmd.action, LyXAction::AtPoint)
697 && inset && inset->getStatus(view()->cursor(), cmd, flag))
700 bool decided = getLocalStatus(view()->cursor(), cmd, flag);
702 // try the BufferView
703 decided = view()->getStatus(cmd, flag);
706 view()->buffer().getStatus(cmd, flag);
710 flag.setEnabled(false);
712 // Can we use a readonly buffer?
713 if (buf && buf->isReadonly()
714 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
715 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
716 flag.message(from_utf8(N_("Document is read-only")));
717 flag.setEnabled(false);
720 // Are we in a DELETED change-tracking region?
722 && lookupChangeType(view()->cursor(), true) == Change::DELETED
723 && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
724 && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
725 flag.message(from_utf8(N_("This portion of the document is deleted.")));
726 flag.setEnabled(false);
729 // the default error message if we disable the command
730 if (!flag.enabled() && flag.message().empty())
731 flag.message(from_utf8(N_("Command disabled")));
737 bool LyXFunc::ensureBufferClean(BufferView * bv)
739 Buffer & buf = bv->buffer();
740 if (buf.isClean() && !buf.isUnnamed())
743 docstring const file = buf.fileName().displayName(30);
746 if (!buf.isUnnamed()) {
747 text = bformat(_("The document %1$s has unsaved "
748 "changes.\n\nDo you want to save "
749 "the document?"), file);
750 title = _("Save changed document?");
753 text = bformat(_("The document %1$s has not been "
754 "saved yet.\n\nDo you want to save "
755 "the document?"), file);
756 title = _("Save new document?");
758 int const ret = Alert::prompt(title, text, 0, 1, _("&Save"), _("&Cancel"));
761 lyx_view_->dispatch(FuncRequest(LFUN_BUFFER_WRITE));
763 return buf.isClean() && !buf.isUnnamed();
769 bool loadLayoutFile(string const & name, string const & buf_path)
771 if (!LayoutFileList::get().haveClass(name)) {
772 lyxerr << "Document class \"" << name
773 << "\" does not exist."
778 LayoutFile & tc = LayoutFileList::get()[name];
779 if (!tc.load(buf_path)) {
780 docstring s = bformat(_("The document class %1$s "
781 "could not be loaded."), from_utf8(name));
782 Alert::error(_("Could not load class"), s);
789 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
794 void LyXFunc::dispatch(FuncRequest const & cmd)
796 string const argument = to_utf8(cmd.argument());
797 FuncCode const action = cmd.action;
799 LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
800 //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
802 // we have not done anything wrong yet.
804 dispatch_buffer.erase();
806 // redraw the screen at the end (first of the two drawing steps).
807 //This is done unless explicitely requested otherwise
808 Update::flags updateFlags = Update::FitCursor;
810 FuncStatus const flag = getStatus(cmd);
811 if (!flag.enabled()) {
812 // We cannot use this function here
813 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
814 << lyxaction.getActionName(action)
815 << " [" << action << "] is disabled at this location");
816 setErrorMessage(flag.message());
818 lyx_view_->restartCursor();
820 Buffer * buffer = lyx_view_ ? lyx_view_->buffer() : 0;
823 case LFUN_WORD_FIND_FORWARD:
824 case LFUN_WORD_FIND_BACKWARD: {
825 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
826 static docstring last_search;
827 docstring searched_string;
829 if (!cmd.argument().empty()) {
830 last_search = cmd.argument();
831 searched_string = cmd.argument();
833 searched_string = last_search;
836 if (searched_string.empty())
839 bool const fw = action == LFUN_WORD_FIND_FORWARD;
840 docstring const data =
841 find2string(searched_string, true, false, fw);
842 find(view(), FuncRequest(LFUN_WORD_FIND, data));
846 case LFUN_COMMAND_PREFIX:
847 LASSERT(lyx_view_, /**/);
848 lyx_view_->message(keyseq.printOptions(true));
852 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
854 meta_fake_bit = NoModifier;
856 // cancel any selection
857 dispatch(FuncRequest(LFUN_MARK_OFF));
858 setMessage(from_ascii(N_("Cancel")));
861 case LFUN_META_PREFIX:
862 meta_fake_bit = AltModifier;
863 setMessage(keyseq.print(KeySequence::ForGui));
866 case LFUN_BUFFER_TOGGLE_READ_ONLY: {
867 LASSERT(lyx_view_ && lyx_view_->view() && buffer, /**/);
868 if (buffer->lyxvc().inUse())
869 buffer->lyxvc().toggleReadOnly();
871 buffer->setReadonly(!buffer->isReadonly());
875 // --- Menus -----------------------------------------------
876 case LFUN_BUFFER_CLOSE:
877 lyx_view_->closeBuffer();
879 updateFlags = Update::None;
882 case LFUN_BUFFER_CLOSE_ALL:
883 lyx_view_->closeBufferAll();
885 updateFlags = Update::None;
888 case LFUN_BUFFER_RELOAD: {
889 LASSERT(lyx_view_ && buffer, /**/);
890 docstring const file = makeDisplayPath(buffer->absFileName(), 20);
891 docstring text = bformat(_("Any changes will be lost. Are you sure "
892 "you want to revert to the saved version of the document %1$s?"), file);
893 int const ret = Alert::prompt(_("Revert to saved document?"),
894 text, 1, 1, _("&Revert"), _("&Cancel"));
901 case LFUN_BUFFER_UPDATE: {
902 LASSERT(lyx_view_ && buffer, /**/);
903 string format = argument;
904 if (argument.empty())
905 format = buffer->getDefaultOutputFormat();
906 buffer->doExport(format, true);
910 case LFUN_BUFFER_VIEW: {
911 LASSERT(lyx_view_ && buffer, /**/);
912 string format = argument;
913 if (argument.empty())
914 format = buffer->getDefaultOutputFormat();
915 buffer->preview(format);
919 case LFUN_MASTER_BUFFER_UPDATE: {
920 LASSERT(lyx_view_ && buffer && buffer->masterBuffer(), /**/);
921 string format = argument;
922 if (argument.empty())
923 format = buffer->masterBuffer()->getDefaultOutputFormat();
924 buffer->masterBuffer()->doExport(format, true);
928 case LFUN_MASTER_BUFFER_VIEW: {
929 LASSERT(lyx_view_ && buffer && buffer->masterBuffer(), /**/);
930 string format = argument;
931 if (argument.empty())
932 format = buffer->masterBuffer()->getDefaultOutputFormat();
933 buffer->masterBuffer()->preview(format);
937 case LFUN_BUILD_PROGRAM:
938 LASSERT(lyx_view_ && buffer, /**/);
939 buffer->doExport("program", true);
942 case LFUN_BUFFER_CHKTEX:
943 LASSERT(lyx_view_ && buffer, /**/);
947 case LFUN_BUFFER_EXPORT:
948 LASSERT(lyx_view_ && buffer, /**/);
949 if (argument == "custom")
950 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "sendto"));
952 buffer->doExport(argument, false);
955 case LFUN_BUFFER_EXPORT_CUSTOM: {
956 LASSERT(lyx_view_ && buffer, /**/);
958 string command = split(argument, format_name, ' ');
959 Format const * format = formats.getFormat(format_name);
961 lyxerr << "Format \"" << format_name
962 << "\" not recognized!"
967 // The name of the file created by the conversion process
970 // Output to filename
971 if (format->name() == "lyx") {
972 string const latexname = buffer->latexName(false);
973 filename = changeExtension(latexname,
974 format->extension());
975 filename = addName(buffer->temppath(), filename);
977 if (!buffer->writeFile(FileName(filename)))
981 buffer->doExport(format_name, true, filename);
984 // Substitute $$FName for filename
985 if (!contains(command, "$$FName"))
986 command = "( " + command + " ) < $$FName";
987 command = subst(command, "$$FName", filename);
989 // Execute the command in the background
991 call.startscript(Systemcall::DontWait, command);
995 // FIXME: There is need for a command-line import.
997 case LFUN_BUFFER_IMPORT:
1002 case LFUN_BUFFER_AUTO_SAVE:
1006 case LFUN_RECONFIGURE:
1007 // argument is any additional parameter to the configure.py command
1008 reconfigure(lyx_view_, argument);
1011 case LFUN_HELP_OPEN: {
1013 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1014 string const arg = argument;
1016 setErrorMessage(from_utf8(N_("Missing argument")));
1019 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1021 fname = i18nLibFileSearch("examples", arg, "lyx");
1023 if (fname.empty()) {
1024 lyxerr << "LyX: unable to find documentation file `"
1025 << arg << "'. Bad installation?" << endl;
1028 lyx_view_->message(bformat(_("Opening help file %1$s..."),
1029 makeDisplayPath(fname.absFilename())));
1030 Buffer * buf = lyx_view_->loadDocument(fname, false);
1032 buf->updateLabels();
1033 lyx_view_->setBuffer(buf);
1034 buf->errors("Parse");
1036 updateFlags = Update::None;
1040 // --- version control -------------------------------
1041 case LFUN_VC_REGISTER:
1042 LASSERT(lyx_view_ && buffer, /**/);
1043 if (!ensureBufferClean(view()))
1045 if (!buffer->lyxvc().inUse()) {
1046 if (buffer->lyxvc().registrer())
1049 updateFlags = Update::Force;
1052 case LFUN_VC_CHECK_IN:
1053 LASSERT(lyx_view_ && buffer, /**/);
1054 if (!ensureBufferClean(view()))
1056 if (buffer->lyxvc().inUse()
1057 && !buffer->isReadonly()) {
1058 setMessage(from_utf8(buffer->lyxvc().checkIn()));
1063 case LFUN_VC_CHECK_OUT:
1064 LASSERT(lyx_view_ && buffer, /**/);
1065 if (!ensureBufferClean(view()))
1067 if (buffer->lyxvc().inUse()) {
1068 setMessage(from_utf8(buffer->lyxvc().checkOut()));
1073 case LFUN_VC_LOCKING_TOGGLE:
1074 LASSERT(lyx_view_ && buffer, /**/);
1075 if (!ensureBufferClean(view()) || buffer->isReadonly())
1077 if (buffer->lyxvc().inUse()) {
1078 string res = buffer->lyxvc().lockingToggle();
1080 frontend::Alert::error(_("Revision control error."),
1081 _("Error when setting the locking property."));
1083 setMessage(from_utf8(res));
1089 case LFUN_VC_REVERT:
1090 LASSERT(lyx_view_ && buffer, /**/);
1091 buffer->lyxvc().revert();
1095 case LFUN_VC_UNDO_LAST:
1096 LASSERT(lyx_view_ && buffer, /**/);
1097 buffer->lyxvc().undoLast();
1101 // --- lyxserver commands ----------------------------
1102 case LFUN_SERVER_GET_FILENAME:
1103 LASSERT(lyx_view_ && buffer, /**/);
1104 setMessage(from_utf8(buffer->absFileName()));
1105 LYXERR(Debug::INFO, "FNAME["
1106 << buffer->absFileName() << ']');
1109 case LFUN_SERVER_NOTIFY:
1110 dispatch_buffer = keyseq.print(KeySequence::Portable);
1111 theServer().notifyClient(to_utf8(dispatch_buffer));
1114 case LFUN_SERVER_GOTO_FILE_ROW: {
1115 LASSERT(lyx_view_, /**/);
1118 istringstream is(argument);
1119 is >> file_name >> row;
1121 bool loaded = false;
1122 string const abstmp = package().temp_dir().absFilename();
1123 string const realtmp = package().temp_dir().realPath();
1124 // We have to use os::path_prefix_is() here, instead of
1125 // simply prefixIs(), because the file name comes from
1126 // an external application and may need case adjustment.
1127 if (os::path_prefix_is(file_name, abstmp, os::CASE_ADJUSTED)
1128 || os::path_prefix_is(file_name, realtmp, os::CASE_ADJUSTED)) {
1129 // Needed by inverse dvi search. If it is a file
1130 // in tmpdir, call the apropriated function.
1131 // If tmpdir is a symlink, we may have the real
1132 // path passed back, so we correct for that.
1133 if (!prefixIs(file_name, abstmp))
1134 file_name = subst(file_name, realtmp, abstmp);
1135 buf = theBufferList().getBufferFromTmp(file_name);
1137 // Must replace extension of the file to be .lyx
1138 // and get full path
1139 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1140 // Either change buffer or load the file
1141 if (theBufferList().exists(s))
1142 buf = theBufferList().getBuffer(s);
1143 else if (s.exists()) {
1144 buf = lyx_view_->loadDocument(s);
1147 lyx_view_->message(bformat(
1148 _("File does not exist: %1$s"),
1149 makeDisplayPath(file_name)));
1153 updateFlags = Update::None;
1157 buf->updateLabels();
1158 lyx_view_->setBuffer(buf);
1159 view()->setCursorFromRow(row);
1161 buf->errors("Parse");
1162 updateFlags = Update::FitCursor;
1167 case LFUN_DIALOG_SHOW_NEW_INSET: {
1168 LASSERT(lyx_view_, /**/);
1169 string const name = cmd.getArg(0);
1170 InsetCode code = insetCode(name);
1171 string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1172 bool insetCodeOK = true;
1179 case NOMENCL_PRINT_CODE:
1182 case HYPERLINK_CODE: {
1183 InsetCommandParams p(code);
1184 data = InsetCommand::params2string(name, p);
1187 case INCLUDE_CODE: {
1188 // data is the include type: one of "include",
1189 // "input", "verbatiminput" or "verbatiminput*"
1191 // default type is requested
1193 InsetCommandParams p(INCLUDE_CODE, data);
1194 data = InsetCommand::params2string("include", p);
1198 // \c data == "Boxed" || "Frameless" etc
1199 InsetBoxParams p(data);
1200 data = InsetBox::params2string(p);
1204 InsetBranchParams p;
1205 data = InsetBranch::params2string(p);
1209 InsetCommandParams p(CITE_CODE);
1210 data = InsetCommand::params2string(name, p);
1214 data = InsetERT::params2string(InsetCollapsable::Open);
1217 case EXTERNAL_CODE: {
1218 InsetExternalParams p;
1219 data = InsetExternal::params2string(p, *buffer);
1224 data = InsetFloat::params2string(p);
1227 case LISTINGS_CODE: {
1228 InsetListingsParams p;
1229 data = InsetListings::params2string(p);
1232 case GRAPHICS_CODE: {
1233 InsetGraphicsParams p;
1234 data = InsetGraphics::params2string(p, *buffer);
1239 data = InsetNote::params2string(p);
1242 case PHANTOM_CODE: {
1243 InsetPhantomParams p;
1244 data = InsetPhantom::params2string(p);
1249 data = InsetSpace::params2string(p);
1254 data = InsetVSpace::params2string(space);
1259 data = InsetWrap::params2string(p);
1263 lyxerr << "Inset type '" << name <<
1264 "' not recognized in LFUN_DIALOG_SHOW_NEW_INSET" << endl;
1265 insetCodeOK = false;
1267 } // end switch(code)
1269 dispatch(FuncRequest(LFUN_DIALOG_SHOW, name + " " + data));
1273 case LFUN_CITATION_INSERT: {
1274 LASSERT(lyx_view_, /**/);
1275 if (!argument.empty()) {
1276 // we can have one optional argument, delimited by '|'
1277 // citation-insert <key>|<text_before>
1278 // this should be enhanced to also support text_after
1279 // and citation style
1280 string arg = argument;
1282 if (contains(argument, "|")) {
1283 arg = token(argument, '|', 0);
1284 opt1 = token(argument, '|', 1);
1286 InsetCommandParams icp(CITE_CODE);
1287 icp["key"] = from_utf8(arg);
1289 icp["before"] = from_utf8(opt1);
1290 string icstr = InsetCommand::params2string("citation", icp);
1291 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1294 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1298 case LFUN_BUFFER_CHILD_OPEN: {
1299 LASSERT(lyx_view_ && buffer, /**/);
1300 FileName filename = makeAbsPath(argument, buffer->filePath());
1301 view()->saveBookmark(false);
1303 bool parsed = false;
1304 if (theBufferList().exists(filename)) {
1305 child = theBufferList().getBuffer(filename);
1307 setMessage(bformat(_("Opening child document %1$s..."),
1308 makeDisplayPath(filename.absFilename())));
1309 child = lyx_view_->loadDocument(filename, false);
1313 // Set the parent name of the child document.
1314 // This makes insertion of citations and references in the child work,
1315 // when the target is in the parent or another child document.
1316 child->setParent(buffer);
1317 child->masterBuffer()->updateLabels();
1318 lyx_view_->setBuffer(child);
1320 child->errors("Parse");
1323 // If a screen update is required (in case where auto_open is false),
1324 // setBuffer() would have taken care of it already. Otherwise we shall
1325 // reset the update flag because it can cause a circular problem.
1327 updateFlags = Update::None;
1331 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
1332 LASSERT(lyx_view_, /**/);
1333 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1336 case LFUN_KEYMAP_OFF:
1337 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1338 lyx_view_->view()->getIntl().keyMapOn(false);
1341 case LFUN_KEYMAP_PRIMARY:
1342 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1343 lyx_view_->view()->getIntl().keyMapPrim();
1346 case LFUN_KEYMAP_SECONDARY:
1347 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1348 lyx_view_->view()->getIntl().keyMapSec();
1351 case LFUN_KEYMAP_TOGGLE:
1352 LASSERT(lyx_view_ && lyx_view_->view(), /**/);
1353 lyx_view_->view()->getIntl().toggleKeyMap();
1359 string rest = split(argument, countstr, ' ');
1360 istringstream is(countstr);
1363 //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1364 for (int i = 0; i < count; ++i)
1365 dispatch(lyxaction.lookupFunc(rest));
1369 case LFUN_COMMAND_SEQUENCE: {
1370 // argument contains ';'-terminated commands
1371 string arg = argument;
1372 if (theBufferList().isLoaded(buffer))
1373 buffer->undo().beginUndoGroup();
1374 while (!arg.empty()) {
1376 arg = split(arg, first, ';');
1377 FuncRequest func(lyxaction.lookupFunc(first));
1378 func.origin = cmd.origin;
1381 if (theBufferList().isLoaded(buffer))
1382 buffer->undo().endUndoGroup();
1386 case LFUN_COMMAND_ALTERNATIVES: {
1387 // argument contains ';'-terminated commands
1388 string arg = argument;
1389 while (!arg.empty()) {
1391 arg = split(arg, first, ';');
1392 FuncRequest func(lyxaction.lookupFunc(first));
1393 func.origin = cmd.origin;
1394 FuncStatus stat = getStatus(func);
1395 if (stat.enabled()) {
1405 if (theTopLevelCmdDef().lock(argument, func)) {
1406 func.origin = cmd.origin;
1408 theTopLevelCmdDef().release(argument);
1410 if (func.action == LFUN_UNKNOWN_ACTION) {
1411 // unknown command definition
1412 lyxerr << "Warning: unknown command definition `"
1416 // recursion detected
1417 lyxerr << "Warning: Recursion in the command definition `"
1418 << argument << "' detected"
1425 case LFUN_PREFERENCES_SAVE: {
1426 lyxrc.write(makeAbsPath("preferences",
1427 package().user_support().absFilename()),
1433 LASSERT(lyx_view_, /**/);
1434 lyx_view_->message(from_utf8(argument));
1437 case LFUN_BUFFER_LANGUAGE: {
1438 LASSERT(lyx_view_, /**/);
1439 Language const * oldL = buffer->params().language;
1440 Language const * newL = languages.getLanguage(argument);
1441 if (!newL || oldL == newL)
1444 if (oldL->rightToLeft() == newL->rightToLeft()
1445 && !buffer->isMultiLingual())
1446 buffer->changeLanguage(oldL, newL);
1450 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1451 string const fname =
1452 addName(addPath(package().user_support().absFilename(), "templates/"),
1454 Buffer defaults(fname);
1456 istringstream ss(argument);
1459 int const unknown_tokens = defaults.readHeader(lex);
1461 if (unknown_tokens != 0) {
1462 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1463 << unknown_tokens << " unknown token"
1464 << (unknown_tokens == 1 ? "" : "s")
1468 if (defaults.writeFile(FileName(defaults.absFileName())))
1469 setMessage(bformat(_("Document defaults saved in %1$s"),
1470 makeDisplayPath(fname)));
1472 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1476 case LFUN_BUFFER_PARAMS_APPLY: {
1477 LASSERT(lyx_view_, /**/);
1479 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1480 Cursor & cur = view()->cursor();
1481 cur.recordUndoFullDocument();
1483 istringstream ss(argument);
1486 int const unknown_tokens = buffer->readHeader(lex);
1488 if (unknown_tokens != 0) {
1489 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1490 << unknown_tokens << " unknown token"
1491 << (unknown_tokens == 1 ? "" : "s")
1495 updateLayout(oldClass, buffer);
1497 updateFlags = Update::Force | Update::FitCursor;
1498 // We are most certainly here because of a change in the document
1499 // It is then better to make sure that all dialogs are in sync with
1500 // current document settings. LyXView::restartCursor() achieve this.
1501 lyx_view_->restartCursor();
1505 case LFUN_LAYOUT_MODULES_CLEAR: {
1506 LASSERT(lyx_view_, /**/);
1507 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1508 view()->cursor().recordUndoFullDocument();
1509 buffer->params().clearLayoutModules();
1510 buffer->params().makeDocumentClass();
1511 updateLayout(oldClass, buffer);
1512 updateFlags = Update::Force | Update::FitCursor;
1516 case LFUN_LAYOUT_MODULE_ADD: {
1517 LASSERT(lyx_view_, /**/);
1518 BufferParams const & params = buffer->params();
1519 if (!params.moduleCanBeAdded(argument)) {
1520 LYXERR0("Module `" << argument <<
1521 "' cannot be added due to failed requirements or "
1522 "conflicts with installed modules.");
1525 DocumentClass const * const oldClass = params.documentClassPtr();
1526 view()->cursor().recordUndoFullDocument();
1527 buffer->params().addLayoutModule(argument);
1528 buffer->params().makeDocumentClass();
1529 updateLayout(oldClass, buffer);
1530 updateFlags = Update::Force | Update::FitCursor;
1534 case LFUN_TEXTCLASS_APPLY: {
1535 LASSERT(lyx_view_, /**/);
1537 if (!loadLayoutFile(argument, buffer->temppath()) &&
1538 !loadLayoutFile(argument, buffer->filePath()))
1541 LayoutFile const * old_layout = buffer->params().baseClass();
1542 LayoutFile const * new_layout = &(LayoutFileList::get()[argument]);
1544 if (old_layout == new_layout)
1548 //Save the old, possibly modular, layout for use in conversion.
1549 DocumentClass const * const oldDocClass = buffer->params().documentClassPtr();
1550 view()->cursor().recordUndoFullDocument();
1551 buffer->params().setBaseClass(argument);
1552 buffer->params().makeDocumentClass();
1553 updateLayout(oldDocClass, buffer);
1554 updateFlags = Update::Force | Update::FitCursor;
1558 case LFUN_LAYOUT_RELOAD: {
1559 LASSERT(lyx_view_, /**/);
1560 DocumentClass const * const oldClass = buffer->params().documentClassPtr();
1561 LayoutFileIndex bc = buffer->params().baseClassID();
1562 LayoutFileList::get().reset(bc);
1563 buffer->params().setBaseClass(bc);
1564 buffer->params().makeDocumentClass();
1565 updateLayout(oldClass, buffer);
1566 updateFlags = Update::Force | Update::FitCursor;
1570 case LFUN_TEXTCLASS_LOAD:
1571 loadLayoutFile(argument, buffer->temppath()) ||
1572 loadLayoutFile(argument, buffer->filePath());
1575 case LFUN_LYXRC_APPLY: {
1576 // reset active key sequences, since the bindings
1577 // are updated (bug 6064)
1579 LyXRC const lyxrc_orig = lyxrc;
1581 istringstream ss(argument);
1582 bool const success = lyxrc.read(ss) == 0;
1585 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1586 << "Unable to read lyxrc data"
1591 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1593 theApp()->resetGui();
1595 /// We force the redraw in any case because there might be
1596 /// some screen font changes.
1597 /// FIXME: only the current view will be updated. the Gui
1598 /// class is able to furnish the list of views.
1599 updateFlags = Update::Force;
1603 case LFUN_BOOKMARK_GOTO:
1604 // go to bookmark, open unopened file and switch to buffer if necessary
1605 gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1606 updateFlags = Update::FitCursor;
1609 case LFUN_BOOKMARK_CLEAR:
1610 theSession().bookmarks().clear();
1613 case LFUN_VC_COMMAND: {
1614 string flag = cmd.getArg(0);
1615 if (buffer && contains(flag, 'R') && !ensureBufferClean(view()))
1618 if (contains(flag, 'M'))
1619 if (!Alert::askForText(message, _("LyX VC: Log Message")))
1622 string path = cmd.getArg(1);
1623 if (contains(path, "$$p") && buffer)
1624 path = subst(path, "$$p", buffer->filePath());
1625 LYXERR(Debug::LYXVC, "Directory: " << path);
1627 if (!pp.isReadableDirectory()) {
1628 lyxerr << _("Directory is not accessible.") << endl;
1631 support::PathChanger p(pp);
1633 string command = cmd.getArg(2);
1634 if (command.empty())
1637 command = subst(command, "$$i", buffer->absFileName());
1638 command = subst(command, "$$p", buffer->filePath());
1640 command = subst(command, "$$m", to_utf8(message));
1641 LYXERR(Debug::LYXVC, "Command: " << command);
1643 one.startscript(Systemcall::Wait, command);
1647 if (contains(flag, 'I'))
1648 buffer->markDirty();
1649 if (contains(flag, 'R'))
1656 LASSERT(theApp(), /**/);
1657 // Let the frontend dispatch its own actions.
1658 if (theApp()->dispatch(cmd))
1659 // Nothing more to do.
1662 // Everything below is only for active lyx_view_
1666 // Start an undo group. This may be needed for
1667 // some stuff like inset-apply on labels.
1668 if (theBufferList().isLoaded(buffer))
1669 buffer->undo().beginUndoGroup();
1671 // Let the current LyXView dispatch its own actions.
1672 if (lyx_view_->dispatch(cmd)) {
1673 if (lyx_view_->view()) {
1674 updateFlags = lyx_view_->view()->cursor().result().update();
1675 if (theBufferList().isLoaded(buffer))
1676 buffer->undo().endUndoGroup();
1681 LASSERT(lyx_view_->view(), /**/);
1683 // Let the current BufferView dispatch its own actions.
1684 if (view()->dispatch(cmd)) {
1685 // The BufferView took care of its own updates if needed.
1686 updateFlags = Update::None;
1687 if (theBufferList().isLoaded(buffer))
1688 buffer->undo().endUndoGroup();
1692 // OK, so try the Buffer itself
1694 view()->buffer().dispatch(cmd, dr);
1695 if (dr.dispatched()) {
1696 updateFlags = dr.update();
1700 // Is this a function that acts on inset at point?
1701 Inset * inset = view()->cursor().nextInset();
1702 if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
1704 view()->cursor().result().dispatched(true);
1705 view()->cursor().result().update(Update::FitCursor | Update::Force);
1706 FuncRequest tmpcmd = cmd;
1707 inset->dispatch(view()->cursor(), tmpcmd);
1708 if (view()->cursor().result().dispatched()) {
1709 updateFlags = view()->cursor().result().update();
1714 // Let the current Cursor dispatch its own actions.
1715 Cursor old = view()->cursor();
1716 view()->cursor().getPos(cursorPosBeforeDispatchX_,
1717 cursorPosBeforeDispatchY_);
1718 view()->cursor().dispatch(cmd);
1720 // notify insets we just left
1721 if (view()->cursor() != old) {
1723 bool badcursor = notifyCursorLeavesOrEnters(old, view()->cursor());
1725 view()->cursor().fixIfBroken();
1728 if (theBufferList().isLoaded(buffer))
1729 buffer->undo().endUndoGroup();
1731 // update completion. We do it here and not in
1732 // processKeySym to avoid another redraw just for a
1733 // changed inline completion
1734 if (cmd.origin == FuncRequest::KEYBOARD) {
1735 if (cmd.action == LFUN_SELF_INSERT
1736 || (cmd.action == LFUN_ERT_INSERT && view()->cursor().inMathed()))
1737 lyx_view_->updateCompletion(view()->cursor(), true, true);
1738 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
1739 lyx_view_->updateCompletion(view()->cursor(), false, true);
1741 lyx_view_->updateCompletion(view()->cursor(), false, false);
1744 updateFlags = view()->cursor().result().update();
1747 // if we executed a mutating lfun, mark the buffer as dirty
1748 if (theBufferList().isLoaded(buffer) && flag.enabled()
1749 && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1750 && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1751 buffer->markDirty();
1753 if (lyx_view_ && lyx_view_->buffer()) {
1754 // BufferView::update() updates the ViewMetricsInfo and
1755 // also initializes the position cache for all insets in
1756 // (at least partially) visible top-level paragraphs.
1757 // We will redraw the screen only if needed.
1758 view()->processUpdateFlags(updateFlags);
1760 // Do we have a selection?
1761 theSelection().haveSelection(view()->cursor().selection());
1764 lyx_view_->restartCursor();
1768 // Some messages may already be translated, so we cannot use _()
1769 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1774 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1776 const bool verbose = (cmd.origin == FuncRequest::MENU
1777 || cmd.origin == FuncRequest::TOOLBAR
1778 || cmd.origin == FuncRequest::COMMANDBUFFER);
1780 if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1781 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
1783 lyx_view_->message(msg);
1787 docstring dispatch_msg = msg;
1788 if (!dispatch_msg.empty())
1789 dispatch_msg += ' ';
1791 docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1793 bool argsadded = false;
1795 if (!cmd.argument().empty()) {
1796 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1797 comname += ' ' + cmd.argument();
1802 docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
1804 if (!shortcuts.empty())
1805 comname += ": " + shortcuts;
1806 else if (!argsadded && !cmd.argument().empty())
1807 comname += ' ' + cmd.argument();
1809 if (!comname.empty()) {
1810 comname = rtrim(comname);
1811 dispatch_msg += '(' + rtrim(comname) + ')';
1814 LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
1815 if (!dispatch_msg.empty())
1816 lyx_view_->message(dispatch_msg);
1820 void LyXFunc::reloadBuffer()
1822 FileName filename = lyx_view_->buffer()->fileName();
1823 // The user has already confirmed that the changes, if any, should
1824 // be discarded. So we just release the Buffer and don't call closeBuffer();
1825 theBufferList().release(lyx_view_->buffer());
1826 // if the lyx_view_ has been destroyed, create a new one
1828 theApp()->dispatch(FuncRequest(LFUN_WINDOW_NEW));
1829 Buffer * buf = lyx_view_->loadDocument(filename);
1830 docstring const disp_fn = makeDisplayPath(filename.absFilename());
1833 buf->updateLabels();
1834 lyx_view_->setBuffer(buf);
1835 buf->errors("Parse");
1836 str = bformat(_("Document %1$s reloaded."), disp_fn);
1838 str = bformat(_("Could not reload document %1$s"), disp_fn);
1840 lyx_view_->message(str);
1843 // Each "lyx_view_" should have it's own message method. lyxview and
1844 // the minibuffer would use the minibuffer, but lyxserver would
1845 // send an ERROR signal to its client. Alejandro 970603
1846 // This function is bit problematic when it comes to NLS, to make the
1847 // lyx servers client be language indepenent we must not translate
1848 // strings sent to this func.
1849 void LyXFunc::setErrorMessage(docstring const & m) const
1851 dispatch_buffer = m;
1856 void LyXFunc::setMessage(docstring const & m) const
1858 dispatch_buffer = m;
1862 docstring LyXFunc::viewStatusMessage()
1864 // When meta-fake key is pressed, show the key sequence so far + "M-".
1866 return keyseq.print(KeySequence::ForGui) + "M-";
1868 // Else, when a non-complete key sequence is pressed,
1869 // show the available options.
1870 if (keyseq.length() > 0 && !keyseq.deleted())
1871 return keyseq.printOptions(true);
1873 LASSERT(lyx_view_, /**/);
1874 if (!lyx_view_->buffer())
1875 return _("Welcome to LyX!");
1877 return view()->cursor().currentState();
1881 BufferView * LyXFunc::view() const
1883 LASSERT(lyx_view_, /**/);
1884 return lyx_view_->view();
1888 bool LyXFunc::wasMetaKey() const
1890 return (meta_fake_bit != NoModifier);
1894 void LyXFunc::updateLayout(DocumentClass const * const oldlayout, Buffer * buf)
1896 lyx_view_->message(_("Converting document to new document class..."));
1898 StableDocIterator backcur(view()->cursor());
1899 ErrorList & el = buf->errorList("Class Switch");
1900 cap::switchBetweenClasses(
1901 oldlayout, buf->params().documentClassPtr(),
1902 static_cast<InsetText &>(buf->inset()), el);
1904 view()->setCursor(backcur.asDocIterator(buf));
1906 buf->errors("Class Switch");
1907 buf->updateLabels();
1913 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1915 // Why the switch you might ask. It is a trick to ensure that all
1916 // the elements in the LyXRCTags enum is handled. As you can see
1917 // there are no breaks at all. So it is just a huge fall-through.
1918 // The nice thing is that we will get a warning from the compiler
1919 // if we forget an element.
1920 LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1922 case LyXRC::RC_ACCEPT_COMPOUND:
1923 case LyXRC::RC_ALT_LANG:
1924 case LyXRC::RC_PLAINTEXT_LINELEN:
1925 case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1926 case LyXRC::RC_AUTOCORRECTION_MATH:
1927 case LyXRC::RC_AUTOREGIONDELETE:
1928 case LyXRC::RC_AUTORESET_OPTIONS:
1929 case LyXRC::RC_AUTOSAVE:
1930 case LyXRC::RC_AUTO_NUMBER:
1931 case LyXRC::RC_BACKUPDIR_PATH:
1932 case LyXRC::RC_BIBTEX_ALTERNATIVES:
1933 case LyXRC::RC_BIBTEX_COMMAND:
1934 case LyXRC::RC_BINDFILE:
1935 case LyXRC::RC_CHECKLASTFILES:
1936 case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1937 case LyXRC::RC_COMPLETION_INLINE_DELAY:
1938 case LyXRC::RC_COMPLETION_INLINE_DOTS:
1939 case LyXRC::RC_COMPLETION_INLINE_MATH:
1940 case LyXRC::RC_COMPLETION_INLINE_TEXT:
1941 case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1942 case LyXRC::RC_COMPLETION_POPUP_DELAY:
1943 case LyXRC::RC_COMPLETION_POPUP_MATH:
1944 case LyXRC::RC_COMPLETION_POPUP_TEXT:
1945 case LyXRC::RC_USELASTFILEPOS:
1946 case LyXRC::RC_LOADSESSION:
1947 case LyXRC::RC_CHKTEX_COMMAND:
1948 case LyXRC::RC_CONVERTER:
1949 case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1950 case LyXRC::RC_COPIER:
1951 case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1952 case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1953 case LyXRC::RC_DATE_INSERT_FORMAT:
1954 case LyXRC::RC_DEFAULT_LANGUAGE:
1955 case LyXRC::RC_GUI_LANGUAGE:
1956 case LyXRC::RC_DEFAULT_PAPERSIZE:
1957 case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1958 case LyXRC::RC_DEFFILE:
1959 case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1960 case LyXRC::RC_DISPLAY_GRAPHICS:
1961 case LyXRC::RC_DOCUMENTPATH:
1962 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1963 FileName path(lyxrc_new.document_path);
1964 if (path.exists() && path.isDirectory())
1965 package().document_dir() = FileName(lyxrc.document_path);
1967 case LyXRC::RC_ESC_CHARS:
1968 case LyXRC::RC_EXAMPLEPATH:
1969 case LyXRC::RC_FONT_ENCODING:
1970 case LyXRC::RC_FORMAT:
1971 case LyXRC::RC_GROUP_LAYOUTS:
1972 case LyXRC::RC_INDEX_ALTERNATIVES:
1973 case LyXRC::RC_INDEX_COMMAND:
1974 case LyXRC::RC_JBIBTEX_COMMAND:
1975 case LyXRC::RC_JINDEX_COMMAND:
1976 case LyXRC::RC_NOMENCL_COMMAND:
1977 case LyXRC::RC_INPUT:
1978 case LyXRC::RC_KBMAP:
1979 case LyXRC::RC_KBMAP_PRIMARY:
1980 case LyXRC::RC_KBMAP_SECONDARY:
1981 case LyXRC::RC_LABEL_INIT_LENGTH:
1982 case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1983 case LyXRC::RC_LANGUAGE_AUTO_END:
1984 case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1985 case LyXRC::RC_LANGUAGE_COMMAND_END:
1986 case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1987 case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1988 case LyXRC::RC_LANGUAGE_PACKAGE:
1989 case LyXRC::RC_LANGUAGE_USE_BABEL:
1990 case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1991 case LyXRC::RC_MACRO_EDIT_STYLE:
1992 case LyXRC::RC_MAKE_BACKUP:
1993 case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1994 case LyXRC::RC_MOUSE_WHEEL_SPEED:
1995 case LyXRC::RC_NUMLASTFILES:
1996 case LyXRC::RC_PATH_PREFIX:
1997 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1998 prependEnvPath("PATH", lyxrc.path_prefix);
2000 case LyXRC::RC_PERS_DICT:
2001 case LyXRC::RC_PREVIEW:
2002 case LyXRC::RC_PREVIEW_HASHED_LABELS:
2003 case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2004 case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2005 case LyXRC::RC_PRINTCOPIESFLAG:
2006 case LyXRC::RC_PRINTER:
2007 case LyXRC::RC_PRINTEVENPAGEFLAG:
2008 case LyXRC::RC_PRINTEXSTRAOPTIONS:
2009 case LyXRC::RC_PRINTFILEEXTENSION:
2010 case LyXRC::RC_PRINTLANDSCAPEFLAG:
2011 case LyXRC::RC_PRINTODDPAGEFLAG:
2012 case LyXRC::RC_PRINTPAGERANGEFLAG:
2013 case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2014 case LyXRC::RC_PRINTPAPERFLAG:
2015 case LyXRC::RC_PRINTREVERSEFLAG:
2016 case LyXRC::RC_PRINTSPOOL_COMMAND:
2017 case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2018 case LyXRC::RC_PRINTTOFILE:
2019 case LyXRC::RC_PRINTTOPRINTER:
2020 case LyXRC::RC_PRINT_ADAPTOUTPUT:
2021 case LyXRC::RC_PRINT_COMMAND:
2022 case LyXRC::RC_RTL_SUPPORT:
2023 case LyXRC::RC_SCREEN_DPI:
2024 case LyXRC::RC_SCREEN_FONT_ROMAN:
2025 case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2026 case LyXRC::RC_SCREEN_FONT_SANS:
2027 case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2028 case LyXRC::RC_SCREEN_FONT_SCALABLE:
2029 case LyXRC::RC_SCREEN_FONT_SIZES:
2030 case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2031 case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2032 case LyXRC::RC_GEOMETRY_SESSION:
2033 case LyXRC::RC_SCREEN_ZOOM:
2034 case LyXRC::RC_SERVERPIPE:
2035 case LyXRC::RC_SET_COLOR:
2036 case LyXRC::RC_SHOW_BANNER:
2037 case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
2038 case LyXRC::RC_SPELL_COMMAND:
2039 case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
2040 case LyXRC::RC_SPLITINDEX_COMMAND:
2041 case LyXRC::RC_TEMPDIRPATH:
2042 case LyXRC::RC_TEMPLATEPATH:
2043 case LyXRC::RC_TEX_ALLOWS_SPACES:
2044 case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2045 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2046 os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2048 case LyXRC::RC_THESAURUSDIRPATH:
2049 case LyXRC::RC_UIFILE:
2050 case LyXRC::RC_USER_EMAIL:
2051 case LyXRC::RC_USER_NAME:
2052 case LyXRC::RC_USETEMPDIR:
2053 case LyXRC::RC_USE_ALT_LANG:
2054 case LyXRC::RC_USE_CONVERTER_CACHE:
2055 case LyXRC::RC_USE_ESC_CHARS:
2056 case LyXRC::RC_USE_INP_ENC:
2057 case LyXRC::RC_USE_PERS_DICT:
2058 case LyXRC::RC_USE_TOOLTIP:
2059 case LyXRC::RC_USE_PIXMAP_CACHE:
2060 case LyXRC::RC_USE_SPELL_LIB:
2061 case LyXRC::RC_VIEWDVI_PAPEROPTION:
2062 case LyXRC::RC_SORT_LAYOUTS:
2063 case LyXRC::RC_FULL_SCREEN_LIMIT:
2064 case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
2065 case LyXRC::RC_FULL_SCREEN_MENUBAR:
2066 case LyXRC::RC_FULL_SCREEN_TABBAR:
2067 case LyXRC::RC_FULL_SCREEN_TOOLBARS:
2068 case LyXRC::RC_FULL_SCREEN_WIDTH:
2069 case LyXRC::RC_VISUAL_CURSOR:
2070 case LyXRC::RC_VIEWER:
2071 case LyXRC::RC_LAST: